Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 84ad6b78

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
  def _GetCommand(self, hvparams):
360
    """Returns Xen command to use.
361

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

365
    """
366
    if self._cmd is None:
367
      if hvparams is None or constants.HV_XEN_CMD not in hvparams:
368
        raise errors.HypervisorError("Cannot determine xen command.")
369
      else:
370
        cmd = hvparams[constants.HV_XEN_CMD]
371
    else:
372
      cmd = self._cmd
373

    
374
    if cmd not in constants.KNOWN_XEN_COMMANDS:
375
      raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
376

    
377
    return cmd
378

    
379
  def _RunXen(self, args, hvparams):
380
    """Wrapper around L{utils.process.RunCmd} to run Xen command.
381

382
    @type hvparams: dict of strings
383
    @param hvparams: dictionary of hypervisor params
384
    @see: L{utils.process.RunCmd}
385

386
    """
387
    cmd = [self._GetCommand(hvparams)]
388
    cmd.extend(args)
389

    
390
    return self._run_cmd_fn(cmd)
391

    
392
  def _ConfigFileName(self, instance_name):
393
    """Get the config file name for an instance.
394

395
    @param instance_name: instance name
396
    @type instance_name: str
397
    @return: fully qualified path to instance config file
398
    @rtype: str
399

400
    """
401
    return utils.PathJoin(self._cfgdir, instance_name)
402

    
403
  @classmethod
404
  def _WriteNICInfoFile(cls, instance_name, idx, nic):
405
    """Write the Xen config file for the instance.
406

407
    This version of the function just writes the config file from static data.
408

409
    """
410
    dirs = [(dname, constants.RUN_DIRS_MODE)
411
            for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
412
    utils.EnsureDirs(dirs)
413

    
414
    cfg_file = cls._InstanceNICFile(instance_name, idx)
415
    data = StringIO()
416

    
417
    if nic.netinfo:
418
      netinfo = objects.Network.FromDict(nic.netinfo)
419
      data.write("NETWORK_NAME=%s\n" % netinfo.name)
420
      if netinfo.network:
421
        data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
422
      if netinfo.gateway:
423
        data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
424
      if netinfo.network6:
425
        data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
426
      if netinfo.gateway6:
427
        data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
428
      if netinfo.mac_prefix:
429
        data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
430
      if netinfo.tags:
431
        data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
432

    
433
    data.write("MAC=%s\n" % nic.mac)
434
    data.write("IP=%s\n" % nic.ip)
435
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
436
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
437

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

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

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

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

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

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

462
    """
463
    raise NotImplementedError
464

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

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

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

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

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

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

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

    
492
    return file_content
493

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

566
    See L{_GetConfig} for arguments.
567

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
658
      raise utils.RetryAgain()
659

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

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

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

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

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

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

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

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

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

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

    
708
  def GetInstanceConsole(self, instance, primary_node, hvparams, beparams):
709
    """Return a command for connecting to the console of an instance.
710

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

    
720
  def Verify(self, hvparams=None):
721
    """Verify the hypervisor.
722

723
    For Xen, this verifies that the xend process is running.
724

725
    @type hvparams: dict of strings
726
    @param hvparams: hypervisor parameters to be verified against
727

728
    @return: Problem description if something is wrong, C{None} otherwise
729

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

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

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

    
748
    return None
749

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

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

758
    """
759
    return self._ReadConfigFile(instance.name)
760

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

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

771
    """
772
    pass
773

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

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

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

787
    """
788
    if success:
789
      self._WriteConfigFile(instance.name, info)
790

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

794
    The migration will not be attempted if the instance is not
795
    currently running.
796

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

804
    """
805
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
806

    
807
    return self._MigrateInstance(cluster_name, instance.name, target, port,
808
                                 live, instance.hvparams)
809

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

814
    @see: L{MigrateInstance} for details
815

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

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

    
823
    cmd = self._GetCommand(hvparams)
824

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

    
830
    args = ["migrate"]
831

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

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

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

    
846
    args.extend([instance_name, target])
847

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

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

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

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

    
872
  def GetMigrationStatus(self, instance):
873
    """Get the migration status
874

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

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

886
    """
887
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
888

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

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

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

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

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

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

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

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

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

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

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

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

    
951

    
952
class XenPvmHypervisor(XenHypervisor):
953
  """Xen PVM hypervisor interface"""
954

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

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

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

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

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

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

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

    
1026
    config.write("name = '%s'\n" % instance.name)
1027

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

    
1045
    disk_data = \
1046
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1047

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

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

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

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

    
1068
    return config.getvalue()
1069

    
1070

    
1071
class XenHvmHypervisor(XenHypervisor):
1072
  """Xen HVM hypervisor interface"""
1073

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

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

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

1125
    """
1126
    hvp = instance.hvparams
1127

    
1128
    config = StringIO()
1129

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

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

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

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

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

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

    
1189
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1190

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

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

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

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

    
1227
    disk_data = \
1228
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1229

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

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

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

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

    
1256
    return config.getvalue()