Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ cf4a40e5

History | View | Annotate | Download (44.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5
# All rights reserved.
6
#
7
# Redistribution and use in source and binary forms, with or without
8
# modification, are permitted provided that the following conditions are
9
# met:
10
#
11
# 1. Redistributions of source code must retain the above copyright notice,
12
# this list of conditions and the following disclaimer.
13
#
14
# 2. Redistributions in binary form must reproduce the above copyright
15
# notice, this list of conditions and the following disclaimer in the
16
# documentation and/or other materials provided with the distribution.
17
#
18
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29

    
30

    
31
"""Xen hypervisors
32

33
"""
34

    
35
import logging
36
import errno
37
import string # pylint: disable=W0402
38
import shutil
39
from cStringIO import StringIO
40

    
41
from ganeti import constants
42
from ganeti import errors
43
from ganeti import utils
44
from ganeti.hypervisor import hv_base
45
from ganeti import netutils
46
from ganeti import objects
47
from ganeti import pathutils
48

    
49

    
50
XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
51
XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
52
VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
53
                                   "scripts/vif-bridge")
54
_DOM0_NAME = "Domain-0"
55
_DISK_LETTERS = string.ascii_lowercase
56

    
57
_FILE_DRIVER_MAP = {
58
  constants.FD_LOOP: "file",
59
  constants.FD_BLKTAP: "tap:aio",
60
  constants.FD_BLKTAP2: "tap2:tapdisk:aio",
61
  }
62

    
63

    
64
def _CreateConfigCpus(cpu_mask):
65
  """Create a CPU config string for Xen's config file.
66

67
  """
68
  # Convert the string CPU mask to a list of list of int's
69
  cpu_list = utils.ParseMultiCpuMask(cpu_mask)
70

    
71
  if len(cpu_list) == 1:
72
    all_cpu_mapping = cpu_list[0]
73
    if all_cpu_mapping == constants.CPU_PINNING_OFF:
74
      # If CPU pinning has 1 entry that's "all", then remove the
75
      # parameter from the config file
76
      return None
77
    else:
78
      # If CPU pinning has one non-all entry, mapping all vCPUS (the entire
79
      # VM) to one physical CPU, using format 'cpu = "C"'
80
      return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
81
  else:
82

    
83
    def _GetCPUMap(vcpu):
84
      if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
85
        cpu_map = constants.CPU_PINNING_ALL_XEN
86
      else:
87
        cpu_map = ",".join(map(str, vcpu))
88
      return "\"%s\"" % cpu_map
89

    
90
    # build the result string in format 'cpus = [ "c", "c", "c" ]',
91
    # where each c is a physical CPU number, a range, a list, or any
92
    # combination
93
    return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
94

    
95

    
96
def _RunInstanceList(fn, instance_list_errors):
97
  """Helper function for L{_GetInstanceList} to retrieve the list of instances
98
  from xen.
99

100
  @type fn: callable
101
  @param fn: Function to query xen for the list of instances
102
  @type instance_list_errors: list
103
  @param instance_list_errors: Error list
104
  @rtype: list
105

106
  """
107
  result = fn()
108
  if result.failed:
109
    logging.error("Retrieving the instance list from xen failed (%s): %s",
110
                  result.fail_reason, result.output)
111
    instance_list_errors.append(result)
112
    raise utils.RetryAgain()
113

    
114
  # skip over the heading
115
  return result.stdout.splitlines()
116

    
117

    
118
def _ParseInstanceList(lines, include_node):
119
  """Parses the output of listing instances by xen.
120

121
  @type lines: list
122
  @param lines: Result of retrieving the instance list from xen
123
  @type include_node: boolean
124
  @param include_node: If True, return information for Dom0
125
  @return: list of tuple containing (name, id, memory, vcpus, state, time
126
    spent)
127

128
  """
129
  result = []
130

    
131
  # Iterate through all lines while ignoring header
132
  for line in lines[1:]:
133
    # The format of lines is:
134
    # Name      ID Mem(MiB) VCPUs State  Time(s)
135
    # Domain-0   0  3418     4 r-----    266.2
136
    data = line.split()
137
    if len(data) != 6:
138
      raise errors.HypervisorError("Can't parse instance list,"
139
                                   " line: %s" % line)
140
    try:
141
      data[1] = int(data[1])
142
      data[2] = int(data[2])
143
      data[3] = int(data[3])
144
      data[5] = float(data[5])
145
    except (TypeError, ValueError), err:
146
      raise errors.HypervisorError("Can't parse instance list,"
147
                                   " line: %s, error: %s" % (line, err))
148

    
149
    # skip the Domain-0 (optional)
150
    if include_node or data[0] != _DOM0_NAME:
151
      result.append(data)
152

    
153
  return result
154

    
155

    
156
def _GetInstanceList(fn, include_node, _timeout=5):
157
  """Return the list of running instances.
158

159
  See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
160

161
  """
162
  instance_list_errors = []
163
  try:
164
    lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout,
165
                        args=(fn, instance_list_errors))
166
  except utils.RetryTimeout:
167
    if instance_list_errors:
168
      instance_list_result = instance_list_errors.pop()
169

    
170
      errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
171
                (instance_list_result.fail_reason, instance_list_result.output))
172
    else:
173
      errmsg = "listing instances failed"
174

    
175
    raise errors.HypervisorError(errmsg)
176

    
177
  return _ParseInstanceList(lines, include_node)
178

    
179

    
180
def _IsInstanceRunning(instance_info):
181
  """Determine whether an instance is running.
182

183
  An instance is running if it is in the following Xen states:
184
  running, blocked, or paused.
185

186
  For some strange reason, Xen once printed 'rb----' which does not make any
187
  sense because an instance cannot be both running and blocked.  Fortunately,
188
  for Ganeti 'running' or 'blocked' is the same as 'running'.
189

190
  A state of nothing '------' means that the domain is runnable but it is not
191
  currently running.  That means it is in the queue behind other domains waiting
192
  to be scheduled to run.
193
  http://old-list-archives.xenproject.org/xen-users/2007-06/msg00849.html
194

195
  @type instance_info: string
196
  @param instance_info: Information about instance, as supplied by Xen.
197
  @rtype: bool
198
  @return: Whether an instance is running.
199

200
  """
201
  return instance_info == "r-----" \
202
      or instance_info == "rb----" \
203
      or instance_info == "-b----" \
204
      or instance_info == "--p---" \
205
      or instance_info == "------"
206

    
207

    
208
def _IsInstanceShutdown(instance_info):
209
  return instance_info == "---s--"
210

    
211

    
212
def _ParseNodeInfo(info):
213
  """Return information about the node.
214

215
  @return: a dict with the following keys (memory values in MiB):
216
        - memory_total: the total memory size on the node
217
        - memory_free: the available memory on the node for instances
218
        - nr_cpus: total number of CPUs
219
        - nr_nodes: in a NUMA system, the number of domains
220
        - nr_sockets: the number of physical CPU sockets in the node
221
        - hv_version: the hypervisor version in the form (major, minor)
222

223
  """
224
  result = {}
225
  cores_per_socket = threads_per_core = nr_cpus = None
226
  xen_major, xen_minor = None, None
227
  memory_total = None
228
  memory_free = None
229

    
230
  for line in info.splitlines():
231
    fields = line.split(":", 1)
232

    
233
    if len(fields) < 2:
234
      continue
235

    
236
    (key, val) = map(lambda s: s.strip(), fields)
237

    
238
    # Note: in Xen 3, memory has changed to total_memory
239
    if key in ("memory", "total_memory"):
240
      memory_total = int(val)
241
    elif key == "free_memory":
242
      memory_free = int(val)
243
    elif key == "nr_cpus":
244
      nr_cpus = result["cpu_total"] = int(val)
245
    elif key == "nr_nodes":
246
      result["cpu_nodes"] = int(val)
247
    elif key == "cores_per_socket":
248
      cores_per_socket = int(val)
249
    elif key == "threads_per_core":
250
      threads_per_core = int(val)
251
    elif key == "xen_major":
252
      xen_major = int(val)
253
    elif key == "xen_minor":
254
      xen_minor = int(val)
255

    
256
  if None not in [cores_per_socket, threads_per_core, nr_cpus]:
257
    result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
258

    
259
  if memory_free is not None:
260
    result["memory_free"] = memory_free
261

    
262
  if memory_total is not None:
263
    result["memory_total"] = memory_total
264

    
265
  if not (xen_major is None or xen_minor is None):
266
    result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
267

    
268
  return result
269

    
270

    
271
def _MergeInstanceInfo(info, instance_list):
272
  """Updates node information from L{_ParseNodeInfo} with instance info.
273

274
  @type info: dict
275
  @param info: Result from L{_ParseNodeInfo}
276
  @type instance_list: list of tuples
277
  @param instance_list: list of instance information; one tuple per instance
278
  @rtype: dict
279

280
  """
281
  total_instmem = 0
282

    
283
  for (name, _, mem, vcpus, _, _) in instance_list:
284
    if name == _DOM0_NAME:
285
      info["memory_dom0"] = mem
286
      info["cpu_dom0"] = vcpus
287

    
288
    # Include Dom0 in total memory usage
289
    total_instmem += mem
290

    
291
  memory_free = info.get("memory_free")
292
  memory_total = info.get("memory_total")
293

    
294
  # Calculate memory used by hypervisor
295
  if None not in [memory_total, memory_free, total_instmem]:
296
    info["memory_hv"] = memory_total - memory_free - total_instmem
297

    
298
  return info
299

    
300

    
301
def _GetNodeInfo(info, instance_list):
302
  """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
303

304
  @type instance_list: list of tuples
305
  @param instance_list: list of instance information; one tuple per instance
306

307
  """
308
  return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
309

    
310

    
311
def _GetConfigFileDiskData(block_devices, blockdev_prefix,
312
                           _letters=_DISK_LETTERS):
313
  """Get disk directives for Xen config file.
314

315
  This method builds the xen config disk directive according to the
316
  given disk_template and block_devices.
317

318
  @param block_devices: list of tuples (cfdev, rldev):
319
      - cfdev: dict containing ganeti config disk part
320
      - rldev: ganeti.block.bdev.BlockDev object
321
  @param blockdev_prefix: a string containing blockdevice prefix,
322
                          e.g. "sd" for /dev/sda
323

324
  @return: string containing disk directive for xen instance config file
325

326
  """
327
  if len(block_devices) > len(_letters):
328
    raise errors.HypervisorError("Too many disks")
329

    
330
  disk_data = []
331

    
332
  for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
333
    sd_name = blockdev_prefix + sd_suffix
334

    
335
    if cfdev.mode == constants.DISK_RDWR:
336
      mode = "w"
337
    else:
338
      mode = "r"
339

    
340
    if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
341
      driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
342
    else:
343
      driver = "phy"
344

    
345
    disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
346

    
347
  return disk_data
348

    
349

    
350
def _QuoteCpuidField(data):
351
  """Add quotes around the CPUID field only if necessary.
352

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

356
  @param data: Either type of parameter.
357
  @return: The quoted version thereof.
358

359
  """
360
  return "'%s'" % data if data.startswith("host") else data
361

    
362

    
363
class XenHypervisor(hv_base.BaseHypervisor):
364
  """Xen generic hypervisor interface
365

366
  This is the Xen base class used for both Xen PVM and HVM. It contains
367
  all the functionality that is identical for both.
368

369
  """
370
  CAN_MIGRATE = True
371
  REBOOT_RETRY_COUNT = 60
372
  REBOOT_RETRY_INTERVAL = 10
373
  _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
374
  _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
375
  _DIRS = [_ROOT_DIR, _NICS_DIR]
376

    
377
  ANCILLARY_FILES = [
378
    XEND_CONFIG_FILE,
379
    XL_CONFIG_FILE,
380
    VIF_BRIDGE_SCRIPT,
381
    ]
382
  ANCILLARY_FILES_OPT = [
383
    XL_CONFIG_FILE,
384
    ]
385

    
386
  def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
387
    hv_base.BaseHypervisor.__init__(self)
388

    
389
    if _cfgdir is None:
390
      self._cfgdir = pathutils.XEN_CONFIG_DIR
391
    else:
392
      self._cfgdir = _cfgdir
393

    
394
    if _run_cmd_fn is None:
395
      self._run_cmd_fn = utils.RunCmd
396
    else:
397
      self._run_cmd_fn = _run_cmd_fn
398

    
399
    self._cmd = _cmd
400

    
401
  @staticmethod
402
  def _GetCommandFromHvparams(hvparams):
403
    """Returns the Xen command extracted from the given hvparams.
404

405
    @type hvparams: dict of strings
406
    @param hvparams: hypervisor parameters
407

408
    """
409
    if hvparams is None or constants.HV_XEN_CMD not in hvparams:
410
      raise errors.HypervisorError("Cannot determine xen command.")
411
    else:
412
      return hvparams[constants.HV_XEN_CMD]
413

    
414
  def _GetCommand(self, hvparams):
415
    """Returns Xen command to use.
416

417
    @type hvparams: dict of strings
418
    @param hvparams: hypervisor parameters
419

420
    """
421
    if self._cmd is None:
422
      cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
423
    else:
424
      cmd = self._cmd
425

    
426
    if cmd not in constants.KNOWN_XEN_COMMANDS:
427
      raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
428

    
429
    return cmd
430

    
431
  def _RunXen(self, args, hvparams, timeout=None):
432
    """Wrapper around L{utils.process.RunCmd} to run Xen command.
433

434
    @type hvparams: dict of strings
435
    @param hvparams: dictionary of hypervisor params
436
    @type timeout: int or None
437
    @param timeout: if a timeout (in seconds) is specified, the command will be
438
                    terminated after that number of seconds.
439
    @see: L{utils.process.RunCmd}
440

441
    """
442
    cmd = []
443

    
444
    if timeout is not None:
445
      cmd.extend(["timeout", str(timeout)])
446

    
447
    cmd.extend([self._GetCommand(hvparams)])
448
    cmd.extend(args)
449

    
450
    return self._run_cmd_fn(cmd)
451

    
452
  def _ConfigFileName(self, instance_name):
453
    """Get the config file name for an instance.
454

455
    @param instance_name: instance name
456
    @type instance_name: str
457
    @return: fully qualified path to instance config file
458
    @rtype: str
459

460
    """
461
    return utils.PathJoin(self._cfgdir, instance_name)
462

    
463
  @classmethod
464
  def _WriteNICInfoFile(cls, instance, idx, nic):
465
    """Write the Xen config file for the instance.
466

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

469
    """
470
    instance_name = instance.name
471
    dirs = [(dname, constants.RUN_DIRS_MODE)
472
            for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
473
    utils.EnsureDirs(dirs)
474

    
475
    cfg_file = cls._InstanceNICFile(instance_name, idx)
476
    data = StringIO()
477

    
478
    data.write("TAGS=%s\n" % r"\ ".join(instance.GetTags()))
479
    if nic.netinfo:
480
      netinfo = objects.Network.FromDict(nic.netinfo)
481
      for k, v in netinfo.HooksDict().iteritems():
482
        data.write("%s=%s\n" % (k, v))
483

    
484
    data.write("MAC=%s\n" % nic.mac)
485
    if nic.ip:
486
      data.write("IP=%s\n" % nic.ip)
487
    data.write("INTERFACE_INDEX=%s\n" % str(idx))
488
    if nic.name:
489
      data.write("INTERFACE_NAME=%s\n" % nic.name)
490
    data.write("INTERFACE_UUID=%s\n" % nic.uuid)
491
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
492
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
493
    data.write("VLAN=%s\n" % nic.nicparams[constants.NIC_VLAN])
494

    
495
    try:
496
      utils.WriteFile(cfg_file, data=data.getvalue())
497
    except EnvironmentError, err:
498
      raise errors.HypervisorError("Cannot write Xen instance configuration"
499
                                   " file %s: %s" % (cfg_file, err))
500

    
501
  @classmethod
502
  def _InstanceNICDir(cls, instance_name):
503
    """Returns the directory holding the tap device files for a given instance.
504

505
    """
506
    return utils.PathJoin(cls._NICS_DIR, instance_name)
507

    
508
  @classmethod
509
  def _InstanceNICFile(cls, instance_name, seq):
510
    """Returns the name of the file containing the tap device for a given NIC
511

512
    """
513
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
514

    
515
  @classmethod
516
  def _GetConfig(cls, instance, startup_memory, block_devices):
517
    """Build Xen configuration for an instance.
518

519
    """
520
    raise NotImplementedError
521

    
522
  def _WriteNicConfig(self, config, instance, hvp):
523
    vif_data = []
524

    
525
    # only XenHvmHypervisor has these hvparams
526
    nic_type = hvp.get(constants.HV_NIC_TYPE, None)
527
    vif_type = hvp.get(constants.HV_VIF_TYPE, None)
528
    nic_type_str = ""
529
    if nic_type or vif_type:
530
      if nic_type is None:
531
        if vif_type:
532
          nic_type_str = ", type=%s" % vif_type
533
      elif nic_type == constants.HT_NIC_PARAVIRTUAL:
534
        nic_type_str = ", type=paravirtualized"
535
      else:
536
        # parameter 'model' is only valid with type 'ioemu'
537
        nic_type_str = ", model=%s, type=%s" % \
538
          (nic_type, constants.HT_HVM_VIF_IOEMU)
539

    
540
    for idx, nic in enumerate(instance.nics):
541
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
542

    
543
      ip = getattr(nic, "ip", None)
544
      if ip is not None:
545
        nic_str += ", ip=%s" % ip
546

    
547
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
548
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
549
      elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
550
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
551
        if nic.nicparams[constants.NIC_VLAN]:
552
          nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
553

    
554
      if hvp[constants.HV_VIF_SCRIPT]:
555
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
556

    
557
      vif_data.append("'%s'" % nic_str)
558
      self._WriteNICInfoFile(instance, idx, nic)
559

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

    
562
  def _WriteConfigFile(self, instance_name, data):
563
    """Write the Xen config file for the instance.
564

565
    This version of the function just writes the config file from static data.
566

567
    """
568
    # just in case it exists
569
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
570

    
571
    cfg_file = self._ConfigFileName(instance_name)
572
    try:
573
      utils.WriteFile(cfg_file, data=data)
574
    except EnvironmentError, err:
575
      raise errors.HypervisorError("Cannot write Xen instance configuration"
576
                                   " file %s: %s" % (cfg_file, err))
577

    
578
  def _ReadConfigFile(self, instance_name):
579
    """Returns the contents of the instance config file.
580

581
    """
582
    filename = self._ConfigFileName(instance_name)
583

    
584
    try:
585
      file_content = utils.ReadFile(filename)
586
    except EnvironmentError, err:
587
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
588

    
589
    return file_content
590

    
591
  def _RemoveConfigFile(self, instance_name):
592
    """Remove the xen configuration file.
593

594
    """
595
    utils.RemoveFile(self._ConfigFileName(instance_name))
596
    try:
597
      shutil.rmtree(self._InstanceNICDir(instance_name))
598
    except OSError, err:
599
      if err.errno != errno.ENOENT:
600
        raise
601

    
602
  def _StashConfigFile(self, instance_name):
603
    """Move the Xen config file to the log directory and return its new path.
604

605
    """
606
    old_filename = self._ConfigFileName(instance_name)
607
    base = ("%s-%s" %
608
            (instance_name, utils.TimestampForFilename()))
609
    new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
610
    utils.RenameFile(old_filename, new_filename)
611
    return new_filename
612

    
613
  def _GetInstanceList(self, include_node, hvparams):
614
    """Wrapper around module level L{_GetInstanceList}.
615

616
    @type hvparams: dict of strings
617
    @param hvparams: hypervisor parameters to be used on this node
618

619
    """
620
    return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
621
                            include_node)
622

    
623
  def ListInstances(self, hvparams=None):
624
    """Get the list of running instances.
625

626
    """
627
    instance_list = self._GetInstanceList(False, hvparams)
628
    names = [info[0] for info in instance_list]
629
    return names
630

    
631
  def GetInstanceInfo(self, instance_name, hvparams=None):
632
    """Get instance properties.
633

634
    @type instance_name: string
635
    @param instance_name: the instance name
636
    @type hvparams: dict of strings
637
    @param hvparams: the instance's hypervisor params
638

639
    @return: tuple (name, id, memory, vcpus, stat, times)
640

641
    """
642
    instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
643
    result = None
644
    for data in instance_list:
645
      if data[0] == instance_name:
646
        result = data
647
        break
648
    return result
649

    
650
  def GetAllInstancesInfo(self, hvparams=None):
651
    """Get properties of all instances.
652

653
    @type hvparams: dict of strings
654
    @param hvparams: hypervisor parameters
655
    @return: list of tuples (name, id, memory, vcpus, stat, times)
656

657
    """
658
    return self._GetInstanceList(False, hvparams)
659

    
660
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
661
    """Gather configuration details and write to disk.
662

663
    See L{_GetConfig} for arguments.
664

665
    """
666
    buf = StringIO()
667
    buf.write("# Automatically generated by Ganeti. Do not edit!\n")
668
    buf.write("\n")
669
    buf.write(self._GetConfig(instance, startup_memory, block_devices))
670
    buf.write("\n")
671

    
672
    self._WriteConfigFile(instance.name, buf.getvalue())
673

    
674
  def StartInstance(self, instance, block_devices, startup_paused):
675
    """Start an instance.
676

677
    """
678
    startup_memory = self._InstanceStartupMemory(instance)
679

    
680
    self._MakeConfigFile(instance, startup_memory, block_devices)
681

    
682
    cmd = ["create"]
683
    if startup_paused:
684
      cmd.append("-p")
685
    cmd.append(self._ConfigFileName(instance.name))
686

    
687
    result = self._RunXen(cmd, instance.hvparams)
688
    if result.failed:
689
      # Move the Xen configuration file to the log directory to avoid
690
      # leaving a stale config file behind.
691
      stashed_config = self._StashConfigFile(instance.name)
692
      raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
693
                                   " config file to %s" %
694
                                   (instance.name, result.fail_reason,
695
                                    result.output, stashed_config))
696

    
697
  def StopInstance(self, instance, force=False, retry=False, name=None,
698
                   timeout=None):
699
    """Stop an instance.
700

701
    A soft shutdown can be interrupted. A hard shutdown tries forever.
702

703
    """
704
    assert(timeout is None or force is not None)
705

    
706
    if name is None:
707
      name = instance.name
708

    
709
    return self._StopInstance(name, force, instance.hvparams, timeout)
710

    
711
  def _ShutdownInstance(self, name, hvparams, timeout):
712
    """Shutdown an instance if the instance is running.
713

714
    The '-w' flag waits for shutdown to complete which avoids the need
715
    to poll in the case where we want to destroy the domain
716
    immediately after shutdown.
717

718
    @type name: string
719
    @param name: name of the instance to stop
720
    @type hvparams: dict of string
721
    @param hvparams: hypervisor parameters of the instance
722
    @type timeout: int or None
723
    @param timeout: a timeout after which the shutdown command should be killed,
724
                    or None for no timeout
725

726
    """
727
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
728

    
729
    if instance_info is None or _IsInstanceShutdown(instance_info[4]):
730
      logging.info("Failed to shutdown instance %s, not running", name)
731
      return None
732

    
733
    return self._RunXen(["shutdown", "-w", name], hvparams, timeout)
734

    
735
  def _DestroyInstance(self, name, hvparams):
736
    """Destroy an instance if the instance if the instance exists.
737

738
    @type name: string
739
    @param name: name of the instance to destroy
740
    @type hvparams: dict of string
741
    @param hvparams: hypervisor parameters of the instance
742

743
    """
744
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
745

    
746
    if instance_info is None:
747
      logging.info("Failed to destroy instance %s, does not exist", name)
748
      return None
749

    
750
    return self._RunXen(["destroy", name], hvparams)
751

    
752
  def _StopInstance(self, name, force, hvparams, timeout):
753
    """Stop an instance.
754

755
    @type name: string
756
    @param name: name of the instance to destroy
757

758
    @type force: boolean
759
    @param force: whether to do a "hard" stop (destroy)
760

761
    @type hvparams: dict of string
762
    @param hvparams: hypervisor parameters of the instance
763

764
    @type timeout: int or None
765
    @param timeout: a timeout after which the shutdown command should be killed,
766
                    or None for no timeout
767

768
    """
769
    if force:
770
      result = self._DestroyInstance(name, hvparams)
771
    else:
772
      self._ShutdownInstance(name, hvparams, timeout)
773
      result = self._DestroyInstance(name, hvparams)
774

    
775
    if result is not None and result.failed and \
776
          self.GetInstanceInfo(name, hvparams=hvparams) is not None:
777
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
778
                                   (name, result.fail_reason, result.output))
779

    
780
    # Remove configuration file if stopping/starting instance was successful
781
    self._RemoveConfigFile(name)
782

    
783
  def RebootInstance(self, instance):
784
    """Reboot an instance.
785

786
    """
787
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
788

    
789
    if ini_info is None:
790
      raise errors.HypervisorError("Failed to reboot instance %s,"
791
                                   " not running" % instance.name)
792

    
793
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
794
    if result.failed:
795
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
796
                                   (instance.name, result.fail_reason,
797
                                    result.output))
798

    
799
    def _CheckInstance():
800
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
801

    
802
      # check if the domain ID has changed or the run time has decreased
803
      if (new_info is not None and
804
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
805
        return
806

    
807
      raise utils.RetryAgain()
808

    
809
    try:
810
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
811
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
812
    except utils.RetryTimeout:
813
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
814
                                   " did not reboot in the expected interval" %
815
                                   (instance.name, ))
816

    
817
  def BalloonInstanceMemory(self, instance, mem):
818
    """Balloon an instance memory to a certain value.
819

820
    @type instance: L{objects.Instance}
821
    @param instance: instance to be accepted
822
    @type mem: int
823
    @param mem: actual memory size to use for instance runtime
824

825
    """
826
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
827
    if result.failed:
828
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
829
                                   (instance.name, result.fail_reason,
830
                                    result.output))
831

    
832
    # Update configuration file
833
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
834
    cmd.append(self._ConfigFileName(instance.name))
835

    
836
    result = utils.RunCmd(cmd)
837
    if result.failed:
838
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
839
                                   (instance.name, result.fail_reason,
840
                                    result.output))
841

    
842
  def GetNodeInfo(self, hvparams=None):
843
    """Return information about the node.
844

845
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
846

847
    """
848
    result = self._RunXen(["info"], hvparams)
849
    if result.failed:
850
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
851
                    result.fail_reason, result.output)
852
      return None
853

    
854
    instance_list = self._GetInstanceList(True, hvparams)
855
    return _GetNodeInfo(result.stdout, instance_list)
856

    
857
  @classmethod
858
  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
859
    """Return a command for connecting to the console of an instance.
860

861
    """
862
    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
863
    return objects.InstanceConsole(instance=instance.name,
864
                                   kind=constants.CONS_SSH,
865
                                   host=primary_node.name,
866
                                   user=constants.SSH_CONSOLE_USER,
867
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
868
                                            xen_cmd, instance.name])
869

    
870
  def Verify(self, hvparams=None):
871
    """Verify the hypervisor.
872

873
    For Xen, this verifies that the xend process is running.
874

875
    @type hvparams: dict of strings
876
    @param hvparams: hypervisor parameters to be verified against
877

878
    @return: Problem description if something is wrong, C{None} otherwise
879

880
    """
881
    if hvparams is None:
882
      return "Could not verify the hypervisor, because no hvparams were" \
883
             " provided."
884

    
885
    if constants.HV_XEN_CMD in hvparams:
886
      xen_cmd = hvparams[constants.HV_XEN_CMD]
887
      try:
888
        self._CheckToolstack(xen_cmd)
889
      except errors.HypervisorError:
890
        return "The configured xen toolstack '%s' is not available on this" \
891
               " node." % xen_cmd
892

    
893
    result = self._RunXen(["info"], hvparams)
894
    if result.failed:
895
      return "Retrieving information from xen failed: %s, %s" % \
896
        (result.fail_reason, result.output)
897

    
898
    return None
899

    
900
  def MigrationInfo(self, instance):
901
    """Get instance information to perform a migration.
902

903
    @type instance: L{objects.Instance}
904
    @param instance: instance to be migrated
905
    @rtype: string
906
    @return: content of the xen config file
907

908
    """
909
    return self._ReadConfigFile(instance.name)
910

    
911
  def AcceptInstance(self, instance, info, target):
912
    """Prepare to accept an instance.
913

914
    @type instance: L{objects.Instance}
915
    @param instance: instance to be accepted
916
    @type info: string
917
    @param info: content of the xen config file on the source node
918
    @type target: string
919
    @param target: target host (usually ip), on this node
920

921
    """
922
    pass
923

    
924
  def FinalizeMigrationDst(self, instance, info, success):
925
    """Finalize an instance migration.
926

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

930
    @type instance: L{objects.Instance}
931
    @param instance: instance whose migration is being finalized
932
    @type info: string
933
    @param info: content of the xen config file on the source node
934
    @type success: boolean
935
    @param success: whether the migration was a success or a failure
936

937
    """
938
    if success:
939
      self._WriteConfigFile(instance.name, info)
940

    
941
  def MigrateInstance(self, cluster_name, instance, target, live):
942
    """Migrate an instance to a target node.
943

944
    The migration will not be attempted if the instance is not
945
    currently running.
946

947
    @type instance: L{objects.Instance}
948
    @param instance: the instance to be migrated
949
    @type target: string
950
    @param target: ip address of the target node
951
    @type live: boolean
952
    @param live: perform a live migration
953

954
    """
955
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
956

    
957
    return self._MigrateInstance(cluster_name, instance.name, target, port,
958
                                 live, instance.hvparams)
959

    
960
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
961
                       hvparams, _ping_fn=netutils.TcpPing):
962
    """Migrate an instance to a target node.
963

964
    @see: L{MigrateInstance} for details
965

966
    """
967
    if hvparams is None:
968
      raise errors.HypervisorError("No hvparams provided.")
969

    
970
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
971
      raise errors.HypervisorError("Instance not running, cannot migrate")
972

    
973
    cmd = self._GetCommand(hvparams)
974

    
975
    if (cmd == constants.XEN_CMD_XM and
976
        not _ping_fn(target, port, live_port_needed=True)):
977
      raise errors.HypervisorError("Remote host %s not listening on port"
978
                                   " %s, cannot migrate" % (target, port))
979

    
980
    args = ["migrate"]
981

    
982
    if cmd == constants.XEN_CMD_XM:
983
      args.extend(["-p", "%d" % port])
984
      if live:
985
        args.append("-l")
986

    
987
    elif cmd == constants.XEN_CMD_XL:
988
      args.extend([
989
        "-s", constants.XL_SSH_CMD % cluster_name,
990
        "-C", self._ConfigFileName(instance_name),
991
        ])
992

    
993
    else:
994
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
995

    
996
    args.extend([instance_name, target])
997

    
998
    result = self._RunXen(args, hvparams)
999
    if result.failed:
1000
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
1001
                                   (instance_name, result.output))
1002

    
1003
  def FinalizeMigrationSource(self, instance, success, live):
1004
    """Finalize the instance migration on the source node.
1005

1006
    @type instance: L{objects.Instance}
1007
    @param instance: the instance that was migrated
1008
    @type success: bool
1009
    @param success: whether the migration succeeded or not
1010
    @type live: bool
1011
    @param live: whether the user requested a live migration or not
1012

1013
    """
1014
    # pylint: disable=W0613
1015
    if success:
1016
      # remove old xen file after migration succeeded
1017
      try:
1018
        self._RemoveConfigFile(instance.name)
1019
      except EnvironmentError:
1020
        logging.exception("Failure while removing instance config file")
1021

    
1022
  def GetMigrationStatus(self, instance):
1023
    """Get the migration status
1024

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

1029
    @type instance: L{objects.Instance}
1030
    @param instance: the instance that is being migrated
1031
    @rtype: L{objects.MigrationStatus}
1032
    @return: the status of the current migration (one of
1033
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1034
             progress info that can be retrieved from the hypervisor
1035

1036
    """
1037
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
1038

    
1039
  def PowercycleNode(self, hvparams=None):
1040
    """Xen-specific powercycle.
1041

1042
    This first does a Linux reboot (which triggers automatically a Xen
1043
    reboot), and if that fails it tries to do a Xen reboot. The reason
1044
    we don't try a Xen reboot first is that the xen reboot launches an
1045
    external command which connects to the Xen hypervisor, and that
1046
    won't work in case the root filesystem is broken and/or the xend
1047
    daemon is not working.
1048

1049
    @type hvparams: dict of strings
1050
    @param hvparams: hypervisor params to be used on this node
1051

1052
    """
1053
    try:
1054
      self.LinuxPowercycle()
1055
    finally:
1056
      xen_cmd = self._GetCommand(hvparams)
1057
      utils.RunCmd([xen_cmd, "debug", "R"])
1058

    
1059
  def _CheckToolstack(self, xen_cmd):
1060
    """Check whether the given toolstack is available on the node.
1061

1062
    @type xen_cmd: string
1063
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
1064

1065
    """
1066
    binary_found = self._CheckToolstackBinary(xen_cmd)
1067
    if not binary_found:
1068
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
1069
    elif xen_cmd == constants.XEN_CMD_XL:
1070
      if not self._CheckToolstackXlConfigured():
1071
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
1072
                                     "node." % xen_cmd)
1073

    
1074
  def _CheckToolstackBinary(self, xen_cmd):
1075
    """Checks whether the xen command's binary is found on the machine.
1076

1077
    """
1078
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
1079
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
1080
    result = self._run_cmd_fn(["which", xen_cmd])
1081
    return not result.failed
1082

    
1083
  def _CheckToolstackXlConfigured(self):
1084
    """Checks whether xl is enabled on an xl-capable node.
1085

1086
    @rtype: bool
1087
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
1088

1089
    """
1090
    result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
1091
    if not result.failed:
1092
      return True
1093
    elif result.failed:
1094
      if "toolstack" in result.stderr:
1095
        return False
1096
      # xl fails for some other reason than the toolstack
1097
      else:
1098
        raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
1099
                                     % (constants.XEN_CMD_XL, result.stderr))
1100

    
1101

    
1102
class XenPvmHypervisor(XenHypervisor):
1103
  """Xen PVM hypervisor interface"""
1104

    
1105
  PARAMETERS = {
1106
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1107
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1108
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1109
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1110
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1111
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1112
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1113
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1114
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1115
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1116
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1117
    constants.HV_REBOOT_BEHAVIOR:
1118
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1119
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1120
    constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1121
    constants.HV_CPU_WEIGHT:
1122
      (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1123
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1124
    constants.HV_XEN_CMD:
1125
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1126
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1127
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
1128
    }
1129

    
1130
  def _GetConfig(self, instance, startup_memory, block_devices):
1131
    """Write the Xen config file for the instance.
1132

1133
    """
1134
    hvp = instance.hvparams
1135
    config = StringIO()
1136
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1137

    
1138
    # if bootloader is True, use bootloader instead of kernel and ramdisk
1139
    # parameters.
1140
    if hvp[constants.HV_USE_BOOTLOADER]:
1141
      # bootloader handling
1142
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1143
      if bootloader_path:
1144
        config.write("bootloader = '%s'\n" % bootloader_path)
1145
      else:
1146
        raise errors.HypervisorError("Bootloader enabled, but missing"
1147
                                     " bootloader path")
1148

    
1149
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1150
      if bootloader_args:
1151
        config.write("bootargs = '%s'\n" % bootloader_args)
1152
    else:
1153
      # kernel handling
1154
      kpath = hvp[constants.HV_KERNEL_PATH]
1155
      config.write("kernel = '%s'\n" % kpath)
1156

    
1157
      # initrd handling
1158
      initrd_path = hvp[constants.HV_INITRD_PATH]
1159
      if initrd_path:
1160
        config.write("ramdisk = '%s'\n" % initrd_path)
1161

    
1162
    # rest of the settings
1163
    config.write("memory = %d\n" % startup_memory)
1164
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1165
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1166
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1167
    if cpu_pinning:
1168
      config.write("%s\n" % cpu_pinning)
1169
    cpu_cap = hvp[constants.HV_CPU_CAP]
1170
    if cpu_cap:
1171
      config.write("cpu_cap=%d\n" % cpu_cap)
1172
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1173
    if cpu_weight:
1174
      config.write("cpu_weight=%d\n" % cpu_weight)
1175

    
1176
    config.write("name = '%s'\n" % instance.name)
1177

    
1178
    self._WriteNicConfig(config, instance, hvp)
1179

    
1180
    disk_data = \
1181
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1182
    config.write("disk = [%s]\n" % ",".join(disk_data))
1183

    
1184
    if hvp[constants.HV_ROOT_PATH]:
1185
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1186
    config.write("on_poweroff = 'destroy'\n")
1187
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1188
      config.write("on_reboot = 'restart'\n")
1189
    else:
1190
      config.write("on_reboot = 'destroy'\n")
1191
    config.write("on_crash = 'restart'\n")
1192
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1193

    
1194
    cpuid = hvp[constants.HV_XEN_CPUID]
1195
    if cpuid:
1196
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1197

    
1198
    if hvp[constants.HV_SOUNDHW]:
1199
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1200

    
1201
    return config.getvalue()
1202

    
1203

    
1204
class XenHvmHypervisor(XenHypervisor):
1205
  """Xen HVM hypervisor interface"""
1206

    
1207
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1208
    pathutils.VNC_PASSWORD_FILE,
1209
    ]
1210
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1211
    pathutils.VNC_PASSWORD_FILE,
1212
    ]
1213

    
1214
  PARAMETERS = {
1215
    constants.HV_ACPI: hv_base.NO_CHECK,
1216
    constants.HV_BOOT_ORDER: (True, ) +
1217
      (lambda x: x and len(x.strip("acdn")) == 0,
1218
       "Invalid boot order specified, must be one or more of [acdn]",
1219
       None, None),
1220
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1221
    constants.HV_DISK_TYPE:
1222
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1223
    constants.HV_NIC_TYPE:
1224
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1225
    constants.HV_PAE: hv_base.NO_CHECK,
1226
    constants.HV_VNC_BIND_ADDRESS:
1227
      (False, netutils.IP4Address.IsValid,
1228
       "VNC bind address is not a valid IP address", None, None),
1229
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1230
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1231
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1232
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1233
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1234
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1235
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1236
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1237
    # Add PCI passthrough
1238
    constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1239
    constants.HV_REBOOT_BEHAVIOR:
1240
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1241
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1242
    constants.HV_CPU_CAP: hv_base.NO_CHECK,
1243
    constants.HV_CPU_WEIGHT:
1244
      (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1245
    constants.HV_VIF_TYPE:
1246
      hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1247
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1248
    constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1249
    constants.HV_XEN_CMD:
1250
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1251
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1252
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
1253
    }
1254

    
1255
  def _GetConfig(self, instance, startup_memory, block_devices):
1256
    """Create a Xen 3.1 HVM config file.
1257

1258
    """
1259
    hvp = instance.hvparams
1260

    
1261
    config = StringIO()
1262

    
1263
    # kernel handling
1264
    kpath = hvp[constants.HV_KERNEL_PATH]
1265
    config.write("kernel = '%s'\n" % kpath)
1266

    
1267
    config.write("builder = 'hvm'\n")
1268
    config.write("memory = %d\n" % startup_memory)
1269
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1270
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1271
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1272
    if cpu_pinning:
1273
      config.write("%s\n" % cpu_pinning)
1274
    cpu_cap = hvp[constants.HV_CPU_CAP]
1275
    if cpu_cap:
1276
      config.write("cpu_cap=%d\n" % cpu_cap)
1277
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1278
    if cpu_weight:
1279
      config.write("cpu_weight=%d\n" % cpu_weight)
1280

    
1281
    config.write("name = '%s'\n" % instance.name)
1282
    if hvp[constants.HV_PAE]:
1283
      config.write("pae = 1\n")
1284
    else:
1285
      config.write("pae = 0\n")
1286
    if hvp[constants.HV_ACPI]:
1287
      config.write("acpi = 1\n")
1288
    else:
1289
      config.write("acpi = 0\n")
1290
    if hvp[constants.HV_VIRIDIAN]:
1291
      config.write("viridian = 1\n")
1292
    else:
1293
      config.write("viridian = 0\n")
1294

    
1295
    config.write("apic = 1\n")
1296
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1297
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1298
    config.write("sdl = 0\n")
1299
    config.write("usb = 1\n")
1300
    config.write("usbdevice = 'tablet'\n")
1301
    config.write("vnc = 1\n")
1302
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1303
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1304
    else:
1305
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1306

    
1307
    if instance.network_port > constants.VNC_BASE_PORT:
1308
      display = instance.network_port - constants.VNC_BASE_PORT
1309
      config.write("vncdisplay = %s\n" % display)
1310
      config.write("vncunused = 0\n")
1311
    else:
1312
      config.write("# vncdisplay = 1\n")
1313
      config.write("vncunused = 1\n")
1314

    
1315
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1316
    try:
1317
      password = utils.ReadFile(vnc_pwd_file)
1318
    except EnvironmentError, err:
1319
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1320
                                   (vnc_pwd_file, err))
1321

    
1322
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1323

    
1324
    config.write("serial = 'pty'\n")
1325
    if hvp[constants.HV_USE_LOCALTIME]:
1326
      config.write("localtime = 1\n")
1327

    
1328
    self._WriteNicConfig(config, instance, hvp)
1329

    
1330
    disk_data = \
1331
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1332

    
1333
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1334
    if iso_path:
1335
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1336
      disk_data.append(iso)
1337

    
1338
    config.write("disk = [%s]\n" % (",".join(disk_data)))
1339
    # Add PCI passthrough
1340
    pci_pass_arr = []
1341
    pci_pass = hvp[constants.HV_PASSTHROUGH]
1342
    if pci_pass:
1343
      pci_pass_arr = pci_pass.split(";")
1344
      config.write("pci = %s\n" % pci_pass_arr)
1345
    config.write("on_poweroff = 'destroy'\n")
1346
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1347
      config.write("on_reboot = 'restart'\n")
1348
    else:
1349
      config.write("on_reboot = 'destroy'\n")
1350
    config.write("on_crash = 'restart'\n")
1351

    
1352
    cpuid = hvp[constants.HV_XEN_CPUID]
1353
    if cpuid:
1354
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1355

    
1356
    if hvp[constants.HV_SOUNDHW]:
1357
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1358

    
1359
    return config.getvalue()