3569a20080709f54b2e3274e3a272ce2637e1945
[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 and up_hvp[constants.HV_CPU_MASK] != constants.CPU_PINNING_ALL:
1905 cpu_pinning = True
1906
1907 if security_model == constants.HT_SM_POOL:
1908 ss = ssconf.SimpleStore()
1909 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1910 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1911 uid = uidpool.RequestUnusedUid(all_uids)
1912 try:
1913 username = pwd.getpwuid(uid.GetUid()).pw_name
1914 kvm_cmd.extend(["-runas", username])
1915 self._RunKVMCmd(name, kvm_cmd, tapfds)
1916 except:
1917 uidpool.ReleaseUid(uid)
1918 raise
1919 else:
1920 uid.Unlock()
1921 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1922 else:
1923 self._RunKVMCmd(name, kvm_cmd, tapfds)
1924
1925 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1926 constants.RUN_DIRS_MODE)])
1927 for nic_seq, tap in enumerate(taps):
1928 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq), data=tap)
1929
1930 if vnc_pwd:
1931 change_cmd = "change vnc password %s" % vnc_pwd
1932 self._CallMonitorCommand(instance.name, change_cmd)
1933
1934 # Setting SPICE password. We are not vulnerable to malicious passwordless
1935 # connection attempts because SPICE by default does not allow connections
1936 # if neither a password nor the "disable_ticketing" options are specified.
1937 # As soon as we send the password via QMP, that password is a valid ticket
1938 # for connection.
1939 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1940 if spice_password_file:
1941 spice_pwd = ""
1942 try:
1943 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1944 except EnvironmentError, err:
1945 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1946 % (spice_password_file, err))
1947
1948 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1949 qmp.connect()
1950 arguments = {
1951 "protocol": "spice",
1952 "password": spice_pwd,
1953 }
1954 qmp.Execute("set_password", arguments)
1955
1956 for filename in temp_files:
1957 utils.RemoveFile(filename)
1958
1959 # If requested, set CPU affinity and resume instance execution
1960 if cpu_pinning:
1961 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK],
1962 up_hvp[constants.HV_WORKER_CPU_MASK])
1963
1964 start_memory = self._InstanceStartupMemory(instance)
1965 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1966 self.BalloonInstanceMemory(instance, start_memory)
1967
1968 if start_kvm_paused:
1969 # To control CPU pinning, ballooning, and vnc/spice passwords
1970 # the VM was started in a frozen state. If freezing was not
1971 # explicitly requested resume the vm status.
1972 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1973
1974 @staticmethod
1975 def _StartKvmd(hvparams):
1976 """Ensure that the Kvm daemon is running.
1977
1978 @type hvparams: dict of strings
1979 @param hvparams: hypervisor parameters
1980
1981 """
1982 if hvparams is None \
1983 or not hvparams[constants.HV_KVM_USER_SHUTDOWN] \
1984 or utils.IsDaemonAlive(constants.KVMD):
1985 return
1986
1987 result = utils.RunCmd([pathutils.DAEMON_UTIL, "start", constants.KVMD])
1988
1989 if result.failed:
1990 raise errors.HypervisorError("Failed to start KVM daemon")
1991
1992 def StartInstance(self, instance, block_devices, startup_paused):
1993 """Start an instance.
1994
1995 """
1996 self._CheckDown(instance.name)
1997 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
1998 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
1999 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
2000 startup_paused, kvmhelp)
2001 self._SaveKVMRuntime(instance, kvm_runtime)
2002 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2003
2004 @classmethod
2005 def _CallMonitorCommand(cls, instance_name, command, timeout=None):
2006 """Invoke a command on the instance monitor.
2007
2008 """
2009 if timeout is not None:
2010 timeout_cmd = "timeout %s" % (timeout, )
2011 else:
2012 timeout_cmd = ""
2013
2014 # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2015 # version. The monitor protocol is designed for human consumption, whereas
2016 # QMP is made for programmatic usage. In the worst case QMP can also
2017 # execute monitor commands. As it is, all calls to socat take at least
2018 # 500ms and likely more: socat can't detect the end of the reply and waits
2019 # for 500ms of no data received before exiting (500 ms is the default for
2020 # the "-t" parameter).
2021 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
2022 (utils.ShellQuote(command),
2023 timeout_cmd,
2024 constants.SOCAT_PATH,
2025 utils.ShellQuote(cls._InstanceMonitor(instance_name))))
2026 result = utils.RunCmd(socat)
2027 if result.failed:
2028 msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2029 " output: %s" %
2030 (command, instance_name, result.fail_reason, result.output))
2031 raise errors.HypervisorError(msg)
2032
2033 return result
2034
2035 @_with_qmp
2036 def VerifyHotplugSupport(self, instance, action, dev_type):
2037 """Verifies that hotplug is supported.
2038
2039 Hotplug is not supported if:
2040
2041 - the instance is not running
2042 - the device type is not hotplug-able
2043 - the QMP version does not support the corresponding commands
2044
2045 @raise errors.HypervisorError: if one of the above applies
2046
2047 """
2048 runtime = self._LoadKVMRuntime(instance)
2049 device_type = _DEVICE_TYPE[dev_type](runtime[2])
2050 if device_type not in _HOTPLUGGABLE_DEVICE_TYPES[dev_type]:
2051 msg = "Hotplug is not supported for device type %s" % device_type
2052 raise errors.HypervisorError(msg)
2053
2054 if dev_type == constants.HOTPLUG_TARGET_DISK:
2055 if action == constants.HOTPLUG_ACTION_ADD:
2056 self.qmp.CheckDiskHotAddSupport()
2057 if dev_type == constants.HOTPLUG_TARGET_NIC:
2058 if action == constants.HOTPLUG_ACTION_ADD:
2059 self.qmp.CheckNicHotAddSupport()
2060
2061 def HotplugSupported(self, instance):
2062 """Checks if hotplug is generally supported.
2063
2064 Hotplug is *not* supported in case of:
2065 - qemu versions < 1.7 (where all qmp related commands are supported)
2066 - for stopped instances
2067
2068 @raise errors.HypervisorError: in one of the previous cases
2069
2070 """
2071 try:
2072 output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2073 except errors.HypervisorError:
2074 raise errors.HotplugError("Instance is probably down")
2075
2076 match = self._INFO_VERSION_RE.search(output.stdout)
2077 if not match:
2078 raise errors.HotplugError("Cannot parse qemu version via monitor")
2079
2080 #TODO: delegate more fine-grained checks to VerifyHotplugSupport
2081 v_major, v_min, _, _ = match.groups()
2082 if (int(v_major), int(v_min)) < (1, 7):
2083 raise errors.HotplugError("Hotplug not supported for qemu versions < 1.7")
2084
2085 def _GetBusSlots(self, hvp=None, runtime=None):
2086 """Helper function to get the slots of PCI and SCSI QEMU buses.
2087
2088 This will return the status of the first PCI and SCSI buses. By default
2089 QEMU boots with one PCI bus (pci.0) and occupies the first 3 PCI slots. If
2090 a SCSI disk is found then a SCSI controller is added on the PCI bus and a
2091 SCSI bus (scsi.0) is created.
2092
2093 During hotplug we could query QEMU via info qtree HMP command but parsing
2094 the result is too complicated. Instead we use the info stored in runtime
2095 files. We parse NIC and disk entries and based on their hvinfo we reserve
2096 the corresponding slots.
2097
2098 The runtime argument is a tuple as returned by _LoadKVMRuntime(). Obtain
2099 disks and NICs from it. In case a runtime file is not available (see
2100 _GenerateKVMRuntime()) we return the bus slots that QEMU boots with by
2101 default.
2102
2103 """
2104 # This is by default and returned during _GenerateKVMRuntime()
2105 bus_slots = {
2106 _PCI_BUS: bitarray(self._DEFAULT_PCI_RESERVATIONS),
2107 _SCSI_BUS: bitarray(self._DEFAULT_SCSI_RESERVATIONS),
2108 }
2109
2110 # Adjust the empty slots depending of the corresponding hvparam
2111 if hvp and constants.HV_KVM_PCI_RESERVATIONS in hvp:
2112 res = hvp[constants.HV_KVM_PCI_RESERVATIONS]
2113 pci = bitarray(constants.QEMU_PCI_SLOTS)
2114 pci.setall(False) # pylint: disable=E1101
2115 pci[0:res:1] = True
2116 bus_slots[_PCI_BUS] = pci
2117
2118 # This is during hot-add
2119 if runtime:
2120 _, nics, _, disks = runtime
2121 disks = [d for d, _, _ in disks]
2122 for d in disks + nics:
2123 if not d.hvinfo or "bus" not in d.hvinfo:
2124 continue
2125 bus = d.hvinfo["bus"]
2126 slots = bus_slots[bus]
2127 if bus == _PCI_BUS:
2128 slot = d.hvinfo["addr"]
2129 slots[int(slot, 16)] = True
2130 elif bus == _SCSI_BUS:
2131 slot = d.hvinfo["scsi-id"]
2132 slots[slot] = True
2133
2134 return bus_slots
2135
2136 @_with_qmp
2137 def _VerifyHotplugCommand(self, _instance, kvm_devid, should_exist):
2138 """Checks if a previous hotplug command has succeeded.
2139
2140 Depending on the should_exist value, verifies that an entry identified by
2141 device ID is present or not.
2142
2143 @raise errors.HypervisorError: if result is not the expected one
2144
2145 """
2146 for i in range(5):
2147 found = self.qmp.HasDevice(kvm_devid)
2148 logging.info("Verifying hotplug command (retry %s): %s", i, found)
2149 if found and should_exist:
2150 break
2151 if not found and not should_exist:
2152 break
2153 time.sleep(1)
2154
2155 if found and not should_exist:
2156 msg = "Device %s should have been removed but is still there" % kvm_devid
2157 raise errors.HypervisorError(msg)
2158
2159 if not found and should_exist:
2160 msg = "Device %s should have been added but is missing" % kvm_devid
2161 raise errors.HypervisorError(msg)
2162
2163 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
2164
2165 @_with_qmp
2166 def HotAddDevice(self, instance, dev_type, device, extra, seq):
2167 """ Helper method to hot-add a new device
2168
2169 It generates the device ID and hvinfo, and invokes the
2170 device-specific method.
2171
2172 """
2173 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2174 runtime = self._LoadKVMRuntime(instance)
2175 up_hvp = runtime[2]
2176 device_type = _DEVICE_TYPE[dev_type](up_hvp)
2177 bus_state = self._GetBusSlots(up_hvp, runtime)
2178 # in case of hot-mod this is given
2179 if not device.hvinfo:
2180 device.hvinfo = _GenerateDeviceHVInfo(dev_type, kvm_devid,
2181 device_type, bus_state)
2182 if dev_type == constants.HOTPLUG_TARGET_DISK:
2183 uri = _GetDriveURI(device, extra[0], extra[1])
2184
2185 def drive_add_fn(filename):
2186 """Helper function that uses HMP to hot-add a drive."""
2187 cmd = "drive_add dummy file=%s,if=none,id=%s,format=raw" % \
2188 (filename, kvm_devid)
2189 self._CallMonitorCommand(instance.name, cmd)
2190
2191 # This must be done indirectly due to the fact that we pass the drive's
2192 # file descriptor via QMP first, then we add the corresponding drive that
2193 # refers to this fd. Note that if the QMP connection terminates before
2194 # a drive which keeps a reference to the fd passed via the add-fd QMP
2195 # command has been created, then the fd gets closed and cannot be used
2196 # later (e.g., via an drive_add HMP command).
2197 self.qmp.HotAddDisk(device, kvm_devid, uri, drive_add_fn)
2198 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2199 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2200 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2201 devlist = self._GetKVMOutput(kvmpath, self._KVMOPT_DEVICELIST)
2202 features, _, _ = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
2203 (tap, tapfds, vhostfds) = OpenTap(features=features)
2204 self._ConfigureNIC(instance, seq, device, tap)
2205 self.qmp.HotAddNic(device, kvm_devid, tapfds, vhostfds, features)
2206 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2207
2208 self._VerifyHotplugCommand(instance, kvm_devid, True)
2209 # update relevant entries in runtime file
2210 index = _DEVICE_RUNTIME_INDEX[dev_type]
2211 entry = _RUNTIME_ENTRY[dev_type](device, extra)
2212 runtime[index].append(entry)
2213 self._SaveKVMRuntime(instance, runtime)
2214
2215 @_with_qmp
2216 def HotDelDevice(self, instance, dev_type, device, _, seq):
2217 """ Helper method for hot-del device
2218
2219 It gets device info from runtime file, generates the device name and
2220 invokes the device-specific method.
2221
2222 """
2223 runtime = self._LoadKVMRuntime(instance)
2224 entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2225 kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2226 kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2227 if dev_type == constants.HOTPLUG_TARGET_DISK:
2228 self.qmp.HotDelDisk(kvm_devid)
2229 # drive_del is not implemented yet in qmp
2230 command = "drive_del %s\n" % kvm_devid
2231 self._CallMonitorCommand(instance.name, command)
2232 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2233 self.qmp.HotDelNic(kvm_devid)
2234 utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2235 self._VerifyHotplugCommand(instance, kvm_devid, False)
2236 index = _DEVICE_RUNTIME_INDEX[dev_type]
2237 runtime[index].remove(entry)
2238 self._SaveKVMRuntime(instance, runtime)
2239
2240 return kvm_device.hvinfo
2241
2242 def HotModDevice(self, instance, dev_type, device, _, seq):
2243 """ Helper method for hot-mod device
2244
2245 It gets device info from runtime file, generates the device name and
2246 invokes the device-specific method. Currently only NICs support hot-mod
2247
2248 """
2249 if dev_type == constants.HOTPLUG_TARGET_NIC:
2250 # putting it back in the same bus and slot
2251 device.hvinfo = self.HotDelDevice(instance, dev_type, device, _, seq)
2252 self.HotAddDevice(instance, dev_type, device, _, seq)
2253
2254 @classmethod
2255 def _ParseKVMVersion(cls, text):
2256 """Parse the KVM version from the --help output.
2257
2258 @type text: string
2259 @param text: output of kvm --help
2260 @return: (version, v_maj, v_min, v_rev)
2261 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2262
2263 """
2264 match = cls._VERSION_RE.search(text.splitlines()[0])
2265 if not match:
2266 raise errors.HypervisorError("Unable to get KVM version")
2267
2268 v_all = match.group(0)
2269 v_maj = int(match.group(1))
2270 v_min = int(match.group(2))
2271 if match.group(4):
2272 v_rev = int(match.group(4))
2273 else:
2274 v_rev = 0
2275 return (v_all, v_maj, v_min, v_rev)
2276
2277 @classmethod
2278 def _GetKVMOutput(cls, kvm_path, option):
2279 """Return the output of a kvm invocation
2280
2281 @type kvm_path: string
2282 @param kvm_path: path to the kvm executable
2283 @type option: a key of _KVMOPTS_CMDS
2284 @param option: kvm option to fetch the output from
2285 @return: output a supported kvm invocation
2286 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2287
2288 """
2289 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2290
2291 optlist, can_fail = cls._KVMOPTS_CMDS[option]
2292
2293 result = utils.RunCmd([kvm_path] + optlist)
2294 if result.failed and not can_fail:
2295 raise errors.HypervisorError("Unable to get KVM %s output" %
2296 " ".join(optlist))
2297 return result.output
2298
2299 @classmethod
2300 def _GetKVMVersion(cls, kvm_path):
2301 """Return the installed KVM version.
2302
2303 @return: (version, v_maj, v_min, v_rev)
2304 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2305
2306 """
2307 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2308
2309 @classmethod
2310 def _GetDefaultMachineVersion(cls, kvm_path):
2311 """Return the default hardware revision (e.g. pc-1.1)
2312
2313 """
2314 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2315 match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2316 if match:
2317 return match.group(1)
2318 else:
2319 return "pc"
2320
2321 @classmethod
2322 def _StopInstance(cls, instance, force=False, name=None, timeout=None):
2323 """Stop an instance.
2324
2325 """
2326 assert(timeout is None or force is not None)
2327
2328 if name is not None and not force:
2329 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2330 if name is None:
2331 name = instance.name
2332 acpi = instance.hvparams[constants.HV_ACPI]
2333 else:
2334 acpi = False
2335 _, pid, alive = cls._InstancePidAlive(name)
2336 if pid > 0 and alive:
2337 if force or not acpi:
2338 utils.KillProcess(pid)
2339 else:
2340 cls._CallMonitorCommand(name, "system_powerdown", timeout)
2341 cls._ClearUserShutdown(instance.name)
2342
2343 def StopInstance(self, instance, force=False, retry=False, name=None,
2344 timeout=None):
2345 """Stop an instance.
2346
2347 """
2348 self._StopInstance(instance, force, name=name, timeout=timeout)
2349
2350 def CleanupInstance(self, instance_name):
2351 """Cleanup after a stopped instance
2352
2353 """
2354 pidfile, pid, alive = self._InstancePidAlive(instance_name)
2355 if pid > 0 and alive:
2356 raise errors.HypervisorError("Cannot cleanup a live instance")
2357 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2358 self._ClearUserShutdown(instance_name)
2359
2360 def RebootInstance(self, instance):
2361 """Reboot an instance.
2362
2363 """
2364 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2365 # socket the instance will stop, but now power up again. So we'll resort
2366 # to shutdown and restart.
2367 _, _, alive = self._InstancePidAlive(instance.name)
2368 if not alive:
2369 raise errors.HypervisorError("Failed to reboot instance %s:"
2370 " not running" % instance.name)
2371 # StopInstance will delete the saved KVM runtime so:
2372 # ...first load it...
2373 kvm_runtime = self._LoadKVMRuntime(instance)
2374 # ...now we can safely call StopInstance...
2375 if not self.StopInstance(instance):
2376 self.StopInstance(instance, force=True)
2377 # ...and finally we can save it again, and execute it...
2378 self._SaveKVMRuntime(instance, kvm_runtime)
2379 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2380 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2381 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2382
2383 def MigrationInfo(self, instance):
2384 """Get instance information to perform a migration.
2385
2386 @type instance: L{objects.Instance}
2387 @param instance: instance to be migrated
2388 @rtype: string
2389 @return: content of the KVM runtime file
2390
2391 """
2392 return self._ReadKVMRuntime(instance.name)
2393
2394 def AcceptInstance(self, instance, info, target):
2395 """Prepare to accept an instance.
2396
2397 @type instance: L{objects.Instance}
2398 @param instance: instance to be accepted
2399 @type info: string
2400 @param info: content of the KVM runtime file on the source node
2401 @type target: string
2402 @param target: target host (usually ip), on this node
2403
2404 """
2405 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2406 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2407 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2408 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2409 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2410 incoming=incoming_address)
2411
2412 def _ConfigureRoutedNICs(self, instance, info):
2413 """Configures all NICs in routed mode
2414
2415 @type instance: L{objects.Instance}
2416 @param instance: the instance to be configured
2417 @type info: string
2418 @param info: serialized KVM runtime info
2419 """
2420 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2421 kvm_nics = kvm_runtime[1]
2422
2423 for nic_seq, nic in enumerate(kvm_nics):
2424 if nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_ROUTED:
2425 # Bridged/OVS interfaces have already been configured
2426 continue
2427 try:
2428 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2429 except EnvironmentError, err:
2430 logging.warning("Failed to find host interface for %s NIC #%d: %s",
2431 instance.name, nic_seq, str(err))
2432 continue
2433 try:
2434 self._ConfigureNIC(instance, nic_seq, nic, tap)
2435 except errors.HypervisorError, err:
2436 logging.warning(str(err))
2437
2438 def FinalizeMigrationDst(self, instance, info, success):
2439 """Finalize the instance migration on the target node.
2440
2441 Stop the incoming mode KVM.
2442
2443 @type instance: L{objects.Instance}
2444 @param instance: instance whose migration is being finalized
2445
2446 """
2447 if success:
2448 self._ConfigureRoutedNICs(instance, info)
2449 self._WriteKVMRuntime(instance.name, info)
2450 else:
2451 self.StopInstance(instance, force=True)
2452
2453 def MigrateInstance(self, cluster_name, instance, target, live_migration):
2454 """Migrate an instance to a target node.
2455
2456 The migration will not be attempted if the instance is not
2457 currently running.
2458
2459 @type cluster_name: string
2460 @param cluster_name: name of the cluster
2461 @type instance: L{objects.Instance}
2462 @param instance: the instance to be migrated
2463 @type target: string
2464 @param target: ip address of the target node
2465 @type live_migration: boolean
2466 @param live_migration: perform a live migration
2467
2468 """
2469 instance_name = instance.name
2470 port = instance.hvparams[constants.HV_MIGRATION_PORT]
2471 _, _, alive = self._InstancePidAlive(instance_name)
2472 if not alive:
2473 raise errors.HypervisorError("Instance not running, cannot migrate")
2474
2475 if not live_migration:
2476 self._CallMonitorCommand(instance_name, "stop")
2477
2478 migrate_command = ("migrate_set_speed %dm" %
2479 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2480 self._CallMonitorCommand(instance_name, migrate_command)
2481
2482 migrate_command = ("migrate_set_downtime %dms" %
2483 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2484 self._CallMonitorCommand(instance_name, migrate_command)
2485
2486 migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS]
2487 if migration_caps:
2488 for c in migration_caps.split(_MIGRATION_CAPS_DELIM):
2489 migrate_command = ("migrate_set_capability %s on" % c)
2490 self._CallMonitorCommand(instance_name, migrate_command)
2491
2492 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2493 self._CallMonitorCommand(instance_name, migrate_command)
2494
2495 def FinalizeMigrationSource(self, instance, success, _):
2496 """Finalize the instance migration on the source node.
2497
2498 @type instance: L{objects.Instance}
2499 @param instance: the instance that was migrated
2500 @type success: bool
2501 @param success: whether the migration succeeded or not
2502
2503 """
2504 if success:
2505 pidfile, pid, _ = self._InstancePidAlive(instance.name)
2506 utils.KillProcess(pid)
2507 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2508 self._ClearUserShutdown(instance.name)
2509 else:
2510 # Detect if PID is alive rather than deciding if we were to perform a live
2511 # migration.
2512 _, _, alive = self._InstancePidAlive(instance.name)
2513 if alive:
2514 self._CallMonitorCommand(instance.name, self._CONT_CMD)
2515 else:
2516 self.CleanupInstance(instance.name)
2517
2518 def GetMigrationStatus(self, instance):
2519 """Get the migration status
2520
2521 @type instance: L{objects.Instance}
2522 @param instance: the instance that is being migrated
2523 @rtype: L{objects.MigrationStatus}
2524 @return: the status of the current migration (one of
2525 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2526 progress info that can be retrieved from the hypervisor
2527
2528 """
2529 info_command = "info migrate"
2530 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2531 result = self._CallMonitorCommand(instance.name, info_command)
2532 match = self._MIGRATION_STATUS_RE.search(result.stdout)
2533 if not match:
2534 if not result.stdout:
2535 logging.info("KVM: empty 'info migrate' result")
2536 else:
2537 logging.warning("KVM: unknown 'info migrate' result: %s",
2538 result.stdout)
2539 else:
2540 status = match.group(1)
2541 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2542 migration_status = objects.MigrationStatus(status=status)
2543 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2544 if match:
2545 migration_status.transferred_ram = match.group("transferred")
2546 migration_status.total_ram = match.group("total")
2547
2548 return migration_status
2549
2550 logging.warning("KVM: unknown migration status '%s'", status)
2551
2552 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2553
2554 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2555
2556 def BalloonInstanceMemory(self, instance, mem):
2557 """Balloon an instance memory to a certain value.
2558
2559 @type instance: L{objects.Instance}
2560 @param instance: instance to be accepted
2561 @type mem: int
2562 @param mem: actual memory size to use for instance runtime
2563
2564 """
2565 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2566
2567 def GetNodeInfo(self, hvparams=None):
2568 """Return information about the node.
2569
2570 @type hvparams: dict of strings
2571 @param hvparams: hypervisor parameters, not used in this class
2572
2573 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2574 the following keys:
2575 - hv_version: the hypervisor version in the form (major, minor,
2576 revision)
2577
2578 """
2579 result = self.GetLinuxNodeInfo()
2580 kvmpath = constants.KVM_PATH
2581 if hvparams is not None:
2582 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2583 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2584 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2585 return result
2586
2587 @classmethod
2588 def GetInstanceConsole(cls, instance, primary_node, node_group,
2589 hvparams, beparams):
2590 """Return a command for connecting to the console of an instance.
2591
2592 """
2593 if hvparams[constants.HV_SERIAL_CONSOLE]:
2594 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2595 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2596 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2597 "STDIO,%s" % cls._SocatUnixConsoleParams(),
2598 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2599 ndparams = node_group.FillND(primary_node)
2600 return objects.InstanceConsole(instance=instance.name,
2601 kind=constants.CONS_SSH,
2602 host=primary_node.name,
2603 port=ndparams.get(constants.ND_SSH_PORT),
2604 user=constants.SSH_CONSOLE_USER,
2605 command=cmd)
2606
2607 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2608 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2609 display = instance.network_port - constants.VNC_BASE_PORT
2610 return objects.InstanceConsole(instance=instance.name,
2611 kind=constants.CONS_VNC,
2612 host=vnc_bind_address,
2613 port=instance.network_port,
2614 display=display)
2615
2616 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2617 if spice_bind:
2618 return objects.InstanceConsole(instance=instance.name,
2619 kind=constants.CONS_SPICE,
2620 host=spice_bind,
2621 port=instance.network_port)
2622
2623 return objects.InstanceConsole(instance=instance.name,
2624 kind=constants.CONS_MESSAGE,
2625 message=("No serial shell for instance %s" %
2626 instance.name))
2627
2628 def Verify(self, hvparams=None):
2629 """Verify the hypervisor.
2630
2631 Check that the required binaries exist.
2632
2633 @type hvparams: dict of strings
2634 @param hvparams: hypervisor parameters to be verified against, not used here
2635
2636 @return: Problem description if something is wrong, C{None} otherwise
2637
2638 """
2639 msgs = []
2640 kvmpath = constants.KVM_PATH
2641 if hvparams is not None:
2642 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2643 if not os.path.exists(kvmpath):
2644 msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2645 if not os.path.exists(constants.SOCAT_PATH):
2646 msgs.append("The socat binary ('%s') does not exist" %
2647 constants.SOCAT_PATH)
2648
2649 return self._FormatVerifyResults(msgs)
2650
2651 @classmethod
2652 def CheckParameterSyntax(cls, hvparams):
2653 """Check the given parameters for validity.
2654
2655 @type hvparams: dict of strings
2656 @param hvparams: hypervisor parameters
2657 @raise errors.HypervisorError: when a parameter is not valid
2658
2659 """
2660 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2661
2662 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2663 if kernel_path:
2664 if not hvparams[constants.HV_ROOT_PATH]:
2665 raise errors.HypervisorError("Need a root partition for the instance,"
2666 " if a kernel is defined")
2667
2668 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2669 not hvparams[constants.HV_VNC_X509]):
2670 raise errors.HypervisorError("%s must be defined, if %s is" %
2671 (constants.HV_VNC_X509,
2672 constants.HV_VNC_X509_VERIFY))
2673
2674 if hvparams[constants.HV_SERIAL_CONSOLE]:
2675 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2676 valid_speeds = constants.VALID_SERIAL_SPEEDS
2677 if not serial_speed or serial_speed not in valid_speeds:
2678 raise errors.HypervisorError("Invalid serial console speed, must be"
2679 " one of: %s" %
2680 utils.CommaJoin(valid_speeds))
2681
2682 boot_order = hvparams[constants.HV_BOOT_ORDER]
2683 if (boot_order == constants.HT_BO_CDROM and
2684 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2685 raise errors.HypervisorError("Cannot boot from cdrom without an"
2686 " ISO path")
2687
2688 security_model = hvparams[constants.HV_SECURITY_MODEL]
2689 if security_model == constants.HT_SM_USER:
2690 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2691 raise errors.HypervisorError("A security domain (user to run kvm as)"
2692 " must be specified")
2693 elif (security_model == constants.HT_SM_NONE or
2694 security_model == constants.HT_SM_POOL):
2695 if hvparams[constants.HV_SECURITY_DOMAIN]:
2696 raise errors.HypervisorError("Cannot have a security domain when the"
2697 " security model is 'none' or 'pool'")
2698
2699 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2700 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2701 if spice_bind:
2702 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2703 # if an IP version is specified, the spice_bind parameter must be an
2704 # IP of that family
2705 if (netutils.IP4Address.IsValid(spice_bind) and
2706 spice_ip_version != constants.IP4_VERSION):
2707 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2708 " the specified IP version is %s" %
2709 (spice_bind, spice_ip_version))
2710
2711 if (netutils.IP6Address.IsValid(spice_bind) and
2712 spice_ip_version != constants.IP6_VERSION):
2713 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2714 " the specified IP version is %s" %
2715 (spice_bind, spice_ip_version))
2716 else:
2717 # All the other SPICE parameters depend on spice_bind being set. Raise an
2718 # error if any of them is set without it.
2719 for param in _SPICE_ADDITIONAL_PARAMS:
2720 if hvparams[param]:
2721 raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2722 (param, constants.HV_KVM_SPICE_BIND))
2723
2724 @classmethod
2725 def ValidateParameters(cls, hvparams):
2726 """Check the given parameters for validity.
2727
2728 @type hvparams: dict of strings
2729 @param hvparams: hypervisor parameters
2730 @raise errors.HypervisorError: when a parameter is not valid
2731
2732 """
2733 super(KVMHypervisor, cls).ValidateParameters(hvparams)
2734
2735 kvm_path = hvparams[constants.HV_KVM_PATH]
2736
2737 security_model = hvparams[constants.HV_SECURITY_MODEL]
2738 if security_model == constants.HT_SM_USER:
2739 username = hvparams[constants.HV_SECURITY_DOMAIN]
2740 try:
2741 pwd.getpwnam(username)
2742 except KeyError:
2743 raise errors.HypervisorError("Unknown security domain user %s"
2744 % username)
2745 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2746 if vnc_bind_address:
2747 bound_to_addr = (netutils.IP4Address.IsValid(vnc_bind_address) or
2748 netutils.IP6Address.IsValid(vnc_bind_address))
2749 is_interface = netutils.IsValidInterface(vnc_bind_address)
2750 is_path = utils.IsNormAbsPath(vnc_bind_address)
2751 if not bound_to_addr and not is_interface and not is_path:
2752 raise errors.HypervisorError("VNC: The %s parameter must be either"
2753 " a valid IP address, an interface name,"
2754 " or an absolute path" %
2755 constants.HV_VNC_BIND_ADDRESS)
2756
2757 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2758 if spice_bind:
2759 # only one of VNC and SPICE can be used currently.
2760 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2761 raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2762 " only one of them can be used at a"
2763 " given time")
2764
2765 # check that KVM supports SPICE
2766 kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2767 if not cls._SPICE_RE.search(kvmhelp):
2768 raise errors.HypervisorError("SPICE is configured, but it is not"
2769 " supported according to 'kvm --help'")
2770
2771 # if spice_bind is not an IP address, it must be a valid interface
2772 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2773 netutils.IP6Address.IsValid(spice_bind))
2774 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2775 raise errors.HypervisorError("SPICE: The %s parameter must be either"
2776 " a valid IP address or interface name" %
2777 constants.HV_KVM_SPICE_BIND)
2778
2779 machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2780 if machine_version:
2781 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2782 if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2783 raise errors.HypervisorError("Unsupported machine version: %s" %
2784 machine_version)
2785
2786 @classmethod
2787 def PowercycleNode(cls, hvparams=None):
2788 """KVM powercycle, just a wrapper over Linux powercycle.
2789
2790 @type hvparams: dict of strings
2791 @param hvparams: hypervisor parameters to be used on this node
2792
2793 """
2794 cls.LinuxPowercycle()