Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 783a6c0b

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

    
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
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
462
    }
463

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

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

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

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

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

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

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

    
512
    disk_data = cls._GetConfigFileDiskData(block_devices)
513

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

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

    
531
    return True
532

    
533

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

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

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

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

568
    """
569
    hvp = instance.hvparams
570

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

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

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

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

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

    
617
    config.write("vncpasswd = '%s'\n" % password.rstrip())
618

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

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

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

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

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

    
669
    return True