Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 2a3bbbaa

History | View | Annotate | Download (41.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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
import errno
28
import string # pylint: disable=W0402
29
import shutil
30
from cStringIO import StringIO
31

    
32
from ganeti import constants
33
from ganeti import errors
34
from ganeti import utils
35
from ganeti.hypervisor import hv_base
36
from ganeti import netutils
37
from ganeti import objects
38
from ganeti import pathutils
39

    
40

    
41
XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
42
XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
43
VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
44
                                   "scripts/vif-bridge")
45
_DOM0_NAME = "Domain-0"
46
_DISK_LETTERS = string.ascii_lowercase
47

    
48
_FILE_DRIVER_MAP = {
49
  constants.FD_LOOP: "file",
50
  constants.FD_BLKTAP: "tap:aio",
51
  }
52

    
53

    
54
def _CreateConfigCpus(cpu_mask):
55
  """Create a CPU config string for Xen's config file.
56

57
  """
58
  # Convert the string CPU mask to a list of list of int's
59
  cpu_list = utils.ParseMultiCpuMask(cpu_mask)
60

    
61
  if len(cpu_list) == 1:
62
    all_cpu_mapping = cpu_list[0]
63
    if all_cpu_mapping == constants.CPU_PINNING_OFF:
64
      # If CPU pinning has 1 entry that's "all", then remove the
65
      # parameter from the config file
66
      return None
67
    else:
68
      # If CPU pinning has one non-all entry, mapping all vCPUS (the entire
69
      # VM) to one physical CPU, using format 'cpu = "C"'
70
      return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
71
  else:
72

    
73
    def _GetCPUMap(vcpu):
74
      if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
75
        cpu_map = constants.CPU_PINNING_ALL_XEN
76
      else:
77
        cpu_map = ",".join(map(str, vcpu))
78
      return "\"%s\"" % cpu_map
79

    
80
    # build the result string in format 'cpus = [ "c", "c", "c" ]',
81
    # where each c is a physical CPU number, a range, a list, or any
82
    # combination
83
    return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
84

    
85

    
86
def _RunInstanceList(fn, instance_list_errors):
87
  """Helper function for L{_GetInstanceList} to retrieve the list of instances
88
  from xen.
89

90
  @type fn: callable
91
  @param fn: Function to query xen for the list of instances
92
  @type instance_list_errors: list
93
  @param instance_list_errors: Error list
94
  @rtype: list
95

96
  """
97
  result = fn()
98
  if result.failed:
99
    logging.error("Retrieving the instance list from xen failed (%s): %s",
100
                  result.fail_reason, result.output)
101
    instance_list_errors.append(result)
102
    raise utils.RetryAgain()
103

    
104
  # skip over the heading
105
  return result.stdout.splitlines()
106

    
107

    
108
def _ParseInstanceList(lines, include_node):
109
  """Parses the output of listing instances by xen.
110

111
  @type lines: list
112
  @param lines: Result of retrieving the instance list from xen
113
  @type include_node: boolean
114
  @param include_node: If True, return information for Dom0
115
  @return: list of tuple containing (name, id, memory, vcpus, state, time
116
    spent)
117

118
  """
119
  result = []
120

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

    
139
    # skip the Domain-0 (optional)
140
    if include_node or data[0] != _DOM0_NAME:
141
      result.append(data)
142

    
143
  return result
144

    
145

    
146
def _GetInstanceList(fn, include_node, _timeout=5):
147
  """Return the list of running instances.
148

149
  See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
150

151
  """
152
  instance_list_errors = []
153
  try:
154
    lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout,
155
                        args=(fn, instance_list_errors))
156
  except utils.RetryTimeout:
157
    if instance_list_errors:
158
      instance_list_result = instance_list_errors.pop()
159

    
160
      errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
161
                (instance_list_result.fail_reason, instance_list_result.output))
162
    else:
163
      errmsg = "listing instances failed"
164

    
165
    raise errors.HypervisorError(errmsg)
166

    
167
  return _ParseInstanceList(lines, include_node)
168

    
169

    
170
def _ParseNodeInfo(info):
171
  """Return information about the node.
172

173
  @return: a dict with the following keys (memory values in MiB):
174
        - memory_total: the total memory size on the node
175
        - memory_free: the available memory on the node for instances
176
        - nr_cpus: total number of CPUs
177
        - nr_nodes: in a NUMA system, the number of domains
178
        - nr_sockets: the number of physical CPU sockets in the node
179
        - hv_version: the hypervisor version in the form (major, minor)
180

181
  """
182
  result = {}
183
  cores_per_socket = threads_per_core = nr_cpus = None
184
  xen_major, xen_minor = None, None
185
  memory_total = None
186
  memory_free = None
187

    
188
  for line in info.splitlines():
189
    fields = line.split(":", 1)
190

    
191
    if len(fields) < 2:
192
      continue
193

    
194
    (key, val) = map(lambda s: s.strip(), fields)
195

    
196
    # Note: in Xen 3, memory has changed to total_memory
197
    if key in ("memory", "total_memory"):
198
      memory_total = int(val)
199
    elif key == "free_memory":
200
      memory_free = int(val)
201
    elif key == "nr_cpus":
202
      nr_cpus = result["cpu_total"] = int(val)
203
    elif key == "nr_nodes":
204
      result["cpu_nodes"] = int(val)
205
    elif key == "cores_per_socket":
206
      cores_per_socket = int(val)
207
    elif key == "threads_per_core":
208
      threads_per_core = int(val)
209
    elif key == "xen_major":
210
      xen_major = int(val)
211
    elif key == "xen_minor":
212
      xen_minor = int(val)
213

    
214
  if None not in [cores_per_socket, threads_per_core, nr_cpus]:
215
    result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
216

    
217
  if memory_free is not None:
218
    result["memory_free"] = memory_free
219

    
220
  if memory_total is not None:
221
    result["memory_total"] = memory_total
222

    
223
  if not (xen_major is None or xen_minor is None):
224
    result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
225

    
226
  return result
227

    
228

    
229
def _MergeInstanceInfo(info, instance_list):
230
  """Updates node information from L{_ParseNodeInfo} with instance info.
231

232
  @type info: dict
233
  @param info: Result from L{_ParseNodeInfo}
234
  @type instance_list: list of tuples
235
  @param instance_list: list of instance information; one tuple per instance
236
  @rtype: dict
237

238
  """
239
  total_instmem = 0
240

    
241
  for (name, _, mem, vcpus, _, _) in instance_list:
242
    if name == _DOM0_NAME:
243
      info["memory_dom0"] = mem
244
      info["cpu_dom0"] = vcpus
245

    
246
    # Include Dom0 in total memory usage
247
    total_instmem += mem
248

    
249
  memory_free = info.get("memory_free")
250
  memory_total = info.get("memory_total")
251

    
252
  # Calculate memory used by hypervisor
253
  if None not in [memory_total, memory_free, total_instmem]:
254
    info["memory_hv"] = memory_total - memory_free - total_instmem
255

    
256
  return info
257

    
258

    
259
def _GetNodeInfo(info, instance_list):
260
  """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
261

262
  @type instance_list: list of tuples
263
  @param instance_list: list of instance information; one tuple per instance
264

265
  """
266
  return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
267

    
268

    
269
def _GetConfigFileDiskData(block_devices, blockdev_prefix,
270
                           _letters=_DISK_LETTERS):
271
  """Get disk directives for Xen config file.
272

273
  This method builds the xen config disk directive according to the
274
  given disk_template and block_devices.
275

276
  @param block_devices: list of tuples (cfdev, rldev):
277
      - cfdev: dict containing ganeti config disk part
278
      - rldev: ganeti.block.bdev.BlockDev object
279
  @param blockdev_prefix: a string containing blockdevice prefix,
280
                          e.g. "sd" for /dev/sda
281

282
  @return: string containing disk directive for xen instance config file
283

284
  """
285
  if len(block_devices) > len(_letters):
286
    raise errors.HypervisorError("Too many disks")
287

    
288
  disk_data = []
289

    
290
  for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
291
    sd_name = blockdev_prefix + sd_suffix
292

    
293
    if cfdev.mode == constants.DISK_RDWR:
294
      mode = "w"
295
    else:
296
      mode = "r"
297

    
298
    if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
299
      driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
300
    else:
301
      driver = "phy"
302

    
303
    disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
304

    
305
  return disk_data
306

    
307

    
308
def _QuoteCpuidField(data):
309
  """Add quotes around the CPUID field only if necessary.
310

311
  Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around
312
  them, and lists of XEND strings, which don't.
313

314
  @param data: Either type of parameter.
315
  @return: The quoted version thereof.
316

317
  """
318
  return "'%s'" % data if data.startswith("host") else data
319

    
320

    
321
class XenHypervisor(hv_base.BaseHypervisor):
322
  """Xen generic hypervisor interface
323

324
  This is the Xen base class used for both Xen PVM and HVM. It contains
325
  all the functionality that is identical for both.
326

327
  """
328
  CAN_MIGRATE = True
329
  REBOOT_RETRY_COUNT = 60
330
  REBOOT_RETRY_INTERVAL = 10
331
  _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
332
  _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
333
  _DIRS = [_ROOT_DIR, _NICS_DIR]
334

    
335
  ANCILLARY_FILES = [
336
    XEND_CONFIG_FILE,
337
    XL_CONFIG_FILE,
338
    VIF_BRIDGE_SCRIPT,
339
    ]
340
  ANCILLARY_FILES_OPT = [
341
    XL_CONFIG_FILE,
342
    ]
343

    
344
  def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
345
    hv_base.BaseHypervisor.__init__(self)
346

    
347
    if _cfgdir is None:
348
      self._cfgdir = pathutils.XEN_CONFIG_DIR
349
    else:
350
      self._cfgdir = _cfgdir
351

    
352
    if _run_cmd_fn is None:
353
      self._run_cmd_fn = utils.RunCmd
354
    else:
355
      self._run_cmd_fn = _run_cmd_fn
356

    
357
    self._cmd = _cmd
358

    
359
  @staticmethod
360
  def _GetCommandFromHvparams(hvparams):
361
    """Returns the Xen command extracted from the given hvparams.
362

363
    @type hvparams: dict of strings
364
    @param hvparams: hypervisor parameters
365

366
    """
367
    if hvparams is None or constants.HV_XEN_CMD not in hvparams:
368
      raise errors.HypervisorError("Cannot determine xen command.")
369
    else:
370
      return hvparams[constants.HV_XEN_CMD]
371

    
372
  def _GetCommand(self, hvparams):
373
    """Returns Xen command to use.
374

375
    @type hvparams: dict of strings
376
    @param hvparams: hypervisor parameters
377

378
    """
379
    if self._cmd is None:
380
      cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
381
    else:
382
      cmd = self._cmd
383

    
384
    if cmd not in constants.KNOWN_XEN_COMMANDS:
385
      raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
386

    
387
    return cmd
388

    
389
  def _RunXen(self, args, hvparams):
390
    """Wrapper around L{utils.process.RunCmd} to run Xen command.
391

392
    @type hvparams: dict of strings
393
    @param hvparams: dictionary of hypervisor params
394
    @see: L{utils.process.RunCmd}
395

396
    """
397
    cmd = [self._GetCommand(hvparams)]
398
    cmd.extend(args)
399

    
400
    return self._run_cmd_fn(cmd)
401

    
402
  def _ConfigFileName(self, instance_name):
403
    """Get the config file name for an instance.
404

405
    @param instance_name: instance name
406
    @type instance_name: str
407
    @return: fully qualified path to instance config file
408
    @rtype: str
409

410
    """
411
    return utils.PathJoin(self._cfgdir, instance_name)
412

    
413
  @classmethod
414
  def _WriteNICInfoFile(cls, instance, idx, nic):
415
    """Write the Xen config file for the instance.
416

417
    This version of the function just writes the config file from static data.
418

419
    """
420
    instance_name = instance.name
421
    dirs = [(dname, constants.RUN_DIRS_MODE)
422
            for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
423
    utils.EnsureDirs(dirs)
424

    
425
    cfg_file = cls._InstanceNICFile(instance_name, idx)
426
    data = StringIO()
427

    
428
    data.write("TAGS=%s\n" % "\ ".join(instance.GetTags()))
429
    if nic.netinfo:
430
      netinfo = objects.Network.FromDict(nic.netinfo)
431
      data.write("NETWORK_NAME=%s\n" % netinfo.name)
432
      if netinfo.network:
433
        data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
434
      if netinfo.gateway:
435
        data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
436
      if netinfo.network6:
437
        data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
438
      if netinfo.gateway6:
439
        data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
440
      if netinfo.mac_prefix:
441
        data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
442
      if netinfo.tags:
443
        data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
444

    
445
    data.write("MAC=%s\n" % nic.mac)
446
    data.write("IP=%s\n" % nic.ip)
447
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
448
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
449

    
450
    try:
451
      utils.WriteFile(cfg_file, data=data.getvalue())
452
    except EnvironmentError, err:
453
      raise errors.HypervisorError("Cannot write Xen instance configuration"
454
                                   " file %s: %s" % (cfg_file, err))
455

    
456
  @classmethod
457
  def _InstanceNICDir(cls, instance_name):
458
    """Returns the directory holding the tap device files for a given instance.
459

460
    """
461
    return utils.PathJoin(cls._NICS_DIR, instance_name)
462

    
463
  @classmethod
464
  def _InstanceNICFile(cls, instance_name, seq):
465
    """Returns the name of the file containing the tap device for a given NIC
466

467
    """
468
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
469

    
470
  @classmethod
471
  def _GetConfig(cls, instance, startup_memory, block_devices):
472
    """Build Xen configuration for an instance.
473

474
    """
475
    raise NotImplementedError
476

    
477
  def _WriteConfigFile(self, instance_name, data):
478
    """Write the Xen config file for the instance.
479

480
    This version of the function just writes the config file from static data.
481

482
    """
483
    # just in case it exists
484
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
485

    
486
    cfg_file = self._ConfigFileName(instance_name)
487
    try:
488
      utils.WriteFile(cfg_file, data=data)
489
    except EnvironmentError, err:
490
      raise errors.HypervisorError("Cannot write Xen instance configuration"
491
                                   " file %s: %s" % (cfg_file, err))
492

    
493
  def _ReadConfigFile(self, instance_name):
494
    """Returns the contents of the instance config file.
495

496
    """
497
    filename = self._ConfigFileName(instance_name)
498

    
499
    try:
500
      file_content = utils.ReadFile(filename)
501
    except EnvironmentError, err:
502
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
503

    
504
    return file_content
505

    
506
  def _RemoveConfigFile(self, instance_name):
507
    """Remove the xen configuration file.
508

509
    """
510
    utils.RemoveFile(self._ConfigFileName(instance_name))
511
    try:
512
      shutil.rmtree(self._InstanceNICDir(instance_name))
513
    except OSError, err:
514
      if err.errno != errno.ENOENT:
515
        raise
516

    
517
  def _StashConfigFile(self, instance_name):
518
    """Move the Xen config file to the log directory and return its new path.
519

520
    """
521
    old_filename = self._ConfigFileName(instance_name)
522
    base = ("%s-%s" %
523
            (instance_name, utils.TimestampForFilename()))
524
    new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
525
    utils.RenameFile(old_filename, new_filename)
526
    return new_filename
527

    
528
  def _GetInstanceList(self, include_node, hvparams):
529
    """Wrapper around module level L{_GetInstanceList}.
530

531
    @type hvparams: dict of strings
532
    @param hvparams: hypervisor parameters to be used on this node
533

534
    """
535
    return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
536
                            include_node)
537

    
538
  def ListInstances(self, hvparams=None):
539
    """Get the list of running instances.
540

541
    """
542
    instance_list = self._GetInstanceList(False, hvparams)
543
    names = [info[0] for info in instance_list]
544
    return names
545

    
546
  def GetInstanceInfo(self, instance_name, hvparams=None):
547
    """Get instance properties.
548

549
    @type instance_name: string
550
    @param instance_name: the instance name
551
    @type hvparams: dict of strings
552
    @param hvparams: the instance's hypervisor params
553

554
    @return: tuple (name, id, memory, vcpus, stat, times)
555

556
    """
557
    instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
558
    result = None
559
    for data in instance_list:
560
      if data[0] == instance_name:
561
        result = data
562
        break
563
    return result
564

    
565
  def GetAllInstancesInfo(self, hvparams=None):
566
    """Get properties of all instances.
567

568
    @type hvparams: dict of strings
569
    @param hvparams: hypervisor parameters
570
    @return: list of tuples (name, id, memory, vcpus, stat, times)
571

572
    """
573
    return self._GetInstanceList(False, hvparams)
574

    
575
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
576
    """Gather configuration details and write to disk.
577

578
    See L{_GetConfig} for arguments.
579

580
    """
581
    buf = StringIO()
582
    buf.write("# Automatically generated by Ganeti. Do not edit!\n")
583
    buf.write("\n")
584
    buf.write(self._GetConfig(instance, startup_memory, block_devices))
585
    buf.write("\n")
586

    
587
    self._WriteConfigFile(instance.name, buf.getvalue())
588

    
589
  def StartInstance(self, instance, block_devices, startup_paused):
590
    """Start an instance.
591

592
    """
593
    startup_memory = self._InstanceStartupMemory(instance,
594
                                                 hvparams=instance.hvparams)
595

    
596
    self._MakeConfigFile(instance, startup_memory, block_devices)
597

    
598
    cmd = ["create"]
599
    if startup_paused:
600
      cmd.append("-p")
601
    cmd.append(self._ConfigFileName(instance.name))
602

    
603
    result = self._RunXen(cmd, instance.hvparams)
604
    if result.failed:
605
      # Move the Xen configuration file to the log directory to avoid
606
      # leaving a stale config file behind.
607
      stashed_config = self._StashConfigFile(instance.name)
608
      raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
609
                                   " config file to %s" %
610
                                   (instance.name, result.fail_reason,
611
                                    result.output, stashed_config))
612

    
613
  def StopInstance(self, instance, force=False, retry=False, name=None):
614
    """Stop an instance.
615

616
    """
617
    if name is None:
618
      name = instance.name
619

    
620
    return self._StopInstance(name, force, instance.hvparams)
621

    
622
  def _StopInstance(self, name, force, hvparams):
623
    """Stop an instance.
624

625
    @type name: string
626
    @param name: name of the instance to be shutdown
627
    @type force: boolean
628
    @param force: flag specifying whether shutdown should be forced
629
    @type hvparams: dict of string
630
    @param hvparams: hypervisor parameters of the instance
631

632
    """
633
    if force:
634
      action = "destroy"
635
    else:
636
      action = "shutdown"
637

    
638
    result = self._RunXen([action, name], hvparams)
639
    if result.failed:
640
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
641
                                   (name, result.fail_reason, result.output))
642

    
643
    # Remove configuration file if stopping/starting instance was successful
644
    self._RemoveConfigFile(name)
645

    
646
  def RebootInstance(self, instance):
647
    """Reboot an instance.
648

649
    """
650
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
651

    
652
    if ini_info is None:
653
      raise errors.HypervisorError("Failed to reboot instance %s,"
654
                                   " not running" % instance.name)
655

    
656
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
657
    if result.failed:
658
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
659
                                   (instance.name, result.fail_reason,
660
                                    result.output))
661

    
662
    def _CheckInstance():
663
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
664

    
665
      # check if the domain ID has changed or the run time has decreased
666
      if (new_info is not None and
667
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
668
        return
669

    
670
      raise utils.RetryAgain()
671

    
672
    try:
673
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
674
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
675
    except utils.RetryTimeout:
676
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
677
                                   " did not reboot in the expected interval" %
678
                                   (instance.name, ))
679

    
680
  def BalloonInstanceMemory(self, instance, mem):
681
    """Balloon an instance memory to a certain value.
682

683
    @type instance: L{objects.Instance}
684
    @param instance: instance to be accepted
685
    @type mem: int
686
    @param mem: actual memory size to use for instance runtime
687

688
    """
689
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
690
    if result.failed:
691
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
692
                                   (instance.name, result.fail_reason,
693
                                    result.output))
694

    
695
    # Update configuration file
696
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
697
    cmd.append(self._ConfigFileName(instance.name))
698

    
699
    result = utils.RunCmd(cmd)
700
    if result.failed:
701
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
702
                                   (instance.name, result.fail_reason,
703
                                    result.output))
704

    
705
  def GetNodeInfo(self, hvparams=None):
706
    """Return information about the node.
707

708
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
709

710
    """
711
    result = self._RunXen(["info"], hvparams)
712
    if result.failed:
713
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
714
                    result.fail_reason, result.output)
715
      return None
716

    
717
    instance_list = self._GetInstanceList(True, hvparams)
718
    return _GetNodeInfo(result.stdout, instance_list)
719

    
720
  @classmethod
721
  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
722
    """Return a command for connecting to the console of an instance.
723

724
    """
725
    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
726
    return objects.InstanceConsole(instance=instance.name,
727
                                   kind=constants.CONS_SSH,
728
                                   host=primary_node.name,
729
                                   user=constants.SSH_CONSOLE_USER,
730
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
731
                                            xen_cmd, instance.name])
732

    
733
  def Verify(self, hvparams=None):
734
    """Verify the hypervisor.
735

736
    For Xen, this verifies that the xend process is running.
737

738
    @type hvparams: dict of strings
739
    @param hvparams: hypervisor parameters to be verified against
740

741
    @return: Problem description if something is wrong, C{None} otherwise
742

743
    """
744
    if hvparams is None:
745
      return "Could not verify the hypervisor, because no hvparams were" \
746
             " provided."
747

    
748
    if constants.HV_XEN_CMD in hvparams:
749
      xen_cmd = hvparams[constants.HV_XEN_CMD]
750
      try:
751
        self._CheckToolstack(xen_cmd)
752
      except errors.HypervisorError:
753
        return "The configured xen toolstack '%s' is not available on this" \
754
               " node." % xen_cmd
755

    
756
    result = self._RunXen(["info"], hvparams)
757
    if result.failed:
758
      return "Retrieving information from xen failed: %s, %s" % \
759
        (result.fail_reason, result.output)
760

    
761
    return None
762

    
763
  def MigrationInfo(self, instance):
764
    """Get instance information to perform a migration.
765

766
    @type instance: L{objects.Instance}
767
    @param instance: instance to be migrated
768
    @rtype: string
769
    @return: content of the xen config file
770

771
    """
772
    return self._ReadConfigFile(instance.name)
773

    
774
  def AcceptInstance(self, instance, info, target):
775
    """Prepare to accept an instance.
776

777
    @type instance: L{objects.Instance}
778
    @param instance: instance to be accepted
779
    @type info: string
780
    @param info: content of the xen config file on the source node
781
    @type target: string
782
    @param target: target host (usually ip), on this node
783

784
    """
785
    pass
786

    
787
  def FinalizeMigrationDst(self, instance, info, success):
788
    """Finalize an instance migration.
789

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

793
    @type instance: L{objects.Instance}
794
    @param instance: instance whose migration is being finalized
795
    @type info: string
796
    @param info: content of the xen config file on the source node
797
    @type success: boolean
798
    @param success: whether the migration was a success or a failure
799

800
    """
801
    if success:
802
      self._WriteConfigFile(instance.name, info)
803

    
804
  def MigrateInstance(self, cluster_name, instance, target, live):
805
    """Migrate an instance to a target node.
806

807
    The migration will not be attempted if the instance is not
808
    currently running.
809

810
    @type instance: L{objects.Instance}
811
    @param instance: the instance to be migrated
812
    @type target: string
813
    @param target: ip address of the target node
814
    @type live: boolean
815
    @param live: perform a live migration
816

817
    """
818
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
819

    
820
    return self._MigrateInstance(cluster_name, instance.name, target, port,
821
                                 live, instance.hvparams)
822

    
823
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
824
                       hvparams, _ping_fn=netutils.TcpPing):
825
    """Migrate an instance to a target node.
826

827
    @see: L{MigrateInstance} for details
828

829
    """
830
    if hvparams is None:
831
      raise errors.HypervisorError("No hvparams provided.")
832

    
833
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
834
      raise errors.HypervisorError("Instance not running, cannot migrate")
835

    
836
    cmd = self._GetCommand(hvparams)
837

    
838
    if (cmd == constants.XEN_CMD_XM and
839
        not _ping_fn(target, port, live_port_needed=True)):
840
      raise errors.HypervisorError("Remote host %s not listening on port"
841
                                   " %s, cannot migrate" % (target, port))
842

    
843
    args = ["migrate"]
844

    
845
    if cmd == constants.XEN_CMD_XM:
846
      args.extend(["-p", "%d" % port])
847
      if live:
848
        args.append("-l")
849

    
850
    elif cmd == constants.XEN_CMD_XL:
851
      args.extend([
852
        "-s", constants.XL_SSH_CMD % cluster_name,
853
        "-C", self._ConfigFileName(instance_name),
854
        ])
855

    
856
    else:
857
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
858

    
859
    args.extend([instance_name, target])
860

    
861
    result = self._RunXen(args, hvparams)
862
    if result.failed:
863
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
864
                                   (instance_name, result.output))
865

    
866
  def FinalizeMigrationSource(self, instance, success, live):
867
    """Finalize the instance migration on the source node.
868

869
    @type instance: L{objects.Instance}
870
    @param instance: the instance that was migrated
871
    @type success: bool
872
    @param success: whether the migration succeeded or not
873
    @type live: bool
874
    @param live: whether the user requested a live migration or not
875

876
    """
877
    # pylint: disable=W0613
878
    if success:
879
      # remove old xen file after migration succeeded
880
      try:
881
        self._RemoveConfigFile(instance.name)
882
      except EnvironmentError:
883
        logging.exception("Failure while removing instance config file")
884

    
885
  def GetMigrationStatus(self, instance):
886
    """Get the migration status
887

888
    As MigrateInstance for Xen is still blocking, if this method is called it
889
    means that MigrateInstance has completed successfully. So we can safely
890
    assume that the migration was successful and notify this fact to the client.
891

892
    @type instance: L{objects.Instance}
893
    @param instance: the instance that is being migrated
894
    @rtype: L{objects.MigrationStatus}
895
    @return: the status of the current migration (one of
896
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
897
             progress info that can be retrieved from the hypervisor
898

899
    """
900
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
901

    
902
  def PowercycleNode(self, hvparams=None):
903
    """Xen-specific powercycle.
904

905
    This first does a Linux reboot (which triggers automatically a Xen
906
    reboot), and if that fails it tries to do a Xen reboot. The reason
907
    we don't try a Xen reboot first is that the xen reboot launches an
908
    external command which connects to the Xen hypervisor, and that
909
    won't work in case the root filesystem is broken and/or the xend
910
    daemon is not working.
911

912
    @type hvparams: dict of strings
913
    @param hvparams: hypervisor params to be used on this node
914

915
    """
916
    try:
917
      self.LinuxPowercycle()
918
    finally:
919
      xen_cmd = self._GetCommand(hvparams)
920
      utils.RunCmd([xen_cmd, "debug", "R"])
921

    
922
  def _CheckToolstack(self, xen_cmd):
923
    """Check whether the given toolstack is available on the node.
924

925
    @type xen_cmd: string
926
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
927

928
    """
929
    binary_found = self._CheckToolstackBinary(xen_cmd)
930
    if not binary_found:
931
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
932
    elif xen_cmd == constants.XEN_CMD_XL:
933
      if not self._CheckToolstackXlConfigured():
934
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
935
                                     "node." % xen_cmd)
936

    
937
  def _CheckToolstackBinary(self, xen_cmd):
938
    """Checks whether the xen command's binary is found on the machine.
939

940
    """
941
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
942
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
943
    result = self._run_cmd_fn(["which", xen_cmd])
944
    return not result.failed
945

    
946
  def _CheckToolstackXlConfigured(self):
947
    """Checks whether xl is enabled on an xl-capable node.
948

949
    @rtype: bool
950
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
951

952
    """
953
    result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
954
    if not result.failed:
955
      return True
956
    elif result.failed:
957
      if "toolstack" in result.stderr:
958
        return False
959
      # xl fails for some other reason than the toolstack
960
      else:
961
        raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
962
                                     % (constants.XEN_CMD_XL, result.stderr))
963

    
964

    
965
class XenPvmHypervisor(XenHypervisor):
966
  """Xen PVM hypervisor interface"""
967

    
968
  PARAMETERS = {
969
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
970
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
971
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
972
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
973
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
974
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
975
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
976
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
977
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
978
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
979
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
980
    constants.HV_REBOOT_BEHAVIOR:
981
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
982
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
983
    constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
984
    constants.HV_CPU_WEIGHT:
985
      (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
986
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
987
    constants.HV_XEN_CMD:
988
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
989
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
990
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
991
    }
992

    
993
  def _GetConfig(self, instance, startup_memory, block_devices):
994
    """Write the Xen config file for the instance.
995

996
    """
997
    hvp = instance.hvparams
998
    config = StringIO()
999
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1000

    
1001
    # if bootloader is True, use bootloader instead of kernel and ramdisk
1002
    # parameters.
1003
    if hvp[constants.HV_USE_BOOTLOADER]:
1004
      # bootloader handling
1005
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1006
      if bootloader_path:
1007
        config.write("bootloader = '%s'\n" % bootloader_path)
1008
      else:
1009
        raise errors.HypervisorError("Bootloader enabled, but missing"
1010
                                     " bootloader path")
1011

    
1012
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1013
      if bootloader_args:
1014
        config.write("bootargs = '%s'\n" % bootloader_args)
1015
    else:
1016
      # kernel handling
1017
      kpath = hvp[constants.HV_KERNEL_PATH]
1018
      config.write("kernel = '%s'\n" % kpath)
1019

    
1020
      # initrd handling
1021
      initrd_path = hvp[constants.HV_INITRD_PATH]
1022
      if initrd_path:
1023
        config.write("ramdisk = '%s'\n" % initrd_path)
1024

    
1025
    # rest of the settings
1026
    config.write("memory = %d\n" % startup_memory)
1027
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1028
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1029
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1030
    if cpu_pinning:
1031
      config.write("%s\n" % cpu_pinning)
1032
    cpu_cap = hvp[constants.HV_CPU_CAP]
1033
    if cpu_cap:
1034
      config.write("cpu_cap=%d\n" % cpu_cap)
1035
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1036
    if cpu_weight:
1037
      config.write("cpu_weight=%d\n" % cpu_weight)
1038

    
1039
    config.write("name = '%s'\n" % instance.name)
1040

    
1041
    vif_data = []
1042
    for idx, nic in enumerate(instance.nics):
1043
      nic_str = "mac=%s" % (nic.mac)
1044
      ip = getattr(nic, "ip", None)
1045
      if ip is not None:
1046
        nic_str += ", ip=%s" % ip
1047
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1048
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1049
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1050
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1051
        if nic.nicparams[constants.NIC_VLAN]:
1052
          nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
1053
      if hvp[constants.HV_VIF_SCRIPT]:
1054
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1055
      vif_data.append("'%s'" % nic_str)
1056
      self._WriteNICInfoFile(instance, idx, nic)
1057

    
1058
    disk_data = \
1059
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1060

    
1061
    config.write("vif = [%s]\n" % ",".join(vif_data))
1062
    config.write("disk = [%s]\n" % ",".join(disk_data))
1063

    
1064
    if hvp[constants.HV_ROOT_PATH]:
1065
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1066
    config.write("on_poweroff = 'destroy'\n")
1067
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1068
      config.write("on_reboot = 'restart'\n")
1069
    else:
1070
      config.write("on_reboot = 'destroy'\n")
1071
    config.write("on_crash = 'restart'\n")
1072
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1073

    
1074
    cpuid = hvp[constants.HV_XEN_CPUID]
1075
    if cpuid:
1076
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1077

    
1078
    if hvp[constants.HV_SOUNDHW]:
1079
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1080

    
1081
    return config.getvalue()
1082

    
1083

    
1084
class XenHvmHypervisor(XenHypervisor):
1085
  """Xen HVM hypervisor interface"""
1086

    
1087
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1088
    pathutils.VNC_PASSWORD_FILE,
1089
    ]
1090
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1091
    pathutils.VNC_PASSWORD_FILE,
1092
    ]
1093

    
1094
  PARAMETERS = {
1095
    constants.HV_ACPI: hv_base.NO_CHECK,
1096
    constants.HV_BOOT_ORDER: (True, ) +
1097
      (lambda x: x and len(x.strip("acdn")) == 0,
1098
       "Invalid boot order specified, must be one or more of [acdn]",
1099
       None, None),
1100
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1101
    constants.HV_DISK_TYPE:
1102
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1103
    constants.HV_NIC_TYPE:
1104
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1105
    constants.HV_PAE: hv_base.NO_CHECK,
1106
    constants.HV_VNC_BIND_ADDRESS:
1107
      (False, netutils.IP4Address.IsValid,
1108
       "VNC bind address is not a valid IP address", None, None),
1109
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1110
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1111
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1112
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1113
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1114
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1115
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1116
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1117
    # Add PCI passthrough
1118
    constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1119
    constants.HV_REBOOT_BEHAVIOR:
1120
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1121
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1122
    constants.HV_CPU_CAP: hv_base.NO_CHECK,
1123
    constants.HV_CPU_WEIGHT:
1124
      (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1125
    constants.HV_VIF_TYPE:
1126
      hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1127
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1128
    constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1129
    constants.HV_XEN_CMD:
1130
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1131
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1132
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
1133
    }
1134

    
1135
  def _GetConfig(self, instance, startup_memory, block_devices):
1136
    """Create a Xen 3.1 HVM config file.
1137

1138
    """
1139
    hvp = instance.hvparams
1140

    
1141
    config = StringIO()
1142

    
1143
    # kernel handling
1144
    kpath = hvp[constants.HV_KERNEL_PATH]
1145
    config.write("kernel = '%s'\n" % kpath)
1146

    
1147
    config.write("builder = 'hvm'\n")
1148
    config.write("memory = %d\n" % startup_memory)
1149
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1150
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1151
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1152
    if cpu_pinning:
1153
      config.write("%s\n" % cpu_pinning)
1154
    cpu_cap = hvp[constants.HV_CPU_CAP]
1155
    if cpu_cap:
1156
      config.write("cpu_cap=%d\n" % cpu_cap)
1157
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1158
    if cpu_weight:
1159
      config.write("cpu_weight=%d\n" % cpu_weight)
1160

    
1161
    config.write("name = '%s'\n" % instance.name)
1162
    if hvp[constants.HV_PAE]:
1163
      config.write("pae = 1\n")
1164
    else:
1165
      config.write("pae = 0\n")
1166
    if hvp[constants.HV_ACPI]:
1167
      config.write("acpi = 1\n")
1168
    else:
1169
      config.write("acpi = 0\n")
1170
    if hvp[constants.HV_VIRIDIAN]:
1171
      config.write("viridian = 1\n")
1172
    else:
1173
      config.write("viridian = 0\n")
1174

    
1175
    config.write("apic = 1\n")
1176
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1177
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1178
    config.write("sdl = 0\n")
1179
    config.write("usb = 1\n")
1180
    config.write("usbdevice = 'tablet'\n")
1181
    config.write("vnc = 1\n")
1182
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1183
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1184
    else:
1185
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1186

    
1187
    if instance.network_port > constants.VNC_BASE_PORT:
1188
      display = instance.network_port - constants.VNC_BASE_PORT
1189
      config.write("vncdisplay = %s\n" % display)
1190
      config.write("vncunused = 0\n")
1191
    else:
1192
      config.write("# vncdisplay = 1\n")
1193
      config.write("vncunused = 1\n")
1194

    
1195
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1196
    try:
1197
      password = utils.ReadFile(vnc_pwd_file)
1198
    except EnvironmentError, err:
1199
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1200
                                   (vnc_pwd_file, err))
1201

    
1202
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1203

    
1204
    config.write("serial = 'pty'\n")
1205
    if hvp[constants.HV_USE_LOCALTIME]:
1206
      config.write("localtime = 1\n")
1207

    
1208
    vif_data = []
1209
    # Note: what is called 'nic_type' here, is used as value for the xen nic
1210
    # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1211
    # the 'vif_type' to avoid a clash of notation.
1212
    nic_type = hvp[constants.HV_NIC_TYPE]
1213

    
1214
    if nic_type is None:
1215
      vif_type_str = ""
1216
      if hvp[constants.HV_VIF_TYPE]:
1217
        vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1218
      # ensure old instances don't change
1219
      nic_type_str = vif_type_str
1220
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1221
      nic_type_str = ", type=paravirtualized"
1222
    else:
1223
      # parameter 'model' is only valid with type 'ioemu'
1224
      nic_type_str = ", model=%s, type=%s" % \
1225
        (nic_type, constants.HT_HVM_VIF_IOEMU)
1226
    for idx, nic in enumerate(instance.nics):
1227
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1228
      ip = getattr(nic, "ip", None)
1229
      if ip is not None:
1230
        nic_str += ", ip=%s" % ip
1231
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1232
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1233
      if hvp[constants.HV_VIF_SCRIPT]:
1234
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1235
      vif_data.append("'%s'" % nic_str)
1236
      self._WriteNICInfoFile(instance, idx, nic)
1237

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

    
1240
    disk_data = \
1241
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1242

    
1243
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1244
    if iso_path:
1245
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1246
      disk_data.append(iso)
1247

    
1248
    config.write("disk = [%s]\n" % (",".join(disk_data)))
1249
    # Add PCI passthrough
1250
    pci_pass_arr = []
1251
    pci_pass = hvp[constants.HV_PASSTHROUGH]
1252
    if pci_pass:
1253
      pci_pass_arr = pci_pass.split(";")
1254
      config.write("pci = %s\n" % pci_pass_arr)
1255
    config.write("on_poweroff = 'destroy'\n")
1256
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1257
      config.write("on_reboot = 'restart'\n")
1258
    else:
1259
      config.write("on_reboot = 'destroy'\n")
1260
    config.write("on_crash = 'restart'\n")
1261

    
1262
    cpuid = hvp[constants.HV_XEN_CPUID]
1263
    if cpuid:
1264
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1265

    
1266
    if hvp[constants.HV_SOUNDHW]:
1267
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1268

    
1269
    return config.getvalue()