Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 7ed85ffe

History | View | Annotate | Download (21.5 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(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 block_devices: list of tuples (cfdev, rldev):
316
        - cfdev: dict containing ganeti config disk part
317
        - rldev: ganeti.bdev.BlockDev object
318

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

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

    
345
    return disk_data
346

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

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

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

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

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

368
    """
369
    pass
370

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
442

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

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

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

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

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

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

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

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

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

    
505
    disk_data = cls._GetConfigFileDiskData(block_devices)
506

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

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

    
524
    return True
525

    
526

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

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

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

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

560
    """
561
    hvp = instance.hvparams
562

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

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

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

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

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

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

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

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

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

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

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

    
661
    return True