Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 5349519d

History | View | Annotate | Download (45.1 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
  constants.FD_BLKTAP2: "tap2:tapdisk:aio",
52
  }
53

    
54

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

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

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

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

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

    
86

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

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

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

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

    
108

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

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

119
  """
120
  result = []
121

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

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

    
145
  return result
146

    
147

    
148
def _GetAllInstanceList(fn, include_node, _timeout=5):
149
  """Return the list of instances including running and shutdown.
150

151
  See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
152

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

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

    
167
    raise errors.HypervisorError(errmsg)
168

    
169
  return _ParseInstanceList(lines, include_node)
170

    
171

    
172
# Determine whether an instance is running
173
#
174
# An instance is running if it is in the following Xen states:
175
# running, blocked, or paused.
176
def _IsInstanceRunning(instance_info):
177
  return instance_info == "r-----" \
178
      or instance_info == "-b----" \
179
      or instance_info == "--p---"
180

    
181

    
182
def _IsInstanceShutdown(instance_info):
183
  return instance_info == "---s--"
184

    
185

    
186
def _XenToHypervisorInstanceState(instance_info):
187
  if _IsInstanceRunning(instance_info):
188
    return hv_base.HvInstanceState.RUNNING
189
  elif _IsInstanceShutdown(instance_info):
190
    return hv_base.HvInstanceState.SHUTDOWN
191
  else:
192
    raise errors.HypervisorError("hv_xen._XenToHypervisorInstanceState:"
193
                                 " unhandled Xen instance state '%s'" %
194
                                   instance_info)
195

    
196

    
197
def _GetRunningInstanceList(fn, include_node, _timeout=5):
198
  """Return the list of running instances.
199

200
  See L{_GetAllInstanceList} for parameter details.
201

202
  """
203
  instances = _GetAllInstanceList(fn, include_node, _timeout)
204
  return [i for i in instances if hv_base.HvInstanceState.IsRunning(i[4])]
205

    
206

    
207
def _GetShutdownInstanceList(fn, include_node, _timeout=5):
208
  """Return the list of shutdown instances.
209

210
  See L{_GetAllInstanceList} for parameter details.
211

212
  """
213
  instances = _GetAllInstanceList(fn, include_node, _timeout)
214
  return [i for i in instances if hv_base.HvInstanceState.IsShutdown(i[4])]
215

    
216

    
217
def _ParseNodeInfo(info):
218
  """Return information about the node.
219

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

228
  """
229
  result = {}
230
  cores_per_socket = threads_per_core = nr_cpus = None
231
  xen_major, xen_minor = None, None
232
  memory_total = None
233
  memory_free = None
234

    
235
  for line in info.splitlines():
236
    fields = line.split(":", 1)
237

    
238
    if len(fields) < 2:
239
      continue
240

    
241
    (key, val) = map(lambda s: s.strip(), fields)
242

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

    
261
  if None not in [cores_per_socket, threads_per_core, nr_cpus]:
262
    result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
263

    
264
  if memory_free is not None:
265
    result["memory_free"] = memory_free
266

    
267
  if memory_total is not None:
268
    result["memory_total"] = memory_total
269

    
270
  if not (xen_major is None or xen_minor is None):
271
    result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
272

    
273
  return result
274

    
275

    
276
def _MergeInstanceInfo(info, instance_list):
277
  """Updates node information from L{_ParseNodeInfo} with instance info.
278

279
  @type info: dict
280
  @param info: Result from L{_ParseNodeInfo}
281
  @type instance_list: list of tuples
282
  @param instance_list: list of instance information; one tuple per instance
283
  @rtype: dict
284

285
  """
286
  total_instmem = 0
287

    
288
  for (name, _, mem, vcpus, _, _) in instance_list:
289
    if name == _DOM0_NAME:
290
      info["memory_dom0"] = mem
291
      info["cpu_dom0"] = vcpus
292

    
293
    # Include Dom0 in total memory usage
294
    total_instmem += mem
295

    
296
  memory_free = info.get("memory_free")
297
  memory_total = info.get("memory_total")
298

    
299
  # Calculate memory used by hypervisor
300
  if None not in [memory_total, memory_free, total_instmem]:
301
    info["memory_hv"] = memory_total - memory_free - total_instmem
302

    
303
  return info
304

    
305

    
306
def _GetNodeInfo(info, instance_list):
307
  """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
308

309
  @type instance_list: list of tuples
310
  @param instance_list: list of instance information; one tuple per instance
311

312
  """
313
  return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
314

    
315

    
316
def _GetConfigFileDiskData(block_devices, blockdev_prefix,
317
                           _letters=_DISK_LETTERS):
318
  """Get disk directives for Xen config file.
319

320
  This method builds the xen config disk directive according to the
321
  given disk_template and block_devices.
322

323
  @param block_devices: list of tuples (cfdev, rldev):
324
      - cfdev: dict containing ganeti config disk part
325
      - rldev: ganeti.block.bdev.BlockDev object
326
  @param blockdev_prefix: a string containing blockdevice prefix,
327
                          e.g. "sd" for /dev/sda
328

329
  @return: string containing disk directive for xen instance config file
330

331
  """
332
  if len(block_devices) > len(_letters):
333
    raise errors.HypervisorError("Too many disks")
334

    
335
  disk_data = []
336

    
337
  for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
338
    sd_name = blockdev_prefix + sd_suffix
339

    
340
    if cfdev.mode == constants.DISK_RDWR:
341
      mode = "w"
342
    else:
343
      mode = "r"
344

    
345
    if cfdev.dev_type in constants.DTS_FILEBASED:
346
      driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
347
    else:
348
      driver = "phy"
349

    
350
    disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
351

    
352
  return disk_data
353

    
354

    
355
def _QuoteCpuidField(data):
356
  """Add quotes around the CPUID field only if necessary.
357

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

361
  @param data: Either type of parameter.
362
  @return: The quoted version thereof.
363

364
  """
365
  return "'%s'" % data if data.startswith("host") else data
366

    
367

    
368
class XenHypervisor(hv_base.BaseHypervisor):
369
  """Xen generic hypervisor interface
370

371
  This is the Xen base class used for both Xen PVM and HVM. It contains
372
  all the functionality that is identical for both.
373

374
  """
375
  CAN_MIGRATE = True
376
  REBOOT_RETRY_COUNT = 60
377
  REBOOT_RETRY_INTERVAL = 10
378
  _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
379
  _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
380
  _DIRS = [_ROOT_DIR, _NICS_DIR]
381

    
382
  ANCILLARY_FILES = [
383
    XEND_CONFIG_FILE,
384
    XL_CONFIG_FILE,
385
    VIF_BRIDGE_SCRIPT,
386
    ]
387
  ANCILLARY_FILES_OPT = [
388
    XL_CONFIG_FILE,
389
    ]
390

    
391
  def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
392
    hv_base.BaseHypervisor.__init__(self)
393

    
394
    if _cfgdir is None:
395
      self._cfgdir = pathutils.XEN_CONFIG_DIR
396
    else:
397
      self._cfgdir = _cfgdir
398

    
399
    if _run_cmd_fn is None:
400
      self._run_cmd_fn = utils.RunCmd
401
    else:
402
      self._run_cmd_fn = _run_cmd_fn
403

    
404
    self._cmd = _cmd
405

    
406
  @staticmethod
407
  def _GetCommandFromHvparams(hvparams):
408
    """Returns the Xen command extracted from the given hvparams.
409

410
    @type hvparams: dict of strings
411
    @param hvparams: hypervisor parameters
412

413
    """
414
    if hvparams is None or constants.HV_XEN_CMD not in hvparams:
415
      raise errors.HypervisorError("Cannot determine xen command.")
416
    else:
417
      return hvparams[constants.HV_XEN_CMD]
418

    
419
  def _GetCommand(self, hvparams):
420
    """Returns Xen command to use.
421

422
    @type hvparams: dict of strings
423
    @param hvparams: hypervisor parameters
424

425
    """
426
    if self._cmd is None:
427
      cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
428
    else:
429
      cmd = self._cmd
430

    
431
    if cmd not in constants.KNOWN_XEN_COMMANDS:
432
      raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
433

    
434
    return cmd
435

    
436
  def _RunXen(self, args, hvparams):
437
    """Wrapper around L{utils.process.RunCmd} to run Xen command.
438

439
    @type hvparams: dict of strings
440
    @param hvparams: dictionary of hypervisor params
441
    @see: L{utils.process.RunCmd}
442

443
    """
444
    cmd = [self._GetCommand(hvparams)]
445
    cmd.extend(args)
446

    
447
    return self._run_cmd_fn(cmd)
448

    
449
  def _ConfigFileName(self, instance_name):
450
    """Get the config file name for an instance.
451

452
    @param instance_name: instance name
453
    @type instance_name: str
454
    @return: fully qualified path to instance config file
455
    @rtype: str
456

457
    """
458
    return utils.PathJoin(self._cfgdir, instance_name)
459

    
460
  @classmethod
461
  def _WriteNICInfoFile(cls, instance, idx, nic):
462
    """Write the Xen config file for the instance.
463

464
    This version of the function just writes the config file from static data.
465

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

    
472
    cfg_file = cls._InstanceNICFile(instance_name, idx)
473
    data = StringIO()
474

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

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

    
491
    try:
492
      utils.WriteFile(cfg_file, data=data.getvalue())
493
    except EnvironmentError, err:
494
      raise errors.HypervisorError("Cannot write Xen instance configuration"
495
                                   " file %s: %s" % (cfg_file, err))
496

    
497
  @classmethod
498
  def _InstanceNICDir(cls, instance_name):
499
    """Returns the directory holding the tap device files for a given instance.
500

501
    """
502
    return utils.PathJoin(cls._NICS_DIR, instance_name)
503

    
504
  @classmethod
505
  def _InstanceNICFile(cls, instance_name, seq):
506
    """Returns the name of the file containing the tap device for a given NIC
507

508
    """
509
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
510

    
511
  @classmethod
512
  def _GetConfig(cls, instance, startup_memory, block_devices):
513
    """Build Xen configuration for an instance.
514

515
    """
516
    raise NotImplementedError
517

    
518
  def _WriteConfigFile(self, instance_name, data):
519
    """Write the Xen config file for the instance.
520

521
    This version of the function just writes the config file from static data.
522

523
    """
524
    # just in case it exists
525
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
526

    
527
    cfg_file = self._ConfigFileName(instance_name)
528
    try:
529
      utils.WriteFile(cfg_file, data=data)
530
    except EnvironmentError, err:
531
      raise errors.HypervisorError("Cannot write Xen instance configuration"
532
                                   " file %s: %s" % (cfg_file, err))
533

    
534
  def _ReadConfigFile(self, instance_name):
535
    """Returns the contents of the instance config file.
536

537
    """
538
    filename = self._ConfigFileName(instance_name)
539

    
540
    try:
541
      file_content = utils.ReadFile(filename)
542
    except EnvironmentError, err:
543
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
544

    
545
    return file_content
546

    
547
  def _RemoveConfigFile(self, instance_name):
548
    """Remove the xen configuration file.
549

550
    """
551
    utils.RemoveFile(self._ConfigFileName(instance_name))
552
    try:
553
      shutil.rmtree(self._InstanceNICDir(instance_name))
554
    except OSError, err:
555
      if err.errno != errno.ENOENT:
556
        raise
557

    
558
  def _StashConfigFile(self, instance_name):
559
    """Move the Xen config file to the log directory and return its new path.
560

561
    """
562
    old_filename = self._ConfigFileName(instance_name)
563
    base = ("%s-%s" %
564
            (instance_name, utils.TimestampForFilename()))
565
    new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
566
    utils.RenameFile(old_filename, new_filename)
567
    return new_filename
568

    
569
  def _GetInstanceList(self, include_node, hvparams):
570
    """Wrapper around module level L{_GetAllInstanceList}.
571

572
    @type hvparams: dict of strings
573
    @param hvparams: hypervisor parameters to be used on this node
574

575
    """
576
    return _GetAllInstanceList(lambda: self._RunXen(["list"], hvparams),
577
                               include_node)
578

    
579
  def ListInstances(self, hvparams=None):
580
    """Get the list of running instances.
581

582
    """
583
    instance_list = self._GetInstanceList(False, hvparams)
584
    names = [info[0] for info in instance_list]
585
    return names
586

    
587
  def GetInstanceInfo(self, instance_name, hvparams=None):
588
    """Get instance properties.
589

590
    @type instance_name: string
591
    @param instance_name: the instance name
592
    @type hvparams: dict of strings
593
    @param hvparams: the instance's hypervisor params
594

595
    @return: tuple (name, id, memory, vcpus, stat, times)
596

597
    """
598
    instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
599
    result = None
600
    for data in instance_list:
601
      if data[0] == instance_name:
602
        result = data
603
        break
604
    return result
605

    
606
  def GetAllInstancesInfo(self, hvparams=None):
607
    """Get properties of all instances.
608

609
    @type hvparams: dict of strings
610
    @param hvparams: hypervisor parameters
611

612
    @rtype: (string, string, int, int, HypervisorInstanceState, int)
613
    @return: list of tuples (name, id, memory, vcpus, state, times)
614

615
    """
616
    return self._GetInstanceList(False, hvparams)
617

    
618
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
619
    """Gather configuration details and write to disk.
620

621
    See L{_GetConfig} for arguments.
622

623
    """
624
    buf = StringIO()
625
    buf.write("# Automatically generated by Ganeti. Do not edit!\n")
626
    buf.write("\n")
627
    buf.write(self._GetConfig(instance, startup_memory, block_devices))
628
    buf.write("\n")
629

    
630
    self._WriteConfigFile(instance.name, buf.getvalue())
631

    
632
  def StartInstance(self, instance, block_devices, startup_paused):
633
    """Start an instance.
634

635
    """
636
    startup_memory = self._InstanceStartupMemory(instance)
637

    
638
    self._MakeConfigFile(instance, startup_memory, block_devices)
639

    
640
    cmd = ["create"]
641
    if startup_paused:
642
      cmd.append("-p")
643
    cmd.append(self._ConfigFileName(instance.name))
644

    
645
    result = self._RunXen(cmd, instance.hvparams)
646
    if result.failed:
647
      # Move the Xen configuration file to the log directory to avoid
648
      # leaving a stale config file behind.
649
      stashed_config = self._StashConfigFile(instance.name)
650
      raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
651
                                   " config file to %s" %
652
                                   (instance.name, result.fail_reason,
653
                                    result.output, stashed_config))
654

    
655
  def StopInstance(self, instance, force=False, retry=False, name=None):
656
    """Stop an instance.
657

658
    """
659
    if name is None:
660
      name = instance.name
661

    
662
    return self._StopInstance(name, force, instance.hvparams)
663

    
664
  def _ShutdownInstance(self, name, hvparams):
665
    """Shutdown an instance if the instance is running.
666

667
    @type name: string
668
    @param name: name of the instance to stop
669
    @type hvparams: dict of string
670
    @param hvparams: hypervisor parameters of the instance
671

672
    The '-w' flag waits for shutdown to complete which avoids the need
673
    to poll in the case where we want to destroy the domain
674
    immediately after shutdown.
675

676
    """
677
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
678

    
679
    if instance_info is None or _IsInstanceShutdown(instance_info[4]):
680
      logging.info("Failed to shutdown instance %s, not running", name)
681
      return None
682

    
683
    return self._RunXen(["shutdown", "-w", name], hvparams)
684

    
685
  def _DestroyInstance(self, name, hvparams):
686
    """Destroy an instance if the instance if the instance exists.
687

688
    @type name: string
689
    @param name: name of the instance to destroy
690
    @type hvparams: dict of string
691
    @param hvparams: hypervisor parameters of the instance
692

693
    """
694
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
695

    
696
    if instance_info is None:
697
      logging.info("Failed to destroy instance %s, does not exist", name)
698
      return None
699

    
700
    return self._RunXen(["destroy", name], hvparams)
701

    
702
  # Destroy a domain only if necessary
703
  #
704
  # This method checks if the domain has already been destroyed before
705
  # issuing the 'destroy' command.  This step is necessary to handle
706
  # domains created by other versions of Ganeti.  For example, an
707
  # instance created with 2.10 will be destroy by the
708
  # '_ShutdownInstance', thus not requiring an additional destroy,
709
  # which would cause an error if issued.  See issue 619.
710
  def _DestroyInstanceIfAlive(self, name, hvparams):
711
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
712

    
713
    if instance_info is None:
714
      raise errors.HypervisorError("Failed to destroy instance %s, already"
715
                                   " destroyed" % name)
716
    else:
717
      self._DestroyInstance(name, hvparams)
718

    
719
  def _StopInstance(self, name, force, hvparams):
720
    """Stop an instance.
721

722
    @type name: string
723
    @param name: name of the instance to destroy
724

725
    @type force: boolean
726
    @param force: whether to do a "hard" stop (destroy)
727

728
    @type hvparams: dict of string
729
    @param hvparams: hypervisor parameters of the instance
730

731
    """
732
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
733

    
734
    if instance_info is None:
735
      raise errors.HypervisorError("Failed to shutdown instance %s,"
736
                                   " not running" % name)
737

    
738
    if force:
739
      result = self._DestroyInstanceIfAlive(name, hvparams)
740
    else:
741
      self._ShutdownInstance(name, hvparams)
742
      result = self._DestroyInstanceIfAlive(name, hvparams)
743

    
744
    if result is not None and result.failed and \
745
          self.GetInstanceInfo(name, hvparams=hvparams) is not None:
746
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
747
                                   (name, result.fail_reason, result.output))
748

    
749
    # Remove configuration file if stopping/starting instance was successful
750
    self._RemoveConfigFile(name)
751

    
752
  def RebootInstance(self, instance):
753
    """Reboot an instance.
754

755
    """
756
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
757

    
758
    if ini_info is None:
759
      raise errors.HypervisorError("Failed to reboot instance %s,"
760
                                   " not running" % instance.name)
761

    
762
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
763
    if result.failed:
764
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
765
                                   (instance.name, result.fail_reason,
766
                                    result.output))
767

    
768
    def _CheckInstance():
769
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
770

    
771
      # check if the domain ID has changed or the run time has decreased
772
      if (new_info is not None and
773
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
774
        return
775

    
776
      raise utils.RetryAgain()
777

    
778
    try:
779
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
780
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
781
    except utils.RetryTimeout:
782
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
783
                                   " did not reboot in the expected interval" %
784
                                   (instance.name, ))
785

    
786
  def BalloonInstanceMemory(self, instance, mem):
787
    """Balloon an instance memory to a certain value.
788

789
    @type instance: L{objects.Instance}
790
    @param instance: instance to be accepted
791
    @type mem: int
792
    @param mem: actual memory size to use for instance runtime
793

794
    """
795
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
796
    if result.failed:
797
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
798
                                   (instance.name, result.fail_reason,
799
                                    result.output))
800

    
801
    # Update configuration file
802
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
803
    cmd.append(self._ConfigFileName(instance.name))
804

    
805
    result = utils.RunCmd(cmd)
806
    if result.failed:
807
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
808
                                   (instance.name, result.fail_reason,
809
                                    result.output))
810

    
811
  def GetNodeInfo(self, hvparams=None):
812
    """Return information about the node.
813

814
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
815

816
    """
817
    result = self._RunXen(["info"], hvparams)
818
    if result.failed:
819
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
820
                    result.fail_reason, result.output)
821
      return None
822

    
823
    instance_list = self._GetInstanceList(True, hvparams)
824
    return _GetNodeInfo(result.stdout, instance_list)
825

    
826
  @classmethod
827
  def GetInstanceConsole(cls, instance, primary_node, node_group,
828
                         hvparams, beparams):
829
    """Return a command for connecting to the console of an instance.
830

831
    """
832
    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
833
    ndparams = node_group.FillND(primary_node)
834
    return objects.InstanceConsole(instance=instance.name,
835
                                   kind=constants.CONS_SSH,
836
                                   host=primary_node.name,
837
                                   port=ndparams.get(constants.ND_SSH_PORT),
838
                                   user=constants.SSH_CONSOLE_USER,
839
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
840
                                            xen_cmd, instance.name])
841

    
842
  def Verify(self, hvparams=None):
843
    """Verify the hypervisor.
844

845
    For Xen, this verifies that the xend process is running.
846

847
    @type hvparams: dict of strings
848
    @param hvparams: hypervisor parameters to be verified against
849

850
    @return: Problem description if something is wrong, C{None} otherwise
851

852
    """
853
    if hvparams is None:
854
      return "Could not verify the hypervisor, because no hvparams were" \
855
             " provided."
856

    
857
    if constants.HV_XEN_CMD in hvparams:
858
      xen_cmd = hvparams[constants.HV_XEN_CMD]
859
      try:
860
        self._CheckToolstack(xen_cmd)
861
      except errors.HypervisorError:
862
        return "The configured xen toolstack '%s' is not available on this" \
863
               " node." % xen_cmd
864

    
865
    result = self._RunXen(["info"], hvparams)
866
    if result.failed:
867
      return "Retrieving information from xen failed: %s, %s" % \
868
        (result.fail_reason, result.output)
869

    
870
    return None
871

    
872
  def MigrationInfo(self, instance):
873
    """Get instance information to perform a migration.
874

875
    @type instance: L{objects.Instance}
876
    @param instance: instance to be migrated
877
    @rtype: string
878
    @return: content of the xen config file
879

880
    """
881
    return self._ReadConfigFile(instance.name)
882

    
883
  def AcceptInstance(self, instance, info, target):
884
    """Prepare to accept an instance.
885

886
    @type instance: L{objects.Instance}
887
    @param instance: instance to be accepted
888
    @type info: string
889
    @param info: content of the xen config file on the source node
890
    @type target: string
891
    @param target: target host (usually ip), on this node
892

893
    """
894
    pass
895

    
896
  def FinalizeMigrationDst(self, instance, info, success):
897
    """Finalize an instance migration.
898

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

902
    @type instance: L{objects.Instance}
903
    @param instance: instance whose migration is being finalized
904
    @type info: string
905
    @param info: content of the xen config file on the source node
906
    @type success: boolean
907
    @param success: whether the migration was a success or a failure
908

909
    """
910
    if success:
911
      self._WriteConfigFile(instance.name, info)
912

    
913
  def MigrateInstance(self, cluster_name, instance, target, live):
914
    """Migrate an instance to a target node.
915

916
    The migration will not be attempted if the instance is not
917
    currently running.
918

919
    @type instance: L{objects.Instance}
920
    @param instance: the instance to be migrated
921
    @type target: string
922
    @param target: ip address of the target node
923
    @type live: boolean
924
    @param live: perform a live migration
925

926
    """
927
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
928

    
929
    return self._MigrateInstance(cluster_name, instance.name, target, port,
930
                                 live, instance.hvparams)
931

    
932
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
933
                       hvparams, _ping_fn=netutils.TcpPing):
934
    """Migrate an instance to a target node.
935

936
    @see: L{MigrateInstance} for details
937

938
    """
939
    if hvparams is None:
940
      raise errors.HypervisorError("No hvparams provided.")
941

    
942
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
943
      raise errors.HypervisorError("Instance not running, cannot migrate")
944

    
945
    cmd = self._GetCommand(hvparams)
946

    
947
    if (cmd == constants.XEN_CMD_XM and
948
        not _ping_fn(target, port, live_port_needed=True)):
949
      raise errors.HypervisorError("Remote host %s not listening on port"
950
                                   " %s, cannot migrate" % (target, port))
951

    
952
    args = ["migrate"]
953

    
954
    if cmd == constants.XEN_CMD_XM:
955
      args.extend(["-p", "%d" % port])
956
      if live:
957
        args.append("-l")
958

    
959
    elif cmd == constants.XEN_CMD_XL:
960
      args.extend([
961
        "-s", constants.XL_SSH_CMD % cluster_name,
962
        "-C", self._ConfigFileName(instance_name),
963
        ])
964

    
965
    else:
966
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
967

    
968
    args.extend([instance_name, target])
969

    
970
    result = self._RunXen(args, hvparams)
971
    if result.failed:
972
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
973
                                   (instance_name, result.output))
974

    
975
  def FinalizeMigrationSource(self, instance, success, live):
976
    """Finalize the instance migration on the source node.
977

978
    @type instance: L{objects.Instance}
979
    @param instance: the instance that was migrated
980
    @type success: bool
981
    @param success: whether the migration succeeded or not
982
    @type live: bool
983
    @param live: whether the user requested a live migration or not
984

985
    """
986
    # pylint: disable=W0613
987
    if success:
988
      # remove old xen file after migration succeeded
989
      try:
990
        self._RemoveConfigFile(instance.name)
991
      except EnvironmentError:
992
        logging.exception("Failure while removing instance config file")
993

    
994
  def GetMigrationStatus(self, instance):
995
    """Get the migration status
996

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

1001
    @type instance: L{objects.Instance}
1002
    @param instance: the instance that is being migrated
1003
    @rtype: L{objects.MigrationStatus}
1004
    @return: the status of the current migration (one of
1005
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1006
             progress info that can be retrieved from the hypervisor
1007

1008
    """
1009
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
1010

    
1011
  def PowercycleNode(self, hvparams=None):
1012
    """Xen-specific powercycle.
1013

1014
    This first does a Linux reboot (which triggers automatically a Xen
1015
    reboot), and if that fails it tries to do a Xen reboot. The reason
1016
    we don't try a Xen reboot first is that the xen reboot launches an
1017
    external command which connects to the Xen hypervisor, and that
1018
    won't work in case the root filesystem is broken and/or the xend
1019
    daemon is not working.
1020

1021
    @type hvparams: dict of strings
1022
    @param hvparams: hypervisor params to be used on this node
1023

1024
    """
1025
    try:
1026
      self.LinuxPowercycle()
1027
    finally:
1028
      xen_cmd = self._GetCommand(hvparams)
1029
      utils.RunCmd([xen_cmd, "debug", "R"])
1030

    
1031
  def _CheckToolstack(self, xen_cmd):
1032
    """Check whether the given toolstack is available on the node.
1033

1034
    @type xen_cmd: string
1035
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
1036

1037
    """
1038
    binary_found = self._CheckToolstackBinary(xen_cmd)
1039
    if not binary_found:
1040
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
1041
    elif xen_cmd == constants.XEN_CMD_XL:
1042
      if not self._CheckToolstackXlConfigured():
1043
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
1044
                                     "node." % xen_cmd)
1045

    
1046
  def _CheckToolstackBinary(self, xen_cmd):
1047
    """Checks whether the xen command's binary is found on the machine.
1048

1049
    """
1050
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
1051
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
1052
    result = self._run_cmd_fn(["which", xen_cmd])
1053
    return not result.failed
1054

    
1055
  def _CheckToolstackXlConfigured(self):
1056
    """Checks whether xl is enabled on an xl-capable node.
1057

1058
    @rtype: bool
1059
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
1060

1061
    """
1062
    result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
1063
    if not result.failed:
1064
      return True
1065
    elif result.failed:
1066
      if "toolstack" in result.stderr:
1067
        return False
1068
      # xl fails for some other reason than the toolstack
1069
      else:
1070
        raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
1071
                                     % (constants.XEN_CMD_XL, result.stderr))
1072

    
1073

    
1074
def WriteXenConfigEvents(config, hvp):
1075
  config.write("on_poweroff = 'preserve'\n")
1076
  if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1077
    config.write("on_reboot = 'restart'\n")
1078
  else:
1079
    config.write("on_reboot = 'destroy'\n")
1080
  config.write("on_crash = 'restart'\n")
1081

    
1082

    
1083
class XenPvmHypervisor(XenHypervisor):
1084
  """Xen PVM hypervisor interface"""
1085

    
1086
  PARAMETERS = {
1087
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1088
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1089
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1090
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1091
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1092
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1093
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1094
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1095
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1096
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1097
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1098
    constants.HV_REBOOT_BEHAVIOR:
1099
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1100
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1101
    constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1102
    constants.HV_CPU_WEIGHT:
1103
      (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1104
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1105
    constants.HV_XEN_CMD:
1106
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1107
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1108
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
1109
    }
1110

    
1111
  def _GetConfig(self, instance, startup_memory, block_devices):
1112
    """Write the Xen config file for the instance.
1113

1114
    """
1115
    hvp = instance.hvparams
1116
    config = StringIO()
1117
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1118

    
1119
    # if bootloader is True, use bootloader instead of kernel and ramdisk
1120
    # parameters.
1121
    if hvp[constants.HV_USE_BOOTLOADER]:
1122
      # bootloader handling
1123
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1124
      if bootloader_path:
1125
        config.write("bootloader = '%s'\n" % bootloader_path)
1126
      else:
1127
        raise errors.HypervisorError("Bootloader enabled, but missing"
1128
                                     " bootloader path")
1129

    
1130
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1131
      if bootloader_args:
1132
        config.write("bootargs = '%s'\n" % bootloader_args)
1133
    else:
1134
      # kernel handling
1135
      kpath = hvp[constants.HV_KERNEL_PATH]
1136
      config.write("kernel = '%s'\n" % kpath)
1137

    
1138
      # initrd handling
1139
      initrd_path = hvp[constants.HV_INITRD_PATH]
1140
      if initrd_path:
1141
        config.write("ramdisk = '%s'\n" % initrd_path)
1142

    
1143
    # rest of the settings
1144
    config.write("memory = %d\n" % startup_memory)
1145
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1146
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1147
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1148
    if cpu_pinning:
1149
      config.write("%s\n" % cpu_pinning)
1150
    cpu_cap = hvp[constants.HV_CPU_CAP]
1151
    if cpu_cap:
1152
      config.write("cpu_cap=%d\n" % cpu_cap)
1153
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1154
    if cpu_weight:
1155
      config.write("cpu_weight=%d\n" % cpu_weight)
1156

    
1157
    config.write("name = '%s'\n" % instance.name)
1158

    
1159
    vif_data = []
1160
    for idx, nic in enumerate(instance.nics):
1161
      nic_str = "mac=%s" % (nic.mac)
1162
      ip = getattr(nic, "ip", None)
1163
      if ip is not None:
1164
        nic_str += ", ip=%s" % ip
1165
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1166
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1167
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1168
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1169
        if nic.nicparams[constants.NIC_VLAN]:
1170
          nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
1171
      if hvp[constants.HV_VIF_SCRIPT]:
1172
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1173
      vif_data.append("'%s'" % nic_str)
1174
      self._WriteNICInfoFile(instance, idx, nic)
1175

    
1176
    disk_data = \
1177
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1178

    
1179
    config.write("vif = [%s]\n" % ",".join(vif_data))
1180
    config.write("disk = [%s]\n" % ",".join(disk_data))
1181

    
1182
    if hvp[constants.HV_ROOT_PATH]:
1183
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1184

    
1185
    WriteXenConfigEvents(config, hvp)
1186
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1187

    
1188
    cpuid = hvp[constants.HV_XEN_CPUID]
1189
    if cpuid:
1190
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1191

    
1192
    if hvp[constants.HV_SOUNDHW]:
1193
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1194

    
1195
    return config.getvalue()
1196

    
1197

    
1198
class XenHvmHypervisor(XenHypervisor):
1199
  """Xen HVM hypervisor interface"""
1200

    
1201
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1202
    pathutils.VNC_PASSWORD_FILE,
1203
    ]
1204
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1205
    pathutils.VNC_PASSWORD_FILE,
1206
    ]
1207

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

    
1249
  def _GetConfig(self, instance, startup_memory, block_devices):
1250
    """Create a Xen 3.1 HVM config file.
1251

1252
    """
1253
    hvp = instance.hvparams
1254

    
1255
    config = StringIO()
1256

    
1257
    # kernel handling
1258
    kpath = hvp[constants.HV_KERNEL_PATH]
1259
    config.write("kernel = '%s'\n" % kpath)
1260

    
1261
    config.write("builder = 'hvm'\n")
1262
    config.write("memory = %d\n" % startup_memory)
1263
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1264
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1265
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1266
    if cpu_pinning:
1267
      config.write("%s\n" % cpu_pinning)
1268
    cpu_cap = hvp[constants.HV_CPU_CAP]
1269
    if cpu_cap:
1270
      config.write("cpu_cap=%d\n" % cpu_cap)
1271
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1272
    if cpu_weight:
1273
      config.write("cpu_weight=%d\n" % cpu_weight)
1274

    
1275
    config.write("name = '%s'\n" % instance.name)
1276
    if hvp[constants.HV_PAE]:
1277
      config.write("pae = 1\n")
1278
    else:
1279
      config.write("pae = 0\n")
1280
    if hvp[constants.HV_ACPI]:
1281
      config.write("acpi = 1\n")
1282
    else:
1283
      config.write("acpi = 0\n")
1284
    if hvp[constants.HV_VIRIDIAN]:
1285
      config.write("viridian = 1\n")
1286
    else:
1287
      config.write("viridian = 0\n")
1288

    
1289
    config.write("apic = 1\n")
1290
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1291
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1292
    config.write("sdl = 0\n")
1293
    config.write("usb = 1\n")
1294
    config.write("usbdevice = 'tablet'\n")
1295
    config.write("vnc = 1\n")
1296
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1297
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1298
    else:
1299
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1300

    
1301
    if instance.network_port > constants.VNC_BASE_PORT:
1302
      display = instance.network_port - constants.VNC_BASE_PORT
1303
      config.write("vncdisplay = %s\n" % display)
1304
      config.write("vncunused = 0\n")
1305
    else:
1306
      config.write("# vncdisplay = 1\n")
1307
      config.write("vncunused = 1\n")
1308

    
1309
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1310
    try:
1311
      password = utils.ReadFile(vnc_pwd_file)
1312
    except EnvironmentError, err:
1313
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1314
                                   (vnc_pwd_file, err))
1315

    
1316
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1317

    
1318
    config.write("serial = 'pty'\n")
1319
    if hvp[constants.HV_USE_LOCALTIME]:
1320
      config.write("localtime = 1\n")
1321

    
1322
    vif_data = []
1323
    # Note: what is called 'nic_type' here, is used as value for the xen nic
1324
    # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1325
    # the 'vif_type' to avoid a clash of notation.
1326
    nic_type = hvp[constants.HV_NIC_TYPE]
1327

    
1328
    if nic_type is None:
1329
      vif_type_str = ""
1330
      if hvp[constants.HV_VIF_TYPE]:
1331
        vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1332
      # ensure old instances don't change
1333
      nic_type_str = vif_type_str
1334
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1335
      nic_type_str = ", type=paravirtualized"
1336
    else:
1337
      # parameter 'model' is only valid with type 'ioemu'
1338
      nic_type_str = ", model=%s, type=%s" % \
1339
        (nic_type, constants.HT_HVM_VIF_IOEMU)
1340
    for idx, nic in enumerate(instance.nics):
1341
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1342
      ip = getattr(nic, "ip", None)
1343
      if ip is not None:
1344
        nic_str += ", ip=%s" % ip
1345
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1346
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1347
      if hvp[constants.HV_VIF_SCRIPT]:
1348
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1349
      vif_data.append("'%s'" % nic_str)
1350
      self._WriteNICInfoFile(instance, idx, nic)
1351

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

    
1354
    disk_data = \
1355
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1356

    
1357
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1358
    if iso_path:
1359
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1360
      disk_data.append(iso)
1361

    
1362
    config.write("disk = [%s]\n" % (",".join(disk_data)))
1363
    # Add PCI passthrough
1364
    pci_pass_arr = []
1365
    pci_pass = hvp[constants.HV_PASSTHROUGH]
1366
    if pci_pass:
1367
      pci_pass_arr = pci_pass.split(";")
1368
      config.write("pci = %s\n" % pci_pass_arr)
1369

    
1370
    WriteXenConfigEvents(config, hvp)
1371

    
1372
    cpuid = hvp[constants.HV_XEN_CPUID]
1373
    if cpuid:
1374
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1375

    
1376
    if hvp[constants.HV_SOUNDHW]:
1377
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1378

    
1379
    return config.getvalue()