Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 323f9095

History | View | Annotate | Download (22.5 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=[constants.XM_CONSOLE_WRAPPER,
311
                                            instance.name])
312

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

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

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

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

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

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

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

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

    
360
    return disk_data
361

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

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

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

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

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

383
    """
384
    pass
385

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
457

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

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

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

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

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

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

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

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

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

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

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

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

    
544
    return True
545

    
546

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

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

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

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

583
    """
584
    hvp = instance.hvparams
585

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

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

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

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

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

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

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

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

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

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

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

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

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

    
681
    return True