Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ cc6fd3da

History | View | Annotate | Download (41.3 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
      for k, v in netinfo.HooksDict().iteritems():
432
        data.write("%s=%s\n" % (k, v))
433

    
434
    data.write("MAC=%s\n" % nic.mac)
435
    if nic.ip:
436
      data.write("IP=%s\n" % nic.ip)
437
    data.write("INTERFACE_INDEX=%s\n" % str(idx))
438
    if nic.name:
439
      data.write("INTERFACE_NAME=%s\n" % nic.name)
440
    data.write("INTERFACE_UUID=%s\n" % nic.uuid)
441
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
442
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
443

    
444
    try:
445
      utils.WriteFile(cfg_file, data=data.getvalue())
446
    except EnvironmentError, err:
447
      raise errors.HypervisorError("Cannot write Xen instance configuration"
448
                                   " file %s: %s" % (cfg_file, err))
449

    
450
  @classmethod
451
  def _InstanceNICDir(cls, instance_name):
452
    """Returns the directory holding the tap device files for a given instance.
453

454
    """
455
    return utils.PathJoin(cls._NICS_DIR, instance_name)
456

    
457
  @classmethod
458
  def _InstanceNICFile(cls, instance_name, seq):
459
    """Returns the name of the file containing the tap device for a given NIC
460

461
    """
462
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
463

    
464
  @classmethod
465
  def _GetConfig(cls, instance, startup_memory, block_devices):
466
    """Build Xen configuration for an instance.
467

468
    """
469
    raise NotImplementedError
470

    
471
  def _WriteConfigFile(self, instance_name, data):
472
    """Write the Xen config file for the instance.
473

474
    This version of the function just writes the config file from static data.
475

476
    """
477
    # just in case it exists
478
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
479

    
480
    cfg_file = self._ConfigFileName(instance_name)
481
    try:
482
      utils.WriteFile(cfg_file, data=data)
483
    except EnvironmentError, err:
484
      raise errors.HypervisorError("Cannot write Xen instance configuration"
485
                                   " file %s: %s" % (cfg_file, err))
486

    
487
  def _ReadConfigFile(self, instance_name):
488
    """Returns the contents of the instance config file.
489

490
    """
491
    filename = self._ConfigFileName(instance_name)
492

    
493
    try:
494
      file_content = utils.ReadFile(filename)
495
    except EnvironmentError, err:
496
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
497

    
498
    return file_content
499

    
500
  def _RemoveConfigFile(self, instance_name):
501
    """Remove the xen configuration file.
502

503
    """
504
    utils.RemoveFile(self._ConfigFileName(instance_name))
505
    try:
506
      shutil.rmtree(self._InstanceNICDir(instance_name))
507
    except OSError, err:
508
      if err.errno != errno.ENOENT:
509
        raise
510

    
511
  def _StashConfigFile(self, instance_name):
512
    """Move the Xen config file to the log directory and return its new path.
513

514
    """
515
    old_filename = self._ConfigFileName(instance_name)
516
    base = ("%s-%s" %
517
            (instance_name, utils.TimestampForFilename()))
518
    new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
519
    utils.RenameFile(old_filename, new_filename)
520
    return new_filename
521

    
522
  def _GetInstanceList(self, include_node, hvparams):
523
    """Wrapper around module level L{_GetInstanceList}.
524

525
    @type hvparams: dict of strings
526
    @param hvparams: hypervisor parameters to be used on this node
527

528
    """
529
    return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
530
                            include_node)
531

    
532
  def ListInstances(self, hvparams=None):
533
    """Get the list of running instances.
534

535
    """
536
    instance_list = self._GetInstanceList(False, hvparams)
537
    names = [info[0] for info in instance_list]
538
    return names
539

    
540
  def GetInstanceInfo(self, instance_name, hvparams=None):
541
    """Get instance properties.
542

543
    @type instance_name: string
544
    @param instance_name: the instance name
545
    @type hvparams: dict of strings
546
    @param hvparams: the instance's hypervisor params
547

548
    @return: tuple (name, id, memory, vcpus, stat, times)
549

550
    """
551
    instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
552
    result = None
553
    for data in instance_list:
554
      if data[0] == instance_name:
555
        result = data
556
        break
557
    return result
558

    
559
  def GetAllInstancesInfo(self, hvparams=None):
560
    """Get properties of all instances.
561

562
    @type hvparams: dict of strings
563
    @param hvparams: hypervisor parameters
564
    @return: list of tuples (name, id, memory, vcpus, stat, times)
565

566
    """
567
    return self._GetInstanceList(False, hvparams)
568

    
569
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
570
    """Gather configuration details and write to disk.
571

572
    See L{_GetConfig} for arguments.
573

574
    """
575
    buf = StringIO()
576
    buf.write("# Automatically generated by Ganeti. Do not edit!\n")
577
    buf.write("\n")
578
    buf.write(self._GetConfig(instance, startup_memory, block_devices))
579
    buf.write("\n")
580

    
581
    self._WriteConfigFile(instance.name, buf.getvalue())
582

    
583
  def StartInstance(self, instance, block_devices, startup_paused):
584
    """Start an instance.
585

586
    """
587
    startup_memory = self._InstanceStartupMemory(instance,
588
                                                 hvparams=instance.hvparams)
589

    
590
    self._MakeConfigFile(instance, startup_memory, block_devices)
591

    
592
    cmd = ["create"]
593
    if startup_paused:
594
      cmd.append("-p")
595
    cmd.append(self._ConfigFileName(instance.name))
596

    
597
    result = self._RunXen(cmd, instance.hvparams)
598
    if result.failed:
599
      # Move the Xen configuration file to the log directory to avoid
600
      # leaving a stale config file behind.
601
      stashed_config = self._StashConfigFile(instance.name)
602
      raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
603
                                   " config file to %s" %
604
                                   (instance.name, result.fail_reason,
605
                                    result.output, stashed_config))
606

    
607
  def StopInstance(self, instance, force=False, retry=False, name=None):
608
    """Stop an instance.
609

610
    """
611
    if name is None:
612
      name = instance.name
613

    
614
    return self._StopInstance(name, force, instance.hvparams)
615

    
616
  def _StopInstance(self, name, force, hvparams):
617
    """Stop an instance.
618

619
    @type name: string
620
    @param name: name of the instance to be shutdown
621
    @type force: boolean
622
    @param force: flag specifying whether shutdown should be forced
623
    @type hvparams: dict of string
624
    @param hvparams: hypervisor parameters of the instance
625

626
    """
627
    if force:
628
      action = "destroy"
629
    else:
630
      action = "shutdown"
631

    
632
    result = self._RunXen([action, name], hvparams)
633
    if result.failed:
634
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
635
                                   (name, result.fail_reason, result.output))
636

    
637
    # Remove configuration file if stopping/starting instance was successful
638
    self._RemoveConfigFile(name)
639

    
640
  def RebootInstance(self, instance):
641
    """Reboot an instance.
642

643
    """
644
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
645

    
646
    if ini_info is None:
647
      raise errors.HypervisorError("Failed to reboot instance %s,"
648
                                   " not running" % instance.name)
649

    
650
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
651
    if result.failed:
652
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
653
                                   (instance.name, result.fail_reason,
654
                                    result.output))
655

    
656
    def _CheckInstance():
657
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
658

    
659
      # check if the domain ID has changed or the run time has decreased
660
      if (new_info is not None and
661
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
662
        return
663

    
664
      raise utils.RetryAgain()
665

    
666
    try:
667
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
668
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
669
    except utils.RetryTimeout:
670
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
671
                                   " did not reboot in the expected interval" %
672
                                   (instance.name, ))
673

    
674
  def BalloonInstanceMemory(self, instance, mem):
675
    """Balloon an instance memory to a certain value.
676

677
    @type instance: L{objects.Instance}
678
    @param instance: instance to be accepted
679
    @type mem: int
680
    @param mem: actual memory size to use for instance runtime
681

682
    """
683
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
684
    if result.failed:
685
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
686
                                   (instance.name, result.fail_reason,
687
                                    result.output))
688

    
689
    # Update configuration file
690
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
691
    cmd.append(self._ConfigFileName(instance.name))
692

    
693
    result = utils.RunCmd(cmd)
694
    if result.failed:
695
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
696
                                   (instance.name, result.fail_reason,
697
                                    result.output))
698

    
699
  def GetNodeInfo(self, hvparams=None):
700
    """Return information about the node.
701

702
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
703

704
    """
705
    result = self._RunXen(["info"], hvparams)
706
    if result.failed:
707
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
708
                    result.fail_reason, result.output)
709
      return None
710

    
711
    instance_list = self._GetInstanceList(True, hvparams)
712
    return _GetNodeInfo(result.stdout, instance_list)
713

    
714
  @classmethod
715
  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
716
    """Return a command for connecting to the console of an instance.
717

718
    """
719
    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
720
    return objects.InstanceConsole(instance=instance.name,
721
                                   kind=constants.CONS_SSH,
722
                                   host=primary_node.name,
723
                                   user=constants.SSH_CONSOLE_USER,
724
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
725
                                            xen_cmd, instance.name])
726

    
727
  def Verify(self, hvparams=None):
728
    """Verify the hypervisor.
729

730
    For Xen, this verifies that the xend process is running.
731

732
    @type hvparams: dict of strings
733
    @param hvparams: hypervisor parameters to be verified against
734

735
    @return: Problem description if something is wrong, C{None} otherwise
736

737
    """
738
    if hvparams is None:
739
      return "Could not verify the hypervisor, because no hvparams were" \
740
             " provided."
741

    
742
    if constants.HV_XEN_CMD in hvparams:
743
      xen_cmd = hvparams[constants.HV_XEN_CMD]
744
      try:
745
        self._CheckToolstack(xen_cmd)
746
      except errors.HypervisorError:
747
        return "The configured xen toolstack '%s' is not available on this" \
748
               " node." % xen_cmd
749

    
750
    result = self._RunXen(["info"], hvparams)
751
    if result.failed:
752
      return "Retrieving information from xen failed: %s, %s" % \
753
        (result.fail_reason, result.output)
754

    
755
    return None
756

    
757
  def MigrationInfo(self, instance):
758
    """Get instance information to perform a migration.
759

760
    @type instance: L{objects.Instance}
761
    @param instance: instance to be migrated
762
    @rtype: string
763
    @return: content of the xen config file
764

765
    """
766
    return self._ReadConfigFile(instance.name)
767

    
768
  def AcceptInstance(self, instance, info, target):
769
    """Prepare to accept an instance.
770

771
    @type instance: L{objects.Instance}
772
    @param instance: instance to be accepted
773
    @type info: string
774
    @param info: content of the xen config file on the source node
775
    @type target: string
776
    @param target: target host (usually ip), on this node
777

778
    """
779
    pass
780

    
781
  def FinalizeMigrationDst(self, instance, info, success):
782
    """Finalize an instance migration.
783

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

787
    @type instance: L{objects.Instance}
788
    @param instance: instance whose migration is being finalized
789
    @type info: string
790
    @param info: content of the xen config file on the source node
791
    @type success: boolean
792
    @param success: whether the migration was a success or a failure
793

794
    """
795
    if success:
796
      self._WriteConfigFile(instance.name, info)
797

    
798
  def MigrateInstance(self, cluster_name, instance, target, live):
799
    """Migrate an instance to a target node.
800

801
    The migration will not be attempted if the instance is not
802
    currently running.
803

804
    @type instance: L{objects.Instance}
805
    @param instance: the instance to be migrated
806
    @type target: string
807
    @param target: ip address of the target node
808
    @type live: boolean
809
    @param live: perform a live migration
810

811
    """
812
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
813

    
814
    return self._MigrateInstance(cluster_name, instance.name, target, port,
815
                                 live, instance.hvparams)
816

    
817
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
818
                       hvparams, _ping_fn=netutils.TcpPing):
819
    """Migrate an instance to a target node.
820

821
    @see: L{MigrateInstance} for details
822

823
    """
824
    if hvparams is None:
825
      raise errors.HypervisorError("No hvparams provided.")
826

    
827
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
828
      raise errors.HypervisorError("Instance not running, cannot migrate")
829

    
830
    cmd = self._GetCommand(hvparams)
831

    
832
    if (cmd == constants.XEN_CMD_XM and
833
        not _ping_fn(target, port, live_port_needed=True)):
834
      raise errors.HypervisorError("Remote host %s not listening on port"
835
                                   " %s, cannot migrate" % (target, port))
836

    
837
    args = ["migrate"]
838

    
839
    if cmd == constants.XEN_CMD_XM:
840
      args.extend(["-p", "%d" % port])
841
      if live:
842
        args.append("-l")
843

    
844
    elif cmd == constants.XEN_CMD_XL:
845
      args.extend([
846
        "-s", constants.XL_SSH_CMD % cluster_name,
847
        "-C", self._ConfigFileName(instance_name),
848
        ])
849

    
850
    else:
851
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
852

    
853
    args.extend([instance_name, target])
854

    
855
    result = self._RunXen(args, hvparams)
856
    if result.failed:
857
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
858
                                   (instance_name, result.output))
859

    
860
  def FinalizeMigrationSource(self, instance, success, live):
861
    """Finalize the instance migration on the source node.
862

863
    @type instance: L{objects.Instance}
864
    @param instance: the instance that was migrated
865
    @type success: bool
866
    @param success: whether the migration succeeded or not
867
    @type live: bool
868
    @param live: whether the user requested a live migration or not
869

870
    """
871
    # pylint: disable=W0613
872
    if success:
873
      # remove old xen file after migration succeeded
874
      try:
875
        self._RemoveConfigFile(instance.name)
876
      except EnvironmentError:
877
        logging.exception("Failure while removing instance config file")
878

    
879
  def GetMigrationStatus(self, instance):
880
    """Get the migration status
881

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

886
    @type instance: L{objects.Instance}
887
    @param instance: the instance that is being migrated
888
    @rtype: L{objects.MigrationStatus}
889
    @return: the status of the current migration (one of
890
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
891
             progress info that can be retrieved from the hypervisor
892

893
    """
894
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
895

    
896
  def PowercycleNode(self, hvparams=None):
897
    """Xen-specific powercycle.
898

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

906
    @type hvparams: dict of strings
907
    @param hvparams: hypervisor params to be used on this node
908

909
    """
910
    try:
911
      self.LinuxPowercycle()
912
    finally:
913
      xen_cmd = self._GetCommand(hvparams)
914
      utils.RunCmd([xen_cmd, "debug", "R"])
915

    
916
  def _CheckToolstack(self, xen_cmd):
917
    """Check whether the given toolstack is available on the node.
918

919
    @type xen_cmd: string
920
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
921

922
    """
923
    binary_found = self._CheckToolstackBinary(xen_cmd)
924
    if not binary_found:
925
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
926
    elif xen_cmd == constants.XEN_CMD_XL:
927
      if not self._CheckToolstackXlConfigured():
928
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
929
                                     "node." % xen_cmd)
930

    
931
  def _CheckToolstackBinary(self, xen_cmd):
932
    """Checks whether the xen command's binary is found on the machine.
933

934
    """
935
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
936
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
937
    result = self._run_cmd_fn(["which", xen_cmd])
938
    return not result.failed
939

    
940
  def _CheckToolstackXlConfigured(self):
941
    """Checks whether xl is enabled on an xl-capable node.
942

943
    @rtype: bool
944
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
945

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

    
958

    
959
class XenPvmHypervisor(XenHypervisor):
960
  """Xen PVM hypervisor interface"""
961

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

    
987
  def _GetConfig(self, instance, startup_memory, block_devices):
988
    """Write the Xen config file for the instance.
989

990
    """
991
    hvp = instance.hvparams
992
    config = StringIO()
993
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
994

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

    
1006
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1007
      if bootloader_args:
1008
        config.write("bootargs = '%s'\n" % bootloader_args)
1009
    else:
1010
      # kernel handling
1011
      kpath = hvp[constants.HV_KERNEL_PATH]
1012
      config.write("kernel = '%s'\n" % kpath)
1013

    
1014
      # initrd handling
1015
      initrd_path = hvp[constants.HV_INITRD_PATH]
1016
      if initrd_path:
1017
        config.write("ramdisk = '%s'\n" % initrd_path)
1018

    
1019
    # rest of the settings
1020
    config.write("memory = %d\n" % startup_memory)
1021
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1022
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1023
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1024
    if cpu_pinning:
1025
      config.write("%s\n" % cpu_pinning)
1026
    cpu_cap = hvp[constants.HV_CPU_CAP]
1027
    if cpu_cap:
1028
      config.write("cpu_cap=%d\n" % cpu_cap)
1029
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1030
    if cpu_weight:
1031
      config.write("cpu_weight=%d\n" % cpu_weight)
1032

    
1033
    config.write("name = '%s'\n" % instance.name)
1034

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

    
1052
    disk_data = \
1053
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1054

    
1055
    config.write("vif = [%s]\n" % ",".join(vif_data))
1056
    config.write("disk = [%s]\n" % ",".join(disk_data))
1057

    
1058
    if hvp[constants.HV_ROOT_PATH]:
1059
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1060
    config.write("on_poweroff = 'destroy'\n")
1061
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1062
      config.write("on_reboot = 'restart'\n")
1063
    else:
1064
      config.write("on_reboot = 'destroy'\n")
1065
    config.write("on_crash = 'restart'\n")
1066
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1067

    
1068
    cpuid = hvp[constants.HV_XEN_CPUID]
1069
    if cpuid:
1070
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1071

    
1072
    if hvp[constants.HV_SOUNDHW]:
1073
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1074

    
1075
    return config.getvalue()
1076

    
1077

    
1078
class XenHvmHypervisor(XenHypervisor):
1079
  """Xen HVM hypervisor interface"""
1080

    
1081
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1082
    pathutils.VNC_PASSWORD_FILE,
1083
    ]
1084
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1085
    pathutils.VNC_PASSWORD_FILE,
1086
    ]
1087

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

    
1129
  def _GetConfig(self, instance, startup_memory, block_devices):
1130
    """Create a Xen 3.1 HVM config file.
1131

1132
    """
1133
    hvp = instance.hvparams
1134

    
1135
    config = StringIO()
1136

    
1137
    # kernel handling
1138
    kpath = hvp[constants.HV_KERNEL_PATH]
1139
    config.write("kernel = '%s'\n" % kpath)
1140

    
1141
    config.write("builder = 'hvm'\n")
1142
    config.write("memory = %d\n" % startup_memory)
1143
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1144
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1145
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1146
    if cpu_pinning:
1147
      config.write("%s\n" % cpu_pinning)
1148
    cpu_cap = hvp[constants.HV_CPU_CAP]
1149
    if cpu_cap:
1150
      config.write("cpu_cap=%d\n" % cpu_cap)
1151
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1152
    if cpu_weight:
1153
      config.write("cpu_weight=%d\n" % cpu_weight)
1154

    
1155
    config.write("name = '%s'\n" % instance.name)
1156
    if hvp[constants.HV_PAE]:
1157
      config.write("pae = 1\n")
1158
    else:
1159
      config.write("pae = 0\n")
1160
    if hvp[constants.HV_ACPI]:
1161
      config.write("acpi = 1\n")
1162
    else:
1163
      config.write("acpi = 0\n")
1164
    if hvp[constants.HV_VIRIDIAN]:
1165
      config.write("viridian = 1\n")
1166
    else:
1167
      config.write("viridian = 0\n")
1168

    
1169
    config.write("apic = 1\n")
1170
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1171
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1172
    config.write("sdl = 0\n")
1173
    config.write("usb = 1\n")
1174
    config.write("usbdevice = 'tablet'\n")
1175
    config.write("vnc = 1\n")
1176
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1177
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1178
    else:
1179
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1180

    
1181
    if instance.network_port > constants.VNC_BASE_PORT:
1182
      display = instance.network_port - constants.VNC_BASE_PORT
1183
      config.write("vncdisplay = %s\n" % display)
1184
      config.write("vncunused = 0\n")
1185
    else:
1186
      config.write("# vncdisplay = 1\n")
1187
      config.write("vncunused = 1\n")
1188

    
1189
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1190
    try:
1191
      password = utils.ReadFile(vnc_pwd_file)
1192
    except EnvironmentError, err:
1193
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1194
                                   (vnc_pwd_file, err))
1195

    
1196
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1197

    
1198
    config.write("serial = 'pty'\n")
1199
    if hvp[constants.HV_USE_LOCALTIME]:
1200
      config.write("localtime = 1\n")
1201

    
1202
    vif_data = []
1203
    # Note: what is called 'nic_type' here, is used as value for the xen nic
1204
    # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1205
    # the 'vif_type' to avoid a clash of notation.
1206
    nic_type = hvp[constants.HV_NIC_TYPE]
1207

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

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

    
1234
    disk_data = \
1235
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1236

    
1237
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1238
    if iso_path:
1239
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1240
      disk_data.append(iso)
1241

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

    
1256
    cpuid = hvp[constants.HV_XEN_CPUID]
1257
    if cpuid:
1258
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1259

    
1260
    if hvp[constants.HV_SOUNDHW]:
1261
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1262

    
1263
    return config.getvalue()