Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 6555373d

History | View | Annotate | Download (24.1 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Xen hypervisors
23

24
"""
25

    
26
import logging
27
from cStringIO import StringIO
28

    
29
from ganeti import constants
30
from ganeti import errors
31
from ganeti import utils
32
from ganeti.hypervisor import hv_base
33
from ganeti import netutils
34
from ganeti import objects
35

    
36

    
37
class XenHypervisor(hv_base.BaseHypervisor):
38
  """Xen generic hypervisor interface
39

40
  This is the Xen base class used for both Xen PVM and HVM. It contains
41
  all the functionality that is identical for both.
42

43
  """
44
  CAN_MIGRATE = True
45
  REBOOT_RETRY_COUNT = 60
46
  REBOOT_RETRY_INTERVAL = 10
47

    
48
  ANCILLARY_FILES = [
49
    "/etc/xen/xend-config.sxp",
50
    "/etc/xen/xl.conf",
51
    "/etc/xen/scripts/vif-bridge",
52
    ]
53

    
54
  @staticmethod
55
  def _ConfigFileName(instance_name):
56
    """Get the config file name for an instance.
57

58
    @param instance_name: instance name
59
    @type instance_name: str
60
    @return: fully qualified path to instance config file
61
    @rtype: str
62

63
    """
64
    return "/etc/xen/%s" % instance_name
65

    
66
  @classmethod
67
  def _WriteConfigFile(cls, instance, block_devices):
68
    """Write the Xen config file for the instance.
69

70
    """
71
    raise NotImplementedError
72

    
73
  @staticmethod
74
  def _WriteConfigFileStatic(instance_name, data):
75
    """Write the Xen config file for the instance.
76

77
    This version of the function just writes the config file from static data.
78

79
    """
80
    utils.WriteFile(XenHypervisor._ConfigFileName(instance_name), data=data)
81

    
82
  @staticmethod
83
  def _ReadConfigFile(instance_name):
84
    """Returns the contents of the instance config file.
85

86
    """
87
    try:
88
      file_content = utils.ReadFile(
89
                       XenHypervisor._ConfigFileName(instance_name))
90
    except EnvironmentError, err:
91
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
92
    return file_content
93

    
94
  @staticmethod
95
  def _RemoveConfigFile(instance_name):
96
    """Remove the xen configuration file.
97

98
    """
99
    utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
100

    
101
  @staticmethod
102
  def _RunXmList(xmlist_errors):
103
    """Helper function for L{_GetXMList} to run "xm list".
104

105
    """
106
    result = utils.RunCmd([constants.XEN_CMD, "list"])
107
    if result.failed:
108
      logging.error("xm list failed (%s): %s", result.fail_reason,
109
                    result.output)
110
      xmlist_errors.append(result)
111
      raise utils.RetryAgain()
112

    
113
    # skip over the heading
114
    return result.stdout.splitlines()[1:]
115

    
116
  @classmethod
117
  def _GetXMList(cls, include_node):
118
    """Return the list of running instances.
119

120
    If the include_node argument is True, then we return information
121
    for dom0 also, otherwise we filter that from the return value.
122

123
    @return: list of (name, id, memory, vcpus, state, time spent)
124

125
    """
126
    xmlist_errors = []
127
    try:
128
      lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
129
    except utils.RetryTimeout:
130
      if xmlist_errors:
131
        xmlist_result = xmlist_errors.pop()
132

    
133
        errmsg = ("xm list failed, timeout exceeded (%s): %s" %
134
                  (xmlist_result.fail_reason, xmlist_result.output))
135
      else:
136
        errmsg = "xm list failed"
137

    
138
      raise errors.HypervisorError(errmsg)
139

    
140
    result = []
141
    for line in lines:
142
      # The format of lines is:
143
      # Name      ID Mem(MiB) VCPUs State  Time(s)
144
      # Domain-0   0  3418     4 r-----    266.2
145
      data = line.split()
146
      if len(data) != 6:
147
        raise errors.HypervisorError("Can't parse output of xm list,"
148
                                     " line: %s" % line)
149
      try:
150
        data[1] = int(data[1])
151
        data[2] = int(data[2])
152
        data[3] = int(data[3])
153
        data[5] = float(data[5])
154
      except (TypeError, ValueError), err:
155
        raise errors.HypervisorError("Can't parse output of xm list,"
156
                                     " line: %s, error: %s" % (line, err))
157

    
158
      # skip the Domain-0 (optional)
159
      if include_node or data[0] != "Domain-0":
160
        result.append(data)
161

    
162
    return result
163

    
164
  def ListInstances(self):
165
    """Get the list of running instances.
166

167
    """
168
    xm_list = self._GetXMList(False)
169
    names = [info[0] for info in xm_list]
170
    return names
171

    
172
  def GetInstanceInfo(self, instance_name):
173
    """Get instance properties.
174

175
    @param instance_name: the instance name
176

177
    @return: tuple (name, id, memory, vcpus, stat, times)
178

179
    """
180
    xm_list = self._GetXMList(instance_name == "Domain-0")
181
    result = None
182
    for data in xm_list:
183
      if data[0] == instance_name:
184
        result = data
185
        break
186
    return result
187

    
188
  def GetAllInstancesInfo(self):
189
    """Get properties of all instances.
190

191
    @return: list of tuples (name, id, memory, vcpus, stat, times)
192

193
    """
194
    xm_list = self._GetXMList(False)
195
    return xm_list
196

    
197
  def StartInstance(self, instance, block_devices, startup_paused):
198
    """Start an instance.
199

200
    """
201
    self._WriteConfigFile(instance, block_devices)
202
    cmd = [constants.XEN_CMD, "create"]
203
    if startup_paused:
204
      cmd.extend(["-p"])
205
    cmd.extend([self._ConfigFileName(instance.name)])
206
    result = utils.RunCmd(cmd)
207

    
208
    if result.failed:
209
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
210
                                   (instance.name, result.fail_reason,
211
                                    result.output))
212

    
213
  def StopInstance(self, instance, force=False, retry=False, name=None):
214
    """Stop an instance.
215

216
    """
217
    if name is None:
218
      name = instance.name
219
    self._RemoveConfigFile(name)
220
    if force:
221
      command = [constants.XEN_CMD, "destroy", name]
222
    else:
223
      command = [constants.XEN_CMD, "shutdown", name]
224
    result = utils.RunCmd(command)
225

    
226
    if result.failed:
227
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
228
                                   (name, result.fail_reason, result.output))
229

    
230
  def RebootInstance(self, instance):
231
    """Reboot an instance.
232

233
    """
234
    ini_info = self.GetInstanceInfo(instance.name)
235

    
236
    if ini_info is None:
237
      raise errors.HypervisorError("Failed to reboot instance %s,"
238
                                   " not running" % instance.name)
239

    
240
    result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
241
    if result.failed:
242
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
243
                                   (instance.name, result.fail_reason,
244
                                    result.output))
245

    
246
    def _CheckInstance():
247
      new_info = self.GetInstanceInfo(instance.name)
248

    
249
      # check if the domain ID has changed or the run time has decreased
250
      if (new_info is not None and
251
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
252
        return
253

    
254
      raise utils.RetryAgain()
255

    
256
    try:
257
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
258
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
259
    except utils.RetryTimeout:
260
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
261
                                   " did not reboot in the expected interval" %
262
                                   (instance.name, ))
263

    
264
  def GetNodeInfo(self):
265
    """Return information about the node.
266

267
    @return: a dict with the following keys (memory values in MiB):
268
          - memory_total: the total memory size on the node
269
          - memory_free: the available memory on the node for instances
270
          - memory_dom0: the memory used by the node itself, if available
271
          - nr_cpus: total number of CPUs
272
          - nr_nodes: in a NUMA system, the number of domains
273
          - nr_sockets: the number of physical CPU sockets in the node
274
          - hv_version: the hypervisor version in the form (major, minor)
275

276
    """
277
    # note: in xen 3, memory has changed to total_memory
278
    result = utils.RunCmd([constants.XEN_CMD, "info"])
279
    if result.failed:
280
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
281
                    result.output)
282
      return None
283

    
284
    xmoutput = result.stdout.splitlines()
285
    result = {}
286
    cores_per_socket = threads_per_core = nr_cpus = None
287
    xen_major, xen_minor = None, None
288
    for line in xmoutput:
289
      splitfields = line.split(":", 1)
290

    
291
      if len(splitfields) > 1:
292
        key = splitfields[0].strip()
293
        val = splitfields[1].strip()
294
        if key == "memory" or key == "total_memory":
295
          result["memory_total"] = int(val)
296
        elif key == "free_memory":
297
          result["memory_free"] = int(val)
298
        elif key == "nr_cpus":
299
          nr_cpus = result["cpu_total"] = int(val)
300
        elif key == "nr_nodes":
301
          result["cpu_nodes"] = int(val)
302
        elif key == "cores_per_socket":
303
          cores_per_socket = int(val)
304
        elif key == "threads_per_core":
305
          threads_per_core = int(val)
306
        elif key == "xen_major":
307
          xen_major = int(val)
308
        elif key == "xen_minor":
309
          xen_minor = int(val)
310

    
311
    if (cores_per_socket is not None and
312
        threads_per_core is not None and nr_cpus is not None):
313
      result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
314

    
315
    dom0_info = self.GetInstanceInfo("Domain-0")
316
    if dom0_info is not None:
317
      result["memory_dom0"] = dom0_info[2]
318

    
319
    if not (xen_major is None or xen_minor is None):
320
      result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
321

    
322
    return result
323

    
324
  @classmethod
325
  def GetInstanceConsole(cls, instance, hvparams, beparams):
326
    """Return a command for connecting to the console of an instance.
327

328
    """
329
    return objects.InstanceConsole(instance=instance.name,
330
                                   kind=constants.CONS_SSH,
331
                                   host=instance.primary_node,
332
                                   user=constants.GANETI_RUNAS,
333
                                   command=[constants.XM_CONSOLE_WRAPPER,
334
                                            instance.name])
335

    
336
  def Verify(self):
337
    """Verify the hypervisor.
338

339
    For Xen, this verifies that the xend process is running.
340

341
    """
342
    result = utils.RunCmd([constants.XEN_CMD, "info"])
343
    if result.failed:
344
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
345

    
346
  @staticmethod
347
  def _GetConfigFileDiskData(block_devices, blockdev_prefix):
348
    """Get disk directive for xen config file.
349

350
    This method builds the xen config disk directive according to the
351
    given disk_template and block_devices.
352

353
    @param block_devices: list of tuples (cfdev, rldev):
354
        - cfdev: dict containing ganeti config disk part
355
        - rldev: ganeti.bdev.BlockDev object
356
    @param blockdev_prefix: a string containing blockdevice prefix,
357
                            e.g. "sd" for /dev/sda
358

359
    @return: string containing disk directive for xen instance config file
360

361
    """
362
    FILE_DRIVER_MAP = {
363
      constants.FD_LOOP: "file",
364
      constants.FD_BLKTAP: "tap:aio",
365
      }
366
    disk_data = []
367
    if len(block_devices) > 24:
368
      # 'z' - 'a' = 24
369
      raise errors.HypervisorError("Too many disks")
370
    namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)]
371
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
372
      if cfdev.mode == constants.DISK_RDWR:
373
        mode = "w"
374
      else:
375
        mode = "r"
376
      if cfdev.dev_type == constants.LD_FILE:
377
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
378
                                  dev_path, sd_name, mode)
379
      else:
380
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
381
      disk_data.append(line)
382

    
383
    return disk_data
384

    
385
  def MigrationInfo(self, instance):
386
    """Get instance information to perform a migration.
387

388
    @type instance: L{objects.Instance}
389
    @param instance: instance to be migrated
390
    @rtype: string
391
    @return: content of the xen config file
392

393
    """
394
    return self._ReadConfigFile(instance.name)
395

    
396
  def AcceptInstance(self, instance, info, target):
397
    """Prepare to accept an instance.
398

399
    @type instance: L{objects.Instance}
400
    @param instance: instance to be accepted
401
    @type info: string
402
    @param info: content of the xen config file on the source node
403
    @type target: string
404
    @param target: target host (usually ip), on this node
405

406
    """
407
    pass
408

    
409
  def FinalizeMigration(self, instance, info, success):
410
    """Finalize an instance migration.
411

412
    After a successful migration we write the xen config file.
413
    We do nothing on a failure, as we did not change anything at accept time.
414

415
    @type instance: L{objects.Instance}
416
    @param instance: instance whose migration is being finalized
417
    @type info: string
418
    @param info: content of the xen config file on the source node
419
    @type success: boolean
420
    @param success: whether the migration was a success or a failure
421

422
    """
423
    if success:
424
      self._WriteConfigFileStatic(instance.name, info)
425

    
426
  def MigrateInstance(self, instance, target, live):
427
    """Migrate an instance to a target node.
428

429
    The migration will not be attempted if the instance is not
430
    currently running.
431

432
    @type instance: L{objects.Instance}
433
    @param instance: the instance to be migrated
434
    @type target: string
435
    @param target: ip address of the target node
436
    @type live: boolean
437
    @param live: perform a live migration
438

439
    """
440
    if self.GetInstanceInfo(instance.name) is None:
441
      raise errors.HypervisorError("Instance not running, cannot migrate")
442

    
443
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
444

    
445
    if not netutils.TcpPing(target, port, live_port_needed=True):
446
      raise errors.HypervisorError("Remote host %s not listening on port"
447
                                   " %s, cannot migrate" % (target, port))
448

    
449
    # FIXME: migrate must be upgraded for transitioning to "xl" (xen 4.1).
450
    #  -l doesn't exist anymore
451
    #  -p doesn't exist anymore
452
    #  -C config_file must be passed
453
    #  ssh must recognize the key of the target host for the migration
454
    args = [constants.XEN_CMD, "migrate", "-p", "%d" % port]
455
    if live:
456
      args.append("-l")
457
    args.extend([instance.name, target])
458
    result = utils.RunCmd(args)
459
    if result.failed:
460
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
461
                                   (instance.name, result.output))
462
    # remove old xen file after migration succeeded
463
    try:
464
      self._RemoveConfigFile(instance.name)
465
    except EnvironmentError:
466
      logging.exception("Failure while removing instance config file")
467

    
468
  @classmethod
469
  def PowercycleNode(cls):
470
    """Xen-specific powercycle.
471

472
    This first does a Linux reboot (which triggers automatically a Xen
473
    reboot), and if that fails it tries to do a Xen reboot. The reason
474
    we don't try a Xen reboot first is that the xen reboot launches an
475
    external command which connects to the Xen hypervisor, and that
476
    won't work in case the root filesystem is broken and/or the xend
477
    daemon is not working.
478

479
    """
480
    try:
481
      cls.LinuxPowercycle()
482
    finally:
483
      utils.RunCmd([constants.XEN_CMD, "debug", "R"])
484

    
485

    
486
class XenPvmHypervisor(XenHypervisor):
487
  """Xen PVM hypervisor interface"""
488

    
489
  PARAMETERS = {
490
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
491
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
492
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
493
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
494
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
495
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
496
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
497
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
498
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
499
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
500
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
501
    constants.HV_REBOOT_BEHAVIOR:
502
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
503
    }
504

    
505
  @classmethod
506
  def _WriteConfigFile(cls, instance, block_devices):
507
    """Write the Xen config file for the instance.
508

509
    """
510
    hvp = instance.hvparams
511
    config = StringIO()
512
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
513

    
514
    # if bootloader is True, use bootloader instead of kernel and ramdisk
515
    # parameters.
516
    if hvp[constants.HV_USE_BOOTLOADER]:
517
      # bootloader handling
518
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
519
      if bootloader_path:
520
        config.write("bootloader = '%s'\n" % bootloader_path)
521
      else:
522
        raise errors.HypervisorError("Bootloader enabled, but missing"
523
                                     " bootloader path")
524

    
525
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
526
      if bootloader_args:
527
        config.write("bootargs = '%s'\n" % bootloader_args)
528
    else:
529
      # kernel handling
530
      kpath = hvp[constants.HV_KERNEL_PATH]
531
      config.write("kernel = '%s'\n" % kpath)
532

    
533
      # initrd handling
534
      initrd_path = hvp[constants.HV_INITRD_PATH]
535
      if initrd_path:
536
        config.write("ramdisk = '%s'\n" % initrd_path)
537

    
538
    # rest of the settings
539
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
540
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
541
    config.write("name = '%s'\n" % instance.name)
542

    
543
    vif_data = []
544
    for nic in instance.nics:
545
      nic_str = "mac=%s" % (nic.mac)
546
      ip = getattr(nic, "ip", None)
547
      if ip is not None:
548
        nic_str += ", ip=%s" % ip
549
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
550
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
551
      vif_data.append("'%s'" % nic_str)
552

    
553
    disk_data = cls._GetConfigFileDiskData(block_devices,
554
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
555

    
556
    config.write("vif = [%s]\n" % ",".join(vif_data))
557
    config.write("disk = [%s]\n" % ",".join(disk_data))
558

    
559
    if hvp[constants.HV_ROOT_PATH]:
560
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
561
    config.write("on_poweroff = 'destroy'\n")
562
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
563
      config.write("on_reboot = 'restart'\n")
564
    else:
565
      config.write("on_reboot = 'destroy'\n")
566
    config.write("on_crash = 'restart'\n")
567
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
568
    # just in case it exists
569
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
570
    try:
571
      utils.WriteFile(cls._ConfigFileName(instance.name),
572
                      data=config.getvalue())
573
    except EnvironmentError, err:
574
      raise errors.HypervisorError("Cannot write Xen instance confile"
575
                                   " file %s: %s" %
576
                                   (cls._ConfigFileName(instance.name), err))
577

    
578
    return True
579

    
580

    
581
class XenHvmHypervisor(XenHypervisor):
582
  """Xen HVM hypervisor interface"""
583

    
584
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
585
    constants.VNC_PASSWORD_FILE,
586
    ]
587

    
588
  PARAMETERS = {
589
    constants.HV_ACPI: hv_base.NO_CHECK,
590
    constants.HV_BOOT_ORDER: (True, ) +
591
      (lambda x: x and len(x.strip("acdn")) == 0,
592
       "Invalid boot order specified, must be one or more of [acdn]",
593
       None, None),
594
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
595
    constants.HV_DISK_TYPE:
596
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
597
    constants.HV_NIC_TYPE:
598
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
599
    constants.HV_PAE: hv_base.NO_CHECK,
600
    constants.HV_VNC_BIND_ADDRESS:
601
      (False, netutils.IP4Address.IsValid,
602
       "VNC bind address is not a valid IP address", None, None),
603
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
604
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
605
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
606
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
607
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
608
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
609
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
610
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
611
    constants.HV_REBOOT_BEHAVIOR:
612
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
613
    }
614

    
615
  @classmethod
616
  def _WriteConfigFile(cls, instance, block_devices):
617
    """Create a Xen 3.1 HVM config file.
618

619
    """
620
    hvp = instance.hvparams
621

    
622
    config = StringIO()
623
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
624

    
625
    # kernel handling
626
    kpath = hvp[constants.HV_KERNEL_PATH]
627
    config.write("kernel = '%s'\n" % kpath)
628

    
629
    config.write("builder = 'hvm'\n")
630
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
631
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
632
    config.write("name = '%s'\n" % instance.name)
633
    if hvp[constants.HV_PAE]:
634
      config.write("pae = 1\n")
635
    else:
636
      config.write("pae = 0\n")
637
    if hvp[constants.HV_ACPI]:
638
      config.write("acpi = 1\n")
639
    else:
640
      config.write("acpi = 0\n")
641
    config.write("apic = 1\n")
642
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
643
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
644
    config.write("sdl = 0\n")
645
    config.write("usb = 1\n")
646
    config.write("usbdevice = 'tablet'\n")
647
    config.write("vnc = 1\n")
648
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
649
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
650
    else:
651
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
652

    
653
    if instance.network_port > constants.VNC_BASE_PORT:
654
      display = instance.network_port - constants.VNC_BASE_PORT
655
      config.write("vncdisplay = %s\n" % display)
656
      config.write("vncunused = 0\n")
657
    else:
658
      config.write("# vncdisplay = 1\n")
659
      config.write("vncunused = 1\n")
660

    
661
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
662
    try:
663
      password = utils.ReadFile(vnc_pwd_file)
664
    except EnvironmentError, err:
665
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
666
                                   (vnc_pwd_file, err))
667

    
668
    config.write("vncpasswd = '%s'\n" % password.rstrip())
669

    
670
    config.write("serial = 'pty'\n")
671
    if hvp[constants.HV_USE_LOCALTIME]:
672
      config.write("localtime = 1\n")
673

    
674
    vif_data = []
675
    nic_type = hvp[constants.HV_NIC_TYPE]
676
    if nic_type is None:
677
      # ensure old instances don't change
678
      nic_type_str = ", type=ioemu"
679
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
680
      nic_type_str = ", type=paravirtualized"
681
    else:
682
      nic_type_str = ", model=%s, type=ioemu" % nic_type
683
    for nic in instance.nics:
684
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
685
      ip = getattr(nic, "ip", None)
686
      if ip is not None:
687
        nic_str += ", ip=%s" % ip
688
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
689
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
690
      vif_data.append("'%s'" % nic_str)
691

    
692
    config.write("vif = [%s]\n" % ",".join(vif_data))
693

    
694
    disk_data = cls._GetConfigFileDiskData(block_devices,
695
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
696

    
697
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
698
    if iso_path:
699
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
700
      disk_data.append(iso)
701

    
702
    config.write("disk = [%s]\n" % (",".join(disk_data)))
703

    
704
    config.write("on_poweroff = 'destroy'\n")
705
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
706
      config.write("on_reboot = 'restart'\n")
707
    else:
708
      config.write("on_reboot = 'destroy'\n")
709
    config.write("on_crash = 'restart'\n")
710
    # just in case it exists
711
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
712
    try:
713
      utils.WriteFile(cls._ConfigFileName(instance.name),
714
                      data=config.getvalue())
715
    except EnvironmentError, err:
716
      raise errors.HypervisorError("Cannot write Xen instance confile"
717
                                   " file %s: %s" %
718
                                   (cls._ConfigFileName(instance.name), err))
719

    
720
    return True