Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 30b12688

History | View | Annotate | Download (41.9 kB)

1
#
2
#
3

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

    
21

    
22
"""Xen hypervisors
23

24
"""
25

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

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

    
40

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

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

    
54

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

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

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

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

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

    
86

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

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

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

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

    
108

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

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

119
  """
120
  result = []
121

    
122
  # Iterate through all lines while ignoring header
123
  for line in lines[1:]:
124
    # The format of lines is:
125
    # Name      ID Mem(MiB) VCPUs State  Time(s)
126
    # Domain-0   0  3418     4 r-----    266.2
127
    data = line.split()
128
    if len(data) != 6:
129
      raise errors.HypervisorError("Can't parse instance list,"
130
                                   " line: %s" % line)
131
    try:
132
      data[1] = int(data[1])
133
      data[2] = int(data[2])
134
      data[3] = int(data[3])
135
      data[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 _GetInstanceList(fn, include_node, _timeout=5):
148
  """Return the list of running instances.
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 _ParseNodeInfo(info):
181
  """Return information about the node.
182

183
  @return: a dict with the following keys (memory values in MiB):
184
        - memory_total: the total memory size on the node
185
        - memory_free: the available memory on the node for instances
186
        - nr_cpus: total number of CPUs
187
        - nr_nodes: in a NUMA system, the number of domains
188
        - nr_sockets: the number of physical CPU sockets in the node
189
        - hv_version: the hypervisor version in the form (major, minor)
190

191
  """
192
  result = {}
193
  cores_per_socket = threads_per_core = nr_cpus = None
194
  xen_major, xen_minor = None, None
195
  memory_total = None
196
  memory_free = None
197

    
198
  for line in info.splitlines():
199
    fields = line.split(":", 1)
200

    
201
    if len(fields) < 2:
202
      continue
203

    
204
    (key, val) = map(lambda s: s.strip(), fields)
205

    
206
    # Note: in Xen 3, memory has changed to total_memory
207
    if key in ("memory", "total_memory"):
208
      memory_total = int(val)
209
    elif key == "free_memory":
210
      memory_free = int(val)
211
    elif key == "nr_cpus":
212
      nr_cpus = result["cpu_total"] = int(val)
213
    elif key == "nr_nodes":
214
      result["cpu_nodes"] = int(val)
215
    elif key == "cores_per_socket":
216
      cores_per_socket = int(val)
217
    elif key == "threads_per_core":
218
      threads_per_core = int(val)
219
    elif key == "xen_major":
220
      xen_major = int(val)
221
    elif key == "xen_minor":
222
      xen_minor = int(val)
223

    
224
  if None not in [cores_per_socket, threads_per_core, nr_cpus]:
225
    result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
226

    
227
  if memory_free is not None:
228
    result["memory_free"] = memory_free
229

    
230
  if memory_total is not None:
231
    result["memory_total"] = memory_total
232

    
233
  if not (xen_major is None or xen_minor is None):
234
    result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
235

    
236
  return result
237

    
238

    
239
def _MergeInstanceInfo(info, instance_list):
240
  """Updates node information from L{_ParseNodeInfo} with instance info.
241

242
  @type info: dict
243
  @param info: Result from L{_ParseNodeInfo}
244
  @type instance_list: list of tuples
245
  @param instance_list: list of instance information; one tuple per instance
246
  @rtype: dict
247

248
  """
249
  total_instmem = 0
250

    
251
  for (name, _, mem, vcpus, _, _) in instance_list:
252
    if name == _DOM0_NAME:
253
      info["memory_dom0"] = mem
254
      info["cpu_dom0"] = vcpus
255

    
256
    # Include Dom0 in total memory usage
257
    total_instmem += mem
258

    
259
  memory_free = info.get("memory_free")
260
  memory_total = info.get("memory_total")
261

    
262
  # Calculate memory used by hypervisor
263
  if None not in [memory_total, memory_free, total_instmem]:
264
    info["memory_hv"] = memory_total - memory_free - total_instmem
265

    
266
  return info
267

    
268

    
269
def _GetNodeInfo(info, instance_list):
270
  """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
271

272
  @type instance_list: list of tuples
273
  @param instance_list: list of instance information; one tuple per instance
274

275
  """
276
  return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
277

    
278

    
279
def _GetConfigFileDiskData(block_devices, blockdev_prefix,
280
                           _letters=_DISK_LETTERS):
281
  """Get disk directives for Xen config file.
282

283
  This method builds the xen config disk directive according to the
284
  given disk_template and block_devices.
285

286
  @param block_devices: list of tuples (cfdev, rldev):
287
      - cfdev: dict containing ganeti config disk part
288
      - rldev: ganeti.block.bdev.BlockDev object
289
  @param blockdev_prefix: a string containing blockdevice prefix,
290
                          e.g. "sd" for /dev/sda
291

292
  @return: string containing disk directive for xen instance config file
293

294
  """
295
  if len(block_devices) > len(_letters):
296
    raise errors.HypervisorError("Too many disks")
297

    
298
  disk_data = []
299

    
300
  for sd_suffix, (cfdev, dev_path) in zip(_letters, block_devices):
301
    sd_name = blockdev_prefix + sd_suffix
302

    
303
    if cfdev.mode == constants.DISK_RDWR:
304
      mode = "w"
305
    else:
306
      mode = "r"
307

    
308
    if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
309
      driver = _FILE_DRIVER_MAP[cfdev.physical_id[0]]
310
    else:
311
      driver = "phy"
312

    
313
    disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
314

    
315
  return disk_data
316

    
317

    
318
class XenHypervisor(hv_base.BaseHypervisor):
319
  """Xen generic hypervisor interface
320

321
  This is the Xen base class used for both Xen PVM and HVM. It contains
322
  all the functionality that is identical for both.
323

324
  """
325
  CAN_MIGRATE = True
326
  REBOOT_RETRY_COUNT = 60
327
  REBOOT_RETRY_INTERVAL = 10
328
  _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
329
  _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
330
  _DIRS = [_ROOT_DIR, _NICS_DIR]
331

    
332
  ANCILLARY_FILES = [
333
    XEND_CONFIG_FILE,
334
    XL_CONFIG_FILE,
335
    VIF_BRIDGE_SCRIPT,
336
    ]
337
  ANCILLARY_FILES_OPT = [
338
    XL_CONFIG_FILE,
339
    ]
340

    
341
  def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
342
    hv_base.BaseHypervisor.__init__(self)
343

    
344
    if _cfgdir is None:
345
      self._cfgdir = pathutils.XEN_CONFIG_DIR
346
    else:
347
      self._cfgdir = _cfgdir
348

    
349
    if _run_cmd_fn is None:
350
      self._run_cmd_fn = utils.RunCmd
351
    else:
352
      self._run_cmd_fn = _run_cmd_fn
353

    
354
    self._cmd = _cmd
355

    
356
  @staticmethod
357
  def _GetCommandFromHvparams(hvparams):
358
    """Returns the Xen command extracted from the given hvparams.
359

360
    @type hvparams: dict of strings
361
    @param hvparams: hypervisor parameters
362

363
    """
364
    if hvparams is None or constants.HV_XEN_CMD not in hvparams:
365
      raise errors.HypervisorError("Cannot determine xen command.")
366
    else:
367
      return hvparams[constants.HV_XEN_CMD]
368

    
369
  def _GetCommand(self, hvparams):
370
    """Returns Xen command to use.
371

372
    @type hvparams: dict of strings
373
    @param hvparams: hypervisor parameters
374

375
    """
376
    if self._cmd is None:
377
      cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
378
    else:
379
      cmd = self._cmd
380

    
381
    if cmd not in constants.KNOWN_XEN_COMMANDS:
382
      raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
383

    
384
    return cmd
385

    
386
  def _RunXen(self, args, hvparams):
387
    """Wrapper around L{utils.process.RunCmd} to run Xen command.
388

389
    @type hvparams: dict of strings
390
    @param hvparams: dictionary of hypervisor params
391
    @see: L{utils.process.RunCmd}
392

393
    """
394
    cmd = [self._GetCommand(hvparams)]
395
    cmd.extend(args)
396

    
397
    return self._run_cmd_fn(cmd)
398

    
399
  def _ConfigFileName(self, instance_name):
400
    """Get the config file name for an instance.
401

402
    @param instance_name: instance name
403
    @type instance_name: str
404
    @return: fully qualified path to instance config file
405
    @rtype: str
406

407
    """
408
    return utils.PathJoin(self._cfgdir, instance_name)
409

    
410
  @classmethod
411
  def _WriteNICInfoFile(cls, instance_name, idx, nic):
412
    """Write the Xen config file for the instance.
413

414
    This version of the function just writes the config file from static data.
415

416
    """
417
    dirs = [(dname, constants.RUN_DIRS_MODE)
418
            for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
419
    utils.EnsureDirs(dirs)
420

    
421
    cfg_file = cls._InstanceNICFile(instance_name, idx)
422
    data = StringIO()
423

    
424
    if nic.netinfo:
425
      netinfo = objects.Network.FromDict(nic.netinfo)
426
      data.write("NETWORK_NAME=%s\n" % netinfo.name)
427
      if netinfo.network:
428
        data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
429
      if netinfo.gateway:
430
        data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
431
      if netinfo.network6:
432
        data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
433
      if netinfo.gateway6:
434
        data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
435
      if netinfo.mac_prefix:
436
        data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
437
      if netinfo.tags:
438
        data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
439

    
440
    data.write("MAC=%s\n" % nic.mac)
441
    data.write("IP=%s\n" % nic.ip)
442
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
443
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
444

    
445
    try:
446
      utils.WriteFile(cfg_file, data=data.getvalue())
447
    except EnvironmentError, err:
448
      raise errors.HypervisorError("Cannot write Xen instance configuration"
449
                                   " file %s: %s" % (cfg_file, err))
450

    
451
  @classmethod
452
  def _InstanceNICDir(cls, instance_name):
453
    """Returns the directory holding the tap device files for a given instance.
454

455
    """
456
    return utils.PathJoin(cls._NICS_DIR, instance_name)
457

    
458
  @classmethod
459
  def _InstanceNICFile(cls, instance_name, seq):
460
    """Returns the name of the file containing the tap device for a given NIC
461

462
    """
463
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
464

    
465
  @classmethod
466
  def _GetConfig(cls, instance, startup_memory, block_devices):
467
    """Build Xen configuration for an instance.
468

469
    """
470
    raise NotImplementedError
471

    
472
  def _WriteConfigFile(self, instance_name, data):
473
    """Write the Xen config file for the instance.
474

475
    This version of the function just writes the config file from static data.
476

477
    """
478
    # just in case it exists
479
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
480

    
481
    cfg_file = self._ConfigFileName(instance_name)
482
    try:
483
      utils.WriteFile(cfg_file, data=data)
484
    except EnvironmentError, err:
485
      raise errors.HypervisorError("Cannot write Xen instance configuration"
486
                                   " file %s: %s" % (cfg_file, err))
487

    
488
  def _ReadConfigFile(self, instance_name):
489
    """Returns the contents of the instance config file.
490

491
    """
492
    filename = self._ConfigFileName(instance_name)
493

    
494
    try:
495
      file_content = utils.ReadFile(filename)
496
    except EnvironmentError, err:
497
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
498

    
499
    return file_content
500

    
501
  def _RemoveConfigFile(self, instance_name):
502
    """Remove the xen configuration file.
503

504
    """
505
    utils.RemoveFile(self._ConfigFileName(instance_name))
506
    try:
507
      shutil.rmtree(self._InstanceNICDir(instance_name))
508
    except OSError, err:
509
      if err.errno != errno.ENOENT:
510
        raise
511

    
512
  def _StashConfigFile(self, instance_name):
513
    """Move the Xen config file to the log directory and return its new path.
514

515
    """
516
    old_filename = self._ConfigFileName(instance_name)
517
    base = ("%s-%s" %
518
            (instance_name, utils.TimestampForFilename()))
519
    new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
520
    utils.RenameFile(old_filename, new_filename)
521
    return new_filename
522

    
523
  def _GetInstanceList(self, include_node, hvparams):
524
    """Wrapper around module level L{_GetInstanceList}.
525

526
    @type hvparams: dict of strings
527
    @param hvparams: hypervisor parameters to be used on this node
528

529
    """
530
    return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
531
                            include_node)
532

    
533
  def ListInstances(self, hvparams=None):
534
    """Get the list of running instances.
535

536
    """
537
    instance_list = self._GetInstanceList(False, hvparams)
538
    names = [info[0] for info in instance_list]
539
    return names
540

    
541
  def GetInstanceInfo(self, instance_name, hvparams=None):
542
    """Get instance properties.
543

544
    @type instance_name: string
545
    @param instance_name: the instance name
546
    @type hvparams: dict of strings
547
    @param hvparams: the instance's hypervisor params
548

549
    @return: tuple (name, id, memory, vcpus, stat, times)
550

551
    """
552
    instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
553
    result = None
554
    for data in instance_list:
555
      if data[0] == instance_name:
556
        result = data
557
        break
558
    return result
559

    
560
  def GetAllInstancesInfo(self, hvparams=None):
561
    """Get properties of all instances.
562

563
    @type hvparams: dict of strings
564
    @param hvparams: hypervisor parameters
565
    @return: list of tuples (name, id, memory, vcpus, stat, times)
566

567
    """
568
    return self._GetInstanceList(False, hvparams)
569

    
570
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
571
    """Gather configuration details and write to disk.
572

573
    See L{_GetConfig} for arguments.
574

575
    """
576
    buf = StringIO()
577
    buf.write("# Automatically generated by Ganeti. Do not edit!\n")
578
    buf.write("\n")
579
    buf.write(self._GetConfig(instance, startup_memory, block_devices))
580
    buf.write("\n")
581

    
582
    self._WriteConfigFile(instance.name, buf.getvalue())
583

    
584
  def StartInstance(self, instance, block_devices, startup_paused):
585
    """Start an instance.
586

587
    """
588
    startup_memory = self._InstanceStartupMemory(instance)
589

    
590
    self._MakeConfigFile(instance, startup_memory, block_devices)
591

    
592
    cmd = ["create"]
593
    if startup_paused:
594
      cmd.append("-p")
595
    cmd.append(self._ConfigFileName(instance.name))
596

    
597
    result = self._RunXen(cmd, instance.hvparams)
598
    if result.failed:
599
      # Move the Xen configuration file to the log directory to avoid
600
      # leaving a stale config file behind.
601
      stashed_config = self._StashConfigFile(instance.name)
602
      raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
603
                                   " config file to %s" %
604
                                   (instance.name, result.fail_reason,
605
                                    result.output, stashed_config))
606

    
607
  def StopInstance(self, instance, force=False, retry=False, name=None):
608
    """Stop an instance.
609

610
    """
611
    if name is None:
612
      name = instance.name
613

    
614
    return self._StopInstance(name, force, instance.hvparams)
615

    
616
  def _ShutdownInstance(self, name, hvparams):
617
    """Shutdown an instance if the instance is running.
618

619
    @type name: string
620
    @param name: name of the instance to stop
621
    @type hvparams: dict of string
622
    @param hvparams: hypervisor parameters of the instance
623

624
    The '-w' flag waits for shutdown to complete which avoids the need
625
    to poll in the case where we want to destroy the domain
626
    immediately after shutdown.
627

628
    """
629
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
630

    
631
    if instance_info is None or _IsInstanceShutdown(instance_info[4]):
632
      logging.info("Failed to shutdown instance %s, not running", name)
633
      return None
634

    
635
    return self._RunXen(["shutdown", "-w", name], hvparams)
636

    
637
  def _DestroyInstance(self, name, hvparams):
638
    """Destroy an instance if the instance if the instance exists.
639

640
    @type name: string
641
    @param name: name of the instance to destroy
642
    @type hvparams: dict of string
643
    @param hvparams: hypervisor parameters of the instance
644

645
    """
646
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
647

    
648
    if instance_info is None:
649
      logging.info("Failed to destroy instance %s, does not exist", name)
650
      return None
651

    
652
    return self._RunXen(["destroy", name], hvparams)
653

    
654
  def _StopInstance(self, name, force, hvparams):
655
    """Stop an instance.
656

657
    @type name: string
658
    @param name: name of the instance to destroy
659

660
    @type force: boolean
661
    @param force: whether to do a "hard" stop (destroy)
662

663
    @type hvparams: dict of string
664
    @param hvparams: hypervisor parameters of the instance
665

666
    """
667
    if force:
668
      result = self._DestroyInstance(name, hvparams)
669
    else:
670
      self._ShutdownInstance(name, hvparams)
671
      result = self._DestroyInstance(name, hvparams)
672

    
673
    if result is not None and result.failed and \
674
          self.GetInstanceInfo(name, hvparams=hvparams) is not None:
675
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
676
                                   (name, result.fail_reason, result.output))
677

    
678
    # Remove configuration file if stopping/starting instance was successful
679
    self._RemoveConfigFile(name)
680

    
681
  def RebootInstance(self, instance):
682
    """Reboot an instance.
683

684
    """
685
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
686

    
687
    if ini_info is None:
688
      raise errors.HypervisorError("Failed to reboot instance %s,"
689
                                   " not running" % instance.name)
690

    
691
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
692
    if result.failed:
693
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
694
                                   (instance.name, result.fail_reason,
695
                                    result.output))
696

    
697
    def _CheckInstance():
698
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
699

    
700
      # check if the domain ID has changed or the run time has decreased
701
      if (new_info is not None and
702
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
703
        return
704

    
705
      raise utils.RetryAgain()
706

    
707
    try:
708
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
709
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
710
    except utils.RetryTimeout:
711
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
712
                                   " did not reboot in the expected interval" %
713
                                   (instance.name, ))
714

    
715
  def BalloonInstanceMemory(self, instance, mem):
716
    """Balloon an instance memory to a certain value.
717

718
    @type instance: L{objects.Instance}
719
    @param instance: instance to be accepted
720
    @type mem: int
721
    @param mem: actual memory size to use for instance runtime
722

723
    """
724
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
725
    if result.failed:
726
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
727
                                   (instance.name, result.fail_reason,
728
                                    result.output))
729

    
730
    # Update configuration file
731
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
732
    cmd.append(self._ConfigFileName(instance.name))
733

    
734
    result = utils.RunCmd(cmd)
735
    if result.failed:
736
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
737
                                   (instance.name, result.fail_reason,
738
                                    result.output))
739

    
740
  def GetNodeInfo(self, hvparams=None):
741
    """Return information about the node.
742

743
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
744

745
    """
746
    result = self._RunXen(["info"], hvparams)
747
    if result.failed:
748
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
749
                    result.fail_reason, result.output)
750
      return None
751

    
752
    instance_list = self._GetInstanceList(True, hvparams)
753
    return _GetNodeInfo(result.stdout, instance_list)
754

    
755
  @classmethod
756
  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
757
    """Return a command for connecting to the console of an instance.
758

759
    """
760
    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
761
    return objects.InstanceConsole(instance=instance.name,
762
                                   kind=constants.CONS_SSH,
763
                                   host=primary_node.name,
764
                                   user=constants.SSH_CONSOLE_USER,
765
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
766
                                            xen_cmd, instance.name])
767

    
768
  def Verify(self, hvparams=None):
769
    """Verify the hypervisor.
770

771
    For Xen, this verifies that the xend process is running.
772

773
    @type hvparams: dict of strings
774
    @param hvparams: hypervisor parameters to be verified against
775

776
    @return: Problem description if something is wrong, C{None} otherwise
777

778
    """
779
    if hvparams is None:
780
      return "Could not verify the hypervisor, because no hvparams were" \
781
             " provided."
782

    
783
    if constants.HV_XEN_CMD in hvparams:
784
      xen_cmd = hvparams[constants.HV_XEN_CMD]
785
      try:
786
        self._CheckToolstack(xen_cmd)
787
      except errors.HypervisorError:
788
        return "The configured xen toolstack '%s' is not available on this" \
789
               " node." % xen_cmd
790

    
791
    result = self._RunXen(["info"], hvparams)
792
    if result.failed:
793
      return "Retrieving information from xen failed: %s, %s" % \
794
        (result.fail_reason, result.output)
795

    
796
    return None
797

    
798
  def MigrationInfo(self, instance):
799
    """Get instance information to perform a migration.
800

801
    @type instance: L{objects.Instance}
802
    @param instance: instance to be migrated
803
    @rtype: string
804
    @return: content of the xen config file
805

806
    """
807
    return self._ReadConfigFile(instance.name)
808

    
809
  def AcceptInstance(self, instance, info, target):
810
    """Prepare to accept an instance.
811

812
    @type instance: L{objects.Instance}
813
    @param instance: instance to be accepted
814
    @type info: string
815
    @param info: content of the xen config file on the source node
816
    @type target: string
817
    @param target: target host (usually ip), on this node
818

819
    """
820
    pass
821

    
822
  def FinalizeMigrationDst(self, instance, info, success):
823
    """Finalize an instance migration.
824

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

828
    @type instance: L{objects.Instance}
829
    @param instance: instance whose migration is being finalized
830
    @type info: string
831
    @param info: content of the xen config file on the source node
832
    @type success: boolean
833
    @param success: whether the migration was a success or a failure
834

835
    """
836
    if success:
837
      self._WriteConfigFile(instance.name, info)
838

    
839
  def MigrateInstance(self, cluster_name, instance, target, live):
840
    """Migrate an instance to a target node.
841

842
    The migration will not be attempted if the instance is not
843
    currently running.
844

845
    @type instance: L{objects.Instance}
846
    @param instance: the instance to be migrated
847
    @type target: string
848
    @param target: ip address of the target node
849
    @type live: boolean
850
    @param live: perform a live migration
851

852
    """
853
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
854

    
855
    return self._MigrateInstance(cluster_name, instance.name, target, port,
856
                                 live, instance.hvparams)
857

    
858
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
859
                       hvparams, _ping_fn=netutils.TcpPing):
860
    """Migrate an instance to a target node.
861

862
    @see: L{MigrateInstance} for details
863

864
    """
865
    if hvparams is None:
866
      raise errors.HypervisorError("No hvparams provided.")
867

    
868
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
869
      raise errors.HypervisorError("Instance not running, cannot migrate")
870

    
871
    cmd = self._GetCommand(hvparams)
872

    
873
    if (cmd == constants.XEN_CMD_XM and
874
        not _ping_fn(target, port, live_port_needed=True)):
875
      raise errors.HypervisorError("Remote host %s not listening on port"
876
                                   " %s, cannot migrate" % (target, port))
877

    
878
    args = ["migrate"]
879

    
880
    if cmd == constants.XEN_CMD_XM:
881
      args.extend(["-p", "%d" % port])
882
      if live:
883
        args.append("-l")
884

    
885
    elif cmd == constants.XEN_CMD_XL:
886
      args.extend([
887
        "-s", constants.XL_SSH_CMD % cluster_name,
888
        "-C", self._ConfigFileName(instance_name),
889
        ])
890

    
891
    else:
892
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
893

    
894
    args.extend([instance_name, target])
895

    
896
    result = self._RunXen(args, hvparams)
897
    if result.failed:
898
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
899
                                   (instance_name, result.output))
900

    
901
  def FinalizeMigrationSource(self, instance, success, live):
902
    """Finalize the instance migration on the source node.
903

904
    @type instance: L{objects.Instance}
905
    @param instance: the instance that was migrated
906
    @type success: bool
907
    @param success: whether the migration succeeded or not
908
    @type live: bool
909
    @param live: whether the user requested a live migration or not
910

911
    """
912
    # pylint: disable=W0613
913
    if success:
914
      # remove old xen file after migration succeeded
915
      try:
916
        self._RemoveConfigFile(instance.name)
917
      except EnvironmentError:
918
        logging.exception("Failure while removing instance config file")
919

    
920
  def GetMigrationStatus(self, instance):
921
    """Get the migration status
922

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

927
    @type instance: L{objects.Instance}
928
    @param instance: the instance that is being migrated
929
    @rtype: L{objects.MigrationStatus}
930
    @return: the status of the current migration (one of
931
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
932
             progress info that can be retrieved from the hypervisor
933

934
    """
935
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
936

    
937
  def PowercycleNode(self, hvparams=None):
938
    """Xen-specific powercycle.
939

940
    This first does a Linux reboot (which triggers automatically a Xen
941
    reboot), and if that fails it tries to do a Xen reboot. The reason
942
    we don't try a Xen reboot first is that the xen reboot launches an
943
    external command which connects to the Xen hypervisor, and that
944
    won't work in case the root filesystem is broken and/or the xend
945
    daemon is not working.
946

947
    @type hvparams: dict of strings
948
    @param hvparams: hypervisor params to be used on this node
949

950
    """
951
    try:
952
      self.LinuxPowercycle()
953
    finally:
954
      xen_cmd = self._GetCommand(hvparams)
955
      utils.RunCmd([xen_cmd, "debug", "R"])
956

    
957
  def _CheckToolstack(self, xen_cmd):
958
    """Check whether the given toolstack is available on the node.
959

960
    @type xen_cmd: string
961
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
962

963
    """
964
    binary_found = self._CheckToolstackBinary(xen_cmd)
965
    if not binary_found:
966
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
967
    elif xen_cmd == constants.XEN_CMD_XL:
968
      if not self._CheckToolstackXlConfigured():
969
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
970
                                     "node." % xen_cmd)
971

    
972
  def _CheckToolstackBinary(self, xen_cmd):
973
    """Checks whether the xen command's binary is found on the machine.
974

975
    """
976
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
977
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
978
    result = self._run_cmd_fn(["which", xen_cmd])
979
    return not result.failed
980

    
981
  def _CheckToolstackXlConfigured(self):
982
    """Checks whether xl is enabled on an xl-capable node.
983

984
    @rtype: bool
985
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
986

987
    """
988
    result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
989
    if not result.failed:
990
      return True
991
    elif result.failed:
992
      if "toolstack" in result.stderr:
993
        return False
994
      # xl fails for some other reason than the toolstack
995
      else:
996
        raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
997
                                     % (constants.XEN_CMD_XL, result.stderr))
998

    
999

    
1000
class XenPvmHypervisor(XenHypervisor):
1001
  """Xen PVM hypervisor interface"""
1002

    
1003
  PARAMETERS = {
1004
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1005
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1006
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1007
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1008
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1009
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1010
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1011
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1012
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1013
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1014
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1015
    constants.HV_REBOOT_BEHAVIOR:
1016
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1017
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1018
    constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1019
    constants.HV_CPU_WEIGHT:
1020
      (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1021
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1022
    constants.HV_XEN_CMD:
1023
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1024
    }
1025

    
1026
  def _GetConfig(self, instance, startup_memory, block_devices):
1027
    """Write the Xen config file for the instance.
1028

1029
    """
1030
    hvp = instance.hvparams
1031
    config = StringIO()
1032
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1033

    
1034
    # if bootloader is True, use bootloader instead of kernel and ramdisk
1035
    # parameters.
1036
    if hvp[constants.HV_USE_BOOTLOADER]:
1037
      # bootloader handling
1038
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1039
      if bootloader_path:
1040
        config.write("bootloader = '%s'\n" % bootloader_path)
1041
      else:
1042
        raise errors.HypervisorError("Bootloader enabled, but missing"
1043
                                     " bootloader path")
1044

    
1045
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1046
      if bootloader_args:
1047
        config.write("bootargs = '%s'\n" % bootloader_args)
1048
    else:
1049
      # kernel handling
1050
      kpath = hvp[constants.HV_KERNEL_PATH]
1051
      config.write("kernel = '%s'\n" % kpath)
1052

    
1053
      # initrd handling
1054
      initrd_path = hvp[constants.HV_INITRD_PATH]
1055
      if initrd_path:
1056
        config.write("ramdisk = '%s'\n" % initrd_path)
1057

    
1058
    # rest of the settings
1059
    config.write("memory = %d\n" % startup_memory)
1060
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1061
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1062
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1063
    if cpu_pinning:
1064
      config.write("%s\n" % cpu_pinning)
1065
    cpu_cap = hvp[constants.HV_CPU_CAP]
1066
    if cpu_cap:
1067
      config.write("cpu_cap=%d\n" % cpu_cap)
1068
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1069
    if cpu_weight:
1070
      config.write("cpu_weight=%d\n" % cpu_weight)
1071

    
1072
    config.write("name = '%s'\n" % instance.name)
1073

    
1074
    vif_data = []
1075
    for idx, nic in enumerate(instance.nics):
1076
      nic_str = "mac=%s" % (nic.mac)
1077
      ip = getattr(nic, "ip", None)
1078
      if ip is not None:
1079
        nic_str += ", ip=%s" % ip
1080
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1081
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1082
      if hvp[constants.HV_VIF_SCRIPT]:
1083
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1084
      vif_data.append("'%s'" % nic_str)
1085
      self._WriteNICInfoFile(instance.name, idx, nic)
1086

    
1087
    disk_data = \
1088
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1089

    
1090
    config.write("vif = [%s]\n" % ",".join(vif_data))
1091
    config.write("disk = [%s]\n" % ",".join(disk_data))
1092

    
1093
    if hvp[constants.HV_ROOT_PATH]:
1094
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1095
    config.write("on_poweroff = 'destroy'\n")
1096
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1097
      config.write("on_reboot = 'restart'\n")
1098
    else:
1099
      config.write("on_reboot = 'destroy'\n")
1100
    config.write("on_crash = 'restart'\n")
1101
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1102

    
1103
    return config.getvalue()
1104

    
1105

    
1106
class XenHvmHypervisor(XenHypervisor):
1107
  """Xen HVM hypervisor interface"""
1108

    
1109
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1110
    pathutils.VNC_PASSWORD_FILE,
1111
    ]
1112
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1113
    pathutils.VNC_PASSWORD_FILE,
1114
    ]
1115

    
1116
  PARAMETERS = {
1117
    constants.HV_ACPI: hv_base.NO_CHECK,
1118
    constants.HV_BOOT_ORDER: (True, ) +
1119
      (lambda x: x and len(x.strip("acdn")) == 0,
1120
       "Invalid boot order specified, must be one or more of [acdn]",
1121
       None, None),
1122
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1123
    constants.HV_DISK_TYPE:
1124
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1125
    constants.HV_NIC_TYPE:
1126
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1127
    constants.HV_PAE: hv_base.NO_CHECK,
1128
    constants.HV_VNC_BIND_ADDRESS:
1129
      (False, netutils.IP4Address.IsValid,
1130
       "VNC bind address is not a valid IP address", None, None),
1131
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1132
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1133
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1134
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1135
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1136
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1137
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1138
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1139
    # Add PCI passthrough
1140
    constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1141
    constants.HV_REBOOT_BEHAVIOR:
1142
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1143
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1144
    constants.HV_CPU_CAP: hv_base.NO_CHECK,
1145
    constants.HV_CPU_WEIGHT:
1146
      (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1147
    constants.HV_VIF_TYPE:
1148
      hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1149
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1150
    constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1151
    constants.HV_XEN_CMD:
1152
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1153
    }
1154

    
1155
  def _GetConfig(self, instance, startup_memory, block_devices):
1156
    """Create a Xen 3.1 HVM config file.
1157

1158
    """
1159
    hvp = instance.hvparams
1160

    
1161
    config = StringIO()
1162

    
1163
    # kernel handling
1164
    kpath = hvp[constants.HV_KERNEL_PATH]
1165
    config.write("kernel = '%s'\n" % kpath)
1166

    
1167
    config.write("builder = 'hvm'\n")
1168
    config.write("memory = %d\n" % startup_memory)
1169
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1170
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1171
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1172
    if cpu_pinning:
1173
      config.write("%s\n" % cpu_pinning)
1174
    cpu_cap = hvp[constants.HV_CPU_CAP]
1175
    if cpu_cap:
1176
      config.write("cpu_cap=%d\n" % cpu_cap)
1177
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1178
    if cpu_weight:
1179
      config.write("cpu_weight=%d\n" % cpu_weight)
1180

    
1181
    config.write("name = '%s'\n" % instance.name)
1182
    if hvp[constants.HV_PAE]:
1183
      config.write("pae = 1\n")
1184
    else:
1185
      config.write("pae = 0\n")
1186
    if hvp[constants.HV_ACPI]:
1187
      config.write("acpi = 1\n")
1188
    else:
1189
      config.write("acpi = 0\n")
1190
    if hvp[constants.HV_VIRIDIAN]:
1191
      config.write("viridian = 1\n")
1192
    else:
1193
      config.write("viridian = 0\n")
1194

    
1195
    config.write("apic = 1\n")
1196
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1197
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1198
    config.write("sdl = 0\n")
1199
    config.write("usb = 1\n")
1200
    config.write("usbdevice = 'tablet'\n")
1201
    config.write("vnc = 1\n")
1202
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1203
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1204
    else:
1205
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1206

    
1207
    if instance.network_port > constants.VNC_BASE_PORT:
1208
      display = instance.network_port - constants.VNC_BASE_PORT
1209
      config.write("vncdisplay = %s\n" % display)
1210
      config.write("vncunused = 0\n")
1211
    else:
1212
      config.write("# vncdisplay = 1\n")
1213
      config.write("vncunused = 1\n")
1214

    
1215
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1216
    try:
1217
      password = utils.ReadFile(vnc_pwd_file)
1218
    except EnvironmentError, err:
1219
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1220
                                   (vnc_pwd_file, err))
1221

    
1222
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1223

    
1224
    config.write("serial = 'pty'\n")
1225
    if hvp[constants.HV_USE_LOCALTIME]:
1226
      config.write("localtime = 1\n")
1227

    
1228
    vif_data = []
1229
    # Note: what is called 'nic_type' here, is used as value for the xen nic
1230
    # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1231
    # the 'vif_type' to avoid a clash of notation.
1232
    nic_type = hvp[constants.HV_NIC_TYPE]
1233

    
1234
    if nic_type is None:
1235
      vif_type_str = ""
1236
      if hvp[constants.HV_VIF_TYPE]:
1237
        vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1238
      # ensure old instances don't change
1239
      nic_type_str = vif_type_str
1240
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1241
      nic_type_str = ", type=paravirtualized"
1242
    else:
1243
      # parameter 'model' is only valid with type 'ioemu'
1244
      nic_type_str = ", model=%s, type=%s" % \
1245
        (nic_type, constants.HT_HVM_VIF_IOEMU)
1246
    for idx, nic in enumerate(instance.nics):
1247
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1248
      ip = getattr(nic, "ip", None)
1249
      if ip is not None:
1250
        nic_str += ", ip=%s" % ip
1251
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1252
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1253
      if hvp[constants.HV_VIF_SCRIPT]:
1254
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1255
      vif_data.append("'%s'" % nic_str)
1256
      self._WriteNICInfoFile(instance.name, idx, nic)
1257

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

    
1260
    disk_data = \
1261
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1262

    
1263
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1264
    if iso_path:
1265
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1266
      disk_data.append(iso)
1267

    
1268
    config.write("disk = [%s]\n" % (",".join(disk_data)))
1269
    # Add PCI passthrough
1270
    pci_pass_arr = []
1271
    pci_pass = hvp[constants.HV_PASSTHROUGH]
1272
    if pci_pass:
1273
      pci_pass_arr = pci_pass.split(";")
1274
      config.write("pci = %s\n" % pci_pass_arr)
1275
    config.write("on_poweroff = 'destroy'\n")
1276
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1277
      config.write("on_reboot = 'restart'\n")
1278
    else:
1279
      config.write("on_reboot = 'destroy'\n")
1280
    config.write("on_crash = 'restart'\n")
1281

    
1282
    return config.getvalue()