Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ e695efbf

History | View | Annotate | Download (22 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, blockdev_prefix):
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
    @param blockdev_prefix: a string containing blockdevice prefix,
325
                            e.g. "sd" for /dev/sda
326

327
    @return: string containing disk directive for xen instance config file
328

329
    """
330
    FILE_DRIVER_MAP = {
331
      constants.FD_LOOP: "file",
332
      constants.FD_BLKTAP: "tap:aio",
333
      }
334
    disk_data = []
335
    if len(block_devices) > 24:
336
      # 'z' - 'a' = 24
337
      raise errors.HypervisorError("Too many disks")
338
    namespace = [blockdev_prefix + 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
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
463
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
464
    }
465

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

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

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

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

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

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

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

    
514
    disk_data = cls._GetConfigFileDiskData(block_devices,
515
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
516

    
517
    config.write("vif = [%s]\n" % ",".join(vif_data))
518
    config.write("disk = [%s]\n" % ",".join(disk_data))
519

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

    
534
    return True
535

    
536

    
537
class XenHvmHypervisor(XenHypervisor):
538
  """Xen HVM hypervisor interface"""
539

    
540
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
541
    constants.VNC_PASSWORD_FILE,
542
    ]
543

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

    
569
  @classmethod
570
  def _WriteConfigFile(cls, instance, block_devices):
571
    """Create a Xen 3.1 HVM config file.
572

573
    """
574
    hvp = instance.hvparams
575

    
576
    config = StringIO()
577
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
578

    
579
    # kernel handling
580
    kpath = hvp[constants.HV_KERNEL_PATH]
581
    config.write("kernel = '%s'\n" % kpath)
582

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

    
607
    if instance.network_port > constants.VNC_BASE_PORT:
608
      display = instance.network_port - constants.VNC_BASE_PORT
609
      config.write("vncdisplay = %s\n" % display)
610
      config.write("vncunused = 0\n")
611
    else:
612
      config.write("# vncdisplay = 1\n")
613
      config.write("vncunused = 1\n")
614

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

    
622
    config.write("vncpasswd = '%s'\n" % password.rstrip())
623

    
624
    config.write("serial = 'pty'\n")
625
    if hvp[constants.HV_USE_LOCALTIME]:
626
      config.write("localtime = 1\n")
627

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

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

    
648
    disk_data = cls._GetConfigFileDiskData(block_devices,
649
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
650

    
651
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
652
    if iso_path:
653
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
654
      disk_data.append(iso)
655

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

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

    
671
    return True