Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 78411c60

History | View | Annotate | Download (21.3 kB)

1
#
2
#
3

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

    
21

    
22
"""Xen hypervisors
23

24
"""
25

    
26
import os
27
import os.path
28
import logging
29
from cStringIO import StringIO
30

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

    
36

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

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

43
  """
44
  REBOOT_RETRY_COUNT = 60
45
  REBOOT_RETRY_INTERVAL = 10
46

    
47
  ANCILLARY_FILES = [
48
    '/etc/xen/xend-config.sxp',
49
    '/etc/xen/scripts/vif-bridge',
50
    ]
51

    
52
  @classmethod
53
  def _WriteConfigFile(cls, instance, block_devices):
54
    """Write the Xen config file for the instance.
55

56
    """
57
    raise NotImplementedError
58

    
59
  @staticmethod
60
  def _WriteConfigFileStatic(instance_name, data):
61
    """Write the Xen config file for the instance.
62

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

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

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

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

    
79
  @staticmethod
80
  def _RemoveConfigFile(instance_name):
81
    """Remove the xen configuration file.
82

83
    """
84
    utils.RemoveFile("/etc/xen/%s" % instance_name)
85

    
86
  @staticmethod
87
  def _RunXmList(xmlist_errors):
88
    """Helper function for L{_GetXMList} to run "xm list".
89

90
    """
91
    result = utils.RunCmd(["xm", "list"])
92
    if result.failed:
93
      logging.error("xm list failed (%s): %s", result.fail_reason,
94
                    result.output)
95
      xmlist_errors.append(result)
96
      raise utils.RetryAgain()
97

    
98
    # skip over the heading
99
    return result.stdout.splitlines()[1:]
100

    
101
  @classmethod
102
  def _GetXMList(cls, include_node):
103
    """Return the list of running instances.
104

105
    If the include_node argument is True, then we return information
106
    for dom0 also, otherwise we filter that from the return value.
107

108
    @return: list of (name, id, memory, vcpus, state, time spent)
109

110
    """
111
    xmlist_errors = []
112
    try:
113
      lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
114
    except utils.RetryTimeout:
115
      if xmlist_errors:
116
        xmlist_result = xmlist_errors.pop()
117

    
118
        errmsg = ("xm list failed, timeout exceeded (%s): %s" %
119
                  (xmlist_result.fail_reason, xmlist_result.output))
120
      else:
121
        errmsg = "xm list failed"
122

    
123
      raise errors.HypervisorError(errmsg)
124

    
125
    result = []
126
    for line in lines:
127
      # The format of lines is:
128
      # Name      ID Mem(MiB) VCPUs State  Time(s)
129
      # Domain-0   0  3418     4 r-----    266.2
130
      data = line.split()
131
      if len(data) != 6:
132
        raise errors.HypervisorError("Can't parse output of xm list,"
133
                                     " line: %s" % line)
134
      try:
135
        data[1] = int(data[1])
136
        data[2] = int(data[2])
137
        data[3] = int(data[3])
138
        data[5] = float(data[5])
139
      except ValueError, err:
140
        raise errors.HypervisorError("Can't parse output of xm list,"
141
                                     " line: %s, error: %s" % (line, err))
142

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

    
147
    return result
148

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

152
    """
153
    xm_list = self._GetXMList(False)
154
    names = [info[0] for info in xm_list]
155
    return names
156

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

160
    @param instance_name: the instance name
161

162
    @return: tuple (name, id, memory, vcpus, stat, times)
163

164
    """
165
    xm_list = self._GetXMList(instance_name=="Domain-0")
166
    result = None
167
    for data in xm_list:
168
      if data[0] == instance_name:
169
        result = data
170
        break
171
    return result
172

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

176
    @return: list of tuples (name, id, memory, vcpus, stat, times)
177

178
    """
179
    xm_list = self._GetXMList(False)
180
    return xm_list
181

    
182
  def StartInstance(self, instance, block_devices):
183
    """Start an instance.
184

185
    """
186
    self._WriteConfigFile(instance, block_devices)
187
    result = utils.RunCmd(["xm", "create", instance.name])
188

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

    
194
  def StopInstance(self, instance, force=False, retry=False):
195
    """Stop an instance.
196

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

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

    
210
  def RebootInstance(self, instance):
211
    """Reboot an instance.
212

213
    """
214
    ini_info = self.GetInstanceInfo(instance.name)
215

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

    
222
    def _CheckInstance():
223
      new_info = self.GetInstanceInfo(instance.name)
224

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

    
229
      raise utils.RetryAgain()
230

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

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

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

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

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

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

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

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

    
288
    return result
289

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

294
    """
295
    return "xm console %s" % instance.name
296

    
297

    
298
  def Verify(self):
299
    """Verify the hypervisor.
300

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

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

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

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

315
    @param disk_template: string containing instance disk template
316
    @param block_devices: list of tuples (cfdev, rldev):
317
        - cfdev: dict containing ganeti config disk part
318
        - rldev: ganeti.bdev.BlockDev object
319

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

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

    
346
    return disk_data
347

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

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

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

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

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

369
    """
370
    pass
371

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

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

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

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

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

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

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

402
    """
403
    if self.GetInstanceInfo(instance.name) is None:
404
      raise errors.HypervisorError("Instance not running, cannot migrate")
405
    args = ["xm", "migrate"]
406
    if live:
407
      args.append("-l")
408
    args.extend([instance.name, target])
409
    result = utils.RunCmd(args)
410
    if result.failed:
411
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
412
                                   (instance.name, result.output))
413
    # remove old xen file after migration succeeded
414
    try:
415
      self._RemoveConfigFile(instance.name)
416
    except EnvironmentError:
417
      logging.exception("Failure while removing instance config file")
418

    
419
  @classmethod
420
  def PowercycleNode(cls):
421
    """Xen-specific powercycle.
422

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

430
    """
431
    try:
432
      cls.LinuxPowercycle()
433
    finally:
434
      utils.RunCmd(["xm", "debug", "R"])
435

    
436

    
437
class XenPvmHypervisor(XenHypervisor):
438
  """Xen PVM hypervisor interface"""
439

    
440
  PARAMETERS = {
441
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
442
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
443
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
444
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
445
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
446
    constants.HV_ROOT_PATH: hv_base.REQUIRED_CHECK,
447
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
448
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
449
    }
450

    
451
  @classmethod
452
  def _WriteConfigFile(cls, instance, block_devices):
453
    """Write the Xen config file for the instance.
454

455
    """
456
    hvp = instance.hvparams
457
    config = StringIO()
458
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
459

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

    
471
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
472
      if bootloader_args:
473
        config.write("bootargs = '%s'\n" % bootloader_args)
474
    else:
475
      # kernel handling
476
      kpath = hvp[constants.HV_KERNEL_PATH]
477
      config.write("kernel = '%s'\n" % kpath)
478

    
479
      # initrd handling
480
      initrd_path = hvp[constants.HV_INITRD_PATH]
481
      if initrd_path:
482
        config.write("ramdisk = '%s'\n" % initrd_path)
483

    
484
    # rest of the settings
485
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
486
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
487
    config.write("name = '%s'\n" % instance.name)
488

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

    
499
    config.write("vif = [%s]\n" % ",".join(vif_data))
500
    config.write("disk = [%s]\n" % ",".join(
501
                 cls._GetConfigFileDiskData(instance.disk_template,
502
                                            block_devices)))
503

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

    
518
    return True
519

    
520

    
521
class XenHvmHypervisor(XenHypervisor):
522
  """Xen HVM hypervisor interface"""
523

    
524
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
525
    constants.VNC_PASSWORD_FILE,
526
    ]
527

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

    
549
  @classmethod
550
  def _WriteConfigFile(cls, instance, block_devices):
551
    """Create a Xen 3.1 HVM config file.
552

553
    """
554
    hvp = instance.hvparams
555

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

    
559
    # kernel handling
560
    kpath = hvp[constants.HV_KERNEL_PATH]
561
    config.write("kernel = '%s'\n" % kpath)
562

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

    
587
    if instance.network_port > constants.VNC_BASE_PORT:
588
      display = instance.network_port - constants.VNC_BASE_PORT
589
      config.write("vncdisplay = %s\n" % display)
590
      config.write("vncunused = 0\n")
591
    else:
592
      config.write("# vncdisplay = 1\n")
593
      config.write("vncunused = 1\n")
594

    
595
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
596
    try:
597
      password = utils.ReadFile(vnc_pwd_file)
598
    except EnvironmentError, err:
599
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
600
                                   (vnc_pwd_file, err))
601

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

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

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

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

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

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

    
654
    return True