Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 3213d3c8

History | View | Annotate | Download (22.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 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.output))
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, %s" %
187
                                   (instance.name, result.fail_reason,
188
                                    result.output))
189

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

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

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

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

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

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

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

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

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

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

    
250
    return result
251

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

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

    
259

    
260
  def Verify(self):
261
    """Verify the hypervisor.
262

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

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

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

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

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

282
    @return: string containing disk directive for xen instance config file
283

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

    
308
    return disk_data
309

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

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

318
    """
319
    return self._ReadConfigFile(instance.name)
320

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

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

331
    """
332
    pass
333

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

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

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

347
    """
348
    if success:
349
      self._WriteConfigFileStatic(instance.name, info)
350

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

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

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

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

    
381

    
382
class XenPvmHypervisor(XenHypervisor):
383
  """Xen PVM hypervisor interface"""
384

    
385
  PARAMETERS = [
386
    constants.HV_KERNEL_PATH,
387
    constants.HV_INITRD_PATH,
388
    constants.HV_ROOT_PATH,
389
    ]
390

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

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

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

402
    """
403
    super(XenPvmHypervisor, cls).CheckParameterSyntax(hvparams)
404

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

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

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

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

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

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

425
    """
426
    super(XenPvmHypervisor, self).ValidateParameters(hvparams)
427

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

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

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

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

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

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

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

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

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

    
489
    return True
490

    
491

    
492
class XenHvmHypervisor(XenHypervisor):
493
  """Xen HVM hypervisor interface"""
494

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

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

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

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

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

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

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

554
    """
555
    super(XenHvmHypervisor, self).ValidateParameters(hvparams)
556

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

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

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

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

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

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

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

    
621
    vif_data = []
622
    nic_type = instance.hvparams[constants.HV_NIC_TYPE]
623
    if nic_type is None:
624
      # ensure old instances don't change
625
      nic_type_str = ", type=ioemu"
626
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
627
      nic_type_str = ", type=paravirtualized"
628
    else:
629
      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
      ip = getattr(nic, "ip", None)
633
      if ip is not None:
634
        nic_str += ", ip=%s" % ip
635
      vif_data.append("'%s'" % nic_str)
636

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

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

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

    
668
    return True