Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 7238edb5

History | View | Annotate | Download (22.4 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/scripts/vif-bridge',
51
    ]
52

    
53
  @classmethod
54
  def _WriteConfigFile(cls, instance, block_devices):
55
    """Write the Xen config file for the instance.
56

57
    """
58
    raise NotImplementedError
59

    
60
  @staticmethod
61
  def _WriteConfigFileStatic(instance_name, data):
62
    """Write the Xen config file for the instance.
63

64
    This version of the function just writes the config file from static data.
65

66
    """
67
    utils.WriteFile("/etc/xen/%s" % instance_name, data=data)
68

    
69
  @staticmethod
70
  def _ReadConfigFile(instance_name):
71
    """Returns the contents of the instance config file.
72

73
    """
74
    try:
75
      file_content = utils.ReadFile("/etc/xen/%s" % instance_name)
76
    except EnvironmentError, err:
77
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
78
    return file_content
79

    
80
  @staticmethod
81
  def _RemoveConfigFile(instance_name):
82
    """Remove the xen configuration file.
83

84
    """
85
    utils.RemoveFile("/etc/xen/%s" % instance_name)
86

    
87
  @staticmethod
88
  def _RunXmList(xmlist_errors):
89
    """Helper function for L{_GetXMList} to run "xm list".
90

91
    """
92
    result = utils.RunCmd(["xm", "list"])
93
    if result.failed:
94
      logging.error("xm list failed (%s): %s", result.fail_reason,
95
                    result.output)
96
      xmlist_errors.append(result)
97
      raise utils.RetryAgain()
98

    
99
    # skip over the heading
100
    return result.stdout.splitlines()[1:]
101

    
102
  @classmethod
103
  def _GetXMList(cls, include_node):
104
    """Return the list of running instances.
105

106
    If the include_node argument is True, then we return information
107
    for dom0 also, otherwise we filter that from the return value.
108

109
    @return: list of (name, id, memory, vcpus, state, time spent)
110

111
    """
112
    xmlist_errors = []
113
    try:
114
      lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
115
    except utils.RetryTimeout:
116
      if xmlist_errors:
117
        xmlist_result = xmlist_errors.pop()
118

    
119
        errmsg = ("xm list failed, timeout exceeded (%s): %s" %
120
                  (xmlist_result.fail_reason, xmlist_result.output))
121
      else:
122
        errmsg = "xm list failed"
123

    
124
      raise errors.HypervisorError(errmsg)
125

    
126
    result = []
127
    for line in lines:
128
      # The format of lines is:
129
      # Name      ID Mem(MiB) VCPUs State  Time(s)
130
      # Domain-0   0  3418     4 r-----    266.2
131
      data = line.split()
132
      if len(data) != 6:
133
        raise errors.HypervisorError("Can't parse output of xm list,"
134
                                     " line: %s" % line)
135
      try:
136
        data[1] = int(data[1])
137
        data[2] = int(data[2])
138
        data[3] = int(data[3])
139
        data[5] = float(data[5])
140
      except (TypeError, ValueError), err:
141
        raise errors.HypervisorError("Can't parse output of xm list,"
142
                                     " line: %s, error: %s" % (line, err))
143

    
144
      # skip the Domain-0 (optional)
145
      if include_node or data[0] != 'Domain-0':
146
        result.append(data)
147

    
148
    return result
149

    
150
  def ListInstances(self):
151
    """Get the list of running instances.
152

153
    """
154
    xm_list = self._GetXMList(False)
155
    names = [info[0] for info in xm_list]
156
    return names
157

    
158
  def GetInstanceInfo(self, instance_name):
159
    """Get instance properties.
160

161
    @param instance_name: the instance name
162

163
    @return: tuple (name, id, memory, vcpus, stat, times)
164

165
    """
166
    xm_list = self._GetXMList(instance_name=="Domain-0")
167
    result = None
168
    for data in xm_list:
169
      if data[0] == instance_name:
170
        result = data
171
        break
172
    return result
173

    
174
  def GetAllInstancesInfo(self):
175
    """Get properties of all instances.
176

177
    @return: list of tuples (name, id, memory, vcpus, stat, times)
178

179
    """
180
    xm_list = self._GetXMList(False)
181
    return xm_list
182

    
183
  def StartInstance(self, instance, block_devices, startup_paused):
184
    """Start an instance.
185

186
    """
187
    self._WriteConfigFile(instance, block_devices)
188
    cmd = ["xm", "create"]
189
    if startup_paused:
190
      cmd.extend(["--paused"])
191
    cmd.extend([instance.name])
192
    result = utils.RunCmd(cmd)
193

    
194
    if result.failed:
195
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
196
                                   (instance.name, result.fail_reason,
197
                                    result.output))
198

    
199
  def StopInstance(self, instance, force=False, retry=False, name=None):
200
    """Stop an instance.
201

202
    """
203
    if name is None:
204
      name = instance.name
205
    self._RemoveConfigFile(name)
206
    if force:
207
      command = ["xm", "destroy", name]
208
    else:
209
      command = ["xm", "shutdown", name]
210
    result = utils.RunCmd(command)
211

    
212
    if result.failed:
213
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
214
                                   (name, result.fail_reason, result.output))
215

    
216
  def RebootInstance(self, instance):
217
    """Reboot an instance.
218

219
    """
220
    ini_info = self.GetInstanceInfo(instance.name)
221

    
222
    if ini_info is None:
223
      raise errors.HypervisorError("Failed to reboot instance %s,"
224
                                   " not running" % instance.name)
225

    
226
    result = utils.RunCmd(["xm", "reboot", instance.name])
227
    if result.failed:
228
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
229
                                   (instance.name, result.fail_reason,
230
                                    result.output))
231

    
232
    def _CheckInstance():
233
      new_info = self.GetInstanceInfo(instance.name)
234

    
235
      # check if the domain ID has changed or the run time has decreased
236
      if (new_info is not None and
237
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
238
        return
239

    
240
      raise utils.RetryAgain()
241

    
242
    try:
243
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
244
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
245
    except utils.RetryTimeout:
246
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
247
                                   " did not reboot in the expected interval" %
248
                                   (instance.name, ))
249

    
250
  def GetNodeInfo(self):
251
    """Return information about the node.
252

253
    @return: a dict with the following keys (memory values in MiB):
254
          - memory_total: the total memory size on the node
255
          - memory_free: the available memory on the node for instances
256
          - memory_dom0: the memory used by the node itself, if available
257
          - nr_cpus: total number of CPUs
258
          - nr_nodes: in a NUMA system, the number of domains
259
          - nr_sockets: the number of physical CPU sockets in the node
260

261
    """
262
    # note: in xen 3, memory has changed to total_memory
263
    result = utils.RunCmd(["xm", "info"])
264
    if result.failed:
265
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
266
                    result.output)
267
      return None
268

    
269
    xmoutput = result.stdout.splitlines()
270
    result = {}
271
    cores_per_socket = threads_per_core = nr_cpus = None
272
    for line in xmoutput:
273
      splitfields = line.split(":", 1)
274

    
275
      if len(splitfields) > 1:
276
        key = splitfields[0].strip()
277
        val = splitfields[1].strip()
278
        if key == 'memory' or key == 'total_memory':
279
          result['memory_total'] = int(val)
280
        elif key == 'free_memory':
281
          result['memory_free'] = int(val)
282
        elif key == 'nr_cpus':
283
          nr_cpus = result['cpu_total'] = int(val)
284
        elif key == 'nr_nodes':
285
          result['cpu_nodes'] = int(val)
286
        elif key == 'cores_per_socket':
287
          cores_per_socket = int(val)
288
        elif key == 'threads_per_core':
289
          threads_per_core = int(val)
290

    
291
    if (cores_per_socket is not None and
292
        threads_per_core is not None and nr_cpus is not None):
293
      result['cpu_sockets'] = nr_cpus / (cores_per_socket * threads_per_core)
294

    
295
    dom0_info = self.GetInstanceInfo("Domain-0")
296
    if dom0_info is not None:
297
      result['memory_dom0'] = dom0_info[2]
298

    
299
    return result
300

    
301
  @classmethod
302
  def GetInstanceConsole(cls, instance, hvparams, beparams):
303
    """Return a command for connecting to the console of an instance.
304

305
    """
306
    return objects.InstanceConsole(instance=instance.name,
307
                                   kind=constants.CONS_SSH,
308
                                   host=instance.primary_node,
309
                                   user=constants.GANETI_RUNAS,
310
                                   command=["xm", "console", instance.name])
311

    
312
  def Verify(self):
313
    """Verify the hypervisor.
314

315
    For Xen, this verifies that the xend process is running.
316

317
    """
318
    result = utils.RunCmd(["xm", "info"])
319
    if result.failed:
320
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
321

    
322
  @staticmethod
323
  def _GetConfigFileDiskData(block_devices, blockdev_prefix):
324
    """Get disk directive for xen config file.
325

326
    This method builds the xen config disk directive according to the
327
    given disk_template and block_devices.
328

329
    @param block_devices: list of tuples (cfdev, rldev):
330
        - cfdev: dict containing ganeti config disk part
331
        - rldev: ganeti.bdev.BlockDev object
332
    @param blockdev_prefix: a string containing blockdevice prefix,
333
                            e.g. "sd" for /dev/sda
334

335
    @return: string containing disk directive for xen instance config file
336

337
    """
338
    FILE_DRIVER_MAP = {
339
      constants.FD_LOOP: "file",
340
      constants.FD_BLKTAP: "tap:aio",
341
      }
342
    disk_data = []
343
    if len(block_devices) > 24:
344
      # 'z' - 'a' = 24
345
      raise errors.HypervisorError("Too many disks")
346
    namespace = [blockdev_prefix + chr(i + ord('a')) for i in range(24)]
347
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
348
      if cfdev.mode == constants.DISK_RDWR:
349
        mode = "w"
350
      else:
351
        mode = "r"
352
      if cfdev.dev_type == constants.LD_FILE:
353
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
354
                                  dev_path, sd_name, mode)
355
      else:
356
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
357
      disk_data.append(line)
358

    
359
    return disk_data
360

    
361
  def MigrationInfo(self, instance):
362
    """Get instance information to perform a migration.
363

364
    @type instance: L{objects.Instance}
365
    @param instance: instance to be migrated
366
    @rtype: string
367
    @return: content of the xen config file
368

369
    """
370
    return self._ReadConfigFile(instance.name)
371

    
372
  def AcceptInstance(self, instance, info, target):
373
    """Prepare to accept an instance.
374

375
    @type instance: L{objects.Instance}
376
    @param instance: instance to be accepted
377
    @type info: string
378
    @param info: content of the xen config file on the source node
379
    @type target: string
380
    @param target: target host (usually ip), on this node
381

382
    """
383
    pass
384

    
385
  def FinalizeMigration(self, instance, info, success):
386
    """Finalize an instance migration.
387

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

391
    @type instance: L{objects.Instance}
392
    @param instance: instance whose migration is being finalized
393
    @type info: string
394
    @param info: content of the xen config file on the source node
395
    @type success: boolean
396
    @param success: whether the migration was a success or a failure
397

398
    """
399
    if success:
400
      self._WriteConfigFileStatic(instance.name, info)
401

    
402
  def MigrateInstance(self, instance, target, live):
403
    """Migrate an instance to a target node.
404

405
    The migration will not be attempted if the instance is not
406
    currently running.
407

408
    @type instance: L{objects.Instance}
409
    @param instance: the instance to be migrated
410
    @type target: string
411
    @param target: ip address of the target node
412
    @type live: boolean
413
    @param live: perform a live migration
414

415
    """
416
    if self.GetInstanceInfo(instance.name) is None:
417
      raise errors.HypervisorError("Instance not running, cannot migrate")
418

    
419
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
420

    
421
    if not netutils.TcpPing(target, port, live_port_needed=True):
422
      raise errors.HypervisorError("Remote host %s not listening on port"
423
                                   " %s, cannot migrate" % (target, port))
424

    
425
    args = ["xm", "migrate", "-p", "%d" % port]
426
    if live:
427
      args.append("-l")
428
    args.extend([instance.name, target])
429
    result = utils.RunCmd(args)
430
    if result.failed:
431
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
432
                                   (instance.name, result.output))
433
    # remove old xen file after migration succeeded
434
    try:
435
      self._RemoveConfigFile(instance.name)
436
    except EnvironmentError:
437
      logging.exception("Failure while removing instance config file")
438

    
439
  @classmethod
440
  def PowercycleNode(cls):
441
    """Xen-specific powercycle.
442

443
    This first does a Linux reboot (which triggers automatically a Xen
444
    reboot), and if that fails it tries to do a Xen reboot. The reason
445
    we don't try a Xen reboot first is that the xen reboot launches an
446
    external command which connects to the Xen hypervisor, and that
447
    won't work in case the root filesystem is broken and/or the xend
448
    daemon is not working.
449

450
    """
451
    try:
452
      cls.LinuxPowercycle()
453
    finally:
454
      utils.RunCmd(["xm", "debug", "R"])
455

    
456

    
457
class XenPvmHypervisor(XenHypervisor):
458
  """Xen PVM hypervisor interface"""
459

    
460
  PARAMETERS = {
461
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
462
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
463
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
464
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
465
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
466
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
467
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
468
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
469
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
470
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
471
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
472
    }
473

    
474
  @classmethod
475
  def _WriteConfigFile(cls, instance, block_devices):
476
    """Write the Xen config file for the instance.
477

478
    """
479
    hvp = instance.hvparams
480
    config = StringIO()
481
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
482

    
483
    # if bootloader is True, use bootloader instead of kernel and ramdisk
484
    # parameters.
485
    if hvp[constants.HV_USE_BOOTLOADER]:
486
      # bootloader handling
487
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
488
      if bootloader_path:
489
        config.write("bootloader = '%s'\n" % bootloader_path)
490
      else:
491
        raise errors.HypervisorError("Bootloader enabled, but missing"
492
                                     " bootloader path")
493

    
494
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
495
      if bootloader_args:
496
        config.write("bootargs = '%s'\n" % bootloader_args)
497
    else:
498
      # kernel handling
499
      kpath = hvp[constants.HV_KERNEL_PATH]
500
      config.write("kernel = '%s'\n" % kpath)
501

    
502
      # initrd handling
503
      initrd_path = hvp[constants.HV_INITRD_PATH]
504
      if initrd_path:
505
        config.write("ramdisk = '%s'\n" % initrd_path)
506

    
507
    # rest of the settings
508
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
509
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
510
    config.write("name = '%s'\n" % instance.name)
511

    
512
    vif_data = []
513
    for nic in instance.nics:
514
      nic_str = "mac=%s" % (nic.mac)
515
      ip = getattr(nic, "ip", None)
516
      if ip is not None:
517
        nic_str += ", ip=%s" % ip
518
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
519
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
520
      vif_data.append("'%s'" % nic_str)
521

    
522
    disk_data = cls._GetConfigFileDiskData(block_devices,
523
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
524

    
525
    config.write("vif = [%s]\n" % ",".join(vif_data))
526
    config.write("disk = [%s]\n" % ",".join(disk_data))
527

    
528
    if hvp[constants.HV_ROOT_PATH]:
529
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
530
    config.write("on_poweroff = 'destroy'\n")
531
    config.write("on_reboot = 'restart'\n")
532
    config.write("on_crash = 'restart'\n")
533
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
534
    # just in case it exists
535
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
536
    try:
537
      utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
538
    except EnvironmentError, err:
539
      raise errors.HypervisorError("Cannot write Xen instance confile"
540
                                   " file /etc/xen/%s: %s" %
541
                                   (instance.name, err))
542

    
543
    return True
544

    
545

    
546
class XenHvmHypervisor(XenHypervisor):
547
  """Xen HVM hypervisor interface"""
548

    
549
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
550
    constants.VNC_PASSWORD_FILE,
551
    ]
552

    
553
  PARAMETERS = {
554
    constants.HV_ACPI: hv_base.NO_CHECK,
555
    constants.HV_BOOT_ORDER: (True, ) +
556
      (lambda x: x and len(x.strip("acdn")) == 0,
557
       "Invalid boot order specified, must be one or more of [acdn]",
558
       None, None),
559
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
560
    constants.HV_DISK_TYPE:
561
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
562
    constants.HV_NIC_TYPE:
563
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
564
    constants.HV_PAE: hv_base.NO_CHECK,
565
    constants.HV_VNC_BIND_ADDRESS:
566
      (False, netutils.IP4Address.IsValid,
567
       "VNC bind address is not a valid IP address", None, None),
568
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
569
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
570
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
571
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
572
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
573
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
574
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
575
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
576
    }
577

    
578
  @classmethod
579
  def _WriteConfigFile(cls, instance, block_devices):
580
    """Create a Xen 3.1 HVM config file.
581

582
    """
583
    hvp = instance.hvparams
584

    
585
    config = StringIO()
586
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
587

    
588
    # kernel handling
589
    kpath = hvp[constants.HV_KERNEL_PATH]
590
    config.write("kernel = '%s'\n" % kpath)
591

    
592
    config.write("builder = 'hvm'\n")
593
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
594
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
595
    config.write("name = '%s'\n" % instance.name)
596
    if hvp[constants.HV_PAE]:
597
      config.write("pae = 1\n")
598
    else:
599
      config.write("pae = 0\n")
600
    if hvp[constants.HV_ACPI]:
601
      config.write("acpi = 1\n")
602
    else:
603
      config.write("acpi = 0\n")
604
    config.write("apic = 1\n")
605
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
606
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
607
    config.write("sdl = 0\n")
608
    config.write("usb = 1\n")
609
    config.write("usbdevice = 'tablet'\n")
610
    config.write("vnc = 1\n")
611
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
612
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
613
    else:
614
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
615

    
616
    if instance.network_port > constants.VNC_BASE_PORT:
617
      display = instance.network_port - constants.VNC_BASE_PORT
618
      config.write("vncdisplay = %s\n" % display)
619
      config.write("vncunused = 0\n")
620
    else:
621
      config.write("# vncdisplay = 1\n")
622
      config.write("vncunused = 1\n")
623

    
624
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
625
    try:
626
      password = utils.ReadFile(vnc_pwd_file)
627
    except EnvironmentError, err:
628
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
629
                                   (vnc_pwd_file, err))
630

    
631
    config.write("vncpasswd = '%s'\n" % password.rstrip())
632

    
633
    config.write("serial = 'pty'\n")
634
    if hvp[constants.HV_USE_LOCALTIME]:
635
      config.write("localtime = 1\n")
636

    
637
    vif_data = []
638
    nic_type = hvp[constants.HV_NIC_TYPE]
639
    if nic_type is None:
640
      # ensure old instances don't change
641
      nic_type_str = ", type=ioemu"
642
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
643
      nic_type_str = ", type=paravirtualized"
644
    else:
645
      nic_type_str = ", model=%s, type=ioemu" % nic_type
646
    for nic in instance.nics:
647
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
648
      ip = getattr(nic, "ip", None)
649
      if ip is not None:
650
        nic_str += ", ip=%s" % ip
651
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
652
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
653
      vif_data.append("'%s'" % nic_str)
654

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

    
657
    disk_data = cls._GetConfigFileDiskData(block_devices,
658
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
659

    
660
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
661
    if iso_path:
662
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
663
      disk_data.append(iso)
664

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

    
667
    config.write("on_poweroff = 'destroy'\n")
668
    config.write("on_reboot = 'restart'\n")
669
    config.write("on_crash = 'restart'\n")
670
    # just in case it exists
671
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
672
    try:
673
      utils.WriteFile("/etc/xen/%s" % instance.name,
674
                      data=config.getvalue())
675
    except EnvironmentError, err:
676
      raise errors.HypervisorError("Cannot write Xen instance confile"
677
                                   " file /etc/xen/%s: %s" %
678
                                   (instance.name, err))
679

    
680
    return True