Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ c42be2c0

History | View | Annotate | Download (43.7 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Xen hypervisors
23

24
"""
25

    
26
import logging
27
import errno
28
import string # pylint: disable=W0402
29
import shutil
30
from cStringIO import StringIO
31

    
32
from ganeti import constants
33
from ganeti import errors
34
from ganeti import utils
35
from ganeti.hypervisor import hv_base
36
from ganeti import netutils
37
from ganeti import objects
38
from ganeti import pathutils
39

    
40

    
41
XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
42
XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
43
VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
44
                                   "scripts/vif-bridge")
45
_DOM0_NAME = "Domain-0"
46
_DISK_LETTERS = string.ascii_lowercase
47

    
48
_FILE_DRIVER_MAP = {
49
  constants.FD_LOOP: "file",
50
  constants.FD_BLKTAP: "tap:aio",
51
  }
52

    
53

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

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

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

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

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

    
85

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

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

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

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

    
107

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

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

118
  """
119
  result = []
120

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

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

    
144
  return result
145

    
146

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

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

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

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

    
166
    raise errors.HypervisorError(errmsg)
167

    
168
  return _ParseInstanceList(lines, include_node)
169

    
170

    
171
def _IsInstanceRunning(instance_info):
172
  return instance_info == "r-----" \
173
      or instance_info == "-b----"
174

    
175

    
176
def _IsInstanceShutdown(instance_info):
177
  return instance_info == "---s--"
178

    
179

    
180
def _XenToHypervisorInstanceState(instance_info):
181
  if _IsInstanceRunning(instance_info):
182
    return hv_base.HvInstanceState.RUNNING
183
  elif _IsInstanceShutdown(instance_info):
184
    return hv_base.HvInstanceState.SHUTDOWN
185
  else:
186
    raise errors.HypervisorError("hv_xen._XenToHypervisorInstanceState:"
187
                                 " unhandled Xen instance state '%s'" %
188
                                   instance_info)
189

    
190

    
191
def _GetRunningInstanceList(fn, include_node, _timeout=5):
192
  """Return the list of running instances.
193

194
  See L{_GetAllInstanceList} for parameter details.
195

196
  """
197
  instances = _GetAllInstanceList(fn, include_node, _timeout)
198
  return [i for i in instances if hv_base.HvInstanceState.IsRunning(i[4])]
199

    
200

    
201
def _GetShutdownInstanceList(fn, include_node, _timeout=5):
202
  """Return the list of shutdown instances.
203

204
  See L{_GetAllInstanceList} for parameter details.
205

206
  """
207
  instances = _GetAllInstanceList(fn, include_node, _timeout)
208
  return [i for i in instances if hv_base.HvInstanceState.IsShutdown(i[4])]
209

    
210

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

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

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

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

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

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

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

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

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

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

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

    
267
  return result
268

    
269

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

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

279
  """
280
  total_instmem = 0
281

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

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

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

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

    
297
  return info
298

    
299

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

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

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

    
309

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

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

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

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

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

    
329
  disk_data = []
330

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

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

    
339
    if cfdev.dev_type in constants.DTS_FILEBASED:
340
      driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
341
    else:
342
      driver = "phy"
343

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

    
346
  return disk_data
347

    
348

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

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

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

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

    
361

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

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

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

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

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

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

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

    
398
    self._cmd = _cmd
399

    
400
  def _GetCommand(self, hvparams):
401
    """Returns Xen command to use.
402

403
    @type hvparams: dict of strings
404
    @param hvparams: hypervisor parameters
405

406
    """
407
    if self._cmd is None:
408
      if hvparams is None or constants.HV_XEN_CMD not in hvparams:
409
        raise errors.HypervisorError("Cannot determine xen command.")
410
      else:
411
        cmd = hvparams[constants.HV_XEN_CMD]
412
    else:
413
      cmd = self._cmd
414

    
415
    if cmd not in constants.KNOWN_XEN_COMMANDS:
416
      raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
417

    
418
    return cmd
419

    
420
  def _RunXen(self, args, hvparams):
421
    """Wrapper around L{utils.process.RunCmd} to run Xen command.
422

423
    @type hvparams: dict of strings
424
    @param hvparams: dictionary of hypervisor params
425
    @see: L{utils.process.RunCmd}
426

427
    """
428
    cmd = [self._GetCommand(hvparams)]
429
    cmd.extend(args)
430

    
431
    return self._run_cmd_fn(cmd)
432

    
433
  def _ConfigFileName(self, instance_name):
434
    """Get the config file name for an instance.
435

436
    @param instance_name: instance name
437
    @type instance_name: str
438
    @return: fully qualified path to instance config file
439
    @rtype: str
440

441
    """
442
    return utils.PathJoin(self._cfgdir, instance_name)
443

    
444
  @classmethod
445
  def _WriteNICInfoFile(cls, instance_name, idx, nic):
446
    """Write the Xen config file for the instance.
447

448
    This version of the function just writes the config file from static data.
449

450
    """
451
    dirs = [(dname, constants.RUN_DIRS_MODE)
452
            for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
453
    utils.EnsureDirs(dirs)
454

    
455
    cfg_file = cls._InstanceNICFile(instance_name, idx)
456
    data = StringIO()
457

    
458
    if nic.netinfo:
459
      netinfo = objects.Network.FromDict(nic.netinfo)
460
      data.write("NETWORK_NAME=%s\n" % netinfo.name)
461
      if netinfo.network:
462
        data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
463
      if netinfo.gateway:
464
        data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
465
      if netinfo.network6:
466
        data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
467
      if netinfo.gateway6:
468
        data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
469
      if netinfo.mac_prefix:
470
        data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
471
      if netinfo.tags:
472
        data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
473

    
474
    data.write("MAC=%s\n" % nic.mac)
475
    data.write("IP=%s\n" % nic.ip)
476
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
477
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
478

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

    
485
  @classmethod
486
  def _InstanceNICDir(cls, instance_name):
487
    """Returns the directory holding the tap device files for a given instance.
488

489
    """
490
    return utils.PathJoin(cls._NICS_DIR, instance_name)
491

    
492
  @classmethod
493
  def _InstanceNICFile(cls, instance_name, seq):
494
    """Returns the name of the file containing the tap device for a given NIC
495

496
    """
497
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
498

    
499
  @classmethod
500
  def _GetConfig(cls, instance, startup_memory, block_devices):
501
    """Build Xen configuration for an instance.
502

503
    """
504
    raise NotImplementedError
505

    
506
  def _WriteConfigFile(self, instance_name, data):
507
    """Write the Xen config file for the instance.
508

509
    This version of the function just writes the config file from static data.
510

511
    """
512
    # just in case it exists
513
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
514

    
515
    cfg_file = self._ConfigFileName(instance_name)
516
    try:
517
      utils.WriteFile(cfg_file, data=data)
518
    except EnvironmentError, err:
519
      raise errors.HypervisorError("Cannot write Xen instance configuration"
520
                                   " file %s: %s" % (cfg_file, err))
521

    
522
  def _ReadConfigFile(self, instance_name):
523
    """Returns the contents of the instance config file.
524

525
    """
526
    filename = self._ConfigFileName(instance_name)
527

    
528
    try:
529
      file_content = utils.ReadFile(filename)
530
    except EnvironmentError, err:
531
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
532

    
533
    return file_content
534

    
535
  def _RemoveConfigFile(self, instance_name):
536
    """Remove the xen configuration file.
537

538
    """
539
    utils.RemoveFile(self._ConfigFileName(instance_name))
540
    try:
541
      shutil.rmtree(self._InstanceNICDir(instance_name))
542
    except OSError, err:
543
      if err.errno != errno.ENOENT:
544
        raise
545

    
546
  def _StashConfigFile(self, instance_name):
547
    """Move the Xen config file to the log directory and return its new path.
548

549
    """
550
    old_filename = self._ConfigFileName(instance_name)
551
    base = ("%s-%s" %
552
            (instance_name, utils.TimestampForFilename()))
553
    new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
554
    utils.RenameFile(old_filename, new_filename)
555
    return new_filename
556

    
557
  def _GetInstanceList(self, include_node, hvparams):
558
    """Wrapper around module level L{_GetAllInstanceList}.
559

560
    @type hvparams: dict of strings
561
    @param hvparams: hypervisor parameters to be used on this node
562

563
    """
564
    return _GetAllInstanceList(lambda: self._RunXen(["list"], hvparams),
565
                               include_node)
566

    
567
  def ListInstances(self, hvparams=None):
568
    """Get the list of running instances.
569

570
    """
571
    instance_list = self._GetInstanceList(False, hvparams)
572
    names = [info[0] for info in instance_list]
573
    return names
574

    
575
  def GetInstanceInfo(self, instance_name, hvparams=None):
576
    """Get instance properties.
577

578
    @type instance_name: string
579
    @param instance_name: the instance name
580
    @type hvparams: dict of strings
581
    @param hvparams: the instance's hypervisor params
582

583
    @return: tuple (name, id, memory, vcpus, stat, times)
584

585
    """
586
    instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
587
    result = None
588
    for data in instance_list:
589
      if data[0] == instance_name:
590
        result = data
591
        break
592
    return result
593

    
594
  def GetAllInstancesInfo(self, hvparams=None):
595
    """Get properties of all instances.
596

597
    @type hvparams: dict of strings
598
    @param hvparams: hypervisor parameters
599

600
    @rtype: (string, string, int, int, HypervisorInstanceState, int)
601
    @return: list of tuples (name, id, memory, vcpus, state, times)
602

603
    """
604
    return self._GetInstanceList(False, hvparams)
605

    
606
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
607
    """Gather configuration details and write to disk.
608

609
    See L{_GetConfig} for arguments.
610

611
    """
612
    buf = StringIO()
613
    buf.write("# Automatically generated by Ganeti. Do not edit!\n")
614
    buf.write("\n")
615
    buf.write(self._GetConfig(instance, startup_memory, block_devices))
616
    buf.write("\n")
617

    
618
    self._WriteConfigFile(instance.name, buf.getvalue())
619

    
620
  def StartInstance(self, instance, block_devices, startup_paused):
621
    """Start an instance.
622

623
    """
624
    startup_memory = self._InstanceStartupMemory(instance,
625
                                                 hvparams=instance.hvparams)
626

    
627
    self._MakeConfigFile(instance, startup_memory, block_devices)
628

    
629
    cmd = ["create"]
630
    if startup_paused:
631
      cmd.append("-p")
632
    cmd.append(self._ConfigFileName(instance.name))
633

    
634
    result = self._RunXen(cmd, instance.hvparams)
635
    if result.failed:
636
      # Move the Xen configuration file to the log directory to avoid
637
      # leaving a stale config file behind.
638
      stashed_config = self._StashConfigFile(instance.name)
639
      raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
640
                                   " config file to %s" %
641
                                   (instance.name, result.fail_reason,
642
                                    result.output, stashed_config))
643

    
644
  def StopInstance(self, instance, force=False, retry=False, name=None):
645
    """Stop an instance.
646

647
    """
648
    if name is None:
649
      name = instance.name
650

    
651
    return self._StopInstance(name, force, instance.hvparams)
652

    
653
  def _ShutdownInstance(self, name, hvparams, instance_info):
654
    # The '-w' flag waits for shutdown to complete
655
    #
656
    # In the case of shutdown, we want to wait until the shutdown
657
    # process is complete because then we want to also destroy the
658
    # domain, and we do not want to destroy the domain while it is
659
    # shutting down.
660
    if hv_base.HvInstanceState.IsShutdown(instance_info):
661
      logging.info("Instance '%s' is already shutdown, skipping shutdown"
662
                   " command", name)
663
    else:
664
      result = self._RunXen(["shutdown", "-w", name], hvparams)
665
      if result.failed:
666
        raise errors.HypervisorError("Failed to shutdown instance %s: %s, %s" %
667
                                     (name, result.fail_reason, result.output))
668

    
669
  def _DestroyInstance(self, name, hvparams):
670
    result = self._RunXen(["destroy", name], hvparams)
671
    if result.failed:
672
      raise errors.HypervisorError("Failed to destroy instance %s: %s, %s" %
673
                                   (name, result.fail_reason, result.output))
674

    
675
  def _StopInstance(self, name, force, hvparams):
676
    """Stop an instance.
677

678
    @type name: string
679
    @param name: name of the instance to be shutdown
680
    @type force: boolean
681
    @param force: flag specifying whether shutdown should be forced
682
    @type hvparams: dict of string
683
    @param hvparams: hypervisor parameters of the instance
684

685
    """
686
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
687

    
688
    if instance_info is None:
689
      raise errors.HypervisorError("Failed to shutdown instance %s,"
690
                                   " not running" % name)
691

    
692
    if force:
693
      self._DestroyInstance(name, hvparams)
694
    else:
695
      self._ShutdownInstance(name, hvparams, instance_info[4])
696
      self._DestroyInstance(name, hvparams)
697

    
698
    # Remove configuration file if stopping/starting instance was successful
699
    self._RemoveConfigFile(name)
700

    
701
  def RebootInstance(self, instance):
702
    """Reboot an instance.
703

704
    """
705
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
706

    
707
    if ini_info is None:
708
      raise errors.HypervisorError("Failed to reboot instance %s,"
709
                                   " not running" % instance.name)
710

    
711
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
712
    if result.failed:
713
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
714
                                   (instance.name, result.fail_reason,
715
                                    result.output))
716

    
717
    def _CheckInstance():
718
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
719

    
720
      # check if the domain ID has changed or the run time has decreased
721
      if (new_info is not None and
722
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
723
        return
724

    
725
      raise utils.RetryAgain()
726

    
727
    try:
728
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
729
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
730
    except utils.RetryTimeout:
731
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
732
                                   " did not reboot in the expected interval" %
733
                                   (instance.name, ))
734

    
735
  def BalloonInstanceMemory(self, instance, mem):
736
    """Balloon an instance memory to a certain value.
737

738
    @type instance: L{objects.Instance}
739
    @param instance: instance to be accepted
740
    @type mem: int
741
    @param mem: actual memory size to use for instance runtime
742

743
    """
744
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
745
    if result.failed:
746
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
747
                                   (instance.name, result.fail_reason,
748
                                    result.output))
749

    
750
    # Update configuration file
751
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
752
    cmd.append(self._ConfigFileName(instance.name))
753

    
754
    result = utils.RunCmd(cmd)
755
    if result.failed:
756
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
757
                                   (instance.name, result.fail_reason,
758
                                    result.output))
759

    
760
  def GetNodeInfo(self, hvparams=None):
761
    """Return information about the node.
762

763
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
764

765
    """
766
    result = self._RunXen(["info"], hvparams)
767
    if result.failed:
768
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
769
                    result.fail_reason, result.output)
770
      return None
771

    
772
    instance_list = self._GetInstanceList(True, hvparams)
773
    return _GetNodeInfo(result.stdout, instance_list)
774

    
775
  def GetInstanceConsole(self, instance, primary_node, node_group,
776
                         hvparams, beparams):
777
    """Return a command for connecting to the console of an instance.
778

779
    """
780
    xen_cmd = self._GetCommand(hvparams)
781
    ndparams = node_group.FillND(primary_node)
782
    return objects.InstanceConsole(instance=instance.name,
783
                                   kind=constants.CONS_SSH,
784
                                   host=primary_node.name,
785
                                   port=ndparams.get(constants.ND_SSH_PORT),
786
                                   user=constants.SSH_CONSOLE_USER,
787
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
788
                                            xen_cmd, instance.name])
789

    
790
  def Verify(self, hvparams=None):
791
    """Verify the hypervisor.
792

793
    For Xen, this verifies that the xend process is running.
794

795
    @type hvparams: dict of strings
796
    @param hvparams: hypervisor parameters to be verified against
797

798
    @return: Problem description if something is wrong, C{None} otherwise
799

800
    """
801
    if hvparams is None:
802
      return "Could not verify the hypervisor, because no hvparams were" \
803
             " provided."
804

    
805
    if constants.HV_XEN_CMD in hvparams:
806
      xen_cmd = hvparams[constants.HV_XEN_CMD]
807
      try:
808
        self._CheckToolstack(xen_cmd)
809
      except errors.HypervisorError:
810
        return "The configured xen toolstack '%s' is not available on this" \
811
               " node." % xen_cmd
812

    
813
    result = self._RunXen(["info"], hvparams)
814
    if result.failed:
815
      return "Retrieving information from xen failed: %s, %s" % \
816
        (result.fail_reason, result.output)
817

    
818
    return None
819

    
820
  def MigrationInfo(self, instance):
821
    """Get instance information to perform a migration.
822

823
    @type instance: L{objects.Instance}
824
    @param instance: instance to be migrated
825
    @rtype: string
826
    @return: content of the xen config file
827

828
    """
829
    return self._ReadConfigFile(instance.name)
830

    
831
  def AcceptInstance(self, instance, info, target):
832
    """Prepare to accept an instance.
833

834
    @type instance: L{objects.Instance}
835
    @param instance: instance to be accepted
836
    @type info: string
837
    @param info: content of the xen config file on the source node
838
    @type target: string
839
    @param target: target host (usually ip), on this node
840

841
    """
842
    pass
843

    
844
  def FinalizeMigrationDst(self, instance, info, success):
845
    """Finalize an instance migration.
846

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

850
    @type instance: L{objects.Instance}
851
    @param instance: instance whose migration is being finalized
852
    @type info: string
853
    @param info: content of the xen config file on the source node
854
    @type success: boolean
855
    @param success: whether the migration was a success or a failure
856

857
    """
858
    if success:
859
      self._WriteConfigFile(instance.name, info)
860

    
861
  def MigrateInstance(self, cluster_name, instance, target, live):
862
    """Migrate an instance to a target node.
863

864
    The migration will not be attempted if the instance is not
865
    currently running.
866

867
    @type instance: L{objects.Instance}
868
    @param instance: the instance to be migrated
869
    @type target: string
870
    @param target: ip address of the target node
871
    @type live: boolean
872
    @param live: perform a live migration
873

874
    """
875
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
876

    
877
    return self._MigrateInstance(cluster_name, instance.name, target, port,
878
                                 live, instance.hvparams)
879

    
880
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
881
                       hvparams, _ping_fn=netutils.TcpPing):
882
    """Migrate an instance to a target node.
883

884
    @see: L{MigrateInstance} for details
885

886
    """
887
    if hvparams is None:
888
      raise errors.HypervisorError("No hvparams provided.")
889

    
890
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
891
      raise errors.HypervisorError("Instance not running, cannot migrate")
892

    
893
    cmd = self._GetCommand(hvparams)
894

    
895
    if (cmd == constants.XEN_CMD_XM and
896
        not _ping_fn(target, port, live_port_needed=True)):
897
      raise errors.HypervisorError("Remote host %s not listening on port"
898
                                   " %s, cannot migrate" % (target, port))
899

    
900
    args = ["migrate"]
901

    
902
    if cmd == constants.XEN_CMD_XM:
903
      args.extend(["-p", "%d" % port])
904
      if live:
905
        args.append("-l")
906

    
907
    elif cmd == constants.XEN_CMD_XL:
908
      args.extend([
909
        "-s", constants.XL_SSH_CMD % cluster_name,
910
        "-C", self._ConfigFileName(instance_name),
911
        ])
912

    
913
    else:
914
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
915

    
916
    args.extend([instance_name, target])
917

    
918
    result = self._RunXen(args, hvparams)
919
    if result.failed:
920
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
921
                                   (instance_name, result.output))
922

    
923
  def FinalizeMigrationSource(self, instance, success, live):
924
    """Finalize the instance migration on the source node.
925

926
    @type instance: L{objects.Instance}
927
    @param instance: the instance that was migrated
928
    @type success: bool
929
    @param success: whether the migration succeeded or not
930
    @type live: bool
931
    @param live: whether the user requested a live migration or not
932

933
    """
934
    # pylint: disable=W0613
935
    if success:
936
      # remove old xen file after migration succeeded
937
      try:
938
        self._RemoveConfigFile(instance.name)
939
      except EnvironmentError:
940
        logging.exception("Failure while removing instance config file")
941

    
942
  def GetMigrationStatus(self, instance):
943
    """Get the migration status
944

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

949
    @type instance: L{objects.Instance}
950
    @param instance: the instance that is being migrated
951
    @rtype: L{objects.MigrationStatus}
952
    @return: the status of the current migration (one of
953
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
954
             progress info that can be retrieved from the hypervisor
955

956
    """
957
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
958

    
959
  def PowercycleNode(self, hvparams=None):
960
    """Xen-specific powercycle.
961

962
    This first does a Linux reboot (which triggers automatically a Xen
963
    reboot), and if that fails it tries to do a Xen reboot. The reason
964
    we don't try a Xen reboot first is that the xen reboot launches an
965
    external command which connects to the Xen hypervisor, and that
966
    won't work in case the root filesystem is broken and/or the xend
967
    daemon is not working.
968

969
    @type hvparams: dict of strings
970
    @param hvparams: hypervisor params to be used on this node
971

972
    """
973
    try:
974
      self.LinuxPowercycle()
975
    finally:
976
      xen_cmd = self._GetCommand(hvparams)
977
      utils.RunCmd([xen_cmd, "debug", "R"])
978

    
979
  def _CheckToolstack(self, xen_cmd):
980
    """Check whether the given toolstack is available on the node.
981

982
    @type xen_cmd: string
983
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
984

985
    """
986
    binary_found = self._CheckToolstackBinary(xen_cmd)
987
    if not binary_found:
988
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
989
    elif xen_cmd == constants.XEN_CMD_XL:
990
      if not self._CheckToolstackXlConfigured():
991
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
992
                                     "node." % xen_cmd)
993

    
994
  def _CheckToolstackBinary(self, xen_cmd):
995
    """Checks whether the xen command's binary is found on the machine.
996

997
    """
998
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
999
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
1000
    result = self._run_cmd_fn(["which", xen_cmd])
1001
    return not result.failed
1002

    
1003
  def _CheckToolstackXlConfigured(self):
1004
    """Checks whether xl is enabled on an xl-capable node.
1005

1006
    @rtype: bool
1007
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
1008

1009
    """
1010
    result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
1011
    if not result.failed:
1012
      return True
1013
    elif result.failed:
1014
      if "toolstack" in result.stderr:
1015
        return False
1016
      # xl fails for some other reason than the toolstack
1017
      else:
1018
        raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
1019
                                     % (constants.XEN_CMD_XL, result.stderr))
1020

    
1021

    
1022
def WriteXenConfigEvents(config, hvp):
1023
  config.write("on_poweroff = 'preserve'\n")
1024
  if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1025
    config.write("on_reboot = 'restart'\n")
1026
  else:
1027
    config.write("on_reboot = 'destroy'\n")
1028
  config.write("on_crash = 'restart'\n")
1029

    
1030

    
1031
class XenPvmHypervisor(XenHypervisor):
1032
  """Xen PVM hypervisor interface"""
1033

    
1034
  PARAMETERS = {
1035
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1036
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1037
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1038
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1039
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1040
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1041
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1042
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1043
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1044
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1045
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1046
    constants.HV_REBOOT_BEHAVIOR:
1047
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1048
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1049
    constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1050
    constants.HV_CPU_WEIGHT:
1051
      (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1052
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1053
    constants.HV_XEN_CMD:
1054
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1055
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1056
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
1057
    }
1058

    
1059
  def _GetConfig(self, instance, startup_memory, block_devices):
1060
    """Write the Xen config file for the instance.
1061

1062
    """
1063
    hvp = instance.hvparams
1064
    config = StringIO()
1065
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1066

    
1067
    # if bootloader is True, use bootloader instead of kernel and ramdisk
1068
    # parameters.
1069
    if hvp[constants.HV_USE_BOOTLOADER]:
1070
      # bootloader handling
1071
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1072
      if bootloader_path:
1073
        config.write("bootloader = '%s'\n" % bootloader_path)
1074
      else:
1075
        raise errors.HypervisorError("Bootloader enabled, but missing"
1076
                                     " bootloader path")
1077

    
1078
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1079
      if bootloader_args:
1080
        config.write("bootargs = '%s'\n" % bootloader_args)
1081
    else:
1082
      # kernel handling
1083
      kpath = hvp[constants.HV_KERNEL_PATH]
1084
      config.write("kernel = '%s'\n" % kpath)
1085

    
1086
      # initrd handling
1087
      initrd_path = hvp[constants.HV_INITRD_PATH]
1088
      if initrd_path:
1089
        config.write("ramdisk = '%s'\n" % initrd_path)
1090

    
1091
    # rest of the settings
1092
    config.write("memory = %d\n" % startup_memory)
1093
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1094
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1095
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1096
    if cpu_pinning:
1097
      config.write("%s\n" % cpu_pinning)
1098
    cpu_cap = hvp[constants.HV_CPU_CAP]
1099
    if cpu_cap:
1100
      config.write("cpu_cap=%d\n" % cpu_cap)
1101
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1102
    if cpu_weight:
1103
      config.write("cpu_weight=%d\n" % cpu_weight)
1104

    
1105
    config.write("name = '%s'\n" % instance.name)
1106

    
1107
    vif_data = []
1108
    for idx, nic in enumerate(instance.nics):
1109
      nic_str = "mac=%s" % (nic.mac)
1110
      ip = getattr(nic, "ip", None)
1111
      if ip is not None:
1112
        nic_str += ", ip=%s" % ip
1113
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1114
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1115
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1116
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1117
        if nic.nicparams[constants.NIC_VLAN]:
1118
          nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
1119
      if hvp[constants.HV_VIF_SCRIPT]:
1120
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1121
      vif_data.append("'%s'" % nic_str)
1122
      self._WriteNICInfoFile(instance.name, idx, nic)
1123

    
1124
    disk_data = \
1125
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1126

    
1127
    config.write("vif = [%s]\n" % ",".join(vif_data))
1128
    config.write("disk = [%s]\n" % ",".join(disk_data))
1129

    
1130
    if hvp[constants.HV_ROOT_PATH]:
1131
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1132

    
1133
    WriteXenConfigEvents(config, hvp)
1134
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1135

    
1136
    cpuid = hvp[constants.HV_XEN_CPUID]
1137
    if cpuid:
1138
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1139

    
1140
    if hvp[constants.HV_SOUNDHW]:
1141
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1142

    
1143
    return config.getvalue()
1144

    
1145

    
1146
class XenHvmHypervisor(XenHypervisor):
1147
  """Xen HVM hypervisor interface"""
1148

    
1149
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1150
    pathutils.VNC_PASSWORD_FILE,
1151
    ]
1152
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1153
    pathutils.VNC_PASSWORD_FILE,
1154
    ]
1155

    
1156
  PARAMETERS = {
1157
    constants.HV_ACPI: hv_base.NO_CHECK,
1158
    constants.HV_BOOT_ORDER: (True, ) +
1159
      (lambda x: x and len(x.strip("acdn")) == 0,
1160
       "Invalid boot order specified, must be one or more of [acdn]",
1161
       None, None),
1162
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1163
    constants.HV_DISK_TYPE:
1164
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1165
    constants.HV_NIC_TYPE:
1166
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1167
    constants.HV_PAE: hv_base.NO_CHECK,
1168
    constants.HV_VNC_BIND_ADDRESS:
1169
      (False, netutils.IP4Address.IsValid,
1170
       "VNC bind address is not a valid IP address", None, None),
1171
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1172
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1173
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1174
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1175
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1176
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1177
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1178
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1179
    # Add PCI passthrough
1180
    constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1181
    constants.HV_REBOOT_BEHAVIOR:
1182
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1183
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1184
    constants.HV_CPU_CAP: hv_base.NO_CHECK,
1185
    constants.HV_CPU_WEIGHT:
1186
      (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1187
    constants.HV_VIF_TYPE:
1188
      hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1189
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1190
    constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1191
    constants.HV_XEN_CMD:
1192
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1193
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1194
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
1195
    }
1196

    
1197
  def _GetConfig(self, instance, startup_memory, block_devices):
1198
    """Create a Xen 3.1 HVM config file.
1199

1200
    """
1201
    hvp = instance.hvparams
1202

    
1203
    config = StringIO()
1204

    
1205
    # kernel handling
1206
    kpath = hvp[constants.HV_KERNEL_PATH]
1207
    config.write("kernel = '%s'\n" % kpath)
1208

    
1209
    config.write("builder = 'hvm'\n")
1210
    config.write("memory = %d\n" % startup_memory)
1211
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1212
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1213
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1214
    if cpu_pinning:
1215
      config.write("%s\n" % cpu_pinning)
1216
    cpu_cap = hvp[constants.HV_CPU_CAP]
1217
    if cpu_cap:
1218
      config.write("cpu_cap=%d\n" % cpu_cap)
1219
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1220
    if cpu_weight:
1221
      config.write("cpu_weight=%d\n" % cpu_weight)
1222

    
1223
    config.write("name = '%s'\n" % instance.name)
1224
    if hvp[constants.HV_PAE]:
1225
      config.write("pae = 1\n")
1226
    else:
1227
      config.write("pae = 0\n")
1228
    if hvp[constants.HV_ACPI]:
1229
      config.write("acpi = 1\n")
1230
    else:
1231
      config.write("acpi = 0\n")
1232
    if hvp[constants.HV_VIRIDIAN]:
1233
      config.write("viridian = 1\n")
1234
    else:
1235
      config.write("viridian = 0\n")
1236

    
1237
    config.write("apic = 1\n")
1238
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1239
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1240
    config.write("sdl = 0\n")
1241
    config.write("usb = 1\n")
1242
    config.write("usbdevice = 'tablet'\n")
1243
    config.write("vnc = 1\n")
1244
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1245
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1246
    else:
1247
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1248

    
1249
    if instance.network_port > constants.VNC_BASE_PORT:
1250
      display = instance.network_port - constants.VNC_BASE_PORT
1251
      config.write("vncdisplay = %s\n" % display)
1252
      config.write("vncunused = 0\n")
1253
    else:
1254
      config.write("# vncdisplay = 1\n")
1255
      config.write("vncunused = 1\n")
1256

    
1257
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1258
    try:
1259
      password = utils.ReadFile(vnc_pwd_file)
1260
    except EnvironmentError, err:
1261
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1262
                                   (vnc_pwd_file, err))
1263

    
1264
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1265

    
1266
    config.write("serial = 'pty'\n")
1267
    if hvp[constants.HV_USE_LOCALTIME]:
1268
      config.write("localtime = 1\n")
1269

    
1270
    vif_data = []
1271
    # Note: what is called 'nic_type' here, is used as value for the xen nic
1272
    # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1273
    # the 'vif_type' to avoid a clash of notation.
1274
    nic_type = hvp[constants.HV_NIC_TYPE]
1275

    
1276
    if nic_type is None:
1277
      vif_type_str = ""
1278
      if hvp[constants.HV_VIF_TYPE]:
1279
        vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1280
      # ensure old instances don't change
1281
      nic_type_str = vif_type_str
1282
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1283
      nic_type_str = ", type=paravirtualized"
1284
    else:
1285
      # parameter 'model' is only valid with type 'ioemu'
1286
      nic_type_str = ", model=%s, type=%s" % \
1287
        (nic_type, constants.HT_HVM_VIF_IOEMU)
1288
    for idx, nic in enumerate(instance.nics):
1289
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1290
      ip = getattr(nic, "ip", None)
1291
      if ip is not None:
1292
        nic_str += ", ip=%s" % ip
1293
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1294
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1295
      if hvp[constants.HV_VIF_SCRIPT]:
1296
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1297
      vif_data.append("'%s'" % nic_str)
1298
      self._WriteNICInfoFile(instance.name, idx, nic)
1299

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

    
1302
    disk_data = \
1303
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1304

    
1305
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1306
    if iso_path:
1307
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1308
      disk_data.append(iso)
1309

    
1310
    config.write("disk = [%s]\n" % (",".join(disk_data)))
1311
    # Add PCI passthrough
1312
    pci_pass_arr = []
1313
    pci_pass = hvp[constants.HV_PASSTHROUGH]
1314
    if pci_pass:
1315
      pci_pass_arr = pci_pass.split(";")
1316
      config.write("pci = %s\n" % pci_pass_arr)
1317

    
1318
    WriteXenConfigEvents(config, hvp)
1319

    
1320
    cpuid = hvp[constants.HV_XEN_CPUID]
1321
    if cpuid:
1322
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1323

    
1324
    if hvp[constants.HV_SOUNDHW]:
1325
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1326

    
1327
    return config.getvalue()