Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ bbcf7ad0

History | View | Annotate | Download (21.4 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 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

    
34

    
35
class XenHypervisor(hv_base.BaseHypervisor):
36
  """Xen generic hypervisor interface
37

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

41
  """
42
  REBOOT_RETRY_COUNT = 60
43
  REBOOT_RETRY_INTERVAL = 10
44

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

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

54
    """
55
    raise NotImplementedError
56

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

61
    This version of the function just writes the config file from static data.
62

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

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

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

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

81
    """
82
    utils.RemoveFile("/etc/xen/%s" % instance_name)
83

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

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

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

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

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

106
    @return: list of (name, id, memory, vcpus, state, time spent)
107

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

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

    
121
      raise errors.HypervisorError(errmsg)
122

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

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

    
145
    return result
146

    
147
  def ListInstances(self):
148
    """Get the list of running instances.
149

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

    
155
  def GetInstanceInfo(self, instance_name):
156
    """Get instance properties.
157

158
    @param instance_name: the instance name
159

160
    @return: tuple (name, id, memory, vcpus, stat, times)
161

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

    
171
  def GetAllInstancesInfo(self):
172
    """Get properties of all instances.
173

174
    @return: list of tuples (name, id, memory, vcpus, stat, times)
175

176
    """
177
    xm_list = self._GetXMList(False)
178
    return xm_list
179

    
180
  def StartInstance(self, instance, block_devices):
181
    """Start an instance.
182

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

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

    
192
  def StopInstance(self, instance, force=False, retry=False, name=None):
193
    """Stop an instance.
194

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

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

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

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

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

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

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

    
228
      raise utils.RetryAgain()
229

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

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

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

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

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

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

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

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

    
287
    return result
288

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

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

    
296

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

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

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

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

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

314
    @param block_devices: list of tuples (cfdev, rldev):
315
        - cfdev: dict containing ganeti config disk part
316
        - rldev: ganeti.bdev.BlockDev object
317

318
    @return: string containing disk directive for xen instance config file
319

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

    
344
    return disk_data
345

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

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

354
    """
355
    return self._ReadConfigFile(instance.name)
356

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

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

367
    """
368
    pass
369

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

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

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

383
    """
384
    if success:
385
      self._WriteConfigFileStatic(instance.name, info)
386

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

390
    The migration will not be attempted if the instance is not
391
    currently running.
392

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

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

    
404
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
405

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

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

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

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

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

    
441

    
442
class XenPvmHypervisor(XenHypervisor):
443
  """Xen PVM hypervisor interface"""
444

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

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

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

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

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

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

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

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

    
504
    disk_data = cls._GetConfigFileDiskData(block_devices)
505

    
506
    config.write("vif = [%s]\n" % ",".join(vif_data))
507
    config.write("disk = [%s]\n" % ",".join(disk_data))
508

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

    
523
    return True
524

    
525

    
526
class XenHvmHypervisor(XenHypervisor):
527
  """Xen HVM hypervisor interface"""
528

    
529
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
530
    constants.VNC_PASSWORD_FILE,
531
    ]
532

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

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

559
    """
560
    hvp = instance.hvparams
561

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

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

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

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

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

    
608
    config.write("vncpasswd = '%s'\n" % password.rstrip())
609

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

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

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

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

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

    
660
    return True