Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 58d38b02

History | View | Annotate | Download (21.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 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
    }
449

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

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

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

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

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

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

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

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

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

    
517
    return True
518

    
519

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

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

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

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

551
    """
552
    hvp = instance.hvparams
553

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

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

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

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

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

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

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

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

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

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

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

    
652
    return True