hv_xen.py 22.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#
#

# Copyright (C) 2006, 2007, 2008 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


"""Xen hypervisors

"""

import os
import os.path
import time
29
import logging
30
31
32
33
34
from cStringIO import StringIO

from ganeti import constants
from ganeti import errors
from ganeti import utils
35
from ganeti.hypervisor import hv_base
36
37


38
class XenHypervisor(hv_base.BaseHypervisor):
39
40
41
42
43
44
45
  """Xen generic hypervisor interface

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

  """

Iustin Pop's avatar
Iustin Pop committed
46
47
  @classmethod
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
48
49
50
51
52
    """Write the Xen config file for the instance.

    """
    raise NotImplementedError

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  @staticmethod
  def _WriteConfigFileStatic(instance_name, data):
    """Write the Xen config file for the instance.

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

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

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

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

73
  @staticmethod
74
  def _RemoveConfigFile(instance_name):
75
76
77
    """Remove the xen configuration file.

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

  @staticmethod
  def _GetXMList(include_node):
    """Return the list of running instances.

Iustin Pop's avatar
Iustin Pop committed
84
    If the include_node argument is True, then we return information
85
86
    for dom0 also, otherwise we filter that from the return value.

Iustin Pop's avatar
Iustin Pop committed
87
    @return: list of (name, id, memory, vcpus, state, time spent)
88
89
90
91
92
93

    """
    for dummy in range(5):
      result = utils.RunCmd(["xm", "list"])
      if not result.failed:
        break
94
95
      logging.error("xm list failed (%s): %s", result.fail_reason,
                    result.output)
96
97
98
99
100
      time.sleep(1)

    if result.failed:
      raise errors.HypervisorError("xm list failed, retries"
                                   " exceeded (%s): %s" %
101
                                   (result.fail_reason, result.output))
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

    # skip over the heading
    lines = result.stdout.splitlines()[1:]
    result = []
    for line in lines:
      # The format of lines is:
      # Name      ID Mem(MiB) VCPUs State  Time(s)
      # Domain-0   0  3418     4 r-----    266.2
      data = line.split()
      if len(data) != 6:
        raise errors.HypervisorError("Can't parse output of xm list,"
                                     " line: %s" % line)
      try:
        data[1] = int(data[1])
        data[2] = int(data[2])
        data[3] = int(data[3])
        data[5] = float(data[5])
      except ValueError, err:
        raise errors.HypervisorError("Can't parse output of xm list,"
                                     " line: %s, error: %s" % (line, err))

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

    return result

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

    """
    xm_list = self._GetXMList(False)
    names = [info[0] for info in xm_list]
    return names

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

Iustin Pop's avatar
Iustin Pop committed
140
141
142
    @param instance_name: the instance name

    @return: tuple (name, id, memory, vcpus, stat, times)
143
144
145
146
147
148
149
150
151
152
153
154
155

    """
    xm_list = self._GetXMList(instance_name=="Domain-0")
    result = None
    for data in xm_list:
      if data[0] == instance_name:
        result = data
        break
    return result

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

Iustin Pop's avatar
Iustin Pop committed
156
157
    @return: list of tuples (name, id, memory, vcpus, stat, times)

158
159
160
161
162
    """
    xm_list = self._GetXMList(False)
    return xm_list

  def StartInstance(self, instance, block_devices, extra_args):
Iustin Pop's avatar
Iustin Pop committed
163
164
165
    """Start an instance.

    """
166
167
168
169
170
171
172
173
174
    self._WriteConfigFile(instance, block_devices, extra_args)
    result = utils.RunCmd(["xm", "create", instance.name])

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

  def StopInstance(self, instance, force=False):
Iustin Pop's avatar
Iustin Pop committed
175
176
177
    """Stop an instance.

    """
178
    self._RemoveConfigFile(instance.name)
179
180
181
182
183
184
185
    if force:
      command = ["xm", "destroy", instance.name]
    else:
      command = ["xm", "shutdown", instance.name]
    result = utils.RunCmd(command)

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

  def RebootInstance(self, instance):
Iustin Pop's avatar
Iustin Pop committed
191
192
193
    """Reboot an instance.

    """
194
195
196
    result = utils.RunCmd(["xm", "reboot", instance.name])

    if result.failed:
197
198
199
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
                                   (instance.name, result.fail_reason,
                                    result.output))
200
201
202
203

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

204
    @return: a dict with the following keys (memory values in MiB):
Iustin Pop's avatar
Iustin Pop committed
205
206
207
          - memory_total: the total memory size on the node
          - memory_free: the available memory on the node for instances
          - memory_dom0: the memory used by the node itself, if available
208
209
210
          - nr_cpus: total number of CPUs
          - nr_nodes: in a NUMA system, the number of domains
          - nr_sockets: the number of physical CPU sockets in the node
211
212
213
214
215

    """
    # note: in xen 3, memory has changed to total_memory
    result = utils.RunCmd(["xm", "info"])
    if result.failed:
216
217
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
                    result.output)
218
219
220
221
      return None

    xmoutput = result.stdout.splitlines()
    result = {}
222
    cores_per_socket = threads_per_core = nr_cpus = None
223
224
225
226
227
228
229
230
231
232
    for line in xmoutput:
      splitfields = line.split(":", 1)

      if len(splitfields) > 1:
        key = splitfields[0].strip()
        val = splitfields[1].strip()
        if key == 'memory' or key == 'total_memory':
          result['memory_total'] = int(val)
        elif key == 'free_memory':
          result['memory_free'] = int(val)
233
        elif key == 'nr_cpus':
234
235
236
237
238
239
240
241
242
243
244
245
          nr_cpus = result['cpu_total'] = int(val)
        elif key == 'nr_nodes':
          result['cpu_nodes'] = int(val)
        elif key == 'cores_per_socket':
          cores_per_socket = int(val)
        elif key == 'threads_per_core':
          threads_per_core = int(val)

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

246
247
248
249
250
251
    dom0_info = self.GetInstanceInfo("Domain-0")
    if dom0_info is not None:
      result['memory_dom0'] = dom0_info[2]

    return result

252
  @classmethod
253
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
254
255
256
    """Return a command for connecting to the console of an instance.

    """
257
258
    return "xm console %s" % instance.name

259
260
261
262
263
264
265

  def Verify(self):
    """Verify the hypervisor.

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

    """
266
267
    result = utils.RunCmd(["xm", "info"])
    if result.failed:
268
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
269
270
271
272
273
274
275
276

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

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

Iustin Pop's avatar
Iustin Pop committed
277
278
279
280
    @param disk_template: string containing instance disk template
    @param block_devices: list of tuples (cfdev, rldev):
        - cfdev: dict containing ganeti config disk part
        - rldev: ganeti.bdev.BlockDev object
281

Iustin Pop's avatar
Iustin Pop committed
282
    @return: string containing disk directive for xen instance config file
283
284
285
286
287
288
289

    """
    FILE_DRIVER_MAP = {
      constants.FD_LOOP: "file",
      constants.FD_BLKTAP: "tap:aio",
      }
    disk_data = []
290
291
292
293
294
295
    if len(block_devices) > 24:
      # 'z' - 'a' = 24
      raise errors.HypervisorError("Too many disks")
    # FIXME: instead of this hardcoding here, each of PVM/HVM should
    # directly export their info (currently HVM will just sed this info)
    namespace = ["sd" + chr(i + ord('a')) for i in range(24)]
296
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
297
298
299
300
      if cfdev.mode == constants.DISK_RDWR:
        mode = "w"
      else:
        mode = "r"
301
      if cfdev.dev_type == constants.LD_FILE:
302
303
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
                                  dev_path, sd_name, mode)
304
      else:
305
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
306
307
308
309
      disk_data.append(line)

    return disk_data

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  def MigrationInfo(self, instance):
    """Get instance information to perform a migration.

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

    """
    return self._ReadConfigFile(instance.name)

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

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

    """
    pass

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

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

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

    """
    if success:
      self._WriteConfigFileStatic(instance.name, info)

351
352
353
354
355
356
  def MigrateInstance(self, instance, target, live):
    """Migrate an instance to a target node.

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

357
358
359
360
361
362
363
    @type instance: string
    @param instance: instance name
    @type target: string
    @param target: ip address of the target node
    @type live: boolean
    @param live: perform a live migration

364
365
366
367
368
369
370
371
372
373
374
    """
    if self.GetInstanceInfo(instance) is None:
      raise errors.HypervisorError("Instance not running, cannot migrate")
    args = ["xm", "migrate"]
    if live:
      args.append("-l")
    args.extend([instance, target])
    result = utils.RunCmd(args)
    if result.failed:
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
                                   (instance, result.output))
375
376
377
    # remove old xen file after migration succeeded
    try:
      self._RemoveConfigFile(instance)
Iustin Pop's avatar
Iustin Pop committed
378
379
    except EnvironmentError:
      logging.exception("Failure while removing instance config file")
380

381
382
383
384

class XenPvmHypervisor(XenHypervisor):
  """Xen PVM hypervisor interface"""

385
386
387
  PARAMETERS = [
    constants.HV_KERNEL_PATH,
    constants.HV_INITRD_PATH,
388
    constants.HV_ROOT_PATH,
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
    ]

  @classmethod
  def CheckParameterSyntax(cls, hvparams):
    """Check the given parameters for validity.

    For the PVM hypervisor, this only check the existence of the
    kernel.

    @type hvparams:  dict
    @param hvparams: dictionary with parameter names/value
    @raise errors.HypervisorError: when a parameter is not valid

    """
    super(XenPvmHypervisor, cls).CheckParameterSyntax(hvparams)

    if not hvparams[constants.HV_KERNEL_PATH]:
      raise errors.HypervisorError("Need a kernel for the instance")

    if not os.path.isabs(hvparams[constants.HV_KERNEL_PATH]):
409
      raise errors.HypervisorError("The kernel path must be an absolute path")
410

411
412
413
    if not hvparams[constants.HV_ROOT_PATH]:
      raise errors.HypervisorError("Need a root partition for the instance")

414
415
    if hvparams[constants.HV_INITRD_PATH]:
      if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
416
        raise errors.HypervisorError("The initrd path must be an absolute path"
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
                                     ", if defined")

  def ValidateParameters(self, hvparams):
    """Check the given parameters for validity.

    For the PVM hypervisor, this only check the existence of the
    kernel.

    """
    super(XenPvmHypervisor, self).ValidateParameters(hvparams)

    kernel_path = hvparams[constants.HV_KERNEL_PATH]
    if not os.path.isfile(kernel_path):
      raise errors.HypervisorError("Instance kernel '%s' not found or"
                                   " not a file" % kernel_path)
    initrd_path = hvparams[constants.HV_INITRD_PATH]
    if initrd_path and not os.path.isfile(initrd_path):
      raise errors.HypervisorError("Instance initrd '%s' not found or"
                                   " not a file" % initrd_path)

437
438
439
440
441
442
443
444
445
  @classmethod
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
    """Write the Xen config file for the instance.

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

    # kernel handling
446
    kpath = instance.hvparams[constants.HV_KERNEL_PATH]
447
448
449
    config.write("kernel = '%s'\n" % kpath)

    # initrd handling
450
    initrd_path = instance.hvparams[constants.HV_INITRD_PATH]
451
452
453
454
    if initrd_path:
      config.write("ramdisk = '%s'\n" % initrd_path)

    # rest of the settings
455
456
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
457
458
459
460
461
462
463
464
465
466
467
468
469
470
    config.write("name = '%s'\n" % instance.name)

    vif_data = []
    for nic in instance.nics:
      nic_str = "mac=%s, bridge=%s" % (nic.mac, nic.bridge)
      ip = getattr(nic, "ip", None)
      if ip is not None:
        nic_str += ", ip=%s" % ip
      vif_data.append("'%s'" % nic_str)

    config.write("vif = [%s]\n" % ",".join(vif_data))
    config.write("disk = [%s]\n" % ",".join(
                 cls._GetConfigFileDiskData(instance.disk_template,
                                            block_devices)))
471
472
473

    rpath = instance.hvparams[constants.HV_ROOT_PATH]
    config.write("root = '%s ro'\n" % rpath)
474
475
476
477
478
479
480
481
    config.write("on_poweroff = 'destroy'\n")
    config.write("on_reboot = 'restart'\n")
    config.write("on_crash = 'restart'\n")
    if extra_args:
      config.write("extra = '%s'\n" % extra_args)
    # just in case it exists
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
    try:
482
483
484
485
486
487
488
      utils.WriteFile("/etc/xen/%s" % instance.name,
                      data=config.getvalue())
    except EnvironmentError, err:
      raise errors.HypervisorError("Cannot write Xen instance confile"
                                   " file /etc/xen/%s: %s" %
                                   (instance.name, err))

489
490
491
492
493
494
    return True


class XenHvmHypervisor(XenHypervisor):
  """Xen HVM hypervisor interface"""

495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
  PARAMETERS = [
    constants.HV_ACPI,
    constants.HV_BOOT_ORDER,
    constants.HV_CDROM_IMAGE_PATH,
    constants.HV_DISK_TYPE,
    constants.HV_NIC_TYPE,
    constants.HV_PAE,
    constants.HV_VNC_BIND_ADDRESS,
    ]

  @classmethod
  def CheckParameterSyntax(cls, hvparams):
    """Check the given parameter syntax.

    """
    super(XenHvmHypervisor, cls).CheckParameterSyntax(hvparams)
    # boot order verification
    boot_order = hvparams[constants.HV_BOOT_ORDER]
513
    if not boot_order or len(boot_order.strip("acdn")) != 0:
514
515
516
517
518
519
      raise errors.HypervisorError("Invalid boot order '%s' specified,"
                                   " must be one or more of [acdn]" %
                                   boot_order)
    # device type checks
    nic_type = hvparams[constants.HV_NIC_TYPE]
    if nic_type not in constants.HT_HVM_VALID_NIC_TYPES:
520
521
522
523
      raise errors.HypervisorError("Invalid NIC type %s specified for the Xen"
                                   " HVM hypervisor. Please choose one of: %s"
                                   % (nic_type,
                                      constants.HT_HVM_VALID_NIC_TYPES))
524
525
    disk_type = hvparams[constants.HV_DISK_TYPE]
    if disk_type not in constants.HT_HVM_VALID_DISK_TYPES:
526
527
528
529
      raise errors.HypervisorError("Invalid disk type %s specified for the Xen"
                                   " HVM hypervisor. Please choose one of: %s"
                                   % (disk_type,
                                      constants.HT_HVM_VALID_DISK_TYPES))
530
531
    # vnc_bind_address verification
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
Iustin Pop's avatar
Iustin Pop committed
532
    if vnc_bind_address:
533
534
535
536
537
538
539
      if not utils.IsValidIP(vnc_bind_address):
        raise errors.OpPrereqError("given VNC bind address '%s' doesn't look"
                                   " like a valid IP address" %
                                   vnc_bind_address)

    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
    if iso_path and not os.path.isabs(iso_path):
Iustin Pop's avatar
Iustin Pop committed
540
541
542
      raise errors.HypervisorError("The path to the HVM CDROM image must"
                                   " be an absolute path or None, not %s" %
                                   iso_path)
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559

  def ValidateParameters(self, hvparams):
    """Check the given parameters for validity.

    For the PVM hypervisor, this only check the existence of the
    kernel.

    @type hvparams:  dict
    @param hvparams: dictionary with parameter names/value
    @raise errors.HypervisorError: when a parameter is not valid

    """
    super(XenHvmHypervisor, self).ValidateParameters(hvparams)

    # hvm_cdrom_image_path verification
    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
    if iso_path and not os.path.isfile(iso_path):
Iustin Pop's avatar
Iustin Pop committed
560
561
562
563
      raise errors.HypervisorError("The HVM CDROM image must either be a"
                                   " regular file or a symlink pointing to"
                                   " an existing regular file, not %s" %
                                   iso_path)
564

565
566
567
568
569
570
571
572
573
  @classmethod
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
    """Create a Xen 3.1 HVM config file.

    """
    config = StringIO()
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
    config.write("kernel = '/usr/lib/xen/boot/hvmloader'\n")
    config.write("builder = 'hvm'\n")
574
575
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
576
    config.write("name = '%s'\n" % instance.name)
577
    if instance.hvparams[constants.HV_PAE]:
578
579
580
      config.write("pae = 1\n")
    else:
      config.write("pae = 0\n")
581
    if instance.hvparams[constants.HV_ACPI]:
582
583
584
      config.write("acpi = 1\n")
    else:
      config.write("acpi = 0\n")
585
586
587
588
589
590
    config.write("apic = 1\n")
    arch = os.uname()[4]
    if '64' in arch:
      config.write("device_model = '/usr/lib64/xen/bin/qemu-dm'\n")
    else:
      config.write("device_model = '/usr/lib/xen/bin/qemu-dm'\n")
591
    config.write("boot = '%s'\n" % instance.hvparams[constants.HV_BOOT_ORDER])
592
    config.write("sdl = 0\n")
593
594
    config.write("usb = 1\n")
    config.write("usbdevice = 'tablet'\n")
595
    config.write("vnc = 1\n")
596
    if instance.hvparams[constants.HV_VNC_BIND_ADDRESS] is None:
597
598
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
    else:
599
600
      config.write("vnclisten = '%s'\n" %
                   instance.hvparams["vnc_bind_address"])
601

602
603
    if instance.network_port > constants.VNC_BASE_PORT:
      display = instance.network_port - constants.VNC_BASE_PORT
604
605
606
607
608
609
610
      config.write("vncdisplay = %s\n" % display)
      config.write("vncunused = 0\n")
    else:
      config.write("# vncdisplay = 1\n")
      config.write("vncunused = 1\n")

    try:
611
612
613
614
      password = utils.ReadFile(constants.VNC_PASSWORD_FILE)
    except EnvironmentError, err:
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
                                   (constants.VNC_PASSWORD_FILE, err))
615
616
617
618
619
620
621

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

    config.write("serial = 'pty'\n")
    config.write("localtime = 1\n")

    vif_data = []
622
623
624
625
    nic_type = instance.hvparams[constants.HV_NIC_TYPE]
    if nic_type is None:
      # ensure old instances don't change
      nic_type_str = ", type=ioemu"
626
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
627
628
629
      nic_type_str = ", type=paravirtualized"
    else:
      nic_type_str = ", model=%s, type=ioemu" % nic_type
630
    for nic in instance.nics:
631
      nic_str = "mac=%s, bridge=%s%s" % (nic.mac, nic.bridge, nic_type_str)
632
633
634
635
636
637
      ip = getattr(nic, "ip", None)
      if ip is not None:
        nic_str += ", ip=%s" % ip
      vif_data.append("'%s'" % nic_str)

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

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

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

668
    return True