Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ fd201010

History | View | Annotate | Download (44.2 kB)

1
#
2
#
3

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

    
21

    
22
"""Xen hypervisors
23

24
"""
25

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

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

    
40

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

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

    
53

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

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

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

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

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

    
85

    
86
def _RunInstanceList(fn, instance_list_errors):
87
  """Helper function for L{_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
# Determine whether an instance is running
172
#
173
# An instance is running if it is in the following Xen states:
174
# running, blocked, or paused.
175
def _IsInstanceRunning(instance_info):
176
  return instance_info == "r-----" \
177
      or instance_info == "-b----" \
178
      or instance_info == "--p---"
179

    
180

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

    
184

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

    
195

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

199
  See L{_GetAllInstanceList} for parameter details.
200

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

    
205

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

209
  See L{_GetAllInstanceList} for parameter details.
210

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

    
215

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

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

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

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

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

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

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

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

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

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

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

    
272
  return result
273

    
274

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

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

284
  """
285
  total_instmem = 0
286

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

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

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

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

    
302
  return info
303

    
304

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

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

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

    
314

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

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

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

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

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

    
334
  disk_data = []
335

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

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

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

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

    
351
  return disk_data
352

    
353

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

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

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

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

    
366

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

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

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

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

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

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

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

    
403
    self._cmd = _cmd
404

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

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

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

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

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

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

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

    
433
    return cmd
434

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

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

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

    
446
    return self._run_cmd_fn(cmd)
447

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

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

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

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

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

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

    
470
    cfg_file = cls._InstanceNICFile(instance_name, idx)
471
    data = StringIO()
472

    
473
    if nic.netinfo:
474
      netinfo = objects.Network.FromDict(nic.netinfo)
475
      data.write("NETWORK_NAME=%s\n" % netinfo.name)
476
      if netinfo.network:
477
        data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
478
      if netinfo.gateway:
479
        data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
480
      if netinfo.network6:
481
        data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
482
      if netinfo.gateway6:
483
        data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
484
      if netinfo.mac_prefix:
485
        data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
486
      if netinfo.tags:
487
        data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
488

    
489
    data.write("MAC=%s\n" % nic.mac)
490
    data.write("IP=%s\n" % nic.ip)
491
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
492
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
493

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

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

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

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

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

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

518
    """
519
    raise NotImplementedError
520

    
521
  def _WriteConfigFile(self, instance_name, data):
522
    """Write the Xen config file for the instance.
523

524
    This version of the function just writes the config file from static data.
525

526
    """
527
    # just in case it exists
528
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
529

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

    
537
  def _ReadConfigFile(self, instance_name):
538
    """Returns the contents of the instance config file.
539

540
    """
541
    filename = self._ConfigFileName(instance_name)
542

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

    
548
    return file_content
549

    
550
  def _RemoveConfigFile(self, instance_name):
551
    """Remove the xen configuration file.
552

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

    
561
  def _StashConfigFile(self, instance_name):
562
    """Move the Xen config file to the log directory and return its new path.
563

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

    
572
  def _GetInstanceList(self, include_node, hvparams):
573
    """Wrapper around module level L{_GetAllInstanceList}.
574

575
    @type hvparams: dict of strings
576
    @param hvparams: hypervisor parameters to be used on this node
577

578
    """
579
    return _GetAllInstanceList(lambda: self._RunXen(["list"], hvparams),
580
                               include_node)
581

    
582
  def ListInstances(self, hvparams=None):
583
    """Get the list of running instances.
584

585
    """
586
    instance_list = self._GetInstanceList(False, hvparams)
587
    names = [info[0] for info in instance_list]
588
    return names
589

    
590
  def GetInstanceInfo(self, instance_name, hvparams=None):
591
    """Get instance properties.
592

593
    @type instance_name: string
594
    @param instance_name: the instance name
595
    @type hvparams: dict of strings
596
    @param hvparams: the instance's hypervisor params
597

598
    @return: tuple (name, id, memory, vcpus, stat, times)
599

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

    
609
  def GetAllInstancesInfo(self, hvparams=None):
610
    """Get properties of all instances.
611

612
    @type hvparams: dict of strings
613
    @param hvparams: hypervisor parameters
614

615
    @rtype: (string, string, int, int, HypervisorInstanceState, int)
616
    @return: list of tuples (name, id, memory, vcpus, state, times)
617

618
    """
619
    return self._GetInstanceList(False, hvparams)
620

    
621
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
622
    """Gather configuration details and write to disk.
623

624
    See L{_GetConfig} for arguments.
625

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

    
633
    self._WriteConfigFile(instance.name, buf.getvalue())
634

    
635
  def StartInstance(self, instance, block_devices, startup_paused):
636
    """Start an instance.
637

638
    """
639
    startup_memory = self._InstanceStartupMemory(instance,
640
                                                 hvparams=instance.hvparams)
641

    
642
    self._MakeConfigFile(instance, startup_memory, block_devices)
643

    
644
    cmd = ["create"]
645
    if startup_paused:
646
      cmd.append("-p")
647
    cmd.append(self._ConfigFileName(instance.name))
648

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

    
659
  def StopInstance(self, instance, force=False, retry=False, name=None):
660
    """Stop an instance.
661

662
    """
663
    if name is None:
664
      name = instance.name
665

    
666
    return self._StopInstance(name, force, instance.hvparams)
667

    
668
  def _ShutdownInstance(self, name, hvparams, instance_info):
669
    # The '-w' flag waits for shutdown to complete
670
    #
671
    # In the case of shutdown, we want to wait until the shutdown
672
    # process is complete because then we want to also destroy the
673
    # domain, and we do not want to destroy the domain while it is
674
    # shutting down.
675
    if hv_base.HvInstanceState.IsShutdown(instance_info):
676
      logging.info("Instance '%s' is already shutdown, skipping shutdown"
677
                   " command", name)
678
    else:
679
      result = self._RunXen(["shutdown", "-w", name], hvparams)
680
      if result.failed:
681
        raise errors.HypervisorError("Failed to shutdown instance %s: %s, %s" %
682
                                     (name, result.fail_reason, result.output))
683

    
684
  def _DestroyInstance(self, name, hvparams):
685
    result = self._RunXen(["destroy", name], hvparams)
686
    if result.failed:
687
      raise errors.HypervisorError("Failed to destroy instance %s: %s, %s" %
688
                                   (name, result.fail_reason, result.output))
689

    
690
  def _StopInstance(self, name, force, hvparams):
691
    """Stop an instance.
692

693
    @type name: string
694
    @param name: name of the instance to be shutdown
695
    @type force: boolean
696
    @param force: flag specifying whether shutdown should be forced
697
    @type hvparams: dict of string
698
    @param hvparams: hypervisor parameters of the instance
699

700
    """
701
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
702

    
703
    if instance_info is None:
704
      raise errors.HypervisorError("Failed to shutdown instance %s,"
705
                                   " not running" % name)
706

    
707
    if force:
708
      self._DestroyInstance(name, hvparams)
709
    else:
710
      self._ShutdownInstance(name, hvparams, instance_info[4])
711
      self._DestroyInstance(name, hvparams)
712

    
713
    # Remove configuration file if stopping/starting instance was successful
714
    self._RemoveConfigFile(name)
715

    
716
  def RebootInstance(self, instance):
717
    """Reboot an instance.
718

719
    """
720
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
721

    
722
    if ini_info is None:
723
      raise errors.HypervisorError("Failed to reboot instance %s,"
724
                                   " not running" % instance.name)
725

    
726
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
727
    if result.failed:
728
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
729
                                   (instance.name, result.fail_reason,
730
                                    result.output))
731

    
732
    def _CheckInstance():
733
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
734

    
735
      # check if the domain ID has changed or the run time has decreased
736
      if (new_info is not None and
737
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
738
        return
739

    
740
      raise utils.RetryAgain()
741

    
742
    try:
743
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
744
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
745
    except utils.RetryTimeout:
746
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
747
                                   " did not reboot in the expected interval" %
748
                                   (instance.name, ))
749

    
750
  def BalloonInstanceMemory(self, instance, mem):
751
    """Balloon an instance memory to a certain value.
752

753
    @type instance: L{objects.Instance}
754
    @param instance: instance to be accepted
755
    @type mem: int
756
    @param mem: actual memory size to use for instance runtime
757

758
    """
759
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
760
    if result.failed:
761
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
762
                                   (instance.name, result.fail_reason,
763
                                    result.output))
764

    
765
    # Update configuration file
766
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
767
    cmd.append(self._ConfigFileName(instance.name))
768

    
769
    result = utils.RunCmd(cmd)
770
    if result.failed:
771
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
772
                                   (instance.name, result.fail_reason,
773
                                    result.output))
774

    
775
  def GetNodeInfo(self, hvparams=None):
776
    """Return information about the node.
777

778
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
779

780
    """
781
    result = self._RunXen(["info"], hvparams)
782
    if result.failed:
783
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
784
                    result.fail_reason, result.output)
785
      return None
786

    
787
    instance_list = self._GetInstanceList(True, hvparams)
788
    return _GetNodeInfo(result.stdout, instance_list)
789

    
790
  @classmethod
791
  def GetInstanceConsole(cls, instance, primary_node, node_group,
792
                         hvparams, beparams):
793
    """Return a command for connecting to the console of an instance.
794

795
    """
796
    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
797
    ndparams = node_group.FillND(primary_node)
798
    return objects.InstanceConsole(instance=instance.name,
799
                                   kind=constants.CONS_SSH,
800
                                   host=primary_node.name,
801
                                   port=ndparams.get(constants.ND_SSH_PORT),
802
                                   user=constants.SSH_CONSOLE_USER,
803
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
804
                                            xen_cmd, instance.name])
805

    
806
  def Verify(self, hvparams=None):
807
    """Verify the hypervisor.
808

809
    For Xen, this verifies that the xend process is running.
810

811
    @type hvparams: dict of strings
812
    @param hvparams: hypervisor parameters to be verified against
813

814
    @return: Problem description if something is wrong, C{None} otherwise
815

816
    """
817
    if hvparams is None:
818
      return "Could not verify the hypervisor, because no hvparams were" \
819
             " provided."
820

    
821
    if constants.HV_XEN_CMD in hvparams:
822
      xen_cmd = hvparams[constants.HV_XEN_CMD]
823
      try:
824
        self._CheckToolstack(xen_cmd)
825
      except errors.HypervisorError:
826
        return "The configured xen toolstack '%s' is not available on this" \
827
               " node." % xen_cmd
828

    
829
    result = self._RunXen(["info"], hvparams)
830
    if result.failed:
831
      return "Retrieving information from xen failed: %s, %s" % \
832
        (result.fail_reason, result.output)
833

    
834
    return None
835

    
836
  def MigrationInfo(self, instance):
837
    """Get instance information to perform a migration.
838

839
    @type instance: L{objects.Instance}
840
    @param instance: instance to be migrated
841
    @rtype: string
842
    @return: content of the xen config file
843

844
    """
845
    return self._ReadConfigFile(instance.name)
846

    
847
  def AcceptInstance(self, instance, info, target):
848
    """Prepare to accept an instance.
849

850
    @type instance: L{objects.Instance}
851
    @param instance: instance to be accepted
852
    @type info: string
853
    @param info: content of the xen config file on the source node
854
    @type target: string
855
    @param target: target host (usually ip), on this node
856

857
    """
858
    pass
859

    
860
  def FinalizeMigrationDst(self, instance, info, success):
861
    """Finalize an instance migration.
862

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

866
    @type instance: L{objects.Instance}
867
    @param instance: instance whose migration is being finalized
868
    @type info: string
869
    @param info: content of the xen config file on the source node
870
    @type success: boolean
871
    @param success: whether the migration was a success or a failure
872

873
    """
874
    if success:
875
      self._WriteConfigFile(instance.name, info)
876

    
877
  def MigrateInstance(self, cluster_name, instance, target, live):
878
    """Migrate an instance to a target node.
879

880
    The migration will not be attempted if the instance is not
881
    currently running.
882

883
    @type instance: L{objects.Instance}
884
    @param instance: the instance to be migrated
885
    @type target: string
886
    @param target: ip address of the target node
887
    @type live: boolean
888
    @param live: perform a live migration
889

890
    """
891
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
892

    
893
    return self._MigrateInstance(cluster_name, instance.name, target, port,
894
                                 live, instance.hvparams)
895

    
896
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
897
                       hvparams, _ping_fn=netutils.TcpPing):
898
    """Migrate an instance to a target node.
899

900
    @see: L{MigrateInstance} for details
901

902
    """
903
    if hvparams is None:
904
      raise errors.HypervisorError("No hvparams provided.")
905

    
906
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
907
      raise errors.HypervisorError("Instance not running, cannot migrate")
908

    
909
    cmd = self._GetCommand(hvparams)
910

    
911
    if (cmd == constants.XEN_CMD_XM and
912
        not _ping_fn(target, port, live_port_needed=True)):
913
      raise errors.HypervisorError("Remote host %s not listening on port"
914
                                   " %s, cannot migrate" % (target, port))
915

    
916
    args = ["migrate"]
917

    
918
    if cmd == constants.XEN_CMD_XM:
919
      args.extend(["-p", "%d" % port])
920
      if live:
921
        args.append("-l")
922

    
923
    elif cmd == constants.XEN_CMD_XL:
924
      args.extend([
925
        "-s", constants.XL_SSH_CMD % cluster_name,
926
        "-C", self._ConfigFileName(instance_name),
927
        ])
928

    
929
    else:
930
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
931

    
932
    args.extend([instance_name, target])
933

    
934
    result = self._RunXen(args, hvparams)
935
    if result.failed:
936
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
937
                                   (instance_name, result.output))
938

    
939
  def FinalizeMigrationSource(self, instance, success, live):
940
    """Finalize the instance migration on the source node.
941

942
    @type instance: L{objects.Instance}
943
    @param instance: the instance that was migrated
944
    @type success: bool
945
    @param success: whether the migration succeeded or not
946
    @type live: bool
947
    @param live: whether the user requested a live migration or not
948

949
    """
950
    # pylint: disable=W0613
951
    if success:
952
      # remove old xen file after migration succeeded
953
      try:
954
        self._RemoveConfigFile(instance.name)
955
      except EnvironmentError:
956
        logging.exception("Failure while removing instance config file")
957

    
958
  def GetMigrationStatus(self, instance):
959
    """Get the migration status
960

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

965
    @type instance: L{objects.Instance}
966
    @param instance: the instance that is being migrated
967
    @rtype: L{objects.MigrationStatus}
968
    @return: the status of the current migration (one of
969
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
970
             progress info that can be retrieved from the hypervisor
971

972
    """
973
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
974

    
975
  def PowercycleNode(self, hvparams=None):
976
    """Xen-specific powercycle.
977

978
    This first does a Linux reboot (which triggers automatically a Xen
979
    reboot), and if that fails it tries to do a Xen reboot. The reason
980
    we don't try a Xen reboot first is that the xen reboot launches an
981
    external command which connects to the Xen hypervisor, and that
982
    won't work in case the root filesystem is broken and/or the xend
983
    daemon is not working.
984

985
    @type hvparams: dict of strings
986
    @param hvparams: hypervisor params to be used on this node
987

988
    """
989
    try:
990
      self.LinuxPowercycle()
991
    finally:
992
      xen_cmd = self._GetCommand(hvparams)
993
      utils.RunCmd([xen_cmd, "debug", "R"])
994

    
995
  def _CheckToolstack(self, xen_cmd):
996
    """Check whether the given toolstack is available on the node.
997

998
    @type xen_cmd: string
999
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
1000

1001
    """
1002
    binary_found = self._CheckToolstackBinary(xen_cmd)
1003
    if not binary_found:
1004
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
1005
    elif xen_cmd == constants.XEN_CMD_XL:
1006
      if not self._CheckToolstackXlConfigured():
1007
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
1008
                                     "node." % xen_cmd)
1009

    
1010
  def _CheckToolstackBinary(self, xen_cmd):
1011
    """Checks whether the xen command's binary is found on the machine.
1012

1013
    """
1014
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
1015
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
1016
    result = self._run_cmd_fn(["which", xen_cmd])
1017
    return not result.failed
1018

    
1019
  def _CheckToolstackXlConfigured(self):
1020
    """Checks whether xl is enabled on an xl-capable node.
1021

1022
    @rtype: bool
1023
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
1024

1025
    """
1026
    result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
1027
    if not result.failed:
1028
      return True
1029
    elif result.failed:
1030
      if "toolstack" in result.stderr:
1031
        return False
1032
      # xl fails for some other reason than the toolstack
1033
      else:
1034
        raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
1035
                                     % (constants.XEN_CMD_XL, result.stderr))
1036

    
1037

    
1038
def WriteXenConfigEvents(config, hvp):
1039
  config.write("on_poweroff = 'preserve'\n")
1040
  if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1041
    config.write("on_reboot = 'restart'\n")
1042
  else:
1043
    config.write("on_reboot = 'destroy'\n")
1044
  config.write("on_crash = 'restart'\n")
1045

    
1046

    
1047
class XenPvmHypervisor(XenHypervisor):
1048
  """Xen PVM hypervisor interface"""
1049

    
1050
  PARAMETERS = {
1051
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1052
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1053
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1054
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1055
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1056
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1057
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1058
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1059
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1060
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1061
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1062
    constants.HV_REBOOT_BEHAVIOR:
1063
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1064
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1065
    constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1066
    constants.HV_CPU_WEIGHT:
1067
      (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1068
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1069
    constants.HV_XEN_CMD:
1070
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1071
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1072
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
1073
    }
1074

    
1075
  def _GetConfig(self, instance, startup_memory, block_devices):
1076
    """Write the Xen config file for the instance.
1077

1078
    """
1079
    hvp = instance.hvparams
1080
    config = StringIO()
1081
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1082

    
1083
    # if bootloader is True, use bootloader instead of kernel and ramdisk
1084
    # parameters.
1085
    if hvp[constants.HV_USE_BOOTLOADER]:
1086
      # bootloader handling
1087
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1088
      if bootloader_path:
1089
        config.write("bootloader = '%s'\n" % bootloader_path)
1090
      else:
1091
        raise errors.HypervisorError("Bootloader enabled, but missing"
1092
                                     " bootloader path")
1093

    
1094
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1095
      if bootloader_args:
1096
        config.write("bootargs = '%s'\n" % bootloader_args)
1097
    else:
1098
      # kernel handling
1099
      kpath = hvp[constants.HV_KERNEL_PATH]
1100
      config.write("kernel = '%s'\n" % kpath)
1101

    
1102
      # initrd handling
1103
      initrd_path = hvp[constants.HV_INITRD_PATH]
1104
      if initrd_path:
1105
        config.write("ramdisk = '%s'\n" % initrd_path)
1106

    
1107
    # rest of the settings
1108
    config.write("memory = %d\n" % startup_memory)
1109
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1110
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1111
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1112
    if cpu_pinning:
1113
      config.write("%s\n" % cpu_pinning)
1114
    cpu_cap = hvp[constants.HV_CPU_CAP]
1115
    if cpu_cap:
1116
      config.write("cpu_cap=%d\n" % cpu_cap)
1117
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1118
    if cpu_weight:
1119
      config.write("cpu_weight=%d\n" % cpu_weight)
1120

    
1121
    config.write("name = '%s'\n" % instance.name)
1122

    
1123
    vif_data = []
1124
    for idx, nic in enumerate(instance.nics):
1125
      nic_str = "mac=%s" % (nic.mac)
1126
      ip = getattr(nic, "ip", None)
1127
      if ip is not None:
1128
        nic_str += ", ip=%s" % ip
1129
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1130
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1131
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1132
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1133
        if nic.nicparams[constants.NIC_VLAN]:
1134
          nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
1135
      if hvp[constants.HV_VIF_SCRIPT]:
1136
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1137
      vif_data.append("'%s'" % nic_str)
1138
      self._WriteNICInfoFile(instance.name, idx, nic)
1139

    
1140
    disk_data = \
1141
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1142

    
1143
    config.write("vif = [%s]\n" % ",".join(vif_data))
1144
    config.write("disk = [%s]\n" % ",".join(disk_data))
1145

    
1146
    if hvp[constants.HV_ROOT_PATH]:
1147
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1148

    
1149
    WriteXenConfigEvents(config, hvp)
1150
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1151

    
1152
    cpuid = hvp[constants.HV_XEN_CPUID]
1153
    if cpuid:
1154
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1155

    
1156
    if hvp[constants.HV_SOUNDHW]:
1157
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1158

    
1159
    return config.getvalue()
1160

    
1161

    
1162
class XenHvmHypervisor(XenHypervisor):
1163
  """Xen HVM hypervisor interface"""
1164

    
1165
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1166
    pathutils.VNC_PASSWORD_FILE,
1167
    ]
1168
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1169
    pathutils.VNC_PASSWORD_FILE,
1170
    ]
1171

    
1172
  PARAMETERS = {
1173
    constants.HV_ACPI: hv_base.NO_CHECK,
1174
    constants.HV_BOOT_ORDER: (True, ) +
1175
      (lambda x: x and len(x.strip("acdn")) == 0,
1176
       "Invalid boot order specified, must be one or more of [acdn]",
1177
       None, None),
1178
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1179
    constants.HV_DISK_TYPE:
1180
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1181
    constants.HV_NIC_TYPE:
1182
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1183
    constants.HV_PAE: hv_base.NO_CHECK,
1184
    constants.HV_VNC_BIND_ADDRESS:
1185
      (False, netutils.IP4Address.IsValid,
1186
       "VNC bind address is not a valid IP address", None, None),
1187
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1188
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1189
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1190
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1191
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1192
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1193
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1194
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1195
    # Add PCI passthrough
1196
    constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1197
    constants.HV_REBOOT_BEHAVIOR:
1198
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1199
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1200
    constants.HV_CPU_CAP: hv_base.NO_CHECK,
1201
    constants.HV_CPU_WEIGHT:
1202
      (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1203
    constants.HV_VIF_TYPE:
1204
      hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1205
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1206
    constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1207
    constants.HV_XEN_CMD:
1208
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1209
    constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1210
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
1211
    }
1212

    
1213
  def _GetConfig(self, instance, startup_memory, block_devices):
1214
    """Create a Xen 3.1 HVM config file.
1215

1216
    """
1217
    hvp = instance.hvparams
1218

    
1219
    config = StringIO()
1220

    
1221
    # kernel handling
1222
    kpath = hvp[constants.HV_KERNEL_PATH]
1223
    config.write("kernel = '%s'\n" % kpath)
1224

    
1225
    config.write("builder = 'hvm'\n")
1226
    config.write("memory = %d\n" % startup_memory)
1227
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1228
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1229
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1230
    if cpu_pinning:
1231
      config.write("%s\n" % cpu_pinning)
1232
    cpu_cap = hvp[constants.HV_CPU_CAP]
1233
    if cpu_cap:
1234
      config.write("cpu_cap=%d\n" % cpu_cap)
1235
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1236
    if cpu_weight:
1237
      config.write("cpu_weight=%d\n" % cpu_weight)
1238

    
1239
    config.write("name = '%s'\n" % instance.name)
1240
    if hvp[constants.HV_PAE]:
1241
      config.write("pae = 1\n")
1242
    else:
1243
      config.write("pae = 0\n")
1244
    if hvp[constants.HV_ACPI]:
1245
      config.write("acpi = 1\n")
1246
    else:
1247
      config.write("acpi = 0\n")
1248
    if hvp[constants.HV_VIRIDIAN]:
1249
      config.write("viridian = 1\n")
1250
    else:
1251
      config.write("viridian = 0\n")
1252

    
1253
    config.write("apic = 1\n")
1254
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1255
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1256
    config.write("sdl = 0\n")
1257
    config.write("usb = 1\n")
1258
    config.write("usbdevice = 'tablet'\n")
1259
    config.write("vnc = 1\n")
1260
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1261
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1262
    else:
1263
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1264

    
1265
    if instance.network_port > constants.VNC_BASE_PORT:
1266
      display = instance.network_port - constants.VNC_BASE_PORT
1267
      config.write("vncdisplay = %s\n" % display)
1268
      config.write("vncunused = 0\n")
1269
    else:
1270
      config.write("# vncdisplay = 1\n")
1271
      config.write("vncunused = 1\n")
1272

    
1273
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1274
    try:
1275
      password = utils.ReadFile(vnc_pwd_file)
1276
    except EnvironmentError, err:
1277
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1278
                                   (vnc_pwd_file, err))
1279

    
1280
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1281

    
1282
    config.write("serial = 'pty'\n")
1283
    if hvp[constants.HV_USE_LOCALTIME]:
1284
      config.write("localtime = 1\n")
1285

    
1286
    vif_data = []
1287
    # Note: what is called 'nic_type' here, is used as value for the xen nic
1288
    # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1289
    # the 'vif_type' to avoid a clash of notation.
1290
    nic_type = hvp[constants.HV_NIC_TYPE]
1291

    
1292
    if nic_type is None:
1293
      vif_type_str = ""
1294
      if hvp[constants.HV_VIF_TYPE]:
1295
        vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1296
      # ensure old instances don't change
1297
      nic_type_str = vif_type_str
1298
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1299
      nic_type_str = ", type=paravirtualized"
1300
    else:
1301
      # parameter 'model' is only valid with type 'ioemu'
1302
      nic_type_str = ", model=%s, type=%s" % \
1303
        (nic_type, constants.HT_HVM_VIF_IOEMU)
1304
    for idx, nic in enumerate(instance.nics):
1305
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1306
      ip = getattr(nic, "ip", None)
1307
      if ip is not None:
1308
        nic_str += ", ip=%s" % ip
1309
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1310
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1311
      if hvp[constants.HV_VIF_SCRIPT]:
1312
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1313
      vif_data.append("'%s'" % nic_str)
1314
      self._WriteNICInfoFile(instance.name, idx, nic)
1315

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

    
1318
    disk_data = \
1319
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1320

    
1321
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1322
    if iso_path:
1323
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1324
      disk_data.append(iso)
1325

    
1326
    config.write("disk = [%s]\n" % (",".join(disk_data)))
1327
    # Add PCI passthrough
1328
    pci_pass_arr = []
1329
    pci_pass = hvp[constants.HV_PASSTHROUGH]
1330
    if pci_pass:
1331
      pci_pass_arr = pci_pass.split(";")
1332
      config.write("pci = %s\n" % pci_pass_arr)
1333

    
1334
    WriteXenConfigEvents(config, hvp)
1335

    
1336
    cpuid = hvp[constants.HV_XEN_CPUID]
1337
    if cpuid:
1338
      config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1339

    
1340
    if hvp[constants.HV_SOUNDHW]:
1341
      config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1342

    
1343
    return config.getvalue()