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