Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 55cc0a44

History | View | Annotate | Download (22.3 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010 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
from ganeti import objects
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
  CAN_MIGRATE = True
45
  REBOOT_RETRY_COUNT = 60
46
  REBOOT_RETRY_INTERVAL = 10
47

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

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

57
    """
58
    raise NotImplementedError
59

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
124
      raise errors.HypervisorError(errmsg)
125

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

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

    
148
    return result
149

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

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

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

161
    @param instance_name: the instance name
162

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
236
      raise utils.RetryAgain()
237

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

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

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

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

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

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

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

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

    
295
    return result
296

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

301
    """
302
    return objects.InstanceConsole(instance=instance.name,
303
                                   kind=constants.CONS_SSH,
304
                                   host=instance.primary_node,
305
                                   user=constants.GANETI_RUNAS,
306
                                   command=["xm", "console", instance.name])
307

    
308
  def Verify(self):
309
    """Verify the hypervisor.
310

311
    For Xen, this verifies that the xend process is running.
312

313
    """
314
    result = utils.RunCmd(["xm", "info"])
315
    if result.failed:
316
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
317

    
318
  @staticmethod
319
  def _GetConfigFileDiskData(block_devices, blockdev_prefix):
320
    """Get disk directive for xen config file.
321

322
    This method builds the xen config disk directive according to the
323
    given disk_template and block_devices.
324

325
    @param block_devices: list of tuples (cfdev, rldev):
326
        - cfdev: dict containing ganeti config disk part
327
        - rldev: ganeti.bdev.BlockDev object
328
    @param blockdev_prefix: a string containing blockdevice prefix,
329
                            e.g. "sd" for /dev/sda
330

331
    @return: string containing disk directive for xen instance config file
332

333
    """
334
    FILE_DRIVER_MAP = {
335
      constants.FD_LOOP: "file",
336
      constants.FD_BLKTAP: "tap:aio",
337
      }
338
    disk_data = []
339
    if len(block_devices) > 24:
340
      # 'z' - 'a' = 24
341
      raise errors.HypervisorError("Too many disks")
342
    namespace = [blockdev_prefix + chr(i + ord('a')) for i in range(24)]
343
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
344
      if cfdev.mode == constants.DISK_RDWR:
345
        mode = "w"
346
      else:
347
        mode = "r"
348
      if cfdev.dev_type == constants.LD_FILE:
349
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
350
                                  dev_path, sd_name, mode)
351
      else:
352
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
353
      disk_data.append(line)
354

    
355
    return disk_data
356

    
357
  def MigrationInfo(self, instance):
358
    """Get instance information to perform a migration.
359

360
    @type instance: L{objects.Instance}
361
    @param instance: instance to be migrated
362
    @rtype: string
363
    @return: content of the xen config file
364

365
    """
366
    return self._ReadConfigFile(instance.name)
367

    
368
  def AcceptInstance(self, instance, info, target):
369
    """Prepare to accept an instance.
370

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

378
    """
379
    pass
380

    
381
  def FinalizeMigration(self, instance, info, success):
382
    """Finalize an instance migration.
383

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

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

394
    """
395
    if success:
396
      self._WriteConfigFileStatic(instance.name, info)
397

    
398
  def MigrateInstance(self, instance, target, live):
399
    """Migrate an instance to a target node.
400

401
    The migration will not be attempted if the instance is not
402
    currently running.
403

404
    @type instance: L{objects.Instance}
405
    @param instance: the instance to be migrated
406
    @type target: string
407
    @param target: ip address of the target node
408
    @type live: boolean
409
    @param live: perform a live migration
410

411
    """
412
    if self.GetInstanceInfo(instance.name) is None:
413
      raise errors.HypervisorError("Instance not running, cannot migrate")
414

    
415
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
416

    
417
    if not netutils.TcpPing(target, port, live_port_needed=True):
418
      raise errors.HypervisorError("Remote host %s not listening on port"
419
                                   " %s, cannot migrate" % (target, port))
420

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

    
435
  @classmethod
436
  def PowercycleNode(cls):
437
    """Xen-specific powercycle.
438

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

446
    """
447
    try:
448
      cls.LinuxPowercycle()
449
    finally:
450
      utils.RunCmd(["xm", "debug", "R"])
451

    
452

    
453
class XenPvmHypervisor(XenHypervisor):
454
  """Xen PVM hypervisor interface"""
455

    
456
  PARAMETERS = {
457
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
458
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
459
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
460
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
461
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
462
    constants.HV_ROOT_PATH: hv_base.REQUIRED_CHECK,
463
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
464
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
465
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
466
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
467
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
468
    }
469

    
470
  @classmethod
471
  def _WriteConfigFile(cls, instance, block_devices):
472
    """Write the Xen config file for the instance.
473

474
    """
475
    hvp = instance.hvparams
476
    config = StringIO()
477
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
478

    
479
    # if bootloader is True, use bootloader instead of kernel and ramdisk
480
    # parameters.
481
    if hvp[constants.HV_USE_BOOTLOADER]:
482
      # bootloader handling
483
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
484
      if bootloader_path:
485
        config.write("bootloader = '%s'\n" % bootloader_path)
486
      else:
487
        raise errors.HypervisorError("Bootloader enabled, but missing"
488
                                     " bootloader path")
489

    
490
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
491
      if bootloader_args:
492
        config.write("bootargs = '%s'\n" % bootloader_args)
493
    else:
494
      # kernel handling
495
      kpath = hvp[constants.HV_KERNEL_PATH]
496
      config.write("kernel = '%s'\n" % kpath)
497

    
498
      # initrd handling
499
      initrd_path = hvp[constants.HV_INITRD_PATH]
500
      if initrd_path:
501
        config.write("ramdisk = '%s'\n" % initrd_path)
502

    
503
    # rest of the settings
504
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
505
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
506
    config.write("name = '%s'\n" % instance.name)
507

    
508
    vif_data = []
509
    for nic in instance.nics:
510
      nic_str = "mac=%s" % (nic.mac)
511
      ip = getattr(nic, "ip", None)
512
      if ip is not None:
513
        nic_str += ", ip=%s" % ip
514
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
515
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
516
      vif_data.append("'%s'" % nic_str)
517

    
518
    disk_data = cls._GetConfigFileDiskData(block_devices,
519
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
520

    
521
    config.write("vif = [%s]\n" % ",".join(vif_data))
522
    config.write("disk = [%s]\n" % ",".join(disk_data))
523

    
524
    config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
525
    config.write("on_poweroff = 'destroy'\n")
526
    config.write("on_reboot = 'restart'\n")
527
    config.write("on_crash = 'restart'\n")
528
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
529
    # just in case it exists
530
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
531
    try:
532
      utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
533
    except EnvironmentError, err:
534
      raise errors.HypervisorError("Cannot write Xen instance confile"
535
                                   " file /etc/xen/%s: %s" %
536
                                   (instance.name, err))
537

    
538
    return True
539

    
540

    
541
class XenHvmHypervisor(XenHypervisor):
542
  """Xen HVM hypervisor interface"""
543

    
544
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
545
    constants.VNC_PASSWORD_FILE,
546
    ]
547

    
548
  PARAMETERS = {
549
    constants.HV_ACPI: hv_base.NO_CHECK,
550
    constants.HV_BOOT_ORDER: (True, ) +
551
      (lambda x: x and len(x.strip("acdn")) == 0,
552
       "Invalid boot order specified, must be one or more of [acdn]",
553
       None, None),
554
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
555
    constants.HV_DISK_TYPE:
556
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
557
    constants.HV_NIC_TYPE:
558
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
559
    constants.HV_PAE: hv_base.NO_CHECK,
560
    constants.HV_VNC_BIND_ADDRESS:
561
      (False, netutils.IP4Address.IsValid,
562
       "VNC bind address is not a valid IP address", None, None),
563
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
564
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
565
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
566
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
567
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
568
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
569
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
570
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
571
    }
572

    
573
  @classmethod
574
  def _WriteConfigFile(cls, instance, block_devices):
575
    """Create a Xen 3.1 HVM config file.
576

577
    """
578
    hvp = instance.hvparams
579

    
580
    config = StringIO()
581
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
582

    
583
    # kernel handling
584
    kpath = hvp[constants.HV_KERNEL_PATH]
585
    config.write("kernel = '%s'\n" % kpath)
586

    
587
    config.write("builder = 'hvm'\n")
588
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
589
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
590
    config.write("name = '%s'\n" % instance.name)
591
    if hvp[constants.HV_PAE]:
592
      config.write("pae = 1\n")
593
    else:
594
      config.write("pae = 0\n")
595
    if hvp[constants.HV_ACPI]:
596
      config.write("acpi = 1\n")
597
    else:
598
      config.write("acpi = 0\n")
599
    config.write("apic = 1\n")
600
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
601
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
602
    config.write("sdl = 0\n")
603
    config.write("usb = 1\n")
604
    config.write("usbdevice = 'tablet'\n")
605
    config.write("vnc = 1\n")
606
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
607
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
608
    else:
609
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
610

    
611
    if instance.network_port > constants.VNC_BASE_PORT:
612
      display = instance.network_port - constants.VNC_BASE_PORT
613
      config.write("vncdisplay = %s\n" % display)
614
      config.write("vncunused = 0\n")
615
    else:
616
      config.write("# vncdisplay = 1\n")
617
      config.write("vncunused = 1\n")
618

    
619
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
620
    try:
621
      password = utils.ReadFile(vnc_pwd_file)
622
    except EnvironmentError, err:
623
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
624
                                   (vnc_pwd_file, err))
625

    
626
    config.write("vncpasswd = '%s'\n" % password.rstrip())
627

    
628
    config.write("serial = 'pty'\n")
629
    if hvp[constants.HV_USE_LOCALTIME]:
630
      config.write("localtime = 1\n")
631

    
632
    vif_data = []
633
    nic_type = hvp[constants.HV_NIC_TYPE]
634
    if nic_type is None:
635
      # ensure old instances don't change
636
      nic_type_str = ", type=ioemu"
637
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
638
      nic_type_str = ", type=paravirtualized"
639
    else:
640
      nic_type_str = ", model=%s, type=ioemu" % nic_type
641
    for nic in instance.nics:
642
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
643
      ip = getattr(nic, "ip", None)
644
      if ip is not None:
645
        nic_str += ", ip=%s" % ip
646
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
647
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
648
      vif_data.append("'%s'" % nic_str)
649

    
650
    config.write("vif = [%s]\n" % ",".join(vif_data))
651

    
652
    disk_data = cls._GetConfigFileDiskData(block_devices,
653
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
654

    
655
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
656
    if iso_path:
657
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
658
      disk_data.append(iso)
659

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

    
662
    config.write("on_poweroff = 'destroy'\n")
663
    config.write("on_reboot = 'restart'\n")
664
    config.write("on_crash = 'restart'\n")
665
    # just in case it exists
666
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
667
    try:
668
      utils.WriteFile("/etc/xen/%s" % instance.name,
669
                      data=config.getvalue())
670
    except EnvironmentError, err:
671
      raise errors.HypervisorError("Cannot write Xen instance confile"
672
                                   " file /etc/xen/%s: %s" %
673
                                   (instance.name, err))
674

    
675
    return True