Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 5b460366

History | View | Annotate | Download (22.2 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 time
29
import logging
30
from cStringIO import StringIO
31

    
32
from ganeti import constants
33
from ganeti import errors
34
from ganeti import utils
35
from ganeti.hypervisor import hv_base
36

    
37

    
38
class XenHypervisor(hv_base.BaseHypervisor):
39
  """Xen generic hypervisor interface
40

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

44
  """
45

    
46
  @classmethod
47
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
48
    """Write the Xen config file for the instance.
49

50
    """
51
    raise NotImplementedError
52

    
53
  @staticmethod
54
  def _WriteConfigFileStatic(instance_name, data):
55
    """Write the Xen config file for the instance.
56

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

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

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

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

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

77
    """
78
    utils.RemoveFile("/etc/xen/%s" % instance_name)
79

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

84
    If the include_node argument is True, then we return information
85
    for dom0 also, otherwise we filter that from the return value.
86

87
    @return: list of (name, id, memory, vcpus, state, time spent)
88

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

    
98
    if result.failed:
99
      raise errors.HypervisorError("xm list failed, retries"
100
                                   " exceeded (%s): %s" %
101
                                   (result.fail_reason, result.stderr))
102

    
103
    # skip over the heading
104
    lines = result.stdout.splitlines()[1:]
105
    result = []
106
    for line in lines:
107
      # The format of lines is:
108
      # Name      ID Mem(MiB) VCPUs State  Time(s)
109
      # Domain-0   0  3418     4 r-----    266.2
110
      data = line.split()
111
      if len(data) != 6:
112
        raise errors.HypervisorError("Can't parse output of xm list,"
113
                                     " line: %s" % line)
114
      try:
115
        data[1] = int(data[1])
116
        data[2] = int(data[2])
117
        data[3] = int(data[3])
118
        data[5] = float(data[5])
119
      except ValueError, err:
120
        raise errors.HypervisorError("Can't parse output of xm list,"
121
                                     " line: %s, error: %s" % (line, err))
122

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

    
127
    return result
128

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

132
    """
133
    xm_list = self._GetXMList(False)
134
    names = [info[0] for info in xm_list]
135
    return names
136

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

140
    @param instance_name: the instance name
141

142
    @return: tuple (name, id, memory, vcpus, stat, times)
143

144
    """
145
    xm_list = self._GetXMList(instance_name=="Domain-0")
146
    result = None
147
    for data in xm_list:
148
      if data[0] == instance_name:
149
        result = data
150
        break
151
    return result
152

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

156
    @return: list of tuples (name, id, memory, vcpus, stat, times)
157

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

    
162
  def StartInstance(self, instance, block_devices, extra_args):
163
    """Start an instance.
164

165
    """
166
    self._WriteConfigFile(instance, block_devices, extra_args)
167
    result = utils.RunCmd(["xm", "create", instance.name])
168

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

    
174
  def StopInstance(self, instance, force=False):
175
    """Stop an instance.
176

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

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

    
189
  def RebootInstance(self, instance):
190
    """Reboot an instance.
191

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

    
195
    if result.failed:
196
      raise errors.HypervisorError("Failed to reboot instance %s: %s" %
197
                                   (instance.name, result.fail_reason))
198

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

202
    @return: a dict with the following keys (memory values in MiB):
203
          - memory_total: the total memory size on the node
204
          - memory_free: the available memory on the node for instances
205
          - memory_dom0: the memory used by the node itself, if available
206
          - nr_cpus: total number of CPUs
207
          - nr_nodes: in a NUMA system, the number of domains
208
          - nr_sockets: the number of physical CPU sockets in the node
209

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

    
218
    xmoutput = result.stdout.splitlines()
219
    result = {}
220
    cores_per_socket = threads_per_core = nr_cpus = None
221
    for line in xmoutput:
222
      splitfields = line.split(":", 1)
223

    
224
      if len(splitfields) > 1:
225
        key = splitfields[0].strip()
226
        val = splitfields[1].strip()
227
        if key == 'memory' or key == 'total_memory':
228
          result['memory_total'] = int(val)
229
        elif key == 'free_memory':
230
          result['memory_free'] = int(val)
231
        elif key == 'nr_cpus':
232
          nr_cpus = result['cpu_total'] = int(val)
233
        elif key == 'nr_nodes':
234
          result['cpu_nodes'] = int(val)
235
        elif key == 'cores_per_socket':
236
          cores_per_socket = int(val)
237
        elif key == 'threads_per_core':
238
          threads_per_core = int(val)
239

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

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

    
248
    return result
249

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

254
    """
255
    return "xm console %s" % instance.name
256

    
257

    
258
  def Verify(self):
259
    """Verify the hypervisor.
260

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

263
    """
264
    result = utils.RunCmd(["xm", "info"])
265
    if result.failed:
266
      return "'xm info' failed: %s" % result.fail_reason
267

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

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

275
    @param disk_template: string containing instance disk template
276
    @param block_devices: list of tuples (cfdev, rldev):
277
        - cfdev: dict containing ganeti config disk part
278
        - rldev: ganeti.bdev.BlockDev object
279

280
    @return: string containing disk directive for xen instance config file
281

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

    
306
    return disk_data
307

    
308
  def MigrationInfo(self, instance):
309
    """Get instance information to perform a migration.
310

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

316
    """
317
    return self._ReadConfigFile(instance.name)
318

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

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

329
    """
330
    pass
331

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

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

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

345
    """
346
    if success:
347
      self._WriteConfigFileStatic(instance.name, info)
348

    
349
  def MigrateInstance(self, instance, target, live):
350
    """Migrate an instance to a target node.
351

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

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

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

    
379

    
380
class XenPvmHypervisor(XenHypervisor):
381
  """Xen PVM hypervisor interface"""
382

    
383
  PARAMETERS = [
384
    constants.HV_KERNEL_PATH,
385
    constants.HV_INITRD_PATH,
386
    constants.HV_ROOT_PATH,
387
    ]
388

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

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

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

400
    """
401
    super(XenPvmHypervisor, cls).CheckParameterSyntax(hvparams)
402

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

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

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

    
412
    if hvparams[constants.HV_INITRD_PATH]:
413
      if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
414
        raise errors.HypervisorError("The initrd path must be an absolute path"
415
                                     ", if defined")
416

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

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

423
    """
424
    super(XenPvmHypervisor, self).ValidateParameters(hvparams)
425

    
426
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
427
    if not os.path.isfile(kernel_path):
428
      raise errors.HypervisorError("Instance kernel '%s' not found or"
429
                                   " not a file" % kernel_path)
430
    initrd_path = hvparams[constants.HV_INITRD_PATH]
431
    if initrd_path and not os.path.isfile(initrd_path):
432
      raise errors.HypervisorError("Instance initrd '%s' not found or"
433
                                   " not a file" % initrd_path)
434

    
435
  @classmethod
436
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
437
    """Write the Xen config file for the instance.
438

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

    
443
    # kernel handling
444
    kpath = instance.hvparams[constants.HV_KERNEL_PATH]
445
    config.write("kernel = '%s'\n" % kpath)
446

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

    
452
    # rest of the settings
453
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
454
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
455
    config.write("name = '%s'\n" % instance.name)
456

    
457
    vif_data = []
458
    for nic in instance.nics:
459
      nic_str = "mac=%s, bridge=%s" % (nic.mac, nic.bridge)
460
      ip = getattr(nic, "ip", None)
461
      if ip is not None:
462
        nic_str += ", ip=%s" % ip
463
      vif_data.append("'%s'" % nic_str)
464

    
465
    config.write("vif = [%s]\n" % ",".join(vif_data))
466
    config.write("disk = [%s]\n" % ",".join(
467
                 cls._GetConfigFileDiskData(instance.disk_template,
468
                                            block_devices)))
469

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

    
487
    return True
488

    
489

    
490
class XenHvmHypervisor(XenHypervisor):
491
  """Xen HVM hypervisor interface"""
492

    
493
  PARAMETERS = [
494
    constants.HV_ACPI,
495
    constants.HV_BOOT_ORDER,
496
    constants.HV_CDROM_IMAGE_PATH,
497
    constants.HV_DISK_TYPE,
498
    constants.HV_NIC_TYPE,
499
    constants.HV_PAE,
500
    constants.HV_VNC_BIND_ADDRESS,
501
    ]
502

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

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

    
536
    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
537
    if iso_path and not os.path.isabs(iso_path):
538
      raise errors.HypervisorError("The path to the HVM CDROM image must"
539
                                   " be an absolute path or None, not %s" %
540
                                   iso_path)
541

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

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

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

552
    """
553
    super(XenHvmHypervisor, self).ValidateParameters(hvparams)
554

    
555
    # hvm_cdrom_image_path verification
556
    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
557
    if iso_path and not os.path.isfile(iso_path):
558
      raise errors.HypervisorError("The HVM CDROM image must either be a"
559
                                   " regular file or a symlink pointing to"
560
                                   " an existing regular file, not %s" %
561
                                   iso_path)
562

    
563
  @classmethod
564
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
565
    """Create a Xen 3.1 HVM config file.
566

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

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

    
608
    try:
609
      password = utils.ReadFile(constants.VNC_PASSWORD_FILE)
610
    except EnvironmentError, err:
611
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
612
                                   (constants.VNC_PASSWORD_FILE, err))
613

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

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

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

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

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

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

    
666
    return True