__init__.py 90.1 KB
Newer Older
Guido Trotter's avatar
Guido Trotter committed
1
2
3
#
#

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


"""KVM hypervisor

"""

Balazs Lecz's avatar
Balazs Lecz committed
26
import errno
Guido Trotter's avatar
Guido Trotter committed
27
28
29
30
import os
import os.path
import re
import tempfile
31
import time
Guido Trotter's avatar
Guido Trotter committed
32
import logging
33
import pwd
34
import shutil
35
import urllib2
36
from bitarray import bitarray
37
try:
Andrea Spadaccini's avatar
Andrea Spadaccini committed
38
  import affinity   # pylint: disable=F0401
39
40
except ImportError:
  affinity = None
41
42
43
44
try:
  import fdsend   # pylint: disable=F0401
except ImportError:
  fdsend = None
Guido Trotter's avatar
Guido Trotter committed
45
46
47
48

from ganeti import utils
from ganeti import constants
from ganeti import errors
49
50
from ganeti import serializer
from ganeti import objects
51
52
from ganeti import uidpool
from ganeti import ssconf
53
from ganeti import netutils
54
55
from ganeti import pathutils
from ganeti.hypervisor import hv_base
56
from ganeti.utils import wrapper as utils_wrapper
Guido Trotter's avatar
Guido Trotter committed
57

58
59
from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \
                                             MonitorSocket
60
from ganeti.hypervisor.hv_kvm.netdev import OpenTap
61

Guido Trotter's avatar
Guido Trotter committed
62

63
_KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
64
_KVM_START_PAUSED_FLAG = "-S"
65

66
67
68
69
70
71
72
73
74
75
76
#: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND}
_SPICE_ADDITIONAL_PARAMS = frozenset([
  constants.HV_KVM_SPICE_IP_VERSION,
  constants.HV_KVM_SPICE_PASSWORD_FILE,
  constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
  constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
  constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
  constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
  constants.HV_KVM_SPICE_USE_TLS,
  ])

77
78
79
80
# Constant bitarray that reflects to a free pci slot
# Use it with bitarray.search()
_AVAILABLE_PCI_SLOT = bitarray("0")

81
82
# below constants show the format of runtime file
# the nics are in second possition, while the disks in 4th (last)
83
84
# moreover disk entries are stored as a list of in tuples
# (L{objects.Disk}, link_name, uri)
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
_KVM_NICS_RUNTIME_INDEX = 1
_KVM_DISKS_RUNTIME_INDEX = 3
_DEVICE_RUNTIME_INDEX = {
  constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX,
  constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX
  }
_FIND_RUNTIME_ENTRY = {
  constants.HOTPLUG_TARGET_NIC:
    lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid],
  constants.HOTPLUG_TARGET_DISK:
    lambda disk, kvm_disks: [(d, l, u) for (d, l, u) in kvm_disks
                             if d.uuid == disk.uuid]
  }
_RUNTIME_DEVICE = {
  constants.HOTPLUG_TARGET_NIC: lambda d: d,
  constants.HOTPLUG_TARGET_DISK: lambda (d, e, _): d
  }
_RUNTIME_ENTRY = {
  constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
  constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e, None)
  }

107
108
_MIGRATION_CAPS_DELIM = ":"

109

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def _GenerateDeviceKVMId(dev_type, dev):
  """Helper function to generate a unique device name used by KVM

  QEMU monitor commands use names to identify devices. Here we use their pci
  slot and a part of their UUID to name them. dev.pci might be None for old
  devices in the cluster.

  @type dev_type: sting
  @param dev_type: device type of param dev
  @type dev: L{objects.Disk} or L{objects.NIC}
  @param dev: the device object for which we generate a kvm name
  @raise errors.HotplugError: in case a device has no pci slot (old devices)

  """

  if not dev.pci:
    raise errors.HotplugError("Hotplug is not supported for %s with UUID %s" %
                              (dev_type, dev.uuid))

  return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)


132
133
134
135
136
137
138
139
140
141
142
143
def _GetFreeSlot(slots, slot=None, reserve=False):
  """Helper method to get first available slot in a bitarray

  @type slots: bitarray
  @param slots: the bitarray to operate on
  @type slot: integer
  @param slot: if given we check whether the slot is free
  @type reserve: boolean
  @param reserve: whether to reserve the first available slot or not
  @return: the idx of the (first) available slot
  @raise errors.HotplugError: If all slots in a bitarray are occupied
    or the given slot is not free.
144

145
146
147
148
149
  """
  if slot is not None:
    assert slot < len(slots)
    if slots[slot]:
      raise errors.HypervisorError("Slots %d occupied" % slot)
150

151
152
153
154
  else:
    avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
    if not avail:
      raise errors.HypervisorError("All slots occupied")
155

156
    slot = int(avail[0])
157

158
159
  if reserve:
    slots[slot] = True
160

161
  return slot
162
163


164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def _GetExistingDeviceInfo(dev_type, device, runtime):
  """Helper function to get an existing device inside the runtime file

  Used when an instance is running. Load kvm runtime file and search
  for a device based on its type and uuid.

  @type dev_type: sting
  @param dev_type: device type of param dev
  @type device: L{objects.Disk} or L{objects.NIC}
  @param device: the device object for which we generate a kvm name
  @type runtime: tuple (cmd, nics, hvparams, disks)
  @param runtime: the runtime data to search for the device
  @raise errors.HotplugError: in case the requested device does not
    exist (e.g. device has been added without --hotplug option) or
    device info has not pci slot (e.g. old devices in the cluster)

  """
  index = _DEVICE_RUNTIME_INDEX[dev_type]
  found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
  if not found:
    raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
                              (dev_type, device.uuid))

  return found[0]


190
191
192
193
194
195
def _UpgradeSerializedRuntime(serialized_runtime):
  """Upgrade runtime data

  Remove any deprecated fields or change the format of the data.
  The runtime files are not upgraded when Ganeti is upgraded, so the required
  modification have to be performed here.
196
197
198

  @type serialized_runtime: string
  @param serialized_runtime: raw text data read from actual runtime file
199
200
  @return: (cmd, nic dicts, hvparams, bdev dicts)
  @rtype: tuple
201
202
203

  """
  loaded_runtime = serializer.Load(serialized_runtime)
204
205
206
  kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
  if len(loaded_runtime) >= 4:
    serialized_disks = loaded_runtime[3]
207
  else:
208
209
210
211
212
213
214
215
216
    serialized_disks = []

  for nic in serialized_nics:
    # Add a dummy uuid slot if an pre-2.8 NIC is found
    if "uuid" not in nic:
      nic["uuid"] = utils.NewUUID()

  return kvm_cmd, serialized_nics, hvparams, serialized_disks

217

218
219
def _AnalyzeSerializedRuntime(serialized_runtime):
  """Return runtime entries for a serialized runtime file
220

221
222
223
224
225
226
227
228
  @type serialized_runtime: string
  @param serialized_runtime: raw text data read from actual runtime file
  @return: (cmd, nics, hvparams, bdevs)
  @rtype: tuple

  """
  kvm_cmd, serialized_nics, hvparams, serialized_disks = \
    _UpgradeSerializedRuntime(serialized_runtime)
229
  kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
230
231
  kvm_disks = [(objects.Disk.FromDict(sdisk), link, uri)
               for sdisk, link, uri in serialized_disks]
232

233
  return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
234

235

236
237
238
239
240
class HeadRequest(urllib2.Request):
  def get_method(self):
    return "HEAD"


241
242
243
244
245
def _CheckUrl(url):
  """Check if a given URL exists on the server

  """
  try:
246
247
    urllib2.urlopen(HeadRequest(url))
    return True
248
249
250
251
  except urllib2.URLError:
    return False


Guido Trotter's avatar
Guido Trotter committed
252
class KVMHypervisor(hv_base.BaseHypervisor):
Michael Hanselmann's avatar
Michael Hanselmann committed
253
254
255
  """KVM hypervisor interface

  """
256
  CAN_MIGRATE = True
Guido Trotter's avatar
Guido Trotter committed
257

258
  _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
Guido Trotter's avatar
Guido Trotter committed
259
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
260
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
Guido Trotter's avatar
Guido Trotter committed
261
262
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
263
  _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
264
  _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
Balazs Lecz's avatar
Balazs Lecz committed
265
266
267
268
269
270
271
272
  # KVM instances with chroot enabled are started in empty chroot directories.
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
  # After an instance is stopped, its chroot directory is removed.
  # If the chroot directory is not empty, it can't be removed.
  # A non-empty chroot directory indicates a possible security incident.
  # To support forensics, the non-empty chroot directory is quarantined in
  # a separate directory, called 'chroot-quarantine'.
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
273
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
274
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
Guido Trotter's avatar
Guido Trotter committed
275

276
  PARAMETERS = {
277
    constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
278
279
280
281
282
283
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
    constants.HV_ACPI: hv_base.NO_CHECK,
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
Guido Trotter's avatar
Guido Trotter committed
284
    constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
285
    constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK, # will be checked later
286
287
288
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
289
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
290
291
292
293
    constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
    constants.HV_KVM_SPICE_IP_VERSION:
      (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
                         x in constants.VALID_IP_VERSIONS),
294
       "The SPICE IP version should be 4 or 6",
295
       None, None),
296
    constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
297
    constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
Iustin Pop's avatar
Iustin Pop committed
298
299
      hv_base.ParamInSet(
        False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
300
    constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
Iustin Pop's avatar
Iustin Pop committed
301
302
      hv_base.ParamInSet(
        False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
303
    constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
Iustin Pop's avatar
Iustin Pop committed
304
305
      hv_base.ParamInSet(
        False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
306
    constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
Iustin Pop's avatar
Iustin Pop committed
307
308
      hv_base.ParamInSet(
        False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
309
    constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
310
    constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
311
312
    constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
    constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
313
    constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
Jose A. Lopes's avatar
Jose A. Lopes committed
314
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
315
    constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
Michael Hanselmann's avatar
Michael Hanselmann committed
316
317
318
319
320
321
    constants.HV_BOOT_ORDER:
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
    constants.HV_NIC_TYPE:
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
    constants.HV_DISK_TYPE:
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
322
323
    constants.HV_KVM_CDROM_DISK_TYPE:
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
Michael Hanselmann's avatar
Michael Hanselmann committed
324
325
    constants.HV_USB_MOUSE:
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
326
    constants.HV_KEYMAP: hv_base.NO_CHECK,
327
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
328
329
    constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
    constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
330
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
331
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
332
333
    constants.HV_DISK_CACHE:
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
334
335
336
    constants.HV_SECURITY_MODEL:
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
Guido Trotter's avatar
Guido Trotter committed
337
338
    constants.HV_KVM_FLAG:
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
339
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
Balazs Lecz's avatar
Balazs Lecz committed
340
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
341
    constants.HV_KVM_USER_SHUTDOWN: hv_base.NO_CHECK,
342
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
343
    constants.HV_REBOOT_BEHAVIOR:
344
345
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
346
    constants.HV_CPU_TYPE: hv_base.NO_CHECK,
347
348
349
    constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
    constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
    constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
Guido Trotter's avatar
Guido Trotter committed
350
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
Guido Trotter's avatar
Guido Trotter committed
351
    constants.HV_USB_DEVICES: hv_base.NO_CHECK,
Guido Trotter's avatar
Guido Trotter committed
352
    constants.HV_VGA: hv_base.NO_CHECK,
Guido Trotter's avatar
Guido Trotter committed
353
    constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
354
    constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
355
    constants.HV_KVM_MIGRATION_CAPS: hv_base.NO_CHECK,
356
    constants.HV_VNET_HDR: hv_base.NO_CHECK,
357
    }
358

359
360
  _VIRTIO = "virtio"
  _VIRTIO_NET_PCI = "virtio-net-pci"
361
  _VIRTIO_BLK_PCI = "virtio-blk-pci"
362

Michele Tartara's avatar
Michele Tartara committed
363
  _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)",
Guido Trotter's avatar
Guido Trotter committed
364
                                    re.M | re.I)
Michael Hanselmann's avatar
Michael Hanselmann committed
365
  _MIGRATION_PROGRESS_RE = \
366
367
368
    re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
               r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
               r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
369

370
371
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
  _MIGRATION_INFO_RETRY_DELAY = 2
Guido Trotter's avatar
Guido Trotter committed
372

373
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
374

375
376
377
378
  _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
  _CPU_INFO_CMD = "info cpus"
  _CONT_CMD = "cont"

379
  _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
380
381
  _CHECK_MACHINE_VERSION_RE = \
    staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
382

383
384
385
386
387
388
  _QMP_RE = re.compile(r"^-qmp\s", re.M)
  _SPICE_RE = re.compile(r"^-spice\s", re.M)
  _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
  _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
  _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
  _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
389
  _DISPLAY_RE = re.compile(r"^-display\s", re.M)
390
  _MACHINE_RE = re.compile(r"^-machine\s", re.M)
391
392
  _VIRTIO_NET_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M)
  _VIRTIO_BLK_RE = re.compile(r"^name \"%s\"" % _VIRTIO_BLK_PCI, re.M)
393
394
395
396
  # match  -drive.*boot=on|off on different lines, but in between accept only
  # dashes not preceeded by a new line (which would mean another option
  # different than -drive is starting)
  _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
397
  _UUID_RE = re.compile(r"^-uuid\s", re.M)
398

399
400
  _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
  _INFO_PCI_CMD = "info pci"
401
402
403
404
405
  _FIND_PCI_DEVICE_RE = \
    staticmethod(
      lambda pci, devid: re.compile(r'Bus.*device[ ]*%d,(.*\n){5,6}.*id "%s"' %
                                    (pci, devid), re.M))

406
407
408
409
  _INFO_VERSION_RE = \
    re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
  _INFO_VERSION_CMD = "info version"

410
411
412
  # Slot 0 for Host bridge, Slot 1 for ISA bridge, Slot 2 for VGA controller
  _DEFAULT_PCI_RESERVATIONS = "11100000000000000000000000000000"
  _SOUNDHW_WITH_PCI_SLOT = ["ac97", "es1370", "hda"]
413

414
415
416
  ANCILLARY_FILES = [
    _KVM_NETWORK_SCRIPT,
    ]
417
418
419
  ANCILLARY_FILES_OPT = [
    _KVM_NETWORK_SCRIPT,
    ]
420

421
422
423
  # Supported kvm options to get output from
  _KVMOPT_HELP = "help"
  _KVMOPT_MLIST = "mlist"
424
  _KVMOPT_DEVICELIST = "devicelist"
425
426
427

  # Command to execute to get the output from kvm, and whether to
  # accept the output even on failure.
428
  _KVMOPTS_CMDS = {
429
430
431
    _KVMOPT_HELP: (["--help"], False),
    _KVMOPT_MLIST: (["-M", "?"], False),
    _KVMOPT_DEVICELIST: (["-device", "?"], True),
432
433
  }

Guido Trotter's avatar
Guido Trotter committed
434
435
436
437
  def __init__(self):
    hv_base.BaseHypervisor.__init__(self)
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
    # in a tmpfs filesystem or has been otherwise wiped out.
Iustin Pop's avatar
Iustin Pop committed
438
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
Guido Trotter's avatar
Guido Trotter committed
439
    utils.EnsureDirs(dirs)
Guido Trotter's avatar
Guido Trotter committed
440

441
442
  @classmethod
  def _InstancePidFile(cls, instance_name):
443
444
445
    """Returns the instance pidfile.

    """
446
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
447

448
449
450
451
452
453
454
  @classmethod
  def _InstanceUidFile(cls, instance_name):
    """Returns the instance uidfile.

    """
    return utils.PathJoin(cls._UIDS_DIR, instance_name)

455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
  @classmethod
  def _InstancePidInfo(cls, pid):
    """Check pid file for instance information.

    Check that a pid file is associated with an instance, and retrieve
    information from its command line.

    @type pid: string or int
    @param pid: process id of the instance to check
    @rtype: tuple
    @return: (instance_name, memory, vcpus)
    @raise errors.HypervisorError: when an instance cannot be found

    """
    alive = utils.IsProcessAlive(pid)
    if not alive:
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)

    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
    try:
      cmdline = utils.ReadFile(cmdline_file)
    except EnvironmentError, err:
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
                                   (pid, err))

    instance = None
    memory = 0
    vcpus = 0

Iustin Pop's avatar
Iustin Pop committed
484
    arg_list = cmdline.split("\x00")
485
    while arg_list:
Michael Hanselmann's avatar
Michael Hanselmann committed
486
      arg = arg_list.pop(0)
487
488
489
490
491
      if arg == "-name":
        instance = arg_list.pop(0)
      elif arg == "-m":
        memory = int(arg_list.pop(0))
      elif arg == "-smp":
492
        vcpus = int(arg_list.pop(0).split(",")[0])
493
494
495
496
497
498
499

    if instance is None:
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
                                   " instance" % pid)

    return (instance, memory, vcpus)

500
501
  @classmethod
  def _InstancePidAlive(cls, instance_name):
502
503
504
505
506
507
    """Returns the instance pidfile, pid, and liveness.

    @type instance_name: string
    @param instance_name: instance name
    @rtype: tuple
    @return: (pid file name, pid, liveness)
508
509

    """
510
    pidfile = cls._InstancePidFile(instance_name)
511
    pid = utils.ReadPidFile(pidfile)
512
513
514

    alive = False
    try:
515
      cmd_instance = cls._InstancePidInfo(pid)[0]
516
517
518
      alive = (cmd_instance == instance_name)
    except errors.HypervisorError:
      pass
519
520
521

    return (pidfile, pid, alive)

522
523
  @classmethod
  def _CheckDown(cls, instance_name):
524
525
526
    """Raises an error unless the given instance is down.

    """
527
    alive = cls._InstancePidAlive(instance_name)[2]
528
529
530
531
    if alive:
      raise errors.HypervisorError("Failed to start instance %s: %s" %
                                   (instance_name, "already running"))

532
533
  @classmethod
  def _InstanceMonitor(cls, instance_name):
534
535
536
    """Returns the instance monitor socket name

    """
537
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
538

539
540
  @classmethod
  def _InstanceSerial(cls, instance_name):
541
542
543
    """Returns the instance serial socket name

    """
544
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
545

546
547
548
549
550
551
552
  @classmethod
  def _InstanceQmpMonitor(cls, instance_name):
    """Returns the instance serial QMP socket name

    """
    return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)

553
554
555
556
557
558
559
  @classmethod
  def _InstanceShutdownMonitor(cls, instance_name):
    """Returns the instance QMP output filename

    """
    return utils.PathJoin(cls._CTRL_DIR, "%s.shutdown" % instance_name)

560
561
562
563
564
565
566
  @staticmethod
  def _SocatUnixConsoleParams():
    """Returns the correct parameters for socat

    If we have a new-enough socat we can use raw mode with an escape character.

    """
567
    if constants.SOCAT_USE_ESCAPE:
568
569
570
571
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
    else:
      return "echo=0,icanon=0"

572
573
  @classmethod
  def _InstanceKVMRuntime(cls, instance_name):
574
575
576
    """Returns the instance KVM runtime filename

    """
577
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
578

Balazs Lecz's avatar
Balazs Lecz committed
579
580
581
582
583
584
585
  @classmethod
  def _InstanceChrootDir(cls, instance_name):
    """Returns the name of the KVM chroot dir of the instance

    """
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)

586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  @classmethod
  def _InstanceNICDir(cls, instance_name):
    """Returns the name of the directory holding the tap device files for a
    given instance.

    """
    return utils.PathJoin(cls._NICS_DIR, instance_name)

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

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

601
602
603
604
605
606
607
  @classmethod
  def _InstanceKeymapFile(cls, instance_name):
    """Returns the name of the file containing the keymap for a given instance

    """
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)

608
609
610
611
612
613
614
  @classmethod
  def _TryReadUidFile(cls, uid_file):
    """Try to read a uid file

    """
    if os.path.exists(uid_file):
      try:
615
        uid = int(utils.ReadOneLineFile(uid_file))
616
        return uid
617
618
619
620
      except EnvironmentError:
        logging.warning("Can't read uid file", exc_info=True)
      except (TypeError, ValueError):
        logging.warning("Can't parse uid file contents", exc_info=True)
621
    return None
622

623
624
  @classmethod
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
Balazs Lecz's avatar
Balazs Lecz committed
625
    """Removes an instance's rutime sockets/files/dirs.
626
627
628
629
630

    """
    utils.RemoveFile(pidfile)
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
    utils.RemoveFile(cls._InstanceSerial(instance_name))
631
    utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
632
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
633
    utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
634
635
636
637
638
    uid_file = cls._InstanceUidFile(instance_name)
    uid = cls._TryReadUidFile(uid_file)
    utils.RemoveFile(uid_file)
    if uid is not None:
      uidpool.ReleaseUid(uid)
639
640
641
642
643
    try:
      shutil.rmtree(cls._InstanceNICDir(instance_name))
    except OSError, err:
      if err.errno != errno.ENOENT:
        raise
Balazs Lecz's avatar
Balazs Lecz committed
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
    try:
      chroot_dir = cls._InstanceChrootDir(instance_name)
      utils.RemoveDir(chroot_dir)
    except OSError, err:
      if err.errno == errno.ENOTEMPTY:
        # The chroot directory is expected to be empty, but it isn't.
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
                                          prefix="%s-%s-" %
                                          (instance_name,
                                           utils.TimestampForFilename()))
        logging.warning("The chroot directory of instance %s can not be"
                        " removed as it is not empty. Moving it to the"
                        " quarantine instead. Please investigate the"
                        " contents (%s) and clean up manually",
                        instance_name, new_chroot_dir)
        utils.RenameFile(chroot_dir, new_chroot_dir)
      else:
        raise
662

663
  @staticmethod
664
665
  def _ConfigureNIC(instance, seq, nic, tap):
    """Run the network configuration script for a specified NIC
Guido Trotter's avatar
Guido Trotter committed
666

667
668
    See L{hv_base.ConfigureNIC}.

Guido Trotter's avatar
Guido Trotter committed
669
670
671
672
673
674
    @param instance: instance we're acting on
    @type instance: instance object
    @param seq: nic sequence number
    @type seq: int
    @param nic: nic we're acting on
    @type nic: nic object
675
676
    @param tap: the host's tap interface this NIC corresponds to
    @type tap: str
Guido Trotter's avatar
Guido Trotter committed
677
678

    """
679
    hv_base.ConfigureNIC([pathutils.KVM_IFUP, tap], instance, seq, nic, tap)
Guido Trotter's avatar
Guido Trotter committed
680

681
682
683
684
  @staticmethod
  def _VerifyAffinityPackage():
    if affinity is None:
      raise errors.HypervisorError("affinity Python package not"
Iustin Pop's avatar
Iustin Pop committed
685
                                   " found; cannot use CPU pinning under KVM")
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730

  @staticmethod
  def _BuildAffinityCpuMask(cpu_list):
    """Create a CPU mask suitable for sched_setaffinity from a list of
    CPUs.

    See man taskset for more info on sched_setaffinity masks.
    For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).

    @type cpu_list: list of int
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
    @rtype: int
    @return: a bit mask of CPU affinities

    """
    if cpu_list == constants.CPU_PINNING_OFF:
      return constants.CPU_PINNING_ALL_KVM
    else:
      return sum(2 ** cpu for cpu in cpu_list)

  @classmethod
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
    """Change CPU affinity for running VM according to given CPU mask.

    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
    @type cpu_mask: string
    @param process_id: process ID of KVM process. Used to pin entire VM
                       to physical CPUs.
    @type process_id: int
    @param thread_dict: map of virtual CPUs to KVM thread IDs
    @type thread_dict: dict int:int

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

    if len(cpu_list) == 1:
      all_cpu_mapping = cpu_list[0]
      if all_cpu_mapping == constants.CPU_PINNING_OFF:
        # If CPU pinning has 1 entry that's "all", then do nothing
        pass
      else:
        # If CPU pinning has one non-all entry, map the entire VM to
        # one set of physical CPUs
        cls._VerifyAffinityPackage()
Iustin Pop's avatar
Iustin Pop committed
731
732
        affinity.set_process_affinity_mask(
          process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
733
734
735
736
737
738
739
740
741
742
    else:
      # The number of vCPUs mapped should match the number of vCPUs
      # reported by KVM. This was already verified earlier, so
      # here only as a sanity check.
      assert len(thread_dict) == len(cpu_list)
      cls._VerifyAffinityPackage()

      # For each vCPU, map it to the proper list of physical CPUs
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
        affinity.set_process_affinity_mask(thread_dict[i],
Iustin Pop's avatar
Iustin Pop committed
743
                                           cls._BuildAffinityCpuMask(vcpu))
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764

  def _GetVcpuThreadIds(self, instance_name):
    """Get a mapping of vCPU no. to thread IDs for the instance

    @type instance_name: string
    @param instance_name: instance in question
    @rtype: dictionary of int:int
    @return: a dictionary mapping vCPU numbers to thread IDs

    """
    result = {}
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
    for line in output.stdout.splitlines():
      match = self._CPU_INFO_RE.search(line)
      if not match:
        continue
      grp = map(int, match.groups())
      result[grp[0]] = grp[1]

    return result

765
766
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
    """Complete CPU pinning.
767
768
769
770
771
772
773

    @type instance_name: string
    @param instance_name: name of instance
    @type cpu_mask: string
    @param cpu_mask: CPU pinning mask as entered by user

    """
774
775
776
777
778
779
    # Get KVM process ID, to be used if need to pin entire VM
    _, pid, _ = self._InstancePidAlive(instance_name)
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
    thread_dict = self._GetVcpuThreadIds(instance_name)
    # Run CPU pinning, based on configured mask
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
780

781
  def ListInstances(self, hvparams=None):
Guido Trotter's avatar
Guido Trotter committed
782
783
    """Get the list of running instances.

Iustin Pop's avatar
Iustin Pop committed
784
785
    We can do this by listing our live instances directory and
    checking whether the associated kvm process is still alive.
Guido Trotter's avatar
Guido Trotter committed
786
787
788
789

    """
    result = []
    for name in os.listdir(self._PIDS_DIR):
790
      if self._InstancePidAlive(name)[2] or self._IsUserShutdown(name):
Guido Trotter's avatar
Guido Trotter committed
791
792
793
        result.append(name)
    return result

794
795
796
797
798
799
800
801
  @classmethod
  def _IsUserShutdown(cls, instance_name):
    return os.path.exists(cls._InstanceShutdownMonitor(instance_name))

  @classmethod
  def _ClearUserShutdown(cls, instance_name):
    utils.RemoveFile(cls._InstanceShutdownMonitor(instance_name))

802
  def GetInstanceInfo(self, instance_name, hvparams=None):
Guido Trotter's avatar
Guido Trotter committed
803
804
    """Get instance properties.

805
    @type instance_name: string
Iustin Pop's avatar
Iustin Pop committed
806
    @param instance_name: the instance name
807
808
    @type hvparams: dict of strings
    @param hvparams: hvparams to be used with this instance
809
810
    @rtype: tuple of strings
    @return: (name, id, memory, vcpus, stat, times)
Guido Trotter's avatar
Guido Trotter committed
811
812

    """
813
    _, pid, alive = self._InstancePidAlive(instance_name)
814
    if not alive:
815
816
817
818
      if self._IsUserShutdown(instance_name):
        return (instance_name, -1, 0, 0, hv_base.HvInstanceState.SHUTDOWN, 0)
      else:
        return None
Guido Trotter's avatar
Guido Trotter committed
819

820
    _, memory, vcpus = self._InstancePidInfo(pid)
821
    istat = hv_base.HvInstanceState.RUNNING
822
    times = 0
Guido Trotter's avatar
Guido Trotter committed
823

824
825
826
    try:
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
      qmp.connect()
827
      vcpus = len(qmp.Execute("query-cpus"))
828
829
      # Will fail if ballooning is not enabled, but we can then just resort to
      # the value above.
830
      mem_bytes = qmp.Execute("query-balloon")[qmp.ACTUAL_KEY]
831
832
833
834
      memory = mem_bytes / 1048576
    except errors.HypervisorError:
      pass

835
    return (instance_name, pid, memory, vcpus, istat, times)
Guido Trotter's avatar
Guido Trotter committed
836

837
  def GetAllInstancesInfo(self, hvparams=None):
Guido Trotter's avatar
Guido Trotter committed
838
839
    """Get properties of all instances.

840
841
    @type hvparams: dict of strings
    @param hvparams: hypervisor parameter
Iustin Pop's avatar
Iustin Pop committed
842
843
    @return: list of tuples (name, id, memory, vcpus, stat, times)

Guido Trotter's avatar
Guido Trotter committed
844
845
846
    """
    data = []
    for name in os.listdir(self._PIDS_DIR):
847
848
849
      try:
        info = self.GetInstanceInfo(name)
      except errors.HypervisorError:
850
        # Ignore exceptions due to instances being shut down
851
852
853
        continue
      if info:
        data.append(info)
Guido Trotter's avatar
Guido Trotter committed
854
855
    return data

856
  def _GenerateKVMBlockDevicesOptions(self, instance, up_hvp, kvm_disks,
857
858
859
860
861
                                      kvmhelp, devlist):
    """Generate KVM options regarding instance's block devices.

    @type instance: L{objects.Instance}
    @param instance: the instance object
862
863
    @type up_hvp: dict
    @param up_hvp: the instance's runtime hypervisor parameters
864
865
    @type kvm_disks: list of tuples
    @param kvm_disks: list of tuples [(disk, link_name, uri)..]
866
867
868
869
870
871
    @type kvmhelp: string
    @param kvmhelp: output of kvm --help
    @type devlist: string
    @param devlist: output of kvm -device ?
    @rtype: list
    @return: list of command line options eventually used by kvm executable
872

873
    """
874
    kernel_path = up_hvp[constants.HV_KERNEL_PATH]
875
876
877
    if kernel_path:
      boot_disk = False
    else:
878
      boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
879
880
881
882
883
884

    # whether this is an older KVM version that uses the boot=on flag
    # on devices
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)

    dev_opts = []
885
    device_driver = None
886
    disk_type = up_hvp[constants.HV_DISK_TYPE]
887
888
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
      if_val = ",if=%s" % self._VIRTIO
889
890
      try:
        if self._VIRTIO_BLK_RE.search(devlist):
891
          if_val = ",if=none"
892
893
894
895
          # will be passed in -device option as driver
          device_driver = self._VIRTIO_BLK_PCI
      except errors.HypervisorError, _:
        pass
896
897
898
    else:
      if_val = ",if=%s" % disk_type
    # Cache mode
899
    disk_cache = up_hvp[constants.HV_DISK_CACHE]
900
901
902
903
904
905
906
907
908
909
910
    if instance.disk_template in constants.DTS_EXT_MIRROR:
      if disk_cache != "none":
        # TODO: make this a hard error, instead of a silent overwrite
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
                        " to prevent shared storage corruption on migration",
                        disk_cache)
      cache_val = ",cache=none"
    elif disk_cache != constants.HT_CACHE_DEFAULT:
      cache_val = ",cache=%s" % disk_cache
    else:
      cache_val = ""
911
    for cfdev, link_name, uri in kvm_disks:
912
913
914
915
916
917
918
919
920
921
922
923
924
      if cfdev.mode != constants.DISK_RDWR:
        raise errors.HypervisorError("Instance has read-only disks which"
                                     " are not supported by KVM")
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
      boot_val = ""
      if boot_disk:
        dev_opts.extend(["-boot", "c"])
        boot_disk = False
        if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
          boot_val = ",boot=on"

      access_mode = cfdev.params.get(constants.LDP_ACCESS,
                                     constants.DISK_KERNELSPACE)
925
926
      if (uri and access_mode == constants.DISK_USERSPACE):
        drive_uri = uri
927
      else:
928
        drive_uri = link_name
929
930
931
932

      drive_val = "file=%s,format=raw%s%s%s" % \
                  (drive_uri, if_val, boot_val, cache_val)

933
      if device_driver:
934
        # kvm_disks are the 4th entry of runtime file that did not exist in
935
936
937
938
939
940
941
942
943
944
        # the past. That means that cfdev should always have pci slot and
        # _GenerateDeviceKVMId() will not raise a exception.
        kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK, cfdev)
        drive_val += (",id=%s" % kvm_devid)
        drive_val += (",bus=0,unit=%d" % cfdev.pci)
        dev_val = ("%s,drive=%s,id=%s" %
                   (device_driver, kvm_devid, kvm_devid))
        dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
        dev_opts.extend(["-device", dev_val])

945
946
947
948
      dev_opts.extend(["-drive", drive_val])

    return dev_opts

949
  @staticmethod
950
951
  def _CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image, cdrom_boot,
                   needs_boot_flag):
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
    """Extends L{kvm_cmd} with the '-drive' option for a cdrom, and
    optionally the '-boot' option.

    Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide -boot d

    Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide,boot=on

    Example: -drive file=http://hostname.com/cdrom.iso,media=cdrom

    @type kvm_cmd: string
    @param kvm_cmd: KVM command line

    @type cdrom_disk_type:
    @param cdrom_disk_type:

    @type cdrom_image:
    @param cdrom_image:

    @type cdrom_boot:
    @param cdrom_boot:

    @type needs_boot_flag:
    @param needs_boot_flag:

    """
    # Check that the ISO image is accessible
    # See https://bugs.launchpad.net/qemu/+bug/597575
    if utils.IsUrl(cdrom_image) and not _CheckUrl(cdrom_image):
      raise errors.HypervisorError("Cdrom ISO image '%s' is not accessible" %
                                   cdrom_image)

    # set cdrom 'media' and 'format', if needed
    if utils.IsUrl(cdrom_image):
      options = ",media=cdrom"
    else:
      options = ",media=cdrom,format=raw"

    # set cdrom 'if' type
    if cdrom_boot:
      if_val = ",if=" + constants.HT_DISK_IDE
    elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
      if_val = ",if=virtio"
    else:
      if_val = ",if=" + cdrom_disk_type

    # set boot flag, if needed
    boot_val = ""
    if cdrom_boot:
      kvm_cmd.extend(["-boot", "d"])

      # whether this is an older KVM version that requires the 'boot=on' flag
      # on devices
      if needs_boot_flag:
        boot_val = ",boot=on"

    # build '-drive' option
    drive_val = "file=%s%s%s%s" % (cdrom_image, options, if_val, boot_val)
    kvm_cmd.extend(["-drive", drive_val])

1011
1012
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
                          kvmhelp):
1013
    """Generate KVM information to start an instance.
Guido Trotter's avatar
Guido Trotter committed
1014

1015
1016
    @type kvmhelp: string
    @param kvmhelp: output of kvm --help
1017
1018
1019
1020
1021
1022
1023
    @attention: this function must not have any side-effects; for
        example, it must not write to the filesystem, or read values
        from the current system the are expected to differ between
        nodes, since it is only run once at instance startup;
        actions/kvm arguments that can vary between systems should be
        done in L{_ExecuteKVMRuntime}

Guido Trotter's avatar
Guido Trotter committed
1024
    """
1025
1026
    # pylint: disable=R0912,R0914,R0915
    hvp = instance.hvparams
1027
    self.ValidateParameters(hvp)
1028

Michael Hanselmann's avatar
Michael Hanselmann committed
1029
    pidfile = self._InstancePidFile(instance.name)
1030
    kvm = hvp[constants.HV_KVM_PATH]
Guido Trotter's avatar
Guido Trotter committed
1031
    kvm_cmd = [kvm]
1032
    # used just by the vnc server, if enabled
Iustin Pop's avatar
Iustin Pop committed
1033
    kvm_cmd.extend(["-name", instance.name])
1034
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045

    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
    if hvp[constants.HV_CPU_CORES]:
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
    if hvp[constants.HV_CPU_THREADS]:
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
    if hvp[constants.HV_CPU_SOCKETS]:
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])

    kvm_cmd.extend(["-smp", ",".join(smp_list)])

Iustin Pop's avatar
Iustin Pop committed
1046
    kvm_cmd.extend(["-pidfile", pidfile])
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059

    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)

    # As requested by music lovers
    if hvp[constants.HV_SOUNDHW]:
      soundhw = hvp[constants.HV_SOUNDHW]
      # For some reason only few sound devices require a PCI slot
      # while the Audio controller *must* be in slot 3.
      # That's why we bridge this option early in command line
      if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
        _ = _GetFreeSlot(pci_reservations, reserve=True)
      kvm_cmd.extend(["-soundhw", soundhw])

1060
1061
1062
1063
    if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
      # The SCSI controller requires another PCI slot.
      _ = _GetFreeSlot(pci_reservations, reserve=True)

1064
1065
1066
1067
    # Add id to ballon and place to the first available slot (3 or 4)
    addr = _GetFreeSlot(pci_reservations, reserve=True)
    pci_info = ",bus=pci.0,addr=%s" % hex(addr)
    kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
Iustin Pop's avatar
Iustin Pop committed
1068
    kvm_cmd.extend(["-daemonize"])
1069
    if not instance.hvparams[constants.HV_ACPI]:
Iustin Pop's avatar
Iustin Pop committed
1070
      kvm_cmd.extend(["-no-acpi"])
1071
1072
1073
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
        constants.INSTANCE_REBOOT_EXIT:
      kvm_cmd.extend(["-no-reboot"])
Guido Trotter's avatar
Guido Trotter committed
1074

1075
1076
    mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
    if not mversion:
1077
      mversion = self._GetDefaultMachineVersion(kvm)
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
    if self._MACHINE_RE.search(kvmhelp):
      # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
      # extra hypervisor parameters. We should also investigate whether and how
      # shadow_mem should be considered for the resource model.
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
        specprop = ",accel=kvm"
      else:
        specprop = ""
      machinespec = "%s%s" % (mversion, specprop)
      kvm_cmd.extend(["-machine", machinespec])
    else:
      kvm_cmd.extend(["-M", mversion])
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
          self._ENABLE_KVM_RE.search(kvmhelp)):
        kvm_cmd.extend(["-enable-kvm"])
      elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
            self._DISABLE_KVM_RE.search(kvmhelp)):
        kvm_cmd.extend(["-disable-kvm"])
1096

Iustin Pop's avatar
Iustin Pop committed
1097
1098
    kernel_path = hvp[constants.HV_KERNEL_PATH]
    if kernel_path:
1099
      boot_cdrom = boot_floppy = boot_network = False
Iustin Pop's avatar
Iustin Pop committed
1100
1101
1102
1103
    else:
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
Guido Trotter's avatar
Guido Trotter committed
1104

1105
1106
1107
    if startup_paused:
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])

Guido Trotter's avatar
Guido Trotter committed
1108
    if boot_network:
Iustin Pop's avatar
Iustin Pop committed
1109
      kvm_cmd.extend(["-boot", "n"])
1110

1111
    disk_type = hvp[constants.HV_DISK_TYPE]
Guido Trotter's avatar
Guido Trotter committed
1112

1113
    # Now we can specify a different device type for CDROM devices.
1114
1115
1116
1117
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
    if not cdrom_disk_type:
      cdrom_disk_type = disk_type

1118
1119
1120
    cdrom_image1 = hvp[constants.HV_CDROM_IMAGE_PATH]
    if cdrom_image1:
      needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1121
1122
      self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image1, boot_cdrom,
                        needs_boot_flag)
1123
1124
1125

    cdrom_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
    if cdrom_image2:
1126
      self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image2, False, False)
1127
1128
1129

    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
    if floppy_image:
Iustin Pop's avatar
Iustin Pop committed
1130
      options = ",format=raw,media=disk"
1131
      if boot_floppy:
Iustin Pop's avatar
Iustin Pop committed
1132
1133
1134
1135
1136
1137
        kvm_cmd.extend(["-boot", "a"])
        options = "%s,boot=on" % options
      if_val = ",if=floppy"
      options = "%s%s" % (options, if_val)
      drive_val = "file=%s%s" % (floppy_image, options)
      kvm_cmd.extend(["-drive", drive_val])
1138

1139
    if kernel_path:
Iustin Pop's avatar
Iustin Pop committed
1140
      kvm_cmd.extend(["-kernel", kernel_path])
1141
      initrd_path = hvp[constants.HV_INITRD_PATH]
1142
      if initrd_path:
Iustin Pop's avatar
Iustin Pop committed
1143
1144
        kvm_cmd.extend(["-initrd", initrd_path])
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1145
1146
                     hvp[constants.HV_KERNEL_ARGS]]
      if hvp[constants.HV_SERIAL_CONSOLE]:
Guido Trotter's avatar
Guido Trotter committed
1147
1148
        serial_speed = hvp[constants.HV_SERIAL_SPEED]
        root_append.append("console=ttyS0,%s" % serial_speed)
Iustin Pop's avatar
Iustin Pop committed
1149
      kvm_cmd.extend(["-append", " ".join(root_append)])
Guido Trotter's avatar
Guido Trotter committed
1150

1151
1152
    mem_path = hvp[constants.HV_MEM_PATH]
    if mem_path:
1153
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1154

1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
    monitor_dev = ("unix:%s,server,nowait" %
                   self._InstanceMonitor(instance.name))
    kvm_cmd.extend(["-monitor", monitor_dev])
    if hvp[constants.HV_SERIAL_CONSOLE]:
      serial_dev = ("unix:%s,server,nowait" %
                    self._InstanceSerial(instance.name))
      kvm_cmd.extend(["-serial", serial_dev])
    else:
      kvm_cmd.extend(["-serial", "none"])

1165
    mouse_type = hvp[constants.HV_USB_MOUSE]
1166
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1167
1168
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
    spice_ip_version = None
1169

Guido Trotter's avatar
Guido Trotter committed
1170
1171
    kvm_cmd.extend(["-usb"])

1172
    if mouse_type:
Iustin Pop's avatar
Iustin Pop committed
1173
      kvm_cmd.extend(["-usbdevice", mouse_type])
1174
    elif vnc_bind_address:
Iustin Pop's avatar
Iustin Pop committed
1175
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1176