Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ a744b676

History | View | Annotate | Download (21.7 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 logging
27
from cStringIO import StringIO
28

    
29
from ganeti import constants
30
from ganeti import errors
31
from ganeti import utils
32
from ganeti.hypervisor import hv_base
33
from ganeti import netutils
34

    
35

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

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

42
  """
43
  CAN_MIGRATE = True
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 (TypeError, 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, name=None):
195
    """Stop an instance.
196

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

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

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

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

    
217
    if ini_info is None:
218
      raise errors.HypervisorError("Failed to reboot instance %s,"
219
                                   " not running" % instance.name)
220

    
221
    result = utils.RunCmd(["xm", "reboot", instance.name])
222
    if result.failed:
223
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
224
                                   (instance.name, result.fail_reason,
225
                                    result.output))
226

    
227
    def _CheckInstance():
228
      new_info = self.GetInstanceInfo(instance.name)
229

    
230
      # check if the domain ID has changed or the run time has decreased
231
      if (new_info is not None and
232
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
233
        return
234

    
235
      raise utils.RetryAgain()
236

    
237
    try:
238
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
239
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
240
    except utils.RetryTimeout:
241
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
242
                                   " did not reboot in the expected interval" %
243
                                   (instance.name, ))
244

    
245
  def GetNodeInfo(self):
246
    """Return information about the node.
247

248
    @return: a dict with the following keys (memory values in MiB):
249
          - memory_total: the total memory size on the node
250
          - memory_free: the available memory on the node for instances
251
          - memory_dom0: the memory used by the node itself, if available
252
          - nr_cpus: total number of CPUs
253
          - nr_nodes: in a NUMA system, the number of domains
254
          - nr_sockets: the number of physical CPU sockets in the node
255

256
    """
257
    # note: in xen 3, memory has changed to total_memory
258
    result = utils.RunCmd(["xm", "info"])
259
    if result.failed:
260
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
261
                    result.output)
262
      return None
263

    
264
    xmoutput = result.stdout.splitlines()
265
    result = {}
266
    cores_per_socket = threads_per_core = nr_cpus = None
267
    for line in xmoutput:
268
      splitfields = line.split(":", 1)
269

    
270
      if len(splitfields) > 1:
271
        key = splitfields[0].strip()
272
        val = splitfields[1].strip()
273
        if key == 'memory' or key == 'total_memory':
274
          result['memory_total'] = int(val)
275
        elif key == 'free_memory':
276
          result['memory_free'] = int(val)
277
        elif key == 'nr_cpus':
278
          nr_cpus = result['cpu_total'] = int(val)
279
        elif key == 'nr_nodes':
280
          result['cpu_nodes'] = int(val)
281
        elif key == 'cores_per_socket':
282
          cores_per_socket = int(val)
283
        elif key == 'threads_per_core':
284
          threads_per_core = int(val)
285

    
286
    if (cores_per_socket is not None and
287
        threads_per_core is not None and nr_cpus is not None):
288
      result['cpu_sockets'] = nr_cpus / (cores_per_socket * threads_per_core)
289

    
290
    dom0_info = self.GetInstanceInfo("Domain-0")
291
    if dom0_info is not None:
292
      result['memory_dom0'] = dom0_info[2]
293

    
294
    return result
295

    
296
  @classmethod
297
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
298
    """Return a command for connecting to the console of an instance.
299

300
    """
301
    return "xm console %s" % instance.name
302

    
303

    
304
  def Verify(self):
305
    """Verify the hypervisor.
306

307
    For Xen, this verifies that the xend process is running.
308

309
    """
310
    result = utils.RunCmd(["xm", "info"])
311
    if result.failed:
312
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
313

    
314
  @staticmethod
315
  def _GetConfigFileDiskData(block_devices):
316
    """Get disk directive for xen config file.
317

318
    This method builds the xen config disk directive according to the
319
    given disk_template and block_devices.
320

321
    @param block_devices: list of tuples (cfdev, rldev):
322
        - cfdev: dict containing ganeti config disk part
323
        - rldev: ganeti.bdev.BlockDev object
324

325
    @return: string containing disk directive for xen instance config file
326

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

    
351
    return disk_data
352

    
353
  def MigrationInfo(self, instance):
354
    """Get instance information to perform a migration.
355

356
    @type instance: L{objects.Instance}
357
    @param instance: instance to be migrated
358
    @rtype: string
359
    @return: content of the xen config file
360

361
    """
362
    return self._ReadConfigFile(instance.name)
363

    
364
  def AcceptInstance(self, instance, info, target):
365
    """Prepare to accept an instance.
366

367
    @type instance: L{objects.Instance}
368
    @param instance: instance to be accepted
369
    @type info: string
370
    @param info: content of the xen config file on the source node
371
    @type target: string
372
    @param target: target host (usually ip), on this node
373

374
    """
375
    pass
376

    
377
  def FinalizeMigration(self, instance, info, success):
378
    """Finalize an instance migration.
379

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

383
    @type instance: L{objects.Instance}
384
    @param instance: instance whose migration is being finalized
385
    @type info: string
386
    @param info: content of the xen config file on the source node
387
    @type success: boolean
388
    @param success: whether the migration was a success or a failure
389

390
    """
391
    if success:
392
      self._WriteConfigFileStatic(instance.name, info)
393

    
394
  def MigrateInstance(self, instance, target, live):
395
    """Migrate an instance to a target node.
396

397
    The migration will not be attempted if the instance is not
398
    currently running.
399

400
    @type instance: L{objects.Instance}
401
    @param instance: the instance to be migrated
402
    @type target: string
403
    @param target: ip address of the target node
404
    @type live: boolean
405
    @param live: perform a live migration
406

407
    """
408
    if self.GetInstanceInfo(instance.name) is None:
409
      raise errors.HypervisorError("Instance not running, cannot migrate")
410

    
411
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
412

    
413
    if not netutils.TcpPing(target, port, live_port_needed=True):
414
      raise errors.HypervisorError("Remote host %s not listening on port"
415
                                   " %s, cannot migrate" % (target, port))
416

    
417
    args = ["xm", "migrate", "-p", "%d" % port]
418
    if live:
419
      args.append("-l")
420
    args.extend([instance.name, target])
421
    result = utils.RunCmd(args)
422
    if result.failed:
423
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
424
                                   (instance.name, result.output))
425
    # remove old xen file after migration succeeded
426
    try:
427
      self._RemoveConfigFile(instance.name)
428
    except EnvironmentError:
429
      logging.exception("Failure while removing instance config file")
430

    
431
  @classmethod
432
  def PowercycleNode(cls):
433
    """Xen-specific powercycle.
434

435
    This first does a Linux reboot (which triggers automatically a Xen
436
    reboot), and if that fails it tries to do a Xen reboot. The reason
437
    we don't try a Xen reboot first is that the xen reboot launches an
438
    external command which connects to the Xen hypervisor, and that
439
    won't work in case the root filesystem is broken and/or the xend
440
    daemon is not working.
441

442
    """
443
    try:
444
      cls.LinuxPowercycle()
445
    finally:
446
      utils.RunCmd(["xm", "debug", "R"])
447

    
448

    
449
class XenPvmHypervisor(XenHypervisor):
450
  """Xen PVM hypervisor interface"""
451

    
452
  PARAMETERS = {
453
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
454
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
455
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
456
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
457
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
458
    constants.HV_ROOT_PATH: hv_base.REQUIRED_CHECK,
459
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
460
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
461
    }
462

    
463
  @classmethod
464
  def _WriteConfigFile(cls, instance, block_devices):
465
    """Write the Xen config file for the instance.
466

467
    """
468
    hvp = instance.hvparams
469
    config = StringIO()
470
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
471

    
472
    # if bootloader is True, use bootloader instead of kernel and ramdisk
473
    # parameters.
474
    if hvp[constants.HV_USE_BOOTLOADER]:
475
      # bootloader handling
476
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
477
      if bootloader_path:
478
        config.write("bootloader = '%s'\n" % bootloader_path)
479
      else:
480
        raise errors.HypervisorError("Bootloader enabled, but missing"
481
                                     " bootloader path")
482

    
483
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
484
      if bootloader_args:
485
        config.write("bootargs = '%s'\n" % bootloader_args)
486
    else:
487
      # kernel handling
488
      kpath = hvp[constants.HV_KERNEL_PATH]
489
      config.write("kernel = '%s'\n" % kpath)
490

    
491
      # initrd handling
492
      initrd_path = hvp[constants.HV_INITRD_PATH]
493
      if initrd_path:
494
        config.write("ramdisk = '%s'\n" % initrd_path)
495

    
496
    # rest of the settings
497
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
498
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
499
    config.write("name = '%s'\n" % instance.name)
500

    
501
    vif_data = []
502
    for nic in instance.nics:
503
      nic_str = "mac=%s" % (nic.mac)
504
      ip = getattr(nic, "ip", None)
505
      if ip is not None:
506
        nic_str += ", ip=%s" % ip
507
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
508
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
509
      vif_data.append("'%s'" % nic_str)
510

    
511
    disk_data = cls._GetConfigFileDiskData(block_devices)
512

    
513
    config.write("vif = [%s]\n" % ",".join(vif_data))
514
    config.write("disk = [%s]\n" % ",".join(disk_data))
515

    
516
    config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
517
    config.write("on_poweroff = 'destroy'\n")
518
    config.write("on_reboot = 'restart'\n")
519
    config.write("on_crash = 'restart'\n")
520
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
521
    # just in case it exists
522
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
523
    try:
524
      utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
525
    except EnvironmentError, err:
526
      raise errors.HypervisorError("Cannot write Xen instance confile"
527
                                   " file /etc/xen/%s: %s" %
528
                                   (instance.name, err))
529

    
530
    return True
531

    
532

    
533
class XenHvmHypervisor(XenHypervisor):
534
  """Xen HVM hypervisor interface"""
535

    
536
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
537
    constants.VNC_PASSWORD_FILE,
538
    ]
539

    
540
  PARAMETERS = {
541
    constants.HV_ACPI: hv_base.NO_CHECK,
542
    constants.HV_BOOT_ORDER: (True, ) +
543
      (lambda x: x and len(x.strip("acdn")) == 0,
544
       "Invalid boot order specified, must be one or more of [acdn]",
545
       None, None),
546
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
547
    constants.HV_DISK_TYPE:
548
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
549
    constants.HV_NIC_TYPE:
550
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
551
    constants.HV_PAE: hv_base.NO_CHECK,
552
    constants.HV_VNC_BIND_ADDRESS:
553
      (False, netutils.IsValidIP4,
554
       "VNC bind address is not a valid IP address", None, None),
555
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
556
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
557
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
558
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
559
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
560
    }
561

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

566
    """
567
    hvp = instance.hvparams
568

    
569
    config = StringIO()
570
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
571

    
572
    # kernel handling
573
    kpath = hvp[constants.HV_KERNEL_PATH]
574
    config.write("kernel = '%s'\n" % kpath)
575

    
576
    config.write("builder = 'hvm'\n")
577
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
578
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
579
    config.write("name = '%s'\n" % instance.name)
580
    if hvp[constants.HV_PAE]:
581
      config.write("pae = 1\n")
582
    else:
583
      config.write("pae = 0\n")
584
    if hvp[constants.HV_ACPI]:
585
      config.write("acpi = 1\n")
586
    else:
587
      config.write("acpi = 0\n")
588
    config.write("apic = 1\n")
589
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
590
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
591
    config.write("sdl = 0\n")
592
    config.write("usb = 1\n")
593
    config.write("usbdevice = 'tablet'\n")
594
    config.write("vnc = 1\n")
595
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
596
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
597
    else:
598
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
599

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

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

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

    
617
    config.write("serial = 'pty'\n")
618
    if hvp[constants.HV_USE_LOCALTIME]:
619
      config.write("localtime = 1\n")
620

    
621
    vif_data = []
622
    nic_type = hvp[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%s" % (nic.mac, nic_type_str)
632
      ip = getattr(nic, "ip", None)
633
      if ip is not None:
634
        nic_str += ", ip=%s" % ip
635
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
636
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
637
      vif_data.append("'%s'" % nic_str)
638

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

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

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

    
667
    return True