Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 30e4e741

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 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):
193
    """Stop an instance.
194

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

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

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

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

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

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

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

    
227
      raise utils.RetryAgain()
228

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

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

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

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

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

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

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

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

    
286
    return result
287

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

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

    
295

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

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

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

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

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

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

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

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

    
343
    return disk_data
344

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

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

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

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

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

366
    """
367
    pass
368

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
440

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

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

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

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

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

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

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

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

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

    
503
    disk_data = cls._GetConfigFileDiskData(block_devices)
504

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

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

    
522
    return True
523

    
524

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

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

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

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

558
    """
559
    hvp = instance.hvparams
560

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

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

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

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

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

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

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

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

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

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

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

    
659
    return True