Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ d08f6067

History | View | Annotate | Download (22 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 Xen HVM"
519
                                   " hypervisor" % nic_type)
520
    disk_type = hvparams[constants.HV_DISK_TYPE]
521
    if disk_type not in constants.HT_HVM_VALID_DISK_TYPES:
522
      raise errors.HypervisorError("Invalid disk type %s specified for Xen HVM"
523
                                   " hypervisor" % disk_type)
524
    # vnc_bind_address verification
525
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
526
    if vnc_bind_address is not None:
527
      if not utils.IsValidIP(vnc_bind_address):
528
        raise errors.OpPrereqError("given VNC bind address '%s' doesn't look"
529
                                   " like a valid IP address" %
530
                                   vnc_bind_address)
531

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

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

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

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

548
    """
549
    super(XenHvmHypervisor, self).ValidateParameters(hvparams)
550

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

    
559
  @classmethod
560
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
561
    """Create a Xen 3.1 HVM config file.
562

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

    
596
    if instance.network_port > constants.VNC_BASE_PORT:
597
      display = instance.network_port - constants.VNC_BASE_PORT
598
      config.write("vncdisplay = %s\n" % display)
599
      config.write("vncunused = 0\n")
600
    else:
601
      config.write("# vncdisplay = 1\n")
602
      config.write("vncunused = 1\n")
603

    
604
    try:
605
      password = utils.ReadFile(constants.VNC_PASSWORD_FILE)
606
    except EnvironmentError, err:
607
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
608
                                   (constants.VNC_PASSWORD_FILE, err))
609

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

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

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

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

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

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

    
662
    return True