Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ c0c3fa27

History | View | Annotate | Download (21.6 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

    
34

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

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

41
  """
42
  CAN_MIGRATE = True
43
  REBOOT_RETRY_COUNT = 60
44
  REBOOT_RETRY_INTERVAL = 10
45

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

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

55
    """
56
    raise NotImplementedError
57

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
122
      raise errors.HypervisorError(errmsg)
123

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

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

    
146
    return result
147

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

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

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

159
    @param instance_name: the instance name
160

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
234
      raise utils.RetryAgain()
235

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

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

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

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

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

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

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

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

    
293
    return result
294

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

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

    
302

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

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

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

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

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

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

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

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

    
350
    return disk_data
351

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

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

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

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

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

373
    """
374
    pass
375

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
447

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

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

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

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

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

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

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

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

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

    
510
    disk_data = cls._GetConfigFileDiskData(block_devices)
511

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

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

    
529
    return True
530

    
531

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

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

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

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

565
    """
566
    hvp = instance.hvparams
567

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

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

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

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

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

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

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

    
620
    vif_data = []
621
    nic_type = hvp[constants.HV_NIC_TYPE]
622
    if nic_type is None:
623
      # ensure old instances don't change
624
      nic_type_str = ", type=ioemu"
625
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
626
      nic_type_str = ", type=paravirtualized"
627
    else:
628
      nic_type_str = ", model=%s, type=ioemu" % nic_type
629
    for nic in instance.nics:
630
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
631
      ip = getattr(nic, "ip", None)
632
      if ip is not None:
633
        nic_str += ", ip=%s" % ip
634
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
635
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
636
      vif_data.append("'%s'" % nic_str)
637

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

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

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

    
666
    return True