Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 884ec6d4

History | View | Annotate | Download (41.2 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
    data.write("IP=%s\n" % nic.ip)
436
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
437
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
438

    
439
    try:
440
      utils.WriteFile(cfg_file, data=data.getvalue())
441
    except EnvironmentError, err:
442
      raise errors.HypervisorError("Cannot write Xen instance configuration"
443
                                   " file %s: %s" % (cfg_file, err))
444

    
445
  @classmethod
446
  def _InstanceNICDir(cls, instance_name):
447
    """Returns the directory holding the tap device files for a given instance.
448

449
    """
450
    return utils.PathJoin(cls._NICS_DIR, instance_name)
451

    
452
  @classmethod
453
  def _InstanceNICFile(cls, instance_name, seq):
454
    """Returns the name of the file containing the tap device for a given NIC
455

456
    """
457
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
458

    
459
  @classmethod
460
  def _GetConfig(cls, instance, startup_memory, block_devices):
461
    """Build Xen configuration for an instance.
462

463
    """
464
    raise NotImplementedError
465

    
466
  def _WriteConfigFile(self, instance_name, data):
467
    """Write the Xen config file for the instance.
468

469
    This version of the function just writes the config file from static data.
470

471
    """
472
    # just in case it exists
473
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
474

    
475
    cfg_file = self._ConfigFileName(instance_name)
476
    try:
477
      utils.WriteFile(cfg_file, data=data)
478
    except EnvironmentError, err:
479
      raise errors.HypervisorError("Cannot write Xen instance configuration"
480
                                   " file %s: %s" % (cfg_file, err))
481

    
482
  def _ReadConfigFile(self, instance_name):
483
    """Returns the contents of the instance config file.
484

485
    """
486
    filename = self._ConfigFileName(instance_name)
487

    
488
    try:
489
      file_content = utils.ReadFile(filename)
490
    except EnvironmentError, err:
491
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
492

    
493
    return file_content
494

    
495
  def _RemoveConfigFile(self, instance_name):
496
    """Remove the xen configuration file.
497

498
    """
499
    utils.RemoveFile(self._ConfigFileName(instance_name))
500
    try:
501
      shutil.rmtree(self._InstanceNICDir(instance_name))
502
    except OSError, err:
503
      if err.errno != errno.ENOENT:
504
        raise
505

    
506
  def _StashConfigFile(self, instance_name):
507
    """Move the Xen config file to the log directory and return its new path.
508

509
    """
510
    old_filename = self._ConfigFileName(instance_name)
511
    base = ("%s-%s" %
512
            (instance_name, utils.TimestampForFilename()))
513
    new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
514
    utils.RenameFile(old_filename, new_filename)
515
    return new_filename
516

    
517
  def _GetInstanceList(self, include_node, hvparams):
518
    """Wrapper around module level L{_GetInstanceList}.
519

520
    @type hvparams: dict of strings
521
    @param hvparams: hypervisor parameters to be used on this node
522

523
    """
524
    return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
525
                            include_node)
526

    
527
  def ListInstances(self, hvparams=None):
528
    """Get the list of running instances.
529

530
    """
531
    instance_list = self._GetInstanceList(False, hvparams)
532
    names = [info[0] for info in instance_list]
533
    return names
534

    
535
  def GetInstanceInfo(self, instance_name, hvparams=None):
536
    """Get instance properties.
537

538
    @type instance_name: string
539
    @param instance_name: the instance name
540
    @type hvparams: dict of strings
541
    @param hvparams: the instance's hypervisor params
542

543
    @return: tuple (name, id, memory, vcpus, stat, times)
544

545
    """
546
    instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
547
    result = None
548
    for data in instance_list:
549
      if data[0] == instance_name:
550
        result = data
551
        break
552
    return result
553

    
554
  def GetAllInstancesInfo(self, hvparams=None):
555
    """Get properties of all instances.
556

557
    @type hvparams: dict of strings
558
    @param hvparams: hypervisor parameters
559
    @return: list of tuples (name, id, memory, vcpus, stat, times)
560

561
    """
562
    return self._GetInstanceList(False, hvparams)
563

    
564
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
565
    """Gather configuration details and write to disk.
566

567
    See L{_GetConfig} for arguments.
568

569
    """
570
    buf = StringIO()
571
    buf.write("# Automatically generated by Ganeti. Do not edit!\n")
572
    buf.write("\n")
573
    buf.write(self._GetConfig(instance, startup_memory, block_devices))
574
    buf.write("\n")
575

    
576
    self._WriteConfigFile(instance.name, buf.getvalue())
577

    
578
  def StartInstance(self, instance, block_devices, startup_paused):
579
    """Start an instance.
580

581
    """
582
    startup_memory = self._InstanceStartupMemory(instance,
583
                                                 hvparams=instance.hvparams)
584

    
585
    self._MakeConfigFile(instance, startup_memory, block_devices)
586

    
587
    cmd = ["create"]
588
    if startup_paused:
589
      cmd.append("-p")
590
    cmd.append(self._ConfigFileName(instance.name))
591

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

    
602
  def StopInstance(self, instance, force=False, retry=False, name=None):
603
    """Stop an instance.
604

605
    """
606
    if name is None:
607
      name = instance.name
608

    
609
    return self._StopInstance(name, force, instance.hvparams)
610

    
611
  def _StopInstance(self, name, force, hvparams):
612
    """Stop an instance.
613

614
    @type name: string
615
    @param name: name of the instance to be shutdown
616
    @type force: boolean
617
    @param force: flag specifying whether shutdown should be forced
618
    @type hvparams: dict of string
619
    @param hvparams: hypervisor parameters of the instance
620

621
    """
622
    if force:
623
      action = "destroy"
624
    else:
625
      action = "shutdown"
626

    
627
    result = self._RunXen([action, name], hvparams)
628
    if result.failed:
629
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
630
                                   (name, result.fail_reason, result.output))
631

    
632
    # Remove configuration file if stopping/starting instance was successful
633
    self._RemoveConfigFile(name)
634

    
635
  def RebootInstance(self, instance):
636
    """Reboot an instance.
637

638
    """
639
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
640

    
641
    if ini_info is None:
642
      raise errors.HypervisorError("Failed to reboot instance %s,"
643
                                   " not running" % instance.name)
644

    
645
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
646
    if result.failed:
647
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
648
                                   (instance.name, result.fail_reason,
649
                                    result.output))
650

    
651
    def _CheckInstance():
652
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
653

    
654
      # check if the domain ID has changed or the run time has decreased
655
      if (new_info is not None and
656
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
657
        return
658

    
659
      raise utils.RetryAgain()
660

    
661
    try:
662
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
663
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
664
    except utils.RetryTimeout:
665
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
666
                                   " did not reboot in the expected interval" %
667
                                   (instance.name, ))
668

    
669
  def BalloonInstanceMemory(self, instance, mem):
670
    """Balloon an instance memory to a certain value.
671

672
    @type instance: L{objects.Instance}
673
    @param instance: instance to be accepted
674
    @type mem: int
675
    @param mem: actual memory size to use for instance runtime
676

677
    """
678
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
679
    if result.failed:
680
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
681
                                   (instance.name, result.fail_reason,
682
                                    result.output))
683

    
684
    # Update configuration file
685
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
686
    cmd.append(self._ConfigFileName(instance.name))
687

    
688
    result = utils.RunCmd(cmd)
689
    if result.failed:
690
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
691
                                   (instance.name, result.fail_reason,
692
                                    result.output))
693

    
694
  def GetNodeInfo(self, hvparams=None):
695
    """Return information about the node.
696

697
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
698

699
    """
700
    result = self._RunXen(["info"], hvparams)
701
    if result.failed:
702
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
703
                    result.fail_reason, result.output)
704
      return None
705

    
706
    instance_list = self._GetInstanceList(True, hvparams)
707
    return _GetNodeInfo(result.stdout, instance_list)
708

    
709
  @classmethod
710
  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
711
    """Return a command for connecting to the console of an instance.
712

713
    """
714
    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
715
    return objects.InstanceConsole(instance=instance.name,
716
                                   kind=constants.CONS_SSH,
717
                                   host=primary_node.name,
718
                                   user=constants.SSH_CONSOLE_USER,
719
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
720
                                            xen_cmd, instance.name])
721

    
722
  def Verify(self, hvparams=None):
723
    """Verify the hypervisor.
724

725
    For Xen, this verifies that the xend process is running.
726

727
    @type hvparams: dict of strings
728
    @param hvparams: hypervisor parameters to be verified against
729

730
    @return: Problem description if something is wrong, C{None} otherwise
731

732
    """
733
    if hvparams is None:
734
      return "Could not verify the hypervisor, because no hvparams were" \
735
             " provided."
736

    
737
    if constants.HV_XEN_CMD in hvparams:
738
      xen_cmd = hvparams[constants.HV_XEN_CMD]
739
      try:
740
        self._CheckToolstack(xen_cmd)
741
      except errors.HypervisorError:
742
        return "The configured xen toolstack '%s' is not available on this" \
743
               " node." % xen_cmd
744

    
745
    result = self._RunXen(["info"], hvparams)
746
    if result.failed:
747
      return "Retrieving information from xen failed: %s, %s" % \
748
        (result.fail_reason, result.output)
749

    
750
    return None
751

    
752
  def MigrationInfo(self, instance):
753
    """Get instance information to perform a migration.
754

755
    @type instance: L{objects.Instance}
756
    @param instance: instance to be migrated
757
    @rtype: string
758
    @return: content of the xen config file
759

760
    """
761
    return self._ReadConfigFile(instance.name)
762

    
763
  def AcceptInstance(self, instance, info, target):
764
    """Prepare to accept an instance.
765

766
    @type instance: L{objects.Instance}
767
    @param instance: instance to be accepted
768
    @type info: string
769
    @param info: content of the xen config file on the source node
770
    @type target: string
771
    @param target: target host (usually ip), on this node
772

773
    """
774
    pass
775

    
776
  def FinalizeMigrationDst(self, instance, info, success):
777
    """Finalize an instance migration.
778

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

782
    @type instance: L{objects.Instance}
783
    @param instance: instance whose migration is being finalized
784
    @type info: string
785
    @param info: content of the xen config file on the source node
786
    @type success: boolean
787
    @param success: whether the migration was a success or a failure
788

789
    """
790
    if success:
791
      self._WriteConfigFile(instance.name, info)
792

    
793
  def MigrateInstance(self, cluster_name, instance, target, live):
794
    """Migrate an instance to a target node.
795

796
    The migration will not be attempted if the instance is not
797
    currently running.
798

799
    @type instance: L{objects.Instance}
800
    @param instance: the instance to be migrated
801
    @type target: string
802
    @param target: ip address of the target node
803
    @type live: boolean
804
    @param live: perform a live migration
805

806
    """
807
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
808

    
809
    return self._MigrateInstance(cluster_name, instance.name, target, port,
810
                                 live, instance.hvparams)
811

    
812
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
813
                       hvparams, _ping_fn=netutils.TcpPing):
814
    """Migrate an instance to a target node.
815

816
    @see: L{MigrateInstance} for details
817

818
    """
819
    if hvparams is None:
820
      raise errors.HypervisorError("No hvparams provided.")
821

    
822
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
823
      raise errors.HypervisorError("Instance not running, cannot migrate")
824

    
825
    cmd = self._GetCommand(hvparams)
826

    
827
    if (cmd == constants.XEN_CMD_XM and
828
        not _ping_fn(target, port, live_port_needed=True)):
829
      raise errors.HypervisorError("Remote host %s not listening on port"
830
                                   " %s, cannot migrate" % (target, port))
831

    
832
    args = ["migrate"]
833

    
834
    if cmd == constants.XEN_CMD_XM:
835
      args.extend(["-p", "%d" % port])
836
      if live:
837
        args.append("-l")
838

    
839
    elif cmd == constants.XEN_CMD_XL:
840
      args.extend([
841
        "-s", constants.XL_SSH_CMD % cluster_name,
842
        "-C", self._ConfigFileName(instance_name),
843
        ])
844

    
845
    else:
846
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
847

    
848
    args.extend([instance_name, target])
849

    
850
    result = self._RunXen(args, hvparams)
851
    if result.failed:
852
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
853
                                   (instance_name, result.output))
854

    
855
  def FinalizeMigrationSource(self, instance, success, live):
856
    """Finalize the instance migration on the source node.
857

858
    @type instance: L{objects.Instance}
859
    @param instance: the instance that was migrated
860
    @type success: bool
861
    @param success: whether the migration succeeded or not
862
    @type live: bool
863
    @param live: whether the user requested a live migration or not
864

865
    """
866
    # pylint: disable=W0613
867
    if success:
868
      # remove old xen file after migration succeeded
869
      try:
870
        self._RemoveConfigFile(instance.name)
871
      except EnvironmentError:
872
        logging.exception("Failure while removing instance config file")
873

    
874
  def GetMigrationStatus(self, instance):
875
    """Get the migration status
876

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

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

888
    """
889
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
890

    
891
  def PowercycleNode(self, hvparams=None):
892
    """Xen-specific powercycle.
893

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

901
    @type hvparams: dict of strings
902
    @param hvparams: hypervisor params to be used on this node
903

904
    """
905
    try:
906
      self.LinuxPowercycle()
907
    finally:
908
      xen_cmd = self._GetCommand(hvparams)
909
      utils.RunCmd([xen_cmd, "debug", "R"])
910

    
911
  def _CheckToolstack(self, xen_cmd):
912
    """Check whether the given toolstack is available on the node.
913

914
    @type xen_cmd: string
915
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
916

917
    """
918
    binary_found = self._CheckToolstackBinary(xen_cmd)
919
    if not binary_found:
920
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
921
    elif xen_cmd == constants.XEN_CMD_XL:
922
      if not self._CheckToolstackXlConfigured():
923
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
924
                                     "node." % xen_cmd)
925

    
926
  def _CheckToolstackBinary(self, xen_cmd):
927
    """Checks whether the xen command's binary is found on the machine.
928

929
    """
930
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
931
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
932
    result = self._run_cmd_fn(["which", xen_cmd])
933
    return not result.failed
934

    
935
  def _CheckToolstackXlConfigured(self):
936
    """Checks whether xl is enabled on an xl-capable node.
937

938
    @rtype: bool
939
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
940

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

    
953

    
954
class XenPvmHypervisor(XenHypervisor):
955
  """Xen PVM hypervisor interface"""
956

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

    
982
  def _GetConfig(self, instance, startup_memory, block_devices):
983
    """Write the Xen config file for the instance.
984

985
    """
986
    hvp = instance.hvparams
987
    config = StringIO()
988
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
989

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

    
1001
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1002
      if bootloader_args:
1003
        config.write("bootargs = '%s'\n" % bootloader_args)
1004
    else:
1005
      # kernel handling
1006
      kpath = hvp[constants.HV_KERNEL_PATH]
1007
      config.write("kernel = '%s'\n" % kpath)
1008

    
1009
      # initrd handling
1010
      initrd_path = hvp[constants.HV_INITRD_PATH]
1011
      if initrd_path:
1012
        config.write("ramdisk = '%s'\n" % initrd_path)
1013

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

    
1028
    config.write("name = '%s'\n" % instance.name)
1029

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

    
1047
    disk_data = \
1048
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1049

    
1050
    config.write("vif = [%s]\n" % ",".join(vif_data))
1051
    config.write("disk = [%s]\n" % ",".join(disk_data))
1052

    
1053
    if hvp[constants.HV_ROOT_PATH]:
1054
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1055
    config.write("on_poweroff = 'destroy'\n")
1056
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1057
      config.write("on_reboot = 'restart'\n")
1058
    else:
1059
      config.write("on_reboot = 'destroy'\n")
1060
    config.write("on_crash = 'restart'\n")
1061
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1062

    
1063
    cpuid = hvp[constants.HV_XEN_CPUID]
1064
    if cpuid:
1065
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1066

    
1067
    if hvp[constants.HV_SOUNDHW]:
1068
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1069

    
1070
    return config.getvalue()
1071

    
1072

    
1073
class XenHvmHypervisor(XenHypervisor):
1074
  """Xen HVM hypervisor interface"""
1075

    
1076
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1077
    pathutils.VNC_PASSWORD_FILE,
1078
    ]
1079
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1080
    pathutils.VNC_PASSWORD_FILE,
1081
    ]
1082

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

    
1124
  def _GetConfig(self, instance, startup_memory, block_devices):
1125
    """Create a Xen 3.1 HVM config file.
1126

1127
    """
1128
    hvp = instance.hvparams
1129

    
1130
    config = StringIO()
1131

    
1132
    # kernel handling
1133
    kpath = hvp[constants.HV_KERNEL_PATH]
1134
    config.write("kernel = '%s'\n" % kpath)
1135

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

    
1150
    config.write("name = '%s'\n" % instance.name)
1151
    if hvp[constants.HV_PAE]:
1152
      config.write("pae = 1\n")
1153
    else:
1154
      config.write("pae = 0\n")
1155
    if hvp[constants.HV_ACPI]:
1156
      config.write("acpi = 1\n")
1157
    else:
1158
      config.write("acpi = 0\n")
1159
    if hvp[constants.HV_VIRIDIAN]:
1160
      config.write("viridian = 1\n")
1161
    else:
1162
      config.write("viridian = 0\n")
1163

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

    
1176
    if instance.network_port > constants.VNC_BASE_PORT:
1177
      display = instance.network_port - constants.VNC_BASE_PORT
1178
      config.write("vncdisplay = %s\n" % display)
1179
      config.write("vncunused = 0\n")
1180
    else:
1181
      config.write("# vncdisplay = 1\n")
1182
      config.write("vncunused = 1\n")
1183

    
1184
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1185
    try:
1186
      password = utils.ReadFile(vnc_pwd_file)
1187
    except EnvironmentError, err:
1188
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1189
                                   (vnc_pwd_file, err))
1190

    
1191
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1192

    
1193
    config.write("serial = 'pty'\n")
1194
    if hvp[constants.HV_USE_LOCALTIME]:
1195
      config.write("localtime = 1\n")
1196

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

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

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

    
1229
    disk_data = \
1230
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1231

    
1232
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1233
    if iso_path:
1234
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1235
      disk_data.append(iso)
1236

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

    
1251
    cpuid = hvp[constants.HV_XEN_CPUID]
1252
    if cpuid:
1253
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1254

    
1255
    if hvp[constants.HV_SOUNDHW]:
1256
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1257

    
1258
    return config.getvalue()