Merge branch 'stable-2.15' into stable-2.16
[ganeti-github.git] / lib / hypervisor / hv_kvm / __init__.py
1 #
2 #
3
4 # Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 """KVM hypervisor
32
33 """
34
35 import errno
36 import os
37 import os.path
38 import re
39 import tempfile
40 import time
41 import logging
42 import pwd
43 import shutil
44 import urllib2
45 from bitarray import bitarray
46 try:
47 import psutil # pylint: disable=F0401
48 except ImportError:
49 psutil = None
50 try:
51 import fdsend # pylint: disable=F0401
52 except ImportError:
53 fdsend = None
54
55 from ganeti import utils
56 from ganeti import constants
57 from ganeti import errors
58 from ganeti import serializer
59 from ganeti import objects
60 from ganeti import uidpool
61 from ganeti import ssconf
62 from ganeti import netutils
63 from ganeti import pathutils
64 from ganeti.hypervisor import hv_base
65 from ganeti.utils import wrapper as utils_wrapper
66
67 from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \
68 MonitorSocket
69 from ganeti.hypervisor.hv_kvm.netdev import OpenTap
70
71
72 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
73 _KVM_START_PAUSED_FLAG = "-S"
74
75 #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND}
76 _SPICE_ADDITIONAL_PARAMS = frozenset([
77 constants.HV_KVM_SPICE_IP_VERSION,
78 constants.HV_KVM_SPICE_PASSWORD_FILE,
79 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
80 constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
81 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
82 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
83 constants.HV_KVM_SPICE_USE_TLS,
84 ])
85
86 # below constants show the format of runtime file
87 # the nics are in second possition, while the disks in 4th (last)
88 # moreover disk entries are stored as a list of in tuples
89 # (L{objects.Disk}, link_name, uri)
90 _KVM_NICS_RUNTIME_INDEX = 1
91 _KVM_DISKS_RUNTIME_INDEX = 3
92 _DEVICE_RUNTIME_INDEX = {
93 constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX,
94 constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX
95 }
96 _FIND_RUNTIME_ENTRY = {
97 constants.HOTPLUG_TARGET_NIC:
98 lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid],
99 constants.HOTPLUG_TARGET_DISK:
100 lambda disk, kvm_disks: [(d, l, u) for (d, l, u) in kvm_disks
101 if d.uuid == disk.uuid]
102 }
103 _RUNTIME_DEVICE = {
104 constants.HOTPLUG_TARGET_NIC: lambda d: d,
105 constants.HOTPLUG_TARGET_DISK: lambda (d, e, _): d
106 }
107 _RUNTIME_ENTRY = {
108 constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
109 constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e[0], e[1])
110 }
111
112 _DEVICE_TYPE = {
113 constants.HOTPLUG_TARGET_NIC: lambda hvp: hvp[constants.HV_NIC_TYPE],
114 constants.HOTPLUG_TARGET_DISK: lambda hvp: hvp[constants.HV_DISK_TYPE],
115 }
116
117 _DEVICE_DRIVER = {
118 constants.HOTPLUG_TARGET_NIC:
119 lambda ht: "virtio-net-pci" if ht == constants.HT_NIC_PARAVIRTUAL else ht,
120 constants.HOTPLUG_TARGET_DISK:
121 lambda ht: "virtio-blk-pci" if ht == constants.HT_DISK_PARAVIRTUAL else ht,
122 }
123
124
125 # NICs and paravirtual disks
126 # show up as devices on the PCI bus (one slot per device).
127 # SCSI disks will be placed on the SCSI bus.
128 _DEVICE_BUS = {
129 constants.HOTPLUG_TARGET_NIC:
130 lambda _: _PCI_BUS,
131 constants.HOTPLUG_TARGET_DISK:
132 lambda ht: _SCSI_BUS if ht in constants.HT_SCSI_DEVICE_TYPES else _PCI_BUS
133 }
134
135 _HOTPLUGGABLE_DEVICE_TYPES = {
136 # All available NIC types except for ne2k_isa
137 constants.HOTPLUG_TARGET_NIC: [
138 constants.HT_NIC_E1000,
139 constants.HT_NIC_I82551,
140 constants.HT_NIC_I8259ER,
141 constants.HT_NIC_I85557B,
142 constants.HT_NIC_NE2K_PCI,
143 constants.HT_NIC_PARAVIRTUAL,
144 constants.HT_NIC_PCNET,
145 constants.HT_NIC_RTL8139,
146 ],
147 constants.HOTPLUG_TARGET_DISK: [
148 constants.HT_DISK_PARAVIRTUAL,
149 constants.HT_DISK_SCSI_BLOCK,
150 constants.HT_DISK_SCSI_GENERIC,
151 constants.HT_DISK_SCSI_HD,
152 constants.HT_DISK_SCSI_CD,
153 ]
154 }
155
156 _PCI_BUS = "pci.0"
157 _SCSI_BUS = "scsi.0"
158
159 _MIGRATION_CAPS_DELIM = ":"
160
161
162 def _with_qmp(fn):
163 """Wrapper used on hotplug related methods"""
164 def wrapper(self, instance, *args, **kwargs):
165 """Create a QmpConnection and run the wrapped method"""
166 if not getattr(self, "qmp", None):
167 filename = self._InstanceQmpMonitor(instance.name)# pylint: disable=W0212
168 self.qmp = QmpConnection(filename)
169 return fn(self, instance, *args, **kwargs)
170 return wrapper
171
172
173 def _GetDriveURI(disk, link, uri):
174 """Helper function to get the drive uri to be used in --drive kvm option
175
176 Invoked during startup and disk hot-add. In latter case and if no userspace
177 access mode is used it will be overriden with /dev/fdset/<fdset-id> (see
178 HotAddDisk() and AddFd() of QmpConnection).
179
180 @type disk: L{objects.Disk}
181 @param disk: A disk configuration object
182 @type link: string
183 @param link: The device link as returned by _SymlinkBlockDev()
184 @type uri: string
185 @param uri: The drive uri as returned by _CalculateDeviceURI()
186
187 @return: The drive uri to use in kvm option
188
189 """
190 access_mode = disk.params.get(constants.LDP_ACCESS,
191 constants.DISK_KERNELSPACE)
192 # If uri is available, use it during startup/hot-add
193 if (uri and access_mode == constants.DISK_USERSPACE):
194 drive_uri = uri
195 # Otherwise use the link previously created
196 else:
197 drive_uri = link
198
199 return drive_uri
200
201
202 def _GenerateDeviceKVMId(dev_type, dev):
203 """Helper function to generate a unique device name used by KVM
204
205 QEMU monitor commands use names to identify devices. Since the UUID
206 is too long for a device ID (36 chars vs. 30), we choose to use
207 only the part until the third '-' with a disk/nic prefix.
208 For example if a disk has UUID '932df160-7a22-4067-a566-7e0ca8386133'
209 the resulting device ID would be 'disk-932df160-7a22-4067'.
210
211 @type dev_type: string
212 @param dev_type: device type of param dev (HOTPLUG_TARGET_DISK|NIC)
213 @type dev: L{objects.Disk} or L{objects.NIC}
214 @param dev: the device object for which we generate a kvm name
215
216 """
217 return "%s-%s" % (dev_type.lower(), dev.uuid.rsplit("-", 2)[0])
218
219
220 def _GenerateDeviceHVInfoStr(hvinfo):
221 """Construct the -device option string for hvinfo dict
222
223 PV disk: virtio-blk-pci,id=disk-1234,bus=pci.0,addr=0x9
224 PV NIC: virtio-net-pci,id=nic-1234,bus=pci.0,addr=0x9
225 SG disk: scsi-generic,id=disk-1234,bus=scsi.0,channel=0,scsi-id=1,lun=0
226
227 @type hvinfo: dict
228 @param hvinfo: dictionary created by _GenerateDeviceHVInfo()
229
230 @rtype: string
231 @return: The constructed string to be passed along with a -device option
232
233 """
234
235 # work on a copy
236 d = dict(hvinfo)
237 hvinfo_str = d.pop("driver")
238 for k, v in d.items():
239 hvinfo_str += ",%s=%s" % (k, v)
240
241 return hvinfo_str
242
243
244 def _GenerateDeviceHVInfo(dev_type, kvm_devid, hv_dev_type, bus_slots):
245 """Helper function to generate hvinfo of a device (disk, NIC)
246
247 hvinfo will hold all necessary info for generating the -device QEMU option.
248 We have two main buses: a PCI bus and a SCSI bus (created by a SCSI
249 controller on the PCI bus).
250
251 In case of PCI devices we add them on a free PCI slot (addr) on the first PCI
252 bus (pci.0), and in case of SCSI devices we decide to put each disk on a
253 different SCSI target (scsi-id) on the first SCSI bus (scsi.0).
254
255 @type dev_type: string
256 @param dev_type: either HOTPLUG_TARGET_DISK or HOTPLUG_TARGET_NIC
257 @type kvm_devid: string
258 @param kvm_devid: the id of the device
259 @type hv_dev_type: string
260 @param hv_dev_type: either disk_type or nic_type hvparam
261 @type bus_slots: dict
262 @param bus_slots: the current slots of the first PCI and SCSI buses
263
264 @rtype: dict
265 @return: dict including all necessary info (driver, id, bus and bus location)
266 for generating a -device QEMU option for either a disk or a NIC
267
268 """
269 driver = _DEVICE_DRIVER[dev_type](hv_dev_type)
270 bus = _DEVICE_BUS[dev_type](hv_dev_type)
271 slots = bus_slots[bus]
272 slot = utils.GetFreeSlot(slots, reserve=True)
273
274 hvinfo = {
275 "driver": driver,
276 "id": kvm_devid,
277 "bus": bus,
278 }
279
280 if bus == _PCI_BUS:
281 hvinfo.update({
282 "addr": hex(slot),
283 })
284 elif bus == _SCSI_BUS:
285 hvinfo.update({
286 "channel": 0,
287 "scsi-id": slot,
288 "lun": 0,
289 })
290
291 return hvinfo
292
293
294 def _GetExistingDeviceInfo(dev_type, device, runtime):
295 """Helper function to get an existing device inside the runtime file
296
297 Used when an instance is running. Load kvm runtime file and search
298 for a device based on its type and uuid.
299
300 @type dev_type: sting
301 @param dev_type: device type of param dev
302 @type device: L{objects.Disk} or L{objects.NIC}
303 @param device: the device object for which we generate a kvm name
304 @type runtime: tuple (cmd, nics, hvparams, disks)
305 @param runtime: the runtime data to search for the device
306 @raise errors.HotplugError: in case the requested device does not
307 exist (e.g. device has been added without --hotplug option)
308
309 """
310 index = _DEVICE_RUNTIME_INDEX[dev_type]
311 found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
312 if not found:
313 raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
314 (dev_type, device.uuid))
315
316 return found[0]
317
318
319 def _UpgradeSerializedRuntime(serialized_runtime):
320 """Upgrade runtime data
321
322 Remove any deprecated fields or change the format of the data.
323 The runtime files are not upgraded when Ganeti is upgraded, so the required
324 modification have to be performed here.
325
326 @type serialized_runtime: string
327 @param serialized_runtime: raw text data read from actual runtime file
328 @return: (cmd, nic dicts, hvparams, bdev dicts)
329 @rtype: tuple
330
331 """
332 loaded_runtime = serializer.Load(serialized_runtime)
333 kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
334 if len(loaded_runtime) >= 4:
335 serialized_disks = loaded_runtime[3]
336 else:
337 serialized_disks = []
338
339 def update_hvinfo(dev, dev_type):
340 """ Remove deprecated pci slot and substitute it with hvinfo """
341 if "hvinfo" not in dev:
342 dev["hvinfo"] = {}
343 uuid = dev["uuid"]
344 # Ganeti used to save the PCI slot of paravirtual devices
345 # (virtio-blk-pci, virtio-net-pci) in runtime files during
346 # _GenerateKVMRuntime() and HotAddDevice().
347 # In this case we had a -device QEMU option in the command line with id,
348 # drive|netdev, bus, and addr params. All other devices did not have an
349 # id nor placed explicitly on a bus.
350 # hot- prefix is removed in 2.16. Here we add it explicitly to
351 # handle old instances in the cluster properly.
352 if "pci" in dev:
353 # This is practically the old _GenerateDeviceKVMId()
354 dev["hvinfo"]["id"] = "hot%s-%s-%s-%s" % (dev_type.lower(),
355 uuid.split("-")[0],
356 "pci",
357 dev["pci"])
358 dev["hvinfo"]["addr"] = hex(dev["pci"])
359 dev["hvinfo"]["bus"] = _PCI_BUS
360 del dev["pci"]
361
362 for nic in serialized_nics:
363 # Add a dummy uuid slot if an pre-2.8 NIC is found
364 if "uuid" not in nic:
365 nic["uuid"] = utils.NewUUID()
366 update_hvinfo(nic, constants.HOTPLUG_TARGET_NIC)
367
368 for disk_entry in serialized_disks:
369 # We have a (Disk, link, uri) tuple
370 update_hvinfo(disk_entry[0], constants.HOTPLUG_TARGET_DISK)
371
372 return kvm_cmd, serialized_nics, hvparams, serialized_disks
373
374
375 def _AnalyzeSerializedRuntime(serialized_runtime):
376 """Return runtime entries for a serialized runtime file
377
378 @type serialized_runtime: string
379 @param serialized_runtime: raw text data read from actual runtime file
380 @return: (cmd, nics, hvparams, bdevs)
381 @rtype: tuple
382
383 """
384 kvm_cmd, serialized_nics, hvparams, serialized_disks = \
385 _UpgradeSerializedRuntime(serialized_runtime)
386 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
387 kvm_disks = [(objects.Disk.FromDict(sdisk), link, uri)
388 for sdisk, link, uri in serialized_disks]
389
390 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
391
392
393 class HeadRequest(urllib2.Request):
394 def get_method(self):
395 return "HEAD"
396
397
398 def _CheckUrl(url):
399 """Check if a given URL exists on the server
400
401 """
402 try:
403 urllib2.urlopen(HeadRequest(url))
404 return True
405 except urllib2.URLError:
406 return False
407
408
409 class KVMHypervisor(hv_base.BaseHypervisor):
410 """KVM hypervisor interface
411
412 """
413 CAN_MIGRATE = True
414
415 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
416 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
417 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
418 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
419 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
420 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
421 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
422 # KVM instances with chroot enabled are started in empty chroot directories.
423 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
424 # After an instance is stopped, its chroot directory is removed.
425 # If the chroot directory is not empty, it can't be removed.
426 # A non-empty chroot directory indicates a possible security incident.
427 # To support forensics, the non-empty chroot directory is quarantined in
428 # a separate directory, called 'chroot-quarantine'.
429 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
430 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
431 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
432
433 PARAMETERS = {
434 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
435 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
436 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
437 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
438 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
439 constants.HV_ACPI: hv_base.NO_CHECK,
440 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
441 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
442 constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK, # will be checked later
443 constants.HV_VNC_TLS: hv_base.NO_CHECK,
444 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
445 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
446 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
447 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
448 constants.HV_KVM_SPICE_IP_VERSION:
449 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
450 x in constants.VALID_IP_VERSIONS),
451 "The SPICE IP version should be 4 or 6",
452 None, None),
453 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
454 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
455 hv_base.ParamInSet(
456 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
457 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
458 hv_base.ParamInSet(
459 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
460 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
461 hv_base.ParamInSet(
462 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
463 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
464 hv_base.ParamInSet(
465 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
466 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
467 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
468 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
469 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
470 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
471 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
472 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
473 constants.HV_BOOT_ORDER:
474 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
475 constants.HV_NIC_TYPE:
476 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
477 constants.HV_DISK_TYPE:
478 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
479 constants.HV_KVM_SCSI_CONTROLLER_TYPE:
480 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SCSI_CONTROLLER_TYPES),
481 constants.HV_KVM_CDROM_DISK_TYPE:
482 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
483 constants.HV_USB_MOUSE:
484 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
485 constants.HV_KEYMAP: hv_base.NO_CHECK,
486 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
487 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
488 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
489 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
490 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
491 constants.HV_DISK_CACHE:
492 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
493 constants.HV_KVM_DISK_AIO:
494 hv_base.ParamInSet(False, constants.HT_KVM_VALID_AIO_TYPES),
495 constants.HV_SECURITY_MODEL:
496 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
497 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
498 constants.HV_KVM_FLAG:
499 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
500 constants.HV_VHOST_NET: hv_base.NO_CHECK,
501 constants.HV_VIRTIO_NET_QUEUES: hv_base.OPT_VIRTIO_NET_QUEUES_CHECK,
502 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
503 constants.HV_KVM_USER_SHUTDOWN: hv_base.NO_CHECK,
504 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
505 constants.HV_REBOOT_BEHAVIOR:
506 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
507 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
508 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
509 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
510 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
511 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
512 constants.HV_SOUNDHW: hv_base.NO_CHECK,
513 constants.HV_USB_DEVICES: hv_base.NO_CHECK,
514 constants.HV_VGA: hv_base.NO_CHECK,
515 constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
516 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
517 constants.HV_KVM_MIGRATION_CAPS: hv_base.NO_CHECK,
518 constants.HV_KVM_PCI_RESERVATIONS:
519 (False, lambda x: (x >= 0 and x <= constants.QEMU_PCI_SLOTS),
520 "The number of PCI slots managed by QEMU (max: %s)" %
521 constants.QEMU_PCI_SLOTS,
522 None, None),
523 constants.HV_VNET_HDR: hv_base.NO_CHECK,
524 }
525
526 _VIRTIO = "virtio"
527 _VIRTIO_NET_PCI = "virtio-net-pci"
528 _VIRTIO_BLK_PCI = "virtio-blk-pci"
529
530 _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)",
531 re.M | re.I)
532 _MIGRATION_PROGRESS_RE = \
533 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
534 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
535 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
536
537 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
538 _MIGRATION_INFO_RETRY_DELAY = 2
539
540 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
541
542 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
543 _CPU_INFO_CMD = "info cpus"
544 _CONT_CMD = "cont"
545
546 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
547 _CHECK_MACHINE_VERSION_RE = \
548 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
549
550 _QMP_RE = re.compile(r"^-qmp\s", re.M)
551 _SPICE_RE = re.compile(r"^-spice\s", re.M)
552 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
553 _VIRTIO_NET_QUEUES_RE = re.compile(r"^-net\s.*,fds=x:y:...:z", re.M)
554 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
555 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
556 _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
557 _DISPLAY_RE = re.compile(r"^-display\s", re.M)
558 _MACHINE_RE = re.compile(r"^-machine\s", re.M)
559 _DEVICE_DRIVER_SUPPORTED = \
560 staticmethod(lambda drv, devlist:
561 re.compile(r"^name \"%s\"" % drv, re.M).search(devlist))
562 # match -drive.*boot=on|off on different lines, but in between accept only
563 # dashes not preceeded by a new line (which would mean another option
564 # different than -drive is starting)
565 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
566 _UUID_RE = re.compile(r"^-uuid\s", re.M)
567
568 _INFO_VERSION_RE = \
569 re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
570 _INFO_VERSION_CMD = "info version"
571
572 # Slot 0 for Host bridge, Slot 1 for ISA bridge, Slot 2 for VGA controller
573 # and the rest up to slot 11 will be used by QEMU implicitly.
574 # Ganeti will add disks and NICs from slot 12 onwards.
575 # NOTE: This maps to the default PCI bus created by pc machine type
576 # by default (pci.0). The q35 creates a PCIe bus that is not hotpluggable
577 # and should be handled differently (pcie.0).
578 # NOTE: This bitarray here is defined for more fine-grained control.
579 # Currently the number of slots is QEMU_PCI_SLOTS and the reserved
580 # ones are the first QEMU_DEFAULT_PCI_RESERVATIONS.
581 # If the above constants change without updating _DEFAULT_PCI_RESERVATIONS
582 # properly, TestGenerateDeviceHVInfo() will probably break.
583 _DEFAULT_PCI_RESERVATIONS = "11111111111100000000000000000000"
584 # The SCSI bus is created on demand or automatically and is empty.
585 # For simplicity we decide to use a different target (scsi-id)
586 # for each SCSI disk. Here we support 16 SCSI disks which is
587 # actually the current hard limit (constants.MAX_DISKS).
588 # NOTE: Max device counts depend on the SCSI controller type;
589 # Just for the record, lsi supports up to 7, megasas 64,
590 # and virtio-scsi-pci 255.
591 _DEFAULT_SCSI_RESERVATIONS = "0000000000000000"
592
593 ANCILLARY_FILES = [
594 _KVM_NETWORK_SCRIPT,
595 ]
596 ANCILLARY_FILES_OPT = [
597 _KVM_NETWORK_SCRIPT,
598 ]
599
600 # Supported kvm options to get output from
601 _KVMOPT_HELP = "help"
602 _KVMOPT_MLIST = "mlist"
603 _KVMOPT_DEVICELIST = "devicelist"
604
605 # Command to execute to get the output from kvm, and whether to
606 # accept the output even on failure.
607 _KVMOPTS_CMDS = {
608 _KVMOPT_HELP: (["--help"], False),
609 _KVMOPT_MLIST: (["-M", "?"], False),
610 _KVMOPT_DEVICELIST: (["-device", "?"], True),
611 }
612
613 def __init__(self):
614 hv_base.BaseHypervisor.__init__(self)
615 # Let's make sure the directories we need exist, even if the RUN_DIR lives
616 # in a tmpfs filesystem or has been otherwise wiped out.
617 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
618 utils.EnsureDirs(dirs)
619 self.qmp = None
620
621 @staticmethod
622 def VersionsSafeForMigration(src, target):
623 """Predict if migration is safe between those versions
624
625 """
626 # Actually, it is not that easy. However, with the kvm machine_version
627 # feature, migration suceeds in most cases. So we try not to block
628 # legitimate migrations.
629 return True
630
631 @classmethod
632 def _InstancePidFile(cls, instance_name):
633 """Returns the instance pidfile.
634
635 """
636 return utils.PathJoin(cls._PIDS_DIR, instance_name)
637
638 @classmethod
639 def _InstanceUidFile(cls, instance_name):
640 """Returns the instance uidfile.
641
642 """
643 return utils.PathJoin(cls._UIDS_DIR, instance_name)
644
645 @classmethod
646 def _InstancePidInfo(cls, pid):
647 """Check pid file for instance information.
648
649 Check that a pid file is associated with an instance, and retrieve
650 information from its command line.
651
652 @type pid: string or int
653 @param pid: process id of the instance to check
654 @rtype: tuple
655 @return: (instance_name, memory, vcpus)
656 @raise errors.HypervisorError: when an instance cannot be found
657
658 """
659 alive = utils.IsProcessAlive(pid)
660 if not alive:
661 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
662
663 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
664 try:
665 cmdline = utils.ReadFile(cmdline_file)
666 except EnvironmentError, err:
667 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
668 (pid, err))
669
670 instance = None
671 memory = 0
672 vcpus = 0
673
674 arg_list = cmdline.split("\x00")
675 while arg_list:
676 arg = arg_list.pop(0)
677 if arg == "-name":
678 instance = arg_list.pop(0)
679 elif arg == "-m":
680 memory = int(arg_list.pop(0))
681 elif arg == "-smp":
682 vcpus = int(arg_list.pop(0).split(",")[0])
683
684 if instance is None:
685 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
686 " instance" % pid)
687
688 return (instance, memory, vcpus)
689
690 @classmethod
691 def _InstancePidAlive(cls, instance_name):
692 """Returns the instance pidfile, pid, and liveness.
693
694 @type instance_name: string
695 @param instance_name: instance name
696 @rtype: tuple
697 @return: (pid file name, pid, liveness)
698
699 """
700 pidfile = cls._InstancePidFile(instance_name)
701 pid = utils.ReadPidFile(pidfile)
702
703 alive = False
704 try:
705 cmd_instance = cls._InstancePidInfo(pid)[0]
706 alive = (cmd_instance == instance_name)
707 except errors.HypervisorError:
708 pass
709
710 return (pidfile, pid, alive)
711
712 @classmethod
713 def _CheckDown(cls, instance_name):
714 """Raises an error unless the given instance is down.
715
716 """
717 alive = cls._InstancePidAlive(instance_name)[2]
718 if alive:
719 raise errors.HypervisorError("Failed to start instance %s: %s" %
720 (instance_name, "already running"))
721
722 @classmethod
723 def _InstanceMonitor(cls, instance_name):
724 """Returns the instance monitor socket name
725
726 """
727 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
728
729 @classmethod
730 def _InstanceSerial(cls, instance_name):
731 """Returns the instance serial socket name
732
733 """
734 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
735
736 @classmethod
737 def _InstanceQmpMonitor(cls, instance_name):
738 """Returns the instance serial QMP socket name
739
740 """
741 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
742
743 @classmethod
744 def _InstanceKvmdMonitor(cls, instance_name):
745 """Returns the instance kvm daemon socket name
746
747 """
748 return utils.PathJoin(cls._CTRL_DIR, "%s.kvmd" % instance_name)
749
750 @classmethod
751 def _InstanceShutdownMonitor(cls, instance_name):
752 """Returns the instance QMP output filename
753
754 """
755 return utils.PathJoin(cls._CTRL_DIR, "%s.shutdown" % instance_name)
756
757 @staticmethod
758 def _SocatUnixConsoleParams():
759 """Returns the correct parameters for socat
760
761 If we have a new-enough socat we can use raw mode with an escape character.
762
763 """
764 if constants.SOCAT_USE_ESCAPE:
765 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
766 else:
767 return "echo=0,icanon=0"
768
769 @classmethod
770 def _InstanceKVMRuntime(cls, instance_name):
771 """Returns the instance KVM runtime filename
772
773 """
774 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
775
776 @classmethod
777 def _InstanceChrootDir(cls, instance_name):
778 """Returns the name of the KVM chroot dir of the instance
779
780 """
781 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
782
783 @classmethod
784 def _InstanceNICDir(cls, instance_name):
785 """Returns the name of the directory holding the tap device files for a
786 given instance.
787
788 """
789 return utils.PathJoin(cls._NICS_DIR, instance_name)
790
791 @classmethod
792 def _InstanceNICFile(cls, instance_name, seq):
793 """Returns the name of the file containing the tap device for a given NIC
794
795 """
796 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
797
798 @classmethod
799 def _InstanceKeymapFile(cls, instance_name):
800 """Returns the name of the file containing the keymap for a given instance
801
802 """
803 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
804
805 @classmethod
806 def _TryReadUidFile(cls, uid_file):
807 """Try to read a uid file
808
809 """
810 if os.path.exists(uid_file):
811 try:
812 uid = int(utils.ReadOneLineFile(uid_file))
813 return uid
814 except EnvironmentError:
815 logging.warning("Can't read uid file", exc_info=True)
816 except (TypeError, ValueError):
817 logging.warning("Can't parse uid file contents", exc_info=True)
818 return None
819
820 @classmethod
821 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
822 """Removes an instance's rutime sockets/files/dirs.
823
824 """
825 utils.RemoveFile(pidfile)
826 utils.RemoveFile(cls._InstanceMonitor(instance_name))
827 utils.RemoveFile(cls._InstanceSerial(instance_name))
828 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
829 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
830 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
831 uid_file = cls._InstanceUidFile(instance_name)
832 uid = cls._TryReadUidFile(uid_file)
833 utils.RemoveFile(uid_file)
834 if uid is not None:
835 uidpool.ReleaseUid(uid)
836 try:
837 shutil.rmtree(cls._InstanceNICDir(instance_name))
838 except OSError, err:
839 if err.errno != errno.ENOENT:
840 raise
841 try:
842 chroot_dir = cls._InstanceChrootDir(instance_name)
843 utils.RemoveDir(chroot_dir)
844 except OSError, err:
845 if err.errno == errno.ENOTEMPTY:
846 # The chroot directory is expected to be empty, but it isn't.
847 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
848 prefix="%s-%s-" %
849 (instance_name,
850 utils.TimestampForFilename()))
851 logging.warning("The chroot directory of instance %s can not be"
852 " removed as it is not empty. Moving it to the"
853 " quarantine instead. Please investigate the"
854 " contents (%s) and clean up manually",
855 instance_name, new_chroot_dir)
856 utils.RenameFile(chroot_dir, new_chroot_dir)
857 else:
858 raise
859
860 @staticmethod
861 def _ConfigureNIC(instance, seq, nic, tap):
862 """Run the network configuration script for a specified NIC
863
864 See L{hv_base.ConfigureNIC}.
865
866 @param instance: instance we're acting on
867 @type instance: instance object
868 @param seq: nic sequence number
869 @type seq: int
870 @param nic: nic we're acting on
871 @type nic: nic object
872 @param tap: the host's tap interface this NIC corresponds to
873 @type tap: str
874
875 """
876 hv_base.ConfigureNIC([pathutils.KVM_IFUP, tap], instance, seq, nic, tap)
877
878 @classmethod
879 def _SetProcessAffinity(cls, process_id, cpus):
880 """Sets the affinity of a process to the given CPUs.
881
882 @type process_id: int
883 @type cpus: list of int
884 @param cpus: The list of CPUs the process ID may use.
885
886 """
887 if psutil is None:
888 raise errors.HypervisorError("psutil Python package not"
889 " found; cannot use CPU pinning under KVM")
890
891 target_process = psutil.Process(process_id)
892 if cpus == constants.CPU_PINNING_OFF:
893 target_process.set_cpu_affinity(range(psutil.cpu_count()))
894 else:
895 target_process.set_cpu_affinity(cpus)
896
897 @classmethod
898 def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
899 """Change CPU affinity for running VM according to given CPU mask.
900
901 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
902 @type cpu_mask: string
903 @param process_id: process ID of KVM process. Used to pin entire VM
904 to physical CPUs.
905 @type process_id: int
906 @param thread_dict: map of virtual CPUs to KVM thread IDs
907 @type thread_dict: dict int:int
908
909 """
910 # Convert the string CPU mask to a list of list of int's
911 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
912
913 if len(cpu_list) == 1:
914 all_cpu_mapping = cpu_list[0]
915 if all_cpu_mapping == constants.CPU_PINNING_OFF:
916 # If CPU pinning has 1 entry that's "all", then do nothing
917 pass
918 else:
919 # If CPU pinning has one non-all entry, map the entire VM to
920 # one set of physical CPUs
921 cls._SetProcessAffinity(process_id, all_cpu_mapping)
922 else:
923 # The number of vCPUs mapped should match the number of vCPUs
924 # reported by KVM. This was already verified earlier, so
925 # here only as a sanity check.
926 assert len(thread_dict) == len(cpu_list)
927
928 # For each vCPU, map it to the proper list of physical CPUs
929 for i, vcpu in enumerate(cpu_list):
930 cls._SetProcessAffinity(thread_dict[i], vcpu)
931
932 def _GetVcpuThreadIds(self, instance_name):
933 """Get a mapping of vCPU no. to thread IDs for the instance
934
935 @type instance_name: string
936 @param instance_name: instance in question
937 @rtype: dictionary of int:int
938 @return: a dictionary mapping vCPU numbers to thread IDs
939
940 """
941 result = {}
942 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
943 for line in output.stdout.splitlines():
944 match = self._CPU_INFO_RE.search(line)
945 if not match:
946 continue
947 grp = map(int, match.groups())
948 result[grp[0]] = grp[1]
949
950 return result
951
952 def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
953 """Complete CPU pinning.
954
955 @type instance_name: string
956 @param instance_name: name of instance
957 @type cpu_mask: string
958 @param cpu_mask: CPU pinning mask as entered by user
959
960 """
961 # Get KVM process ID, to be used if need to pin entire VM
962 _, pid, _ = self._InstancePidAlive(instance_name)
963 # Get vCPU thread IDs, to be used if need to pin vCPUs separately
964 thread_dict = self._GetVcpuThreadIds(instance_name)
965 # Run CPU pinning, based on configured mask
966 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
967
968 def ListInstances(self, hvparams=None):
969 """Get the list of running instances.
970
971 We can do this by listing our live instances directory and
972 checking whether the associated kvm process is still alive.
973
974 """
975 result = []
976 for name in os.listdir(self._PIDS_DIR):
977 if self._InstancePidAlive(name)[2]:
978 result.append(name)
979 return result
980
981 @classmethod
982 def _IsUserShutdown(cls, instance_name):
983 return os.path.exists(cls._InstanceShutdownMonitor(instance_name))
984
985 @classmethod
986 def _ClearUserShutdown(cls, instance_name):
987 utils.RemoveFile(cls._InstanceShutdownMonitor(instance_name))
988
989 def GetInstanceInfo(self, instance_name, hvparams=None):
990 """Get instance properties.
991
992 @type instance_name: string
993 @param instance_name: the instance name
994 @type hvparams: dict of strings
995 @param hvparams: hypervisor parameters to be used with this instance
996 @rtype: tuple of strings
997 @return: (name, id, memory, vcpus, stat, times)
998
999 """
1000 _, pid, alive = self._InstancePidAlive(instance_name)
1001 if not alive:
1002 if self._IsUserShutdown(instance_name):
1003 return (instance_name, -1, 0, 0, hv_base.HvInstanceState.SHUTDOWN, 0)
1004 else:
1005 return None
1006
1007 _, memory, vcpus = self._InstancePidInfo(pid)
1008 istat = hv_base.HvInstanceState.RUNNING
1009 times = 0
1010
1011 try:
1012 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1013 qmp.connect()
1014 vcpus = len(qmp.Execute("query-cpus"))
1015 # Will fail if ballooning is not enabled, but we can then just resort to
1016 # the value above.
1017 mem_bytes = qmp.Execute("query-balloon")[qmp.ACTUAL_KEY]
1018 memory = mem_bytes / 1048576
1019 except errors.HypervisorError:
1020 pass
1021
1022 return (instance_name, pid, memory, vcpus, istat, times)
1023
1024 def GetAllInstancesInfo(self, hvparams=None):
1025 """Get properties of all instances.
1026
1027 @type hvparams: dict of strings
1028 @param hvparams: hypervisor parameters
1029 @return: list of tuples (name, id, memory, vcpus, stat, times)
1030
1031 """
1032 data = []
1033 for name in os.listdir(self._PIDS_DIR):
1034 try:
1035 info = self.GetInstanceInfo(name)
1036 except errors.HypervisorError:
1037 # Ignore exceptions due to instances being shut down
1038 continue
1039 if info:
1040 data.append(info)
1041 return data
1042
1043 def _GenerateKVMBlockDevicesOptions(self, up_hvp, kvm_disks,
1044 kvmhelp, devlist):
1045 """Generate KVM options regarding instance's block devices.
1046
1047 @type up_hvp: dict
1048 @param up_hvp: the instance's runtime hypervisor parameters
1049 @type kvm_disks: list of tuples
1050 @param kvm_disks: list of tuples [(disk, link_name, uri)..]
1051 @type kvmhelp: string
1052 @param kvmhelp: output of kvm --help
1053 @type devlist: string
1054 @param devlist: output of kvm -device ?
1055 @rtype: list
1056 @return: list of command line options eventually used by kvm executable
1057
1058 """
1059 kernel_path = up_hvp[constants.HV_KERNEL_PATH]
1060 if kernel_path:
1061 boot_disk = False
1062 else:
1063 boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1064
1065 # whether this is an older KVM version that uses the boot=on flag
1066 # on devices
1067 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1068
1069 dev_opts = []
1070 disk_type = up_hvp[constants.HV_DISK_TYPE]
1071 # paravirtual implies either '-device virtio-blk-pci... -drive if=none...'
1072 # for new QEMU versions or '-drive if=virtio' for old QEMU versions
1073 if disk_type == constants.HT_DISK_PARAVIRTUAL:
1074 driver = self._VIRTIO_BLK_PCI
1075 iface = self._VIRTIO
1076 else:
1077 driver = iface = disk_type
1078
1079 # Check if a specific driver is supported by QEMU device model.
1080 if self._DEVICE_DRIVER_SUPPORTED(driver, devlist):
1081 if_val = ",if=none" # for the -drive option
1082 device_driver = driver # for the -device option
1083 else:
1084 if_val = ",if=%s" % iface # for the -drive option
1085 device_driver = None # without -device option
1086
1087 # AIO mode
1088 aio_mode = up_hvp[constants.HV_KVM_DISK_AIO]
1089 if aio_mode == constants.HT_KVM_AIO_NATIVE:
1090 aio_val = ",aio=%s" % aio_mode
1091 else:
1092 aio_val = ""
1093 # Cache mode
1094 disk_cache = up_hvp[constants.HV_DISK_CACHE]
1095 for cfdev, link_name, uri in kvm_disks:
1096 if cfdev.dev_type in constants.DTS_EXT_MIRROR:
1097 if disk_cache != "none":
1098 # TODO: make this a hard error, instead of a silent overwrite
1099 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1100 " to prevent shared storage corruption on migration",
1101 disk_cache)
1102 cache_val = ",cache=none"
1103 elif disk_cache != constants.HT_CACHE_DEFAULT:
1104 cache_val = ",cache=%s" % disk_cache
1105 else:
1106 cache_val = ""
1107 if cfdev.mode != constants.DISK_RDWR:
1108 raise errors.HypervisorError("Instance has read-only disks which"
1109 " are not supported by KVM")
1110 # TODO: handle FD_LOOP and FD_BLKTAP (?)
1111 boot_val = ""
1112 if boot_disk:
1113 dev_opts.extend(["-boot", "c"])
1114 boot_disk = False
1115 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1116 boot_val = ",boot=on"
1117
1118 drive_uri = _GetDriveURI(cfdev, link_name, uri)
1119
1120 drive_val = "file=%s,format=raw%s%s%s%s" % \
1121 (drive_uri, if_val, boot_val, cache_val, aio_val)
1122
1123 # virtio-blk-pci case
1124 if device_driver is not None:
1125 # hvinfo will exist for paravirtual devices either due to
1126 # _UpgradeSerializedRuntime() for old instances or due to
1127 # _GenerateKVMRuntime() for new instances.
1128 kvm_devid = cfdev.hvinfo["id"]
1129 drive_val += ",id=%s" % kvm_devid
1130 # Add driver, id, bus, and addr or channel, scsi-id, lun if any.
1131 dev_val = _GenerateDeviceHVInfoStr(cfdev.hvinfo)
1132 dev_val += ",drive=%s" % kvm_devid
1133 dev_opts.extend(["-device", dev_val])
1134
1135 dev_opts.extend(["-drive", drive_val])
1136
1137 return dev_opts
1138
1139 @staticmethod
1140 def _CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image, cdrom_boot,
1141 needs_boot_flag):
1142 """Extends L{kvm_cmd} with the '-drive' option for a cdrom, and
1143 optionally the '-boot' option.
1144
1145 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide -boot d
1146
1147 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide,boot=on
1148
1149 Example: -drive file=http://hostname.com/cdrom.iso,media=cdrom
1150
1151 @type kvm_cmd: string
1152 @param kvm_cmd: KVM command line
1153
1154 @type cdrom_disk_type:
1155 @param cdrom_disk_type:
1156
1157 @type cdrom_image:
1158 @param cdrom_image:
1159
1160 @type cdrom_boot:
1161 @param cdrom_boot:
1162
1163 @type needs_boot_flag:
1164 @param needs_boot_flag:
1165
1166 """
1167 # Check that the ISO image is accessible
1168 # See https://bugs.launchpad.net/qemu/+bug/597575
1169 if utils.IsUrl(cdrom_image) and not _CheckUrl(cdrom_image):
1170 raise errors.HypervisorError("Cdrom ISO image '%s' is not accessible" %
1171 cdrom_image)
1172
1173 # set cdrom 'media' and 'format', if needed
1174 if utils.IsUrl(cdrom_image):
1175 options = ",media=cdrom"
1176 else:
1177 options = ",media=cdrom,format=raw"
1178
1179 # set cdrom 'if' type
1180 if cdrom_boot:
1181 if_val = ",if=" + constants.HT_DISK_IDE
1182 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1183 if_val = ",if=virtio"
1184 else:
1185 if_val = ",if=" + cdrom_disk_type
1186
1187 # set boot flag, if needed
1188 boot_val = ""
1189 if cdrom_boot:
1190 kvm_cmd.extend(["-boot", "d"])
1191
1192 # whether this is an older KVM version that requires the 'boot=on' flag
1193 # on devices
1194 if needs_boot_flag:
1195 boot_val = ",boot=on"
1196
1197 # build '-drive' option
1198 drive_val = "file=%s%s%s%s" % (cdrom_image, options, if_val, boot_val)
1199 kvm_cmd.extend(["-drive", drive_val])
1200
1201 def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1202 kvmhelp):
1203 """Generate KVM information to start an instance.
1204
1205 @type kvmhelp: string
1206 @param kvmhelp: output of kvm --help
1207 @attention: this function must not have any side-effects; for
1208 example, it must not write to the filesystem, or read values
1209 from the current system the are expected to differ between
1210 nodes, since it is only run once at instance startup;
1211 actions/kvm arguments that can vary between systems should be
1212 done in L{_ExecuteKVMRuntime}
1213
1214 """
1215 # pylint: disable=R0912,R0914,R0915
1216 hvp = instance.hvparams
1217 self.ValidateParameters(hvp)
1218
1219 pidfile = self._InstancePidFile(instance.name)
1220 kvm = hvp[constants.HV_KVM_PATH]
1221 kvm_cmd = [kvm]
1222 # used just by the vnc server, if enabled
1223 kvm_cmd.extend(["-name", instance.name])
1224 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1225
1226 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1227 if hvp[constants.HV_CPU_CORES]:
1228 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1229 if hvp[constants.HV_CPU_THREADS]:
1230 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1231 if hvp[constants.HV_CPU_SOCKETS]:
1232 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1233
1234 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1235
1236 kvm_cmd.extend(["-pidfile", pidfile])
1237
1238 bus_slots = self._GetBusSlots(hvp)
1239
1240 # As requested by music lovers
1241 if hvp[constants.HV_SOUNDHW]:
1242 soundhw = hvp[constants.HV_SOUNDHW]
1243 kvm_cmd.extend(["-soundhw", soundhw])
1244
1245 if hvp[constants.HV_DISK_TYPE] in constants.HT_SCSI_DEVICE_TYPES:
1246 # In case a SCSI disk is given, QEMU adds a SCSI contorller
1247 # (LSI Logic / Symbios Logic 53c895a) implicitly.
1248 # Here, we add the controller explicitly with the default id.
1249 kvm_cmd.extend([
1250 "-device",
1251 "%s,id=scsi" % hvp[constants.HV_KVM_SCSI_CONTROLLER_TYPE]
1252 ])
1253
1254 kvm_cmd.extend(["-balloon", "virtio"])
1255 kvm_cmd.extend(["-daemonize"])
1256 if not instance.hvparams[constants.HV_ACPI]:
1257 kvm_cmd.extend(["-no-acpi"])
1258 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1259 constants.INSTANCE_REBOOT_EXIT:
1260 kvm_cmd.extend(["-no-reboot"])
1261
1262 mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1263 if not mversion:
1264 mversion = self._GetDefaultMachineVersion(kvm)
1265 if self._MACHINE_RE.search(kvmhelp):
1266 # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
1267 # extra hypervisor parameters. We should also investigate whether and how
1268 # shadow_mem should be considered for the resource model.
1269 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1270 specprop = ",accel=kvm"
1271 else:
1272 specprop = ""
1273 machinespec = "%s%s" % (mversion, specprop)
1274 kvm_cmd.extend(["-machine", machinespec])
1275 else:
1276 kvm_cmd.extend(["-M", mversion])
1277 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1278 self._ENABLE_KVM_RE.search(kvmhelp)):
1279 kvm_cmd.extend(["-enable-kvm"])
1280 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1281 self._DISABLE_KVM_RE.search(kvmhelp)):
1282 kvm_cmd.extend(["-disable-kvm"])
1283
1284 kernel_path = hvp[constants.HV_KERNEL_PATH]
1285 if kernel_path:
1286 boot_cdrom = boot_floppy = boot_network = False
1287 else:
1288 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1289 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1290 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1291
1292 if startup_paused:
1293 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1294
1295 if boot_network:
1296 kvm_cmd.extend(["-boot", "n"])
1297
1298 disk_type = hvp[constants.HV_DISK_TYPE]
1299
1300 # Now we can specify a different device type for CDROM devices.
1301 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1302 if not cdrom_disk_type:
1303 cdrom_disk_type = disk_type
1304
1305 cdrom_image1 = hvp[constants.HV_CDROM_IMAGE_PATH]
1306 if cdrom_image1:
1307 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1308 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image1, boot_cdrom,
1309 needs_boot_flag)
1310
1311 cdrom_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1312 if cdrom_image2:
1313 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image2, False, False)
1314
1315 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1316 if floppy_image:
1317 options = ",format=raw,media=disk"
1318 if boot_floppy:
1319 kvm_cmd.extend(["-boot", "a"])
1320 options = "%s,boot=on" % options
1321 if_val = ",if=floppy"
1322 options = "%s%s" % (options, if_val)
1323 drive_val = "file=%s%s" % (floppy_image, options)
1324 kvm_cmd.extend(["-drive", drive_val])
1325
1326 if kernel_path:
1327 kvm_cmd.extend(["-kernel", kernel_path])
1328 initrd_path = hvp[constants.HV_INITRD_PATH]
1329 if initrd_path:
1330 kvm_cmd.extend(["-initrd", initrd_path])
1331 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1332 hvp[constants.HV_KERNEL_ARGS]]
1333 if hvp[constants.HV_SERIAL_CONSOLE]:
1334 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1335 root_append.append("console=ttyS0,%s" % serial_speed)
1336 kvm_cmd.extend(["-append", " ".join(root_append)])
1337
1338 mem_path = hvp[constants.HV_MEM_PATH]
1339 if mem_path:
1340 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1341
1342 monitor_dev = ("unix:%s,server,nowait" %
1343 self._InstanceMonitor(instance.name))
1344 kvm_cmd.extend(["-monitor", monitor_dev])
1345 if hvp[constants.HV_SERIAL_CONSOLE]:
1346 serial_dev = ("unix:%s,server,nowait" %
1347 self._InstanceSerial(instance.name))
1348 kvm_cmd.extend(["-serial", serial_dev])
1349 else:
1350 kvm_cmd.extend(["-serial", "none"])
1351
1352 mouse_type = hvp[constants.HV_USB_MOUSE]
1353 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1354 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1355 spice_ip_version = None
1356
1357 kvm_cmd.extend(["-usb"])
1358
1359 if mouse_type:
1360 kvm_cmd.extend(["-usbdevice", mouse_type])
1361 elif vnc_bind_address:
1362 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1363
1364 if vnc_bind_address:
1365 if netutils.IsValidInterface(vnc_bind_address):
1366 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1367 if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1368 if len(if_ip4_addresses) < 1:
1369 logging.error("Could not determine IPv4 address of interface %s",
1370 vnc_bind_address)
1371 else:
1372 vnc_bind_address = if_ip4_addresses[0]
1373 if netutils.IP4Address.IsValid(vnc_bind_address):
1374 if instance.network_port > constants.VNC_BASE_PORT:
1375 display = instance.network_port - constants.VNC_BASE_PORT
1376 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1377 vnc_arg = ":%d" % (display)
1378 else:
1379 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1380 else:
1381 logging.error("Network port is not a valid VNC display (%d < %d),"
1382 " not starting VNC",
1383 instance.network_port, constants.VNC_BASE_PORT)
1384 vnc_arg = "none"
1385
1386 # Only allow tls and other option when not binding to a file, for now.
1387 # kvm/qemu gets confused otherwise about the filename to use.
1388 vnc_append = ""
1389 if hvp[constants.HV_VNC_TLS]:
1390 vnc_append = "%s,tls" % vnc_append
1391 if hvp[constants.HV_VNC_X509_VERIFY]:
1392 vnc_append = "%s,x509verify=%s" % (vnc_append,
1393 hvp[constants.HV_VNC_X509])
1394 elif hvp[constants.HV_VNC_X509]:
1395 vnc_append = "%s,x509=%s" % (vnc_append,
1396 hvp[constants.HV_VNC_X509])
1397 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1398 vnc_append = "%s,password" % vnc_append
1399
1400 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1401
1402 else:
1403 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1404
1405 kvm_cmd.extend(["-vnc", vnc_arg])
1406 elif spice_bind:
1407 # FIXME: this is wrong here; the iface ip address differs
1408 # between systems, so it should be done in _ExecuteKVMRuntime
1409 if netutils.IsValidInterface(spice_bind):
1410 # The user specified a network interface, we have to figure out the IP
1411 # address.
1412 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1413 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1414
1415 # if the user specified an IP version and the interface does not
1416 # have that kind of IP addresses, throw an exception
1417 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1418 if not addresses[spice_ip_version]:
1419 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1420 " for %s" % (spice_ip_version,
1421 spice_bind))
1422
1423 # the user did not specify an IP version, we have to figure it out
1424 elif (addresses[constants.IP4_VERSION] and
1425 addresses[constants.IP6_VERSION]):
1426 # we have both ipv4 and ipv6, let's use the cluster default IP
1427 # version
1428 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1429 spice_ip_version = \
1430 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1431 elif addresses[constants.IP4_VERSION]:
1432 spice_ip_version = constants.IP4_VERSION
1433 elif addresses[constants.IP6_VERSION]:
1434 spice_ip_version = constants.IP6_VERSION
1435 else:
1436 raise errors.HypervisorError("SPICE: Unable to get an IP address"
1437 " for %s" % (spice_bind))
1438
1439 spice_address = addresses[spice_ip_version][0]
1440
1441 else:
1442 # spice_bind is known to be a valid IP address, because
1443 # ValidateParameters checked it.
1444 spice_address = spice_bind
1445
1446 spice_arg = "addr=%s" % spice_address
1447 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1448 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1449 (spice_arg, instance.network_port,
1450 pathutils.SPICE_CACERT_FILE))
1451 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1452 (spice_arg, pathutils.SPICE_CERT_FILE,
1453 pathutils.SPICE_CERT_FILE))
1454 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1455 if tls_ciphers:
1456 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1457 else:
1458 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1459
1460 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1461 spice_arg = "%s,disable-ticketing" % spice_arg
1462
1463 if spice_ip_version:
1464 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1465
1466 # Image compression options
1467 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1468 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1469 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1470 if img_lossless:
1471 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1472 if img_jpeg:
1473 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1474 if img_zlib_glz:
1475 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1476
1477 # Video stream detection
1478 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1479 if video_streaming:
1480 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1481
1482 # Audio compression, by default in qemu-kvm it is on
1483 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1484 spice_arg = "%s,playback-compression=off" % spice_arg
1485 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1486 spice_arg = "%s,agent-mouse=off" % spice_arg
1487 else:
1488 # Enable the spice agent communication channel between the host and the
1489 # agent.
1490 kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice"])
1491 kvm_cmd.extend([
1492 "-device",
1493 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1494 ])
1495 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1496
1497 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1498 kvm_cmd.extend(["-spice", spice_arg])
1499
1500 else:
1501 # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1502 # also works in earlier versions though (tested with 1.1 and 1.3)
1503 if self._DISPLAY_RE.search(kvmhelp):
1504 kvm_cmd.extend(["-display", "none"])
1505 else:
1506 kvm_cmd.extend(["-nographic"])
1507
1508 if hvp[constants.HV_USE_LOCALTIME]:
1509 kvm_cmd.extend(["-localtime"])
1510
1511 if hvp[constants.HV_KVM_USE_CHROOT]:
1512 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1513
1514 # Add qemu-KVM -cpu param
1515 if hvp[constants.HV_CPU_TYPE]:
1516 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1517
1518 # Pass a -vga option if requested, or if spice is used, for backwards
1519 # compatibility.
1520 if hvp[constants.HV_VGA]:
1521 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1522 elif spice_bind:
1523 kvm_cmd.extend(["-vga", "qxl"])
1524
1525 # Various types of usb devices, comma separated
1526 if hvp[constants.HV_USB_DEVICES]:
1527 for dev in hvp[constants.HV_USB_DEVICES].split(","):
1528 kvm_cmd.extend(["-usbdevice", dev])
1529
1530 # Set system UUID to instance UUID
1531 if self._UUID_RE.search(kvmhelp):
1532 kvm_cmd.extend(["-uuid", instance.uuid])
1533
1534 if hvp[constants.HV_KVM_EXTRA]:
1535 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1536
1537 def _generate_kvm_device(dev_type, dev):
1538 """Helper for generating a kvm device out of a Ganeti device."""
1539 kvm_devid = _GenerateDeviceKVMId(dev_type, dev)
1540 hv_dev_type = _DEVICE_TYPE[dev_type](hvp)
1541 dev.hvinfo = _GenerateDeviceHVInfo(dev_type, kvm_devid,
1542 hv_dev_type, bus_slots)
1543
1544 kvm_disks = []
1545 for disk, link_name, uri in block_devices:
1546 _generate_kvm_device(constants.HOTPLUG_TARGET_DISK, disk)
1547 kvm_disks.append((disk, link_name, uri))
1548
1549 kvm_nics = []
1550 for nic in instance.nics:
1551 _generate_kvm_device(constants.HOTPLUG_TARGET_NIC, nic)
1552 kvm_nics.append(nic)
1553
1554 hvparams = hvp
1555
1556 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1557
1558 def _WriteKVMRuntime(self, instance_name, data):
1559 """Write an instance's KVM runtime
1560
1561 """
1562 try:
1563 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1564 data=data)
1565 except EnvironmentError, err:
1566 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1567
1568 def _ReadKVMRuntime(self, instance_name):
1569 """Read an instance's KVM runtime
1570
1571 """
1572 try:
1573 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1574 except EnvironmentError, err:
1575 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1576 return file_content
1577
1578 def _SaveKVMRuntime(self, instance, kvm_runtime):
1579 """Save an instance's KVM runtime
1580
1581 """
1582 kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1583
1584 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1585 serialized_disks = [(blk.ToDict(), link, uri)
1586 for blk, link, uri in kvm_disks]
1587 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1588 serialized_disks))
1589
1590 self._WriteKVMRuntime(instance.name, serialized_form)
1591
1592 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1593 """Load an instance's KVM runtime
1594
1595 """
1596 if not serialized_runtime:
1597 serialized_runtime = self._ReadKVMRuntime(instance.name)
1598
1599 return _AnalyzeSerializedRuntime(serialized_runtime)
1600
1601 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1602 """Run the KVM cmd and check for errors
1603
1604 @type name: string
1605 @param name: instance name
1606 @type kvm_cmd: list of strings
1607 @param kvm_cmd: runcmd input for kvm
1608 @type tap_fds: list of int
1609 @param tap_fds: fds of tap devices opened by Ganeti
1610
1611 """
1612 try:
1613 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1614 finally:
1615 for fd in tap_fds:
1616 utils_wrapper.CloseFdNoError(fd)
1617
1618 if result.failed:
1619 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1620 (name, result.fail_reason, result.output))
1621 if not self._InstancePidAlive(name)[2]:
1622 raise errors.HypervisorError("Failed to start instance %s" % name)
1623
1624 @staticmethod
1625 def _GenerateKvmTapName(nic):
1626 """Generate a TAP network interface name for a NIC.
1627
1628 See L{hv_base.GenerateTapName}.
1629
1630 For the case of the empty string, see L{OpenTap}
1631
1632 @type nic: ganeti.objects.NIC
1633 @param nic: NIC object for the name should be generated
1634
1635 @rtype: string
1636 @return: TAP network interface name, or the empty string if the
1637 NIC is not used in instance communication
1638
1639 """
1640 if nic.name is None or not \
1641 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
1642 return ""
1643
1644 return hv_base.GenerateTapName()
1645
1646 def _GetNetworkDeviceFeatures(self, up_hvp, devlist, kvmhelp):
1647 """Get network device options to properly enable supported features.
1648
1649 Return a dict of supported and enabled tap features with nic_model along
1650 with the extra strings to be appended to the --netdev and --device options.
1651 This function is called before opening a new tap device.
1652
1653 Currently the features_dict includes the following attributes:
1654 - vhost (boolean)
1655 - vnet_hdr (boolean)
1656 - mq (boolean, int)
1657
1658 @rtype: (dict, str, str) tuple
1659 @return: The supported features,
1660 the string to be appended to the --netdev option,
1661 the string to be appended to the --device option
1662
1663 """
1664 nic_type = up_hvp[constants.HV_NIC_TYPE]
1665 nic_extra_str = ""
1666 tap_extra_str = ""
1667 features = {
1668 "vhost": False,
1669 "vnet_hdr": False,
1670 "mq": (False, 1)
1671 }
1672 update_features = {}
1673 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1674 if self._DEVICE_DRIVER_SUPPORTED(self._VIRTIO_NET_PCI, devlist):
1675 nic_model = self._VIRTIO_NET_PCI
1676 update_features["vnet_hdr"] = up_hvp[constants.HV_VNET_HDR]
1677 else:
1678 # Older versions of kvm don't support DEVICE_LIST, but they don't
1679 # have new virtio syntax either.
1680 nic_model = self._VIRTIO
1681
1682 if up_hvp[constants.HV_VHOST_NET]:
1683 # Check for vhost_net support.
1684 if self._VHOST_RE.search(kvmhelp):
1685 update_features["vhost"] = True
1686 tap_extra_str = ",vhost=on"
1687 else:
1688 raise errors.HypervisorError("vhost_net is configured"
1689 " but it is not available")
1690 virtio_net_queues = up_hvp.get(constants.HV_VIRTIO_NET_QUEUES, 1)
1691 if virtio_net_queues > 1:
1692 # Check for multiqueue virtio-net support.
1693 if self._VIRTIO_NET_QUEUES_RE.search(kvmhelp):
1694 # As advised at http://www.linux-kvm.org/page/Multiqueue formula
1695 # for calculating vector size is: vectors=2*N+1 where N is the
1696 # number of queues (HV_VIRTIO_NET_QUEUES).
1697 nic_extra_str = ",mq=on,vectors=%d" % (2 * virtio_net_queues + 1)
1698 update_features["mq"] = (True, virtio_net_queues)
1699 else:
1700 raise errors.HypervisorError("virtio_net_queues is configured"
1701 " but it is not available")
1702 else:
1703 nic_model = nic_type
1704
1705 update_features["driver"] = nic_model
1706 features.update(update_features)
1707
1708 return features, tap_extra_str, nic_extra_str
1709
1710 # too many local variables
1711 # pylint: disable=R0914
1712 def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1713 """Execute a KVM cmd, after completing it with some last minute data.
1714
1715 @type instance: L{objects.Instance} object
1716 @param instance: the VM this command acts upon
1717 @type kvm_runtime: tuple of (list of str, list of L{objects.NIC} objects,
1718 dict of hypervisor options, list of tuples (L{objects.Disk}, str, str)
1719 @param kvm_runtime: (kvm command, NICs of the instance, options at startup
1720 of the instance, [(disk, link_name, uri)..])
1721 @type incoming: tuple of strings
1722 @param incoming: (target_host_ip, port) for migration.
1723 @type kvmhelp: string
1724 @param kvmhelp: output of kvm --help
1725
1726 """
1727 # Small _ExecuteKVMRuntime hv parameters programming howto:
1728 # - conf_hvp contains the parameters as configured on ganeti. they might
1729 # have changed since the instance started; only use them if the change
1730 # won't affect the inside of the instance (which hasn't been rebooted).
1731 # - up_hvp contains the parameters as they were when the instance was
1732 # started, plus any new parameter which has been added between ganeti
1733 # versions: it is paramount that those default to a value which won't
1734 # affect the inside of the instance as well.
1735 conf_hvp = instance.hvparams
1736 name = instance.name
1737 self._CheckDown(name)
1738
1739 self._ClearUserShutdown(instance.name)
1740 self._StartKvmd(instance.hvparams)
1741
1742 temp_files = []
1743
1744 kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1745 # the first element of kvm_cmd is always the path to the kvm binary
1746 kvm_path = kvm_cmd[0]
1747 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1748
1749 # We know it's safe to run as a different user upon migration, so we'll use
1750 # the latest conf, from conf_hvp.
1751 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1752 if security_model == constants.HT_SM_USER:
1753 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1754
1755 keymap = conf_hvp[constants.HV_KEYMAP]
1756 if keymap:
1757 keymap_path = self._InstanceKeymapFile(name)
1758 # If a keymap file is specified, KVM won't use its internal defaults. By
1759 # first including the "en-us" layout, an error on loading the actual
1760 # layout (e.g. because it can't be found) won't lead to a non-functional
1761 # keyboard. A keyboard with incorrect keys is still better than none.
1762 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1763 kvm_cmd.extend(["-k", keymap_path])
1764
1765 # We have reasons to believe changing something like the nic driver/type
1766 # upon migration won't exactly fly with the instance kernel, so for nic
1767 # related parameters we'll use up_hvp
1768 tapfds = []
1769 taps = []
1770 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1771 if not kvm_nics:
1772 kvm_cmd.extend(["-net", "none"])
1773 else:
1774 features, tap_extra, nic_extra = \
1775 self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
1776 nic_model = features["driver"]
1777 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1778 for nic_seq, nic in enumerate(kvm_nics):
1779 tapname, nic_tapfds, nic_vhostfds = \
1780 OpenTap(features=features, name=self._GenerateKvmTapName(nic))
1781
1782 tapfds.extend(nic_tapfds)
1783 tapfds.extend(nic_vhostfds)
1784 taps.append(tapname)
1785 tapfd = "%s%s" % ("fds=" if len(nic_tapfds) > 1 else "fd=",
1786 ":".join(str(fd) for fd in nic_tapfds))
1787
1788 if nic_vhostfds:
1789 vhostfd = "%s%s" % (",vhostfds="
1790 if len(nic_vhostfds) > 1 else ",vhostfd=",
1791 ":".join(str(fd) for fd in nic_vhostfds))
1792 else:
1793 vhostfd = ""
1794
1795 if kvm_supports_netdev:
1796 # Non paravirtual NICs hvinfo is empty
1797 if "id" in nic.hvinfo:
1798 nic_val = _GenerateDeviceHVInfoStr(nic.hvinfo)
1799 netdev = nic.hvinfo["id"]
1800 else:
1801 nic_val = "%s" % nic_model
1802 netdev = "netdev%d" % nic_seq
1803 nic_val += (",netdev=%s,mac=%s%s" % (netdev, nic.mac, nic_extra))
1804 tap_val = ("type=tap,id=%s,%s%s%s" %
1805 (netdev, tapfd, vhostfd, tap_extra))
1806 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1807 else:
1808 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1809 nic.mac, nic_model)
1810 tap_val = "tap,vlan=%s,%s" % (nic_seq, tapfd)
1811 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1812
1813 if incoming:
1814 target, port = incoming
1815 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1816
1817 # Changing the vnc password doesn't bother the guest that much. At most it
1818 # will surprise people who connect to it. Whether positively or negatively
1819 # it's debatable.
1820 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1821 vnc_pwd = None
1822 if vnc_pwd_file:
1823 try:
1824 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1825 except EnvironmentError, err:
1826 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1827 % (vnc_pwd_file, err))
1828
1829 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1830 utils.EnsureDirs([(self._InstanceChrootDir(name),
1831 constants.SECURE_DIR_MODE)])
1832
1833 # Automatically enable QMP if version is >= 0.14
1834 if self._QMP_RE.search(kvmhelp):
1835 logging.debug("Enabling QMP")
1836 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1837 self._InstanceQmpMonitor(instance.name)])
1838 # Add a second monitor for kvmd
1839 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1840 self._InstanceKvmdMonitor(instance.name)])
1841
1842 # Configure the network now for starting instances and bridged/OVS
1843 # interfaces, during FinalizeMigration for incoming instances' routed
1844 # interfaces.
1845 for nic_seq, nic in enumerate(kvm_nics):
1846 if (incoming and
1847 nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED):
1848 continue
1849 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1850
1851 bdev_opts = self._GenerateKVMBlockDevicesOptions(up_hvp,
1852 kvm_disks,
1853 kvmhelp,
1854 devlist)
1855 kvm_cmd.extend(bdev_opts)
1856 # CPU affinity requires kvm to start paused, so we set this flag if the
1857 # instance is not already paused and if we are not going to accept a
1858 # migrating instance. In the latter case, pausing is not needed.
1859 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1860 if start_kvm_paused:
1861 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1862
1863 # Note: CPU pinning is using up_hvp since changes take effect
1864 # during instance startup anyway, and to avoid problems when soft
1865 # rebooting the instance.
1866 cpu_pinning = False
1867 if up_hvp.get(constants.HV_CPU_MASK, None):
1868 cpu_pinning = True
1869
1870 if security_model == constants.HT_SM_POOL:
1871 ss = ssconf.SimpleStore()
1872 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1873 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1874 uid = uidpool.RequestUnusedUid(all_uids)
1875 try:
1876 username = pwd.getpwuid(uid.GetUid()).pw_name
1877 kvm_cmd.extend(["-runas", username])
1878 self._RunKVMCmd(name, kvm_cmd, tapfds)
1879 except:
1880 uidpool.ReleaseUid(uid)
1881 raise
1882 else:
1883 uid.Unlock()
1884 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1885 else:
1886 self._RunKVMCmd(name, kvm_cmd, tapfds)
1887
1888 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1889 constants.RUN_DIRS_MODE)])
1890 for nic_seq, tap in enumerate(taps):
1891 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1892 data=tap)
1893
1894 if vnc_pwd:
1895 change_cmd = "change vnc password %s" % vnc_pwd
1896 self._CallMonitorCommand(instance.name, change_cmd)
1897
1898 # Setting SPICE password. We are not vulnerable to malicious passwordless
1899 # connection attempts because SPICE by default does not allow connections
1900 # if neither a password nor the "disable_ticketing" options are specified.
1901 # As soon as we send the password via QMP, that password is a valid ticket
1902 # for connection.
1903 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1904 if spice_password_file:
1905 spice_pwd = ""
1906 try:
1907 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1908 except EnvironmentError, err:
1909 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1910 % (spice_password_file, err))
1911
1912 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1913 qmp.connect()
1914 arguments = {
1915 "protocol": "spice",
1916 "password": spice_pwd,
1917 }
1918 qmp.Execute("set_password", arguments)
1919
1920 for filename in temp_files:
1921 utils.RemoveFile(filename)
1922
1923 # If requested, set CPU affinity and resume instance execution
1924 if cpu_pinning:
1925 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1926
1927 start_memory = self._InstanceStartupMemory(instance)
1928 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1929 self.BalloonInstanceMemory(instance, start_memory)
1930
1931 if start_kvm_paused:
1932 # To control CPU pinning, ballooning, and vnc/spice passwords
1933 # the VM was started in a frozen state. If freezing was not
1934 # explicitly requested resume the vm status.
1935 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1936
1937 @staticmethod
1938 def _StartKvmd(hvparams):
1939 """Ensure that the Kvm daemon is running.
1940
1941 @type hvparams: dict of strings
1942 @param hvparams: hypervisor parameters
1943
1944 """
1945 if hvparams is None \
1946 or not hvparams[constants.HV_KVM_USER_SHUTDOWN] \
1947 or utils.IsDaemonAlive(constants.KVMD):
1948 return
1949
1950 result = utils.RunCmd([pathutils.DAEMON_UTIL, "start", constants.KVMD])
1951
1952 if result.failed:
1953 raise errors.HypervisorError("Failed to start KVM daemon")
1954
1955 def StartInstance(self, instance, block_devices, startup_paused):
1956 """Start an instance.
1957
1958 """
1959 self._CheckDown(instance.name)
1960 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
1961 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
1962 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1963 startup_paused, kvmhelp)
1964 self._SaveKVMRuntime(instance, kvm_runtime)
1965 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
1966
1967 @classmethod
1968 def _CallMonitorCommand(cls, instance_name, command, timeout=None):
1969 """Invoke a command on the instance monitor.
1970
1971 """
1972 if timeout is not None:
1973 timeout_cmd = "timeout %s" % (timeout, )
1974 else:
1975 timeout_cmd = ""
1976
1977 # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
1978 # version. The monitor protocol is designed for human consumption, whereas
1979 # QMP is made for programmatic usage. In the worst case QMP can also
1980 # execute monitor commands. As it is, all calls to socat take at least
1981 # 500ms and likely more: socat can't detect the end of the reply and waits
1982 # for 500ms of no data received before exiting (500 ms is the default for
1983 # the "-t" parameter).
1984 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
1985 (utils.ShellQuote(command),
1986 timeout_cmd,
1987 constants.SOCAT_PATH,
1988 utils.ShellQuote(cls._InstanceMonitor(instance_name))))
1989 result = utils.RunCmd(socat)
1990 if result.failed:
1991 msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
1992 " output: %s" %
1993 (command, instance_name, result.fail_reason, result.output))
1994 raise errors.HypervisorError(msg)
1995
1996 return result
1997
1998 @_with_qmp
1999 def VerifyHotplugSupport(self, instance, action, dev_type):
2000 """Verifies that hotplug is supported.
2001
2002 Hotplug is not supported if:
2003
2004 - the instance is not running
2005 - the device type is not hotplug-able
2006 - the QMP version does not support the corresponding commands
2007
2008 @raise errors.HypervisorError: if one of the above applies
2009
2010 """
2011 runtime = self._LoadKVMRuntime(instance)
2012 device_type = _DEVICE_TYPE[dev_type](runtime[2])
2013 if device_type not in _HOTPLUGGABLE_DEVICE_TYPES[dev_type]:
2014 msg = "Hotplug is not supported for device type %s" % device_type
2015 raise errors.HypervisorError(msg)
2016
2017 if dev_type == constants.HOTPLUG_TARGET_DISK:
2018 if action == constants.HOTPLUG_ACTION_ADD:
2019 self.qmp.CheckDiskHotAddSupport()
2020 if dev_type == constants.HOTPLUG_TARGET_NIC:
2021 if action == constants.HOTPLUG_ACTION_ADD:
2022 self.qmp.CheckNicHotAddSupport()
2023
2024 def HotplugSupported(self, instance):
2025 """Checks if hotplug is generally supported.
2026
2027 Hotplug is *not* supported in case of:
2028 - qemu versions < 1.7 (where all qmp related commands are supported)
2029 - for stopped instances
2030
2031 @raise errors.HypervisorError: in one of the previous cases
2032
2033 """
2034 try:
2035 output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2036 except errors.HypervisorError:
2037 raise errors.HotplugError("Instance is probably down")
2038
2039 match = self._INFO_VERSION_RE.search(output.stdout)
2040 if not match:
2041 raise errors.HotplugError("Cannot parse qemu version via monitor")
2042
2043 #TODO: delegate more fine-grained checks to VerifyHotplugSupport
2044 v_major, v_min, _, _ = match.groups()
2045 if (int(v_major), int(v_min)) < (1, 7):
2046 raise errors.HotplugError("Hotplug not supported for qemu versions < 1.7")
2047
2048 def _GetBusSlots(self, hvp=None, runtime=None):
2049 """Helper function to get the slots of PCI and SCSI QEMU buses.
2050
2051 This will return the status of the first PCI and SCSI buses. By default
2052 QEMU boots with one PCI bus (pci.0) and occupies the first 3 PCI slots. If
2053 a SCSI disk is found then a SCSI controller is added on the PCI bus and a
2054 SCSI bus (scsi.0) is created.
2055
2056 During hotplug we could query QEMU via info qtree HMP command but parsing
2057 the result is too complicated. Instead we use the info stored in runtime
2058 files. We parse NIC and disk entries and based on their hvinfo we reserve
2059 the corresponding slots.
2060
2061 The runtime argument is a tuple as returned by _LoadKVMRuntime(). Obtain
2062 disks and NICs from it. In case a runtime file is not available (see
2063 _GenerateKVMRuntime()) we return the bus slots that QEMU boots with by
2064 default.
2065
2066 """
2067 # This is by default and returned during _GenerateKVMRuntime()
2068 bus_slots = {
2069 _PCI_BUS: bitarray(self._DEFAULT_PCI_RESERVATIONS),
2070 _SCSI_BUS: bitarray(self._DEFAULT_SCSI_RESERVATIONS),
2071 }
2072
2073 # Adjust the empty slots depending of the corresponding hvparam
2074 if hvp and constants.HV_KVM_PCI_RESERVATIONS in hvp:
2075 res = hvp[constants.HV_KVM_PCI_RESERVATIONS]
2076 pci = bitarray(constants.QEMU_PCI_SLOTS)
2077 pci.setall(False) # pylint: disable=E1101
2078 pci[0:res:1] = True
2079 bus_slots[_PCI_BUS] = pci
2080
2081 # This is during hot-add
2082 if runtime:
2083 _, nics, _, disks = runtime
2084 disks = [d for d, _, _ in disks]
2085 for d in disks + nics:
2086 if not d.hvinfo or "bus" not in d.hvinfo:
2087 continue
2088 bus = d.hvinfo["bus"]
2089 slots = bus_slots[bus]
2090 if bus == _PCI_BUS:
2091 slot = d.hvinfo["addr"]
2092 slots[int(slot, 16)] = True
2093 elif bus == _SCSI_BUS:
2094 slot = d.hvinfo["scsi-id"]
2095 slots[slot] = True
2096
2097 return bus_slots
2098
2099 @_with_qmp
2100 def _VerifyHotplugCommand(self, _instance, kvm_devid, should_exist):
2101 """Checks if a previous hotplug command has succeeded.
2102
2103 Depending on the should_exist value, verifies that an entry identified by
2104 device ID is present or not.
2105
2106 @raise errors.HypervisorError: if result is not the expected one
2107
2108 """
2109 for i in range(5):
2110 found = self.qmp.HasDevice(kvm_devid)
2111 logging.info("Verifying hotplug command (retry %s): %s", i, found)
2112 if found and should_exist:
2113 break
2114 if not found and not should_exist:
2115 break
2116 time.sleep(1)
2117
2118 if found and not should_exist:
2119 msg = "Device %s should have been removed but is still there" % kvm_devid
2120 raise errors.HypervisorError(msg)
2121
2122 if not found and should_exist:
2123 msg = "Device %s should have been added but is missing" % kvm_devid
2124 raise errors.HypervisorError(msg)
2125
2126 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
2127
2128 @_with_qmp
2129 def HotAddDevice(self, instance, dev_type, device, extra, seq):
2130 """ Helper method to hot-add a new device
2131
2132 It generates the device ID and hvinfo, and invokes the
2133 device-specific method.
2134
2135 """
2136 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2137 runtime = self._LoadKVMRuntime(instance)
2138 up_hvp = runtime[2]
2139 device_type = _DEVICE_TYPE[dev_type](up_hvp)
2140 bus_state = self._GetBusSlots(up_hvp, runtime)
2141 # in case of hot-mod this is given
2142 if not device.hvinfo:
2143 device.hvinfo = _GenerateDeviceHVInfo(dev_type, kvm_devid,
2144 device_type, bus_state)
2145 if dev_type == constants.HOTPLUG_TARGET_DISK:
2146 uri = _GetDriveURI(device, extra[0], extra[1])
2147
2148 def drive_add_fn(filename):
2149 """Helper function that uses HMP to hot-add a drive."""
2150 cmd = "drive_add dummy file=%s,if=none,id=%s,format=raw" % \
2151 (filename, kvm_devid)
2152 self._CallMonitorCommand(instance.name, cmd)
2153
2154 # This must be done indirectly due to the fact that we pass the drive's
2155 # file descriptor via QMP first, then we add the corresponding drive that
2156 # refers to this fd. Note that if the QMP connection terminates before
2157 # a drive which keeps a reference to the fd passed via the add-fd QMP
2158 # command has been created, then the fd gets closed and cannot be used
2159 # later (e.g., via an drive_add HMP command).
2160 self.qmp.HotAddDisk(device, kvm_devid, uri, drive_add_fn)
2161 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2162 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2163 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2164 devlist = self._GetKVMOutput(kvmpath, self._KVMOPT_DEVICELIST)
2165 features, _, _ = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
2166 (tap, tapfds, vhostfds) = OpenTap(features=features)
2167 self._ConfigureNIC(instance, seq, device, tap)
2168 self.qmp.HotAddNic(device, kvm_devid, tapfds, vhostfds, features)
2169 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2170
2171 self._VerifyHotplugCommand(instance, kvm_devid, True)
2172 # update relevant entries in runtime file
2173 index = _DEVICE_RUNTIME_INDEX[dev_type]
2174 entry = _RUNTIME_ENTRY[dev_type](device, extra)
2175 runtime[index].append(entry)
2176 self._SaveKVMRuntime(instance, runtime)
2177
2178 @_with_qmp
2179 def HotDelDevice(self, instance, dev_type, device, _, seq):
2180 """ Helper method for hot-del device
2181
2182 It gets device info from runtime file, generates the device name and
2183 invokes the device-specific method.
2184
2185 """
2186 runtime = self._LoadKVMRuntime(instance)
2187 entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2188 kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2189 kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2190 if dev_type == constants.HOTPLUG_TARGET_DISK:
2191 self.qmp.HotDelDisk(kvm_devid)
2192 # drive_del is not implemented yet in qmp
2193 command = "drive_del %s\n" % kvm_devid
2194 self._CallMonitorCommand(instance.name, command)
2195 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2196 self.qmp.HotDelNic(kvm_devid)
2197 utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2198 self._VerifyHotplugCommand(instance, kvm_devid, False)
2199 index = _DEVICE_RUNTIME_INDEX[dev_type]
2200 runtime[index].remove(entry)
2201 self._SaveKVMRuntime(instance, runtime)
2202
2203 return kvm_device.hvinfo
2204
2205 def HotModDevice(self, instance, dev_type, device, _, seq):
2206 """ Helper method for hot-mod device
2207
2208 It gets device info from runtime file, generates the device name and
2209 invokes the device-specific method. Currently only NICs support hot-mod
2210
2211 """
2212 if dev_type == constants.HOTPLUG_TARGET_NIC:
2213 # putting it back in the same bus and slot
2214 device.hvinfo = self.HotDelDevice(instance, dev_type, device, _, seq)
2215 self.HotAddDevice(instance, dev_type, device, _, seq)
2216
2217 @classmethod
2218 def _ParseKVMVersion(cls, text):
2219 """Parse the KVM version from the --help output.
2220
2221 @type text: string
2222 @param text: output of kvm --help
2223 @return: (version, v_maj, v_min, v_rev)
2224 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2225
2226 """
2227 match = cls._VERSION_RE.search(text.splitlines()[0])
2228 if not match:
2229 raise errors.HypervisorError("Unable to get KVM version")
2230
2231 v_all = match.group(0)
2232 v_maj = int(match.group(1))
2233 v_min = int(match.group(2))
2234 if match.group(4):
2235 v_rev = int(match.group(4))
2236 else:
2237 v_rev = 0
2238 return (v_all, v_maj, v_min, v_rev)
2239
2240 @classmethod
2241 def _GetKVMOutput(cls, kvm_path, option):
2242 """Return the output of a kvm invocation
2243
2244 @type kvm_path: string
2245 @param kvm_path: path to the kvm executable
2246 @type option: a key of _KVMOPTS_CMDS
2247 @param option: kvm option to fetch the output from
2248 @return: output a supported kvm invocation
2249 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2250
2251 """
2252 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2253
2254 optlist, can_fail = cls._KVMOPTS_CMDS[option]
2255
2256 result = utils.RunCmd([kvm_path] + optlist)
2257 if result.failed and not can_fail:
2258 raise errors.HypervisorError("Unable to get KVM %s output" %
2259 " ".join(optlist))
2260 return result.output
2261
2262 @classmethod
2263 def _GetKVMVersion(cls, kvm_path):
2264 """Return the installed KVM version.
2265
2266 @return: (version, v_maj, v_min, v_rev)
2267 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2268
2269 """
2270 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2271
2272 @classmethod
2273 def _GetDefaultMachineVersion(cls, kvm_path):
2274 """Return the default hardware revision (e.g. pc-1.1)
2275
2276 """
2277 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2278 match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2279 if match:
2280 return match.group(1)
2281 else:
2282 return "pc"
2283
2284 @classmethod
2285 def _StopInstance(cls, instance, force=False, name=None, timeout=None):
2286 """Stop an instance.
2287
2288 """
2289 assert(timeout is None or force is not None)
2290
2291 if name is not None and not force:
2292 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2293 if name is None:
2294 name = instance.name
2295 acpi = instance.hvparams[constants.HV_ACPI]
2296 else:
2297 acpi = False
2298 _, pid, alive = cls._InstancePidAlive(name)
2299 if pid > 0 and alive:
2300 if force or not acpi:
2301 utils.KillProcess(pid)
2302 else:
2303 cls._CallMonitorCommand(name, "system_powerdown", timeout)
2304 cls._ClearUserShutdown(instance.name)
2305
2306 def StopInstance(self, instance, force=False, retry=False, name=None,
2307 timeout=None):
2308 """Stop an instance.
2309
2310 """
2311 self._StopInstance(instance, force, name=name, timeout=timeout)
2312
2313 def CleanupInstance(self, instance_name):
2314 """Cleanup after a stopped instance
2315
2316 """
2317 pidfile, pid, alive = self._InstancePidAlive(instance_name)
2318 if pid > 0 and alive:
2319 raise errors.HypervisorError("Cannot cleanup a live instance")
2320 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2321 self._ClearUserShutdown(instance_name)
2322
2323 def RebootInstance(self, instance):
2324 """Reboot an instance.
2325
2326 """
2327 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2328 # socket the instance will stop, but now power up again. So we'll resort
2329 # to shutdown and restart.
2330 _, _, alive = self._InstancePidAlive(instance.name)
2331 if not alive:
2332 raise errors.HypervisorError("Failed to reboot instance %s:"
2333 " not running" % instance.name)
2334 # StopInstance will delete the saved KVM runtime so:
2335 # ...first load it...
2336 kvm_runtime = self._LoadKVMRuntime(instance)
2337 # ...now we can safely call StopInstance...
2338 if not self.StopInstance(instance):
2339 self.StopInstance(instance, force=True)
2340 # ...and finally we can save it again, and execute it...
2341 self._SaveKVMRuntime(instance, kvm_runtime)
2342 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2343 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2344 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2345
2346 def MigrationInfo(self, instance):
2347 """Get instance information to perform a migration.
2348
2349 @type instance: L{objects.Instance}
2350 @param instance: instance to be migrated
2351 @rtype: string
2352 @return: content of the KVM runtime file
2353
2354 """
2355 return self._ReadKVMRuntime(instance.name)
2356
2357 def AcceptInstance(self, instance, info, target):
2358 """Prepare to accept an instance.
2359
2360 @type instance: L{objects.Instance}
2361 @param instance: instance to be accepted
2362 @type info: string
2363 @param info: content of the KVM runtime file on the source node
2364 @type target: string
2365 @param target: target host (usually ip), on this node
2366
2367 """
2368 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2369 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2370 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2371 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2372 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2373 incoming=incoming_address)
2374
2375 def FinalizeMigrationDst(self, instance, info, success):
2376 """Finalize the instance migration on the target node.
2377
2378 Stop the incoming mode KVM.
2379
2380 @type instance: L{objects.Instance}
2381 @param instance: instance whose migration is being finalized
2382
2383 """
2384 if success:
2385 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2386 kvm_nics = kvm_runtime[1]
2387
2388 for nic_seq, nic in enumerate(kvm_nics):
2389 if nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_ROUTED:
2390 # Bridged/OVS interfaces have already been configured
2391 continue
2392 try:
2393 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2394 except EnvironmentError, err:
2395 logging.warning("Failed to find host interface for %s NIC #%d: %s",
2396 instance.name, nic_seq, str(err))
2397 continue
2398 try:
2399 self._ConfigureNIC(instance, nic_seq, nic, tap)
2400 except errors.HypervisorError, err:
2401 logging.warning(str(err))
2402
2403 self._WriteKVMRuntime(instance.name, info)
2404 else:
2405 self.StopInstance(instance, force=True)
2406
2407 def MigrateInstance(self, cluster_name, instance, target, live):
2408 """Migrate an instance to a target node.
2409
2410 The migration will not be attempted if the instance is not
2411 currently running.
2412
2413 @type cluster_name: string
2414 @param cluster_name: name of the cluster
2415 @type instance: L{objects.Instance}
2416 @param instance: the instance to be migrated
2417 @type target: string
2418 @param target: ip address of the target node
2419 @type live: boolean
2420 @param live: perform a live migration
2421
2422 """
2423 instance_name = instance.name
2424 port = instance.hvparams[constants.HV_MIGRATION_PORT]
2425 _, _, alive = self._InstancePidAlive(instance_name)
2426 if not alive:
2427 raise errors.HypervisorError("Instance not running, cannot migrate")
2428
2429 if not live:
2430 self._CallMonitorCommand(instance_name, "stop")
2431
2432 migrate_command = ("migrate_set_speed %dm" %
2433 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2434 self._CallMonitorCommand(instance_name, migrate_command)
2435
2436 migrate_command = ("migrate_set_downtime %dms" %
2437 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2438 self._CallMonitorCommand(instance_name, migrate_command)
2439
2440 migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS]
2441 if migration_caps:
2442 for c in migration_caps.split(_MIGRATION_CAPS_DELIM):
2443 migrate_command = ("migrate_set_capability %s on" % c)
2444 self._CallMonitorCommand(instance_name, migrate_command)
2445
2446 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2447 self._CallMonitorCommand(instance_name, migrate_command)
2448
2449 def FinalizeMigrationSource(self, instance, success, live):
2450 """Finalize the instance migration on the source node.
2451
2452 @type instance: L{objects.Instance}
2453 @param instance: the instance that was migrated
2454 @type success: bool
2455 @param success: whether the migration succeeded or not
2456 @type live: bool
2457 @param live: whether the user requested a live migration or not
2458
2459 """
2460 if success:
2461 pidfile, pid, _ = self._InstancePidAlive(instance.name)
2462 utils.KillProcess(pid)
2463 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2464 elif live:
2465 self._CallMonitorCommand(instance.name, self._CONT_CMD)
2466 self._ClearUserShutdown(instance.name)
2467
2468 def GetMigrationStatus(self, instance):
2469 """Get the migration status
2470
2471 @type instance: L{objects.Instance}
2472 @param instance: the instance that is being migrated
2473 @rtype: L{objects.MigrationStatus}
2474 @return: the status of the current migration (one of
2475 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2476 progress info that can be retrieved from the hypervisor
2477
2478 """
2479 info_command = "info migrate"
2480 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2481 result = self._CallMonitorCommand(instance.name, info_command)
2482 match = self._MIGRATION_STATUS_RE.search(result.stdout)
2483 if not match:
2484 if not result.stdout:
2485 logging.info("KVM: empty 'info migrate' result")
2486 else:
2487 logging.warning("KVM: unknown 'info migrate' result: %s",
2488 result.stdout)
2489 else:
2490 status = match.group(1)
2491 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2492 migration_status = objects.MigrationStatus(status=status)
2493 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2494 if match:
2495 migration_status.transferred_ram = match.group("transferred")
2496 migration_status.total_ram = match.group("total")
2497
2498 return migration_status
2499
2500 logging.warning("KVM: unknown migration status '%s'", status)
2501
2502 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2503
2504 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2505
2506 def BalloonInstanceMemory(self, instance, mem):
2507 """Balloon an instance memory to a certain value.
2508
2509 @type instance: L{objects.Instance}
2510 @param instance: instance to be accepted
2511 @type mem: int
2512 @param mem: actual memory size to use for instance runtime
2513
2514 """
2515 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2516
2517 def GetNodeInfo(self, hvparams=None):
2518 """Return information about the node.
2519
2520 @type hvparams: dict of strings
2521 @param hvparams: hypervisor parameters, not used in this class
2522
2523 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2524 the following keys:
2525 - hv_version: the hypervisor version in the form (major, minor,
2526 revision)
2527
2528 """
2529 result = self.GetLinuxNodeInfo()
2530 kvmpath = constants.KVM_PATH
2531 if hvparams is not None:
2532 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2533 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2534 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2535 return result
2536
2537 @classmethod
2538 def GetInstanceConsole(cls, instance, primary_node, node_group,
2539 hvparams, beparams):
2540 """Return a command for connecting to the console of an instance.
2541
2542 """
2543 if hvparams[constants.HV_SERIAL_CONSOLE]:
2544 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2545 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2546 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2547 "STDIO,%s" % cls._SocatUnixConsoleParams(),
2548 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2549 ndparams = node_group.FillND(primary_node)
2550 return objects.InstanceConsole(instance=instance.name,
2551 kind=constants.CONS_SSH,
2552 host=primary_node.name,
2553 port=ndparams.get(constants.ND_SSH_PORT),
2554 user=constants.SSH_CONSOLE_USER,
2555 command=cmd)
2556
2557 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2558 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2559 display = instance.network_port - constants.VNC_BASE_PORT
2560 return objects.InstanceConsole(instance=instance.name,
2561 kind=constants.CONS_VNC,
2562 host=vnc_bind_address,
2563 port=instance.network_port,
2564 display=display)
2565
2566 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2567 if spice_bind:
2568 return objects.InstanceConsole(instance=instance.name,
2569 kind=constants.CONS_SPICE,
2570 host=spice_bind,
2571 port=instance.network_port)
2572
2573 return objects.InstanceConsole(instance=instance.name,
2574 kind=constants.CONS_MESSAGE,
2575 message=("No serial shell for instance %s" %
2576 instance.name))
2577
2578 def Verify(self, hvparams=None):
2579 """Verify the hypervisor.
2580
2581 Check that the required binaries exist.
2582
2583 @type hvparams: dict of strings
2584 @param hvparams: hypervisor parameters to be verified against, not used here
2585
2586 @return: Problem description if something is wrong, C{None} otherwise
2587
2588 """
2589 msgs = []
2590 kvmpath = constants.KVM_PATH
2591 if hvparams is not None:
2592 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2593 if not os.path.exists(kvmpath):
2594 msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2595 if not os.path.exists(constants.SOCAT_PATH):
2596 msgs.append("The socat binary ('%s') does not exist" %
2597 constants.SOCAT_PATH)
2598
2599 return self._FormatVerifyResults(msgs)
2600
2601 @classmethod
2602 def CheckParameterSyntax(cls, hvparams):
2603 """Check the given parameters for validity.
2604
2605 @type hvparams: dict of strings
2606 @param hvparams: hypervisor parameters
2607 @raise errors.HypervisorError: when a parameter is not valid
2608
2609 """
2610 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2611
2612 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2613 if kernel_path:
2614 if not hvparams[constants.HV_ROOT_PATH]:
2615 raise errors.HypervisorError("Need a root partition for the instance,"
2616 " if a kernel is defined")
2617
2618 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2619 not hvparams[constants.HV_VNC_X509]):
2620 raise errors.HypervisorError("%s must be defined, if %s is" %
2621 (constants.HV_VNC_X509,
2622 constants.HV_VNC_X509_VERIFY))
2623
2624 if hvparams[constants.HV_SERIAL_CONSOLE]:
2625 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2626 valid_speeds = constants.VALID_SERIAL_SPEEDS
2627 if not serial_speed or serial_speed not in valid_speeds:
2628 raise errors.HypervisorError("Invalid serial console speed, must be"
2629 " one of: %s" %
2630 utils.CommaJoin(valid_speeds))
2631
2632 boot_order = hvparams[constants.HV_BOOT_ORDER]
2633 if (boot_order == constants.HT_BO_CDROM and
2634 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2635 raise errors.HypervisorError("Cannot boot from cdrom without an"
2636 " ISO path")
2637
2638 security_model = hvparams[constants.HV_SECURITY_MODEL]
2639 if security_model == constants.HT_SM_USER:
2640 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2641 raise errors.HypervisorError("A security domain (user to run kvm as)"
2642 " must be specified")
2643 elif (security_model == constants.HT_SM_NONE or
2644 security_model == constants.HT_SM_POOL):
2645 if hvparams[constants.HV_SECURITY_DOMAIN]:
2646 raise errors.HypervisorError("Cannot have a security domain when the"
2647 " security model is 'none' or 'pool'")
2648
2649 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2650 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2651 if spice_bind:
2652 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2653 # if an IP version is specified, the spice_bind parameter must be an
2654 # IP of that family
2655 if (netutils.IP4Address.IsValid(spice_bind) and
2656 spice_ip_version != constants.IP4_VERSION):
2657 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2658 " the specified IP version is %s" %
2659 (spice_bind, spice_ip_version))
2660
2661 if (netutils.IP6Address.IsValid(spice_bind) and
2662 spice_ip_version != constants.IP6_VERSION):
2663 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2664 " the specified IP version is %s" %
2665 (spice_bind, spice_ip_version))
2666 else:
2667 # All the other SPICE parameters depend on spice_bind being set. Raise an
2668 # error if any of them is set without it.
2669 for param in _SPICE_ADDITIONAL_PARAMS:
2670 if hvparams[param]:
2671 raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2672 (param, constants.HV_KVM_SPICE_BIND))
2673
2674 @classmethod
2675 def ValidateParameters(cls, hvparams):
2676 """Check the given parameters for validity.
2677
2678 @type hvparams: dict of strings
2679 @param hvparams: hypervisor parameters
2680 @raise errors.HypervisorError: when a parameter is not valid
2681
2682 """
2683 super(KVMHypervisor, cls).ValidateParameters(hvparams)
2684
2685 kvm_path = hvparams[constants.HV_KVM_PATH]
2686
2687 security_model = hvparams[constants.HV_SECURITY_MODEL]
2688 if security_model == constants.HT_SM_USER:
2689 username = hvparams[constants.HV_SECURITY_DOMAIN]
2690 try:
2691 pwd.getpwnam(username)
2692 except KeyError:
2693 raise errors.HypervisorError("Unknown security domain user %s"
2694 % username)
2695 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2696 if vnc_bind_address:
2697 bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address)
2698 is_interface = netutils.IsValidInterface(vnc_bind_address)
2699 is_path = utils.IsNormAbsPath(vnc_bind_address)
2700 if not bound_to_addr and not is_interface and not is_path:
2701 raise errors.HypervisorError("VNC: The %s parameter must be either"
2702 " a valid IP address, an interface name,"
2703 " or an absolute path" %
2704 constants.HV_KVM_SPICE_BIND)
2705
2706 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2707 if spice_bind:
2708 # only one of VNC and SPICE can be used currently.
2709 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2710 raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2711 " only one of them can be used at a"
2712 " given time")
2713
2714 # check that KVM supports SPICE
2715 kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2716 if not cls._SPICE_RE.search(kvmhelp):
2717 raise errors.HypervisorError("SPICE is configured, but it is not"
2718 " supported according to 'kvm --help'")
2719
2720 # if spice_bind is not an IP address, it must be a valid interface
2721 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2722 netutils.IP6Address.IsValid(spice_bind))
2723 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2724 raise errors.HypervisorError("SPICE: The %s parameter must be either"
2725 " a valid IP address or interface name" %
2726 constants.HV_KVM_SPICE_BIND)
2727
2728 machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2729 if machine_version:
2730 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2731 if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2732 raise errors.HypervisorError("Unsupported machine version: %s" %
2733 machine_version)
2734
2735 @classmethod
2736 def PowercycleNode(cls, hvparams=None):
2737 """KVM powercycle, just a wrapper over Linux powercycle.
2738
2739 @type hvparams: dict of strings
2740 @param hvparams: hypervisor parameters to be used on this node
2741
2742 """
2743 cls.LinuxPowercycle()