Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 6b970cef

History | View | Annotate | Download (21.7 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008 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 os
27
import os.path
28
import logging
29
from cStringIO import StringIO
30

    
31
from ganeti import constants
32
from ganeti import errors
33
from ganeti import utils
34
from ganeti.hypervisor import hv_base
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
  REBOOT_RETRY_COUNT = 60
45
  REBOOT_RETRY_INTERVAL = 10
46

    
47
  ANCILLARY_FILES = [
48
    '/etc/xen/xend-config.sxp',
49
    '/etc/xen/scripts/vif-bridge',
50
    ]
51

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

56
    """
57
    raise NotImplementedError
58

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
123
      raise errors.HypervisorError(errmsg)
124

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

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

    
147
    return result
148

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

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

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

160
    @param instance_name: the instance name
161

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

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

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

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

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

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

185
    """
186
    self._WriteConfigFile(instance, block_devices)
187
    result = utils.RunCmd(["xm", "create", instance.name])
188

    
189
    if result.failed:
190
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
191
                                   (instance.name, result.fail_reason,
192
                                    result.output))
193

    
194
  def StopInstance(self, instance, force=False, retry=False):
195
    """Stop an instance.
196

197
    """
198
    self._RemoveConfigFile(instance.name)
199
    if force:
200
      command = ["xm", "destroy", instance.name]
201
    else:
202
      command = ["xm", "shutdown", instance.name]
203
    result = utils.RunCmd(command)
204

    
205
    if result.failed:
206
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
207
                                   (instance.name, result.fail_reason,
208
                                    result.output))
209

    
210
  def RebootInstance(self, instance):
211
    """Reboot an instance.
212

213
    """
214
    ini_info = self.GetInstanceInfo(instance.name)
215

    
216
    result = utils.RunCmd(["xm", "reboot", instance.name])
217
    if result.failed:
218
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
219
                                   (instance.name, result.fail_reason,
220
                                    result.output))
221

    
222
    def _CheckInstance():
223
      new_info = self.GetInstanceInfo(instance.name)
224

    
225
      # check if the domain ID has changed or the run time has decreased
226
      if new_info[1] != ini_info[1] or new_info[5] < ini_info[5]:
227
        return
228

    
229
      raise utils.RetryAgain()
230

    
231
    try:
232
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
233
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
234
    except utils.RetryTimeout:
235
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
236
                                   " did not reboot in the expected interval" %
237
                                   (instance.name, ))
238

    
239
  def GetNodeInfo(self):
240
    """Return information about the node.
241

242
    @return: a dict with the following keys (memory values in MiB):
243
          - memory_total: the total memory size on the node
244
          - memory_free: the available memory on the node for instances
245
          - memory_dom0: the memory used by the node itself, if available
246
          - nr_cpus: total number of CPUs
247
          - nr_nodes: in a NUMA system, the number of domains
248
          - nr_sockets: the number of physical CPU sockets in the node
249

250
    """
251
    # note: in xen 3, memory has changed to total_memory
252
    result = utils.RunCmd(["xm", "info"])
253
    if result.failed:
254
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
255
                    result.output)
256
      return None
257

    
258
    xmoutput = result.stdout.splitlines()
259
    result = {}
260
    cores_per_socket = threads_per_core = nr_cpus = None
261
    for line in xmoutput:
262
      splitfields = line.split(":", 1)
263

    
264
      if len(splitfields) > 1:
265
        key = splitfields[0].strip()
266
        val = splitfields[1].strip()
267
        if key == 'memory' or key == 'total_memory':
268
          result['memory_total'] = int(val)
269
        elif key == 'free_memory':
270
          result['memory_free'] = int(val)
271
        elif key == 'nr_cpus':
272
          nr_cpus = result['cpu_total'] = int(val)
273
        elif key == 'nr_nodes':
274
          result['cpu_nodes'] = int(val)
275
        elif key == 'cores_per_socket':
276
          cores_per_socket = int(val)
277
        elif key == 'threads_per_core':
278
          threads_per_core = int(val)
279

    
280
    if (cores_per_socket is not None and
281
        threads_per_core is not None and nr_cpus is not None):
282
      result['cpu_sockets'] = nr_cpus / (cores_per_socket * threads_per_core)
283

    
284
    dom0_info = self.GetInstanceInfo("Domain-0")
285
    if dom0_info is not None:
286
      result['memory_dom0'] = dom0_info[2]
287

    
288
    return result
289

    
290
  @classmethod
291
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
292
    """Return a command for connecting to the console of an instance.
293

294
    """
295
    return "xm console %s" % instance.name
296

    
297

    
298
  def Verify(self):
299
    """Verify the hypervisor.
300

301
    For Xen, this verifies that the xend process is running.
302

303
    """
304
    result = utils.RunCmd(["xm", "info"])
305
    if result.failed:
306
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
307

    
308
  @staticmethod
309
  def _GetConfigFileDiskData(disk_template, block_devices):
310
    """Get disk directive for xen config file.
311

312
    This method builds the xen config disk directive according to the
313
    given disk_template and block_devices.
314

315
    @param disk_template: string containing instance disk template
316
    @param block_devices: list of tuples (cfdev, rldev):
317
        - cfdev: dict containing ganeti config disk part
318
        - rldev: ganeti.bdev.BlockDev object
319

320
    @return: string containing disk directive for xen instance config file
321

322
    """
323
    FILE_DRIVER_MAP = {
324
      constants.FD_LOOP: "file",
325
      constants.FD_BLKTAP: "tap:aio",
326
      }
327
    disk_data = []
328
    if len(block_devices) > 24:
329
      # 'z' - 'a' = 24
330
      raise errors.HypervisorError("Too many disks")
331
    # FIXME: instead of this hardcoding here, each of PVM/HVM should
332
    # directly export their info (currently HVM will just sed this info)
333
    namespace = ["sd" + chr(i + ord('a')) for i in range(24)]
334
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
335
      if cfdev.mode == constants.DISK_RDWR:
336
        mode = "w"
337
      else:
338
        mode = "r"
339
      if cfdev.dev_type == constants.LD_FILE:
340
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
341
                                  dev_path, sd_name, mode)
342
      else:
343
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
344
      disk_data.append(line)
345

    
346
    return disk_data
347

    
348
  def MigrationInfo(self, instance):
349
    """Get instance information to perform a migration.
350

351
    @type instance: L{objects.Instance}
352
    @param instance: instance to be migrated
353
    @rtype: string
354
    @return: content of the xen config file
355

356
    """
357
    return self._ReadConfigFile(instance.name)
358

    
359
  def AcceptInstance(self, instance, info, target):
360
    """Prepare to accept an instance.
361

362
    @type instance: L{objects.Instance}
363
    @param instance: instance to be accepted
364
    @type info: string
365
    @param info: content of the xen config file on the source node
366
    @type target: string
367
    @param target: target host (usually ip), on this node
368

369
    """
370
    pass
371

    
372
  def FinalizeMigration(self, instance, info, success):
373
    """Finalize an instance migration.
374

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

378
    @type instance: L{objects.Instance}
379
    @param instance: instance whose migration is being aborted
380
    @type info: string
381
    @param info: content of the xen config file on the source node
382
    @type success: boolean
383
    @param success: whether the migration was a success or a failure
384

385
    """
386
    if success:
387
      self._WriteConfigFileStatic(instance.name, info)
388

    
389
  def MigrateInstance(self, instance, target, live):
390
    """Migrate an instance to a target node.
391

392
    The migration will not be attempted if the instance is not
393
    currently running.
394

395
    @type instance: L{objects.Instance}
396
    @param instance: the instance to be migrated
397
    @type target: string
398
    @param target: ip address of the target node
399
    @type live: boolean
400
    @param live: perform a live migration
401

402
    """
403
    if self.GetInstanceInfo(instance.name) is None:
404
      raise errors.HypervisorError("Instance not running, cannot migrate")
405

    
406
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
407

    
408
    if not utils.TcpPing(target, port, live_port_needed=True):
409
      raise errors.HypervisorError("Remote host %s not listening on port"
410
                                   " %s, cannot migrate" % (target, port))
411

    
412
    args = ["xm", "migrate", "-p", "%d" % port]
413
    if live:
414
      args.append("-l")
415
    args.extend([instance.name, target])
416
    result = utils.RunCmd(args)
417
    if result.failed:
418
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
419
                                   (instance.name, result.output))
420
    # remove old xen file after migration succeeded
421
    try:
422
      self._RemoveConfigFile(instance.name)
423
    except EnvironmentError:
424
      logging.exception("Failure while removing instance config file")
425

    
426
  @classmethod
427
  def PowercycleNode(cls):
428
    """Xen-specific powercycle.
429

430
    This first does a Linux reboot (which triggers automatically a Xen
431
    reboot), and if that fails it tries to do a Xen reboot. The reason
432
    we don't try a Xen reboot first is that the xen reboot launches an
433
    external command which connects to the Xen hypervisor, and that
434
    won't work in case the root filesystem is broken and/or the xend
435
    daemon is not working.
436

437
    """
438
    try:
439
      cls.LinuxPowercycle()
440
    finally:
441
      utils.RunCmd(["xm", "debug", "R"])
442

    
443

    
444
class XenPvmHypervisor(XenHypervisor):
445
  """Xen PVM hypervisor interface"""
446

    
447
  PARAMETERS = {
448
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
449
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
450
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
451
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
452
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
453
    constants.HV_ROOT_PATH: hv_base.REQUIRED_CHECK,
454
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
455
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
456
    }
457

    
458
  @classmethod
459
  def _WriteConfigFile(cls, instance, block_devices):
460
    """Write the Xen config file for the instance.
461

462
    """
463
    hvp = instance.hvparams
464
    config = StringIO()
465
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
466

    
467
    # if bootloader is True, use bootloader instead of kernel and ramdisk
468
    # parameters.
469
    if hvp[constants.HV_USE_BOOTLOADER]:
470
      # bootloader handling
471
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
472
      if bootloader_path:
473
        config.write("bootloader = '%s'\n" % bootloader_path)
474
      else:
475
        raise errors.HypervisorError("Bootloader enabled, but missing"
476
                                     " bootloader path")
477

    
478
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
479
      if bootloader_args:
480
        config.write("bootargs = '%s'\n" % bootloader_args)
481
    else:
482
      # kernel handling
483
      kpath = hvp[constants.HV_KERNEL_PATH]
484
      config.write("kernel = '%s'\n" % kpath)
485

    
486
      # initrd handling
487
      initrd_path = hvp[constants.HV_INITRD_PATH]
488
      if initrd_path:
489
        config.write("ramdisk = '%s'\n" % initrd_path)
490

    
491
    # rest of the settings
492
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
493
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
494
    config.write("name = '%s'\n" % instance.name)
495

    
496
    vif_data = []
497
    for nic in instance.nics:
498
      nic_str = "mac=%s" % (nic.mac)
499
      ip = getattr(nic, "ip", None)
500
      if ip is not None:
501
        nic_str += ", ip=%s" % ip
502
      vif_data.append("'%s'" % nic_str)
503
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
504
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
505

    
506
    config.write("vif = [%s]\n" % ",".join(vif_data))
507
    config.write("disk = [%s]\n" % ",".join(
508
                 cls._GetConfigFileDiskData(instance.disk_template,
509
                                            block_devices)))
510

    
511
    config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
512
    config.write("on_poweroff = 'destroy'\n")
513
    config.write("on_reboot = 'restart'\n")
514
    config.write("on_crash = 'restart'\n")
515
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
516
    # just in case it exists
517
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
518
    try:
519
      utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
520
    except EnvironmentError, err:
521
      raise errors.HypervisorError("Cannot write Xen instance confile"
522
                                   " file /etc/xen/%s: %s" %
523
                                   (instance.name, err))
524

    
525
    return True
526

    
527

    
528
class XenHvmHypervisor(XenHypervisor):
529
  """Xen HVM hypervisor interface"""
530

    
531
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
532
    constants.VNC_PASSWORD_FILE,
533
    ]
534

    
535
  PARAMETERS = {
536
    constants.HV_ACPI: hv_base.NO_CHECK,
537
    constants.HV_BOOT_ORDER: (True, ) +
538
      (lambda x: x and len(x.strip("acdn")) == 0,
539
       "Invalid boot order specified, must be one or more of [acdn]",
540
       None, None),
541
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
542
    constants.HV_DISK_TYPE:
543
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
544
    constants.HV_NIC_TYPE:
545
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
546
    constants.HV_PAE: hv_base.NO_CHECK,
547
    constants.HV_VNC_BIND_ADDRESS:
548
      (False, utils.IsValidIP,
549
       "VNC bind address is not a valid IP address", None, None),
550
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
551
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
552
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
553
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
554
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
555
    }
556

    
557
  @classmethod
558
  def _WriteConfigFile(cls, instance, block_devices):
559
    """Create a Xen 3.1 HVM config file.
560

561
    """
562
    hvp = instance.hvparams
563

    
564
    config = StringIO()
565
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
566

    
567
    # kernel handling
568
    kpath = hvp[constants.HV_KERNEL_PATH]
569
    config.write("kernel = '%s'\n" % kpath)
570

    
571
    config.write("builder = 'hvm'\n")
572
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
573
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
574
    config.write("name = '%s'\n" % instance.name)
575
    if hvp[constants.HV_PAE]:
576
      config.write("pae = 1\n")
577
    else:
578
      config.write("pae = 0\n")
579
    if hvp[constants.HV_ACPI]:
580
      config.write("acpi = 1\n")
581
    else:
582
      config.write("acpi = 0\n")
583
    config.write("apic = 1\n")
584
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
585
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
586
    config.write("sdl = 0\n")
587
    config.write("usb = 1\n")
588
    config.write("usbdevice = 'tablet'\n")
589
    config.write("vnc = 1\n")
590
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
591
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
592
    else:
593
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
594

    
595
    if instance.network_port > constants.VNC_BASE_PORT:
596
      display = instance.network_port - constants.VNC_BASE_PORT
597
      config.write("vncdisplay = %s\n" % display)
598
      config.write("vncunused = 0\n")
599
    else:
600
      config.write("# vncdisplay = 1\n")
601
      config.write("vncunused = 1\n")
602

    
603
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
604
    try:
605
      password = utils.ReadFile(vnc_pwd_file)
606
    except EnvironmentError, err:
607
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
608
                                   (vnc_pwd_file, err))
609

    
610
    config.write("vncpasswd = '%s'\n" % password.rstrip())
611

    
612
    config.write("serial = 'pty'\n")
613
    if hvp[constants.HV_USE_LOCALTIME]:
614
      config.write("localtime = 1\n")
615

    
616
    vif_data = []
617
    nic_type = hvp[constants.HV_NIC_TYPE]
618
    if nic_type is None:
619
      # ensure old instances don't change
620
      nic_type_str = ", type=ioemu"
621
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
622
      nic_type_str = ", type=paravirtualized"
623
    else:
624
      nic_type_str = ", model=%s, type=ioemu" % nic_type
625
    for nic in instance.nics:
626
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
627
      ip = getattr(nic, "ip", None)
628
      if ip is not None:
629
        nic_str += ", ip=%s" % ip
630
      vif_data.append("'%s'" % nic_str)
631
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
632
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
633

    
634
    config.write("vif = [%s]\n" % ",".join(vif_data))
635
    disk_data = cls._GetConfigFileDiskData(instance.disk_template,
636
                                            block_devices)
637
    disk_type = hvp[constants.HV_DISK_TYPE]
638
    if disk_type in (None, constants.HT_DISK_IOEMU):
639
      replacement = ",ioemu:hd"
640
    else:
641
      replacement = ",hd"
642
    disk_data = [line.replace(",sd", replacement) for line in disk_data]
643
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
644
    if iso_path:
645
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
646
      disk_data.append(iso)
647

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

    
650
    config.write("on_poweroff = 'destroy'\n")
651
    config.write("on_reboot = 'restart'\n")
652
    config.write("on_crash = 'restart'\n")
653
    # just in case it exists
654
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
655
    try:
656
      utils.WriteFile("/etc/xen/%s" % instance.name,
657
                      data=config.getvalue())
658
    except EnvironmentError, err:
659
      raise errors.HypervisorError("Cannot write Xen instance confile"
660
                                   " file /etc/xen/%s: %s" %
661
                                   (instance.name, err))
662

    
663
    return True