1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
48 except ImportError:
49 psutil = None
50 try:
51 import fdsend
52 except ImportError:
53 fdsend = None
54
55 from ganeti import utils
56 from ganeti import constants
57 from ganeti import errors
58 from ganeti import serializer
59 from ganeti import objects
60 from ganeti import uidpool
61 from ganeti import ssconf
62 from ganeti import netutils
63 from ganeti import pathutils
64 from ganeti.hypervisor import hv_base
65 from ganeti.utils import wrapper as utils_wrapper
66
67 from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \
68 MonitorSocket
69 from ganeti.hypervisor.hv_kvm.netdev import OpenTap
70
71
72 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
73 _KVM_START_PAUSED_FLAG = "-S"
74
75
76 _SPICE_ADDITIONAL_PARAMS = frozenset([
77 constants.HV_KVM_SPICE_IP_VERSION,
78 constants.HV_KVM_SPICE_PASSWORD_FILE,
79 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
80 constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
81 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
82 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
83 constants.HV_KVM_SPICE_USE_TLS,
84 ])
85
86
87
88
89
90 _KVM_NICS_RUNTIME_INDEX = 1
91 _KVM_DISKS_RUNTIME_INDEX = 3
92 _DEVICE_RUNTIME_INDEX = {
93 constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX,
94 constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX
95 }
96 _FIND_RUNTIME_ENTRY = {
97 constants.HOTPLUG_TARGET_NIC:
98 lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid],
99 constants.HOTPLUG_TARGET_DISK:
100 lambda disk, kvm_disks: [(d, l, u) for (d, l, u) in kvm_disks
101 if d.uuid == disk.uuid]
102 }
103 _RUNTIME_DEVICE = {
104 constants.HOTPLUG_TARGET_NIC: lambda d: d,
105 constants.HOTPLUG_TARGET_DISK: lambda (d, e, _): d
106 }
107 _RUNTIME_ENTRY = {
108 constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
109 constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e[0], e[1])
110 }
111
112 _DEVICE_TYPE = {
113 constants.HOTPLUG_TARGET_NIC: lambda hvp: hvp[constants.HV_NIC_TYPE],
114 constants.HOTPLUG_TARGET_DISK: lambda hvp: hvp[constants.HV_DISK_TYPE],
115 }
116
117 _DEVICE_DRIVER = {
118 constants.HOTPLUG_TARGET_NIC:
119 lambda ht: "virtio-net-pci" if ht == constants.HT_NIC_PARAVIRTUAL else ht,
120 constants.HOTPLUG_TARGET_DISK:
121 lambda ht: "virtio-blk-pci" if ht == constants.HT_DISK_PARAVIRTUAL else ht,
122 }
123
124
125
126
127
128 _DEVICE_BUS = {
129 constants.HOTPLUG_TARGET_NIC:
130 lambda _: _PCI_BUS,
131 constants.HOTPLUG_TARGET_DISK:
132 lambda ht: _SCSI_BUS if ht in constants.HT_SCSI_DEVICE_TYPES else _PCI_BUS
133 }
134
135 _HOTPLUGGABLE_DEVICE_TYPES = {
136
137 constants.HOTPLUG_TARGET_NIC: [
138 constants.HT_NIC_E1000,
139 constants.HT_NIC_I82551,
140 constants.HT_NIC_I8259ER,
141 constants.HT_NIC_I85557B,
142 constants.HT_NIC_NE2K_PCI,
143 constants.HT_NIC_PARAVIRTUAL,
144 constants.HT_NIC_PCNET,
145 constants.HT_NIC_RTL8139,
146 ],
147 constants.HOTPLUG_TARGET_DISK: [
148 constants.HT_DISK_PARAVIRTUAL,
149 constants.HT_DISK_SCSI_BLOCK,
150 constants.HT_DISK_SCSI_GENERIC,
151 constants.HT_DISK_SCSI_HD,
152 constants.HT_DISK_SCSI_CD,
153 ]
154 }
155
156 _PCI_BUS = "pci.0"
157 _SCSI_BUS = "scsi.0"
158
159 _MIGRATION_CAPS_DELIM = ":"
163 """Wrapper used on hotplug related methods"""
164 def wrapper(self, instance, *args, **kwargs):
165 """Create a QmpConnection and run the wrapped method"""
166 if not getattr(self, "qmp", None):
167 filename = self._InstanceQmpMonitor(instance.name)
168 self.qmp = QmpConnection(filename)
169 return fn(self, instance, *args, **kwargs)
170 return wrapper
171
174 """Helper function to get the drive uri to be used in --drive kvm option
175
176 Invoked during startup and disk hot-add. In latter case and if no userspace
177 access mode is used it will be overriden with /dev/fdset/<fdset-id> (see
178 HotAddDisk() and AddFd() of QmpConnection).
179
180 @type disk: L{objects.Disk}
181 @param disk: A disk configuration object
182 @type link: string
183 @param link: The device link as returned by _SymlinkBlockDev()
184 @type uri: string
185 @param uri: The drive uri as returned by _CalculateDeviceURI()
186
187 @return: The drive uri to use in kvm option
188
189 """
190 access_mode = disk.params.get(constants.LDP_ACCESS,
191 constants.DISK_KERNELSPACE)
192
193 if (uri and access_mode == constants.DISK_USERSPACE):
194 drive_uri = uri
195
196 else:
197 drive_uri = link
198
199 return drive_uri
200
203 """Helper function to generate a unique device name used by KVM
204
205 QEMU monitor commands use names to identify devices. Since the UUID
206 is too long for a device ID (36 chars vs. 30), we choose to use
207 only the part until the third '-' with a disk/nic prefix.
208 For example if a disk has UUID '932df160-7a22-4067-a566-7e0ca8386133'
209 the resulting device ID would be 'disk-932df160-7a22-4067'.
210
211 @type dev_type: string
212 @param dev_type: device type of param dev (HOTPLUG_TARGET_DISK|NIC)
213 @type dev: L{objects.Disk} or L{objects.NIC}
214 @param dev: the device object for which we generate a kvm name
215
216 """
217 return "%s-%s" % (dev_type.lower(), dev.uuid.rsplit("-", 2)[0])
218
221 """Construct the -device option string for hvinfo dict
222
223 PV disk: virtio-blk-pci,id=disk-1234,bus=pci.0,addr=0x9
224 PV NIC: virtio-net-pci,id=nic-1234,bus=pci.0,addr=0x9
225 SG disk: scsi-generic,id=disk-1234,bus=scsi.0,channel=0,scsi-id=1,lun=0
226
227 @type hvinfo: dict
228 @param hvinfo: dictionary created by _GenerateDeviceHVInfo()
229
230 @rtype: string
231 @return: The constructed string to be passed along with a -device option
232
233 """
234
235
236 d = dict(hvinfo)
237 hvinfo_str = d.pop("driver")
238 for k, v in d.items():
239 hvinfo_str += ",%s=%s" % (k, v)
240
241 return hvinfo_str
242
245 """Helper function to generate hvinfo of a device (disk, NIC)
246
247 hvinfo will hold all necessary info for generating the -device QEMU option.
248 We have two main buses: a PCI bus and a SCSI bus (created by a SCSI
249 controller on the PCI bus).
250
251 In case of PCI devices we add them on a free PCI slot (addr) on the first PCI
252 bus (pci.0), and in case of SCSI devices we decide to put each disk on a
253 different SCSI target (scsi-id) on the first SCSI bus (scsi.0).
254
255 @type dev_type: string
256 @param dev_type: either HOTPLUG_TARGET_DISK or HOTPLUG_TARGET_NIC
257 @type kvm_devid: string
258 @param kvm_devid: the id of the device
259 @type hv_dev_type: string
260 @param hv_dev_type: either disk_type or nic_type hvparam
261 @type bus_slots: dict
262 @param bus_slots: the current slots of the first PCI and SCSI buses
263
264 @rtype: dict
265 @return: dict including all necessary info (driver, id, bus and bus location)
266 for generating a -device QEMU option for either a disk or a NIC
267
268 """
269 driver = _DEVICE_DRIVER[dev_type](hv_dev_type)
270 bus = _DEVICE_BUS[dev_type](hv_dev_type)
271 slots = bus_slots[bus]
272 slot = utils.GetFreeSlot(slots, reserve=True)
273
274 hvinfo = {
275 "driver": driver,
276 "id": kvm_devid,
277 "bus": bus,
278 }
279
280 if bus == _PCI_BUS:
281 hvinfo.update({
282 "addr": hex(slot),
283 })
284 elif bus == _SCSI_BUS:
285 hvinfo.update({
286 "channel": 0,
287 "scsi-id": slot,
288 "lun": 0,
289 })
290
291 return hvinfo
292
295 """Helper function to get an existing device inside the runtime file
296
297 Used when an instance is running. Load kvm runtime file and search
298 for a device based on its type and uuid.
299
300 @type dev_type: sting
301 @param dev_type: device type of param dev
302 @type device: L{objects.Disk} or L{objects.NIC}
303 @param device: the device object for which we generate a kvm name
304 @type runtime: tuple (cmd, nics, hvparams, disks)
305 @param runtime: the runtime data to search for the device
306 @raise errors.HotplugError: in case the requested device does not
307 exist (e.g. device has been added without --hotplug option)
308
309 """
310 index = _DEVICE_RUNTIME_INDEX[dev_type]
311 found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
312 if not found:
313 raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
314 (dev_type, device.uuid))
315
316 return found[0]
317
320 """Upgrade runtime data
321
322 Remove any deprecated fields or change the format of the data.
323 The runtime files are not upgraded when Ganeti is upgraded, so the required
324 modification have to be performed here.
325
326 @type serialized_runtime: string
327 @param serialized_runtime: raw text data read from actual runtime file
328 @return: (cmd, nic dicts, hvparams, bdev dicts)
329 @rtype: tuple
330
331 """
332 loaded_runtime = serializer.Load(serialized_runtime)
333 kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
334 if len(loaded_runtime) >= 4:
335 serialized_disks = loaded_runtime[3]
336 else:
337 serialized_disks = []
338
339 def update_hvinfo(dev, dev_type):
340 """ Remove deprecated pci slot and substitute it with hvinfo """
341 if "hvinfo" not in dev:
342 dev["hvinfo"] = {}
343 uuid = dev["uuid"]
344
345
346
347
348
349
350
351
352 if "pci" in dev:
353
354 dev["hvinfo"]["id"] = "hot%s-%s-%s-%s" % (dev_type.lower(),
355 uuid.split("-")[0],
356 "pci",
357 dev["pci"])
358 dev["hvinfo"]["addr"] = hex(dev["pci"])
359 dev["hvinfo"]["bus"] = _PCI_BUS
360 del dev["pci"]
361
362 for nic in serialized_nics:
363
364 if "uuid" not in nic:
365 nic["uuid"] = utils.NewUUID()
366 update_hvinfo(nic, constants.HOTPLUG_TARGET_NIC)
367
368 for disk_entry in serialized_disks:
369
370 update_hvinfo(disk_entry[0], constants.HOTPLUG_TARGET_DISK)
371
372 return kvm_cmd, serialized_nics, hvparams, serialized_disks
373
376 """Return runtime entries for a serialized runtime file
377
378 @type serialized_runtime: string
379 @param serialized_runtime: raw text data read from actual runtime file
380 @return: (cmd, nics, hvparams, bdevs)
381 @rtype: tuple
382
383 """
384 kvm_cmd, serialized_nics, hvparams, serialized_disks = \
385 _UpgradeSerializedRuntime(serialized_runtime)
386 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
387 kvm_disks = [(objects.Disk.FromDict(sdisk), link, uri)
388 for sdisk, link, uri in serialized_disks]
389
390 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
391
396
399 """Check if a given URL exists on the server
400
401 """
402 try:
403 urllib2.urlopen(HeadRequest(url))
404 return True
405 except urllib2.URLError:
406 return False
407
410 """KVM hypervisor interface
411
412 """
413 CAN_MIGRATE = True
414
415 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
416 _PIDS_DIR = _ROOT_DIR + "/pid"
417 _UIDS_DIR = _ROOT_DIR + "/uid"
418 _CTRL_DIR = _ROOT_DIR + "/ctrl"
419 _CONF_DIR = _ROOT_DIR + "/conf"
420 _NICS_DIR = _ROOT_DIR + "/nic"
421 _KEYMAP_DIR = _ROOT_DIR + "/keymap"
422
423 _CHROOT_DIR = _ROOT_DIR + "/chroot"
424
425
426
427
428
429 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
430 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
431 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
432
433 PARAMETERS = {
434 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
435 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
436 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
437 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
438 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
439 constants.HV_ACPI: hv_base.NO_CHECK,
440 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
441 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
442 constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK,
443 constants.HV_VNC_TLS: hv_base.NO_CHECK,
444 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
445 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
446 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
447 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK,
448 constants.HV_KVM_SPICE_IP_VERSION:
449 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
450 x in constants.VALID_IP_VERSIONS),
451 "The SPICE IP version should be 4 or 6",
452 None, None),
453 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
454 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
455 hv_base.ParamInSet(
456 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
457 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
458 hv_base.ParamInSet(
459 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
460 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
461 hv_base.ParamInSet(
462 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
463 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
464 hv_base.ParamInSet(
465 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
466 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
467 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
468 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
469 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
470 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
471 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
472 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
473 constants.HV_BOOT_ORDER:
474 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
475 constants.HV_NIC_TYPE:
476 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
477 constants.HV_DISK_TYPE:
478 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
479 constants.HV_KVM_SCSI_CONTROLLER_TYPE:
480 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SCSI_CONTROLLER_TYPES),
481 constants.HV_KVM_CDROM_DISK_TYPE:
482 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
483 constants.HV_USB_MOUSE:
484 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
485 constants.HV_KEYMAP: hv_base.NO_CHECK,
486 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
487 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
488 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
489 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
490 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
491 constants.HV_DISK_CACHE:
492 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
493 constants.HV_KVM_DISK_AIO:
494 hv_base.ParamInSet(False, constants.HT_KVM_VALID_AIO_TYPES),
495 constants.HV_SECURITY_MODEL:
496 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
497 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
498 constants.HV_KVM_FLAG:
499 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
500 constants.HV_VHOST_NET: hv_base.NO_CHECK,
501 constants.HV_VIRTIO_NET_QUEUES: hv_base.OPT_VIRTIO_NET_QUEUES_CHECK,
502 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
503 constants.HV_KVM_USER_SHUTDOWN: hv_base.NO_CHECK,
504 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
505 constants.HV_REBOOT_BEHAVIOR:
506 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
507 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
508 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
509 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
510 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
511 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
512 constants.HV_SOUNDHW: hv_base.NO_CHECK,
513 constants.HV_USB_DEVICES: hv_base.NO_CHECK,
514 constants.HV_VGA: hv_base.NO_CHECK,
515 constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
516 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
517 constants.HV_KVM_MIGRATION_CAPS: hv_base.NO_CHECK,
518 constants.HV_KVM_PCI_RESERVATIONS:
519 (False, lambda x: (x >= 0 and x <= constants.QEMU_PCI_SLOTS),
520 "The number of PCI slots managed by QEMU (max: %s)" %
521 constants.QEMU_PCI_SLOTS,
522 None, None),
523 constants.HV_VNET_HDR: hv_base.NO_CHECK,
524 }
525
526 _VIRTIO = "virtio"
527 _VIRTIO_NET_PCI = "virtio-net-pci"
528 _VIRTIO_BLK_PCI = "virtio-blk-pci"
529
530 _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)",
531 re.M | re.I)
532 _MIGRATION_PROGRESS_RE = \
533 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
534 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
535 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
536
537 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
538 _MIGRATION_INFO_RETRY_DELAY = 2
539
540 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
541
542 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
543 _CPU_INFO_CMD = "info cpus"
544 _CONT_CMD = "cont"
545
546 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
547 _CHECK_MACHINE_VERSION_RE = \
548 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
549
550 _QMP_RE = re.compile(r"^-qmp\s", re.M)
551 _SPICE_RE = re.compile(r"^-spice\s", re.M)
552 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
553 _VIRTIO_NET_QUEUES_RE = re.compile(r"^-net\s.*,fds=x:y:...:z", re.M)
554 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
555 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
556 _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
557 _DISPLAY_RE = re.compile(r"^-display\s", re.M)
558 _MACHINE_RE = re.compile(r"^-machine\s", re.M)
559 _DEVICE_DRIVER_SUPPORTED = \
560 staticmethod(lambda drv, devlist:
561 re.compile(r"^name \"%s\"" % drv, re.M).search(devlist))
562
563
564
565 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
566 _UUID_RE = re.compile(r"^-uuid\s", re.M)
567
568 _INFO_VERSION_RE = \
569 re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
570 _INFO_VERSION_CMD = "info version"
571
572
573
574
575
576
577
578
579
580
581
582
583 _DEFAULT_PCI_RESERVATIONS = "11111111111100000000000000000000"
584
585
586
587
588
589
590
591 _DEFAULT_SCSI_RESERVATIONS = "0000000000000000"
592
593 ANCILLARY_FILES = [
594 _KVM_NETWORK_SCRIPT,
595 ]
596 ANCILLARY_FILES_OPT = [
597 _KVM_NETWORK_SCRIPT,
598 ]
599
600
601 _KVMOPT_HELP = "help"
602 _KVMOPT_MLIST = "mlist"
603 _KVMOPT_DEVICELIST = "devicelist"
604
605
606
607 _KVMOPTS_CMDS = {
608 _KVMOPT_HELP: (["--help"], False),
609 _KVMOPT_MLIST: (["-M", "?"], False),
610 _KVMOPT_DEVICELIST: (["-device", "?"], True),
611 }
612
620
621 @staticmethod
630
631 @classmethod
633 """Returns the instance pidfile.
634
635 """
636 return utils.PathJoin(cls._PIDS_DIR, instance_name)
637
638 @classmethod
640 """Returns the instance uidfile.
641
642 """
643 return utils.PathJoin(cls._UIDS_DIR, instance_name)
644
645 @classmethod
647 """Check pid file for instance information.
648
649 Check that a pid file is associated with an instance, and retrieve
650 information from its command line.
651
652 @type pid: string or int
653 @param pid: process id of the instance to check
654 @rtype: tuple
655 @return: (instance_name, memory, vcpus)
656 @raise errors.HypervisorError: when an instance cannot be found
657
658 """
659 alive = utils.IsProcessAlive(pid)
660 if not alive:
661 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
662
663 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
664 try:
665 cmdline = utils.ReadFile(cmdline_file)
666 except EnvironmentError, err:
667 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
668 (pid, err))
669
670 instance = None
671 memory = 0
672 vcpus = 0
673
674 arg_list = cmdline.split("\x00")
675 while arg_list:
676 arg = arg_list.pop(0)
677 if arg == "-name":
678 instance = arg_list.pop(0)
679 elif arg == "-m":
680 memory = int(arg_list.pop(0))
681 elif arg == "-smp":
682 vcpus = int(arg_list.pop(0).split(",")[0])
683
684 if instance is None:
685 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
686 " instance" % pid)
687
688 return (instance, memory, vcpus)
689
690 @classmethod
692 """Returns the instance pidfile, pid, and liveness.
693
694 @type instance_name: string
695 @param instance_name: instance name
696 @rtype: tuple
697 @return: (pid file name, pid, liveness)
698
699 """
700 pidfile = cls._InstancePidFile(instance_name)
701 pid = utils.ReadPidFile(pidfile)
702
703 alive = False
704 try:
705 cmd_instance = cls._InstancePidInfo(pid)[0]
706 alive = (cmd_instance == instance_name)
707 except errors.HypervisorError:
708 pass
709
710 return (pidfile, pid, alive)
711
712 @classmethod
714 """Raises an error unless the given instance is down.
715
716 """
717 alive = cls._InstancePidAlive(instance_name)[2]
718 if alive:
719 raise errors.HypervisorError("Failed to start instance %s: %s" %
720 (instance_name, "already running"))
721
722 @classmethod
724 """Returns the instance monitor socket name
725
726 """
727 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
728
729 @classmethod
731 """Returns the instance serial socket name
732
733 """
734 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
735
736 @classmethod
738 """Returns the instance serial QMP socket name
739
740 """
741 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
742
743 @classmethod
745 """Returns the instance kvm daemon socket name
746
747 """
748 return utils.PathJoin(cls._CTRL_DIR, "%s.kvmd" % instance_name)
749
750 @classmethod
752 """Returns the instance QMP output filename
753
754 """
755 return utils.PathJoin(cls._CTRL_DIR, "%s.shutdown" % instance_name)
756
757 @staticmethod
759 """Returns the correct parameters for socat
760
761 If we have a new-enough socat we can use raw mode with an escape character.
762
763 """
764 if constants.SOCAT_USE_ESCAPE:
765 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
766 else:
767 return "echo=0,icanon=0"
768
769 @classmethod
771 """Returns the instance KVM runtime filename
772
773 """
774 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
775
776 @classmethod
778 """Returns the name of the KVM chroot dir of the instance
779
780 """
781 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
782
783 @classmethod
785 """Returns the name of the directory holding the tap device files for a
786 given instance.
787
788 """
789 return utils.PathJoin(cls._NICS_DIR, instance_name)
790
791 @classmethod
793 """Returns the name of the file containing the tap device for a given NIC
794
795 """
796 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
797
798 @classmethod
800 """Returns the name of the file containing the keymap for a given instance
801
802 """
803 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
804
805 @classmethod
807 """Try to read a uid file
808
809 """
810 if os.path.exists(uid_file):
811 try:
812 uid = int(utils.ReadOneLineFile(uid_file))
813 return uid
814 except EnvironmentError:
815 logging.warning("Can't read uid file", exc_info=True)
816 except (TypeError, ValueError):
817 logging.warning("Can't parse uid file contents", exc_info=True)
818 return None
819
820 @classmethod
822 """Removes an instance's rutime sockets/files/dirs.
823
824 """
825 utils.RemoveFile(pidfile)
826 utils.RemoveFile(cls._InstanceMonitor(instance_name))
827 utils.RemoveFile(cls._InstanceSerial(instance_name))
828 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
829 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
830 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
831 uid_file = cls._InstanceUidFile(instance_name)
832 uid = cls._TryReadUidFile(uid_file)
833 utils.RemoveFile(uid_file)
834 if uid is not None:
835 uidpool.ReleaseUid(uid)
836 try:
837 shutil.rmtree(cls._InstanceNICDir(instance_name))
838 except OSError, err:
839 if err.errno != errno.ENOENT:
840 raise
841 try:
842 chroot_dir = cls._InstanceChrootDir(instance_name)
843 utils.RemoveDir(chroot_dir)
844 except OSError, err:
845 if err.errno == errno.ENOTEMPTY:
846
847 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
848 prefix="%s-%s-" %
849 (instance_name,
850 utils.TimestampForFilename()))
851 logging.warning("The chroot directory of instance %s can not be"
852 " removed as it is not empty. Moving it to the"
853 " quarantine instead. Please investigate the"
854 " contents (%s) and clean up manually",
855 instance_name, new_chroot_dir)
856 utils.RenameFile(chroot_dir, new_chroot_dir)
857 else:
858 raise
859
860 @staticmethod
877
878 @classmethod
880 """Sets the affinity of a process to the given CPUs.
881
882 @type process_id: int
883 @type cpus: list of int
884 @param cpus: The list of CPUs the process ID may use.
885
886 """
887 if psutil is None:
888 raise errors.HypervisorError("psutil Python package not"
889 " found; cannot use CPU pinning under KVM")
890
891 target_process = psutil.Process(process_id)
892 if cpus == constants.CPU_PINNING_OFF:
893 target_process.set_cpu_affinity(range(psutil.cpu_count()))
894 else:
895 target_process.set_cpu_affinity(cpus)
896
897 @classmethod
899 """Change CPU affinity for running VM according to given CPU mask.
900
901 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
902 @type cpu_mask: string
903 @param process_id: process ID of KVM process. Used to pin entire VM
904 to physical CPUs.
905 @type process_id: int
906 @param thread_dict: map of virtual CPUs to KVM thread IDs
907 @type thread_dict: dict int:int
908
909 """
910
911 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
912
913 if len(cpu_list) == 1:
914 all_cpu_mapping = cpu_list[0]
915 if all_cpu_mapping == constants.CPU_PINNING_OFF:
916
917 pass
918 else:
919
920
921 cls._SetProcessAffinity(process_id, all_cpu_mapping)
922 else:
923
924
925
926 assert len(thread_dict) == len(cpu_list)
927
928
929 for i, vcpu in enumerate(cpu_list):
930 cls._SetProcessAffinity(thread_dict[i], vcpu)
931
933 """Get a mapping of vCPU no. to thread IDs for the instance
934
935 @type instance_name: string
936 @param instance_name: instance in question
937 @rtype: dictionary of int:int
938 @return: a dictionary mapping vCPU numbers to thread IDs
939
940 """
941 result = {}
942 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
943 for line in output.stdout.splitlines():
944 match = self._CPU_INFO_RE.search(line)
945 if not match:
946 continue
947 grp = map(int, match.groups())
948 result[grp[0]] = grp[1]
949
950 return result
951
953 """Complete CPU pinning.
954
955 @type instance_name: string
956 @param instance_name: name of instance
957 @type cpu_mask: string
958 @param cpu_mask: CPU pinning mask as entered by user
959
960 """
961
962 _, pid, _ = self._InstancePidAlive(instance_name)
963
964 thread_dict = self._GetVcpuThreadIds(instance_name)
965
966 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
967
969 """Get the list of running instances.
970
971 We can do this by listing our live instances directory and
972 checking whether the associated kvm process is still alive.
973
974 """
975 result = []
976 for name in os.listdir(self._PIDS_DIR):
977 if self._InstancePidAlive(name)[2]:
978 result.append(name)
979 return result
980
981 @classmethod
984
985 @classmethod
988
990 """Get instance properties.
991
992 @type instance_name: string
993 @param instance_name: the instance name
994 @type hvparams: dict of strings
995 @param hvparams: hypervisor parameters to be used with this instance
996 @rtype: tuple of strings
997 @return: (name, id, memory, vcpus, stat, times)
998
999 """
1000 _, pid, alive = self._InstancePidAlive(instance_name)
1001 if not alive:
1002 if self._IsUserShutdown(instance_name):
1003 return (instance_name, -1, 0, 0, hv_base.HvInstanceState.SHUTDOWN, 0)
1004 else:
1005 return None
1006
1007 _, memory, vcpus = self._InstancePidInfo(pid)
1008 istat = hv_base.HvInstanceState.RUNNING
1009 times = 0
1010
1011 try:
1012 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1013 qmp.connect()
1014 vcpus = len(qmp.Execute("query-cpus"))
1015
1016
1017 mem_bytes = qmp.Execute("query-balloon")[qmp.ACTUAL_KEY]
1018 memory = mem_bytes / 1048576
1019 except errors.HypervisorError:
1020 pass
1021
1022 return (instance_name, pid, memory, vcpus, istat, times)
1023
1025 """Get properties of all instances.
1026
1027 @type hvparams: dict of strings
1028 @param hvparams: hypervisor parameters
1029 @return: list of tuples (name, id, memory, vcpus, stat, times)
1030
1031 """
1032 data = []
1033 for name in os.listdir(self._PIDS_DIR):
1034 try:
1035 info = self.GetInstanceInfo(name)
1036 except errors.HypervisorError:
1037
1038 continue
1039 if info:
1040 data.append(info)
1041 return data
1042
1045 """Generate KVM options regarding instance's block devices.
1046
1047 @type up_hvp: dict
1048 @param up_hvp: the instance's runtime hypervisor parameters
1049 @type kvm_disks: list of tuples
1050 @param kvm_disks: list of tuples [(disk, link_name, uri)..]
1051 @type kvmhelp: string
1052 @param kvmhelp: output of kvm --help
1053 @type devlist: string
1054 @param devlist: output of kvm -device ?
1055 @rtype: list
1056 @return: list of command line options eventually used by kvm executable
1057
1058 """
1059 kernel_path = up_hvp[constants.HV_KERNEL_PATH]
1060 if kernel_path:
1061 boot_disk = False
1062 else:
1063 boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1064
1065
1066
1067 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1068
1069 dev_opts = []
1070 disk_type = up_hvp[constants.HV_DISK_TYPE]
1071
1072
1073 if disk_type == constants.HT_DISK_PARAVIRTUAL:
1074 driver = self._VIRTIO_BLK_PCI
1075 iface = self._VIRTIO
1076 else:
1077 driver = iface = disk_type
1078
1079
1080 if self._DEVICE_DRIVER_SUPPORTED(driver, devlist):
1081 if_val = ",if=none"
1082 device_driver = driver
1083 else:
1084 if_val = ",if=%s" % iface
1085 device_driver = None
1086
1087
1088 aio_mode = up_hvp[constants.HV_KVM_DISK_AIO]
1089 if aio_mode == constants.HT_KVM_AIO_NATIVE:
1090 aio_val = ",aio=%s" % aio_mode
1091 else:
1092 aio_val = ""
1093
1094 disk_cache = up_hvp[constants.HV_DISK_CACHE]
1095 for cfdev, link_name, uri in kvm_disks:
1096 if cfdev.dev_type in constants.DTS_EXT_MIRROR:
1097 if disk_cache != "none":
1098
1099 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1100 " to prevent shared storage corruption on migration",
1101 disk_cache)
1102 cache_val = ",cache=none"
1103 elif disk_cache != constants.HT_CACHE_DEFAULT:
1104 cache_val = ",cache=%s" % disk_cache
1105 else:
1106 cache_val = ""
1107 if cfdev.mode != constants.DISK_RDWR:
1108 raise errors.HypervisorError("Instance has read-only disks which"
1109 " are not supported by KVM")
1110
1111 boot_val = ""
1112 if boot_disk:
1113 dev_opts.extend(["-boot", "c"])
1114 boot_disk = False
1115 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1116 boot_val = ",boot=on"
1117
1118 drive_uri = _GetDriveURI(cfdev, link_name, uri)
1119
1120 drive_val = "file=%s,format=raw%s%s%s%s" % \
1121 (drive_uri, if_val, boot_val, cache_val, aio_val)
1122
1123
1124 if device_driver is not None:
1125
1126
1127
1128 kvm_devid = cfdev.hvinfo["id"]
1129 drive_val += ",id=%s" % kvm_devid
1130
1131 dev_val = _GenerateDeviceHVInfoStr(cfdev.hvinfo)
1132 dev_val += ",drive=%s" % kvm_devid
1133 dev_opts.extend(["-device", dev_val])
1134
1135 dev_opts.extend(["-drive", drive_val])
1136
1137 return dev_opts
1138
1139 @staticmethod
1140 - def _CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image, cdrom_boot,
1141 needs_boot_flag):
1142 """Extends L{kvm_cmd} with the '-drive' option for a cdrom, and
1143 optionally the '-boot' option.
1144
1145 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide -boot d
1146
1147 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide,boot=on
1148
1149 Example: -drive file=http://hostname.com/cdrom.iso,media=cdrom
1150
1151 @type kvm_cmd: string
1152 @param kvm_cmd: KVM command line
1153
1154 @type cdrom_disk_type:
1155 @param cdrom_disk_type:
1156
1157 @type cdrom_image:
1158 @param cdrom_image:
1159
1160 @type cdrom_boot:
1161 @param cdrom_boot:
1162
1163 @type needs_boot_flag:
1164 @param needs_boot_flag:
1165
1166 """
1167
1168
1169 if utils.IsUrl(cdrom_image) and not _CheckUrl(cdrom_image):
1170 raise errors.HypervisorError("Cdrom ISO image '%s' is not accessible" %
1171 cdrom_image)
1172
1173
1174 if utils.IsUrl(cdrom_image):
1175 options = ",media=cdrom"
1176 else:
1177 options = ",media=cdrom,format=raw"
1178
1179
1180 if cdrom_boot:
1181 if_val = ",if=" + constants.HT_DISK_IDE
1182 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1183 if_val = ",if=virtio"
1184 else:
1185 if_val = ",if=" + cdrom_disk_type
1186
1187
1188 boot_val = ""
1189 if cdrom_boot:
1190 kvm_cmd.extend(["-boot", "d"])
1191
1192
1193
1194 if needs_boot_flag:
1195 boot_val = ",boot=on"
1196
1197
1198 drive_val = "file=%s%s%s%s" % (cdrom_image, options, if_val, boot_val)
1199 kvm_cmd.extend(["-drive", drive_val])
1200
1203 """Generate KVM information to start an instance.
1204
1205 @type kvmhelp: string
1206 @param kvmhelp: output of kvm --help
1207 @attention: this function must not have any side-effects; for
1208 example, it must not write to the filesystem, or read values
1209 from the current system the are expected to differ between
1210 nodes, since it is only run once at instance startup;
1211 actions/kvm arguments that can vary between systems should be
1212 done in L{_ExecuteKVMRuntime}
1213
1214 """
1215
1216 hvp = instance.hvparams
1217 self.ValidateParameters(hvp)
1218
1219 pidfile = self._InstancePidFile(instance.name)
1220 kvm = hvp[constants.HV_KVM_PATH]
1221 kvm_cmd = [kvm]
1222
1223 kvm_cmd.extend(["-name", instance.name])
1224 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1225
1226 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1227 if hvp[constants.HV_CPU_CORES]:
1228 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1229 if hvp[constants.HV_CPU_THREADS]:
1230 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1231 if hvp[constants.HV_CPU_SOCKETS]:
1232 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1233
1234 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1235
1236 kvm_cmd.extend(["-pidfile", pidfile])
1237
1238 bus_slots = self._GetBusSlots(hvp)
1239
1240
1241 if hvp[constants.HV_SOUNDHW]:
1242 soundhw = hvp[constants.HV_SOUNDHW]
1243 kvm_cmd.extend(["-soundhw", soundhw])
1244
1245 if hvp[constants.HV_DISK_TYPE] in constants.HT_SCSI_DEVICE_TYPES:
1246
1247
1248
1249 kvm_cmd.extend([
1250 "-device",
1251 "%s,id=scsi" % hvp[constants.HV_KVM_SCSI_CONTROLLER_TYPE]
1252 ])
1253
1254 kvm_cmd.extend(["-balloon", "virtio"])
1255 kvm_cmd.extend(["-daemonize"])
1256 if not instance.hvparams[constants.HV_ACPI]:
1257 kvm_cmd.extend(["-no-acpi"])
1258 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1259 constants.INSTANCE_REBOOT_EXIT:
1260 kvm_cmd.extend(["-no-reboot"])
1261
1262 mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1263 if not mversion:
1264 mversion = self._GetDefaultMachineVersion(kvm)
1265 if self._MACHINE_RE.search(kvmhelp):
1266
1267
1268
1269 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1270 specprop = ",accel=kvm"
1271 else:
1272 specprop = ""
1273 machinespec = "%s%s" % (mversion, specprop)
1274 kvm_cmd.extend(["-machine", machinespec])
1275 else:
1276 kvm_cmd.extend(["-M", mversion])
1277 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1278 self._ENABLE_KVM_RE.search(kvmhelp)):
1279 kvm_cmd.extend(["-enable-kvm"])
1280 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1281 self._DISABLE_KVM_RE.search(kvmhelp)):
1282 kvm_cmd.extend(["-disable-kvm"])
1283
1284 kernel_path = hvp[constants.HV_KERNEL_PATH]
1285 if kernel_path:
1286 boot_cdrom = boot_floppy = boot_network = False
1287 else:
1288 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1289 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1290 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1291
1292 if startup_paused:
1293 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1294
1295 if boot_network:
1296 kvm_cmd.extend(["-boot", "n"])
1297
1298 disk_type = hvp[constants.HV_DISK_TYPE]
1299
1300
1301 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1302 if not cdrom_disk_type:
1303 cdrom_disk_type = disk_type
1304
1305 cdrom_image1 = hvp[constants.HV_CDROM_IMAGE_PATH]
1306 if cdrom_image1:
1307 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1308 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image1, boot_cdrom,
1309 needs_boot_flag)
1310
1311 cdrom_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1312 if cdrom_image2:
1313 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image2, False, False)
1314
1315 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1316 if floppy_image:
1317 options = ",format=raw,media=disk"
1318 if boot_floppy:
1319 kvm_cmd.extend(["-boot", "a"])
1320 options = "%s,boot=on" % options
1321 if_val = ",if=floppy"
1322 options = "%s%s" % (options, if_val)
1323 drive_val = "file=%s%s" % (floppy_image, options)
1324 kvm_cmd.extend(["-drive", drive_val])
1325
1326 if kernel_path:
1327 kvm_cmd.extend(["-kernel", kernel_path])
1328 initrd_path = hvp[constants.HV_INITRD_PATH]
1329 if initrd_path:
1330 kvm_cmd.extend(["-initrd", initrd_path])
1331 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1332 hvp[constants.HV_KERNEL_ARGS]]
1333 if hvp[constants.HV_SERIAL_CONSOLE]:
1334 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1335 root_append.append("console=ttyS0,%s" % serial_speed)
1336 kvm_cmd.extend(["-append", " ".join(root_append)])
1337
1338 mem_path = hvp[constants.HV_MEM_PATH]
1339 if mem_path:
1340 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1341
1342 monitor_dev = ("unix:%s,server,nowait" %
1343 self._InstanceMonitor(instance.name))
1344 kvm_cmd.extend(["-monitor", monitor_dev])
1345 if hvp[constants.HV_SERIAL_CONSOLE]:
1346 serial_dev = ("unix:%s,server,nowait" %
1347 self._InstanceSerial(instance.name))
1348 kvm_cmd.extend(["-serial", serial_dev])
1349 else:
1350 kvm_cmd.extend(["-serial", "none"])
1351
1352 mouse_type = hvp[constants.HV_USB_MOUSE]
1353 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1354 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1355 spice_ip_version = None
1356
1357 kvm_cmd.extend(["-usb"])
1358
1359 if mouse_type:
1360 kvm_cmd.extend(["-usbdevice", mouse_type])
1361 elif vnc_bind_address:
1362 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1363
1364 if vnc_bind_address:
1365 if netutils.IsValidInterface(vnc_bind_address):
1366 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1367 if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1368 if len(if_ip4_addresses) < 1:
1369 logging.error("Could not determine IPv4 address of interface %s",
1370 vnc_bind_address)
1371 else:
1372 vnc_bind_address = if_ip4_addresses[0]
1373 if netutils.IP4Address.IsValid(vnc_bind_address):
1374 if instance.network_port > constants.VNC_BASE_PORT:
1375 display = instance.network_port - constants.VNC_BASE_PORT
1376 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1377 vnc_arg = ":%d" % (display)
1378 else:
1379 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1380 else:
1381 logging.error("Network port is not a valid VNC display (%d < %d),"
1382 " not starting VNC",
1383 instance.network_port, constants.VNC_BASE_PORT)
1384 vnc_arg = "none"
1385
1386
1387
1388 vnc_append = ""
1389 if hvp[constants.HV_VNC_TLS]:
1390 vnc_append = "%s,tls" % vnc_append
1391 if hvp[constants.HV_VNC_X509_VERIFY]:
1392 vnc_append = "%s,x509verify=%s" % (vnc_append,
1393 hvp[constants.HV_VNC_X509])
1394 elif hvp[constants.HV_VNC_X509]:
1395 vnc_append = "%s,x509=%s" % (vnc_append,
1396 hvp[constants.HV_VNC_X509])
1397 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1398 vnc_append = "%s,password" % vnc_append
1399
1400 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1401
1402 else:
1403 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1404
1405 kvm_cmd.extend(["-vnc", vnc_arg])
1406 elif spice_bind:
1407
1408
1409 if netutils.IsValidInterface(spice_bind):
1410
1411
1412 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1413 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1414
1415
1416
1417 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1418 if not addresses[spice_ip_version]:
1419 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1420 " for %s" % (spice_ip_version,
1421 spice_bind))
1422
1423
1424 elif (addresses[constants.IP4_VERSION] and
1425 addresses[constants.IP6_VERSION]):
1426
1427
1428 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1429 spice_ip_version = \
1430 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1431 elif addresses[constants.IP4_VERSION]:
1432 spice_ip_version = constants.IP4_VERSION
1433 elif addresses[constants.IP6_VERSION]:
1434 spice_ip_version = constants.IP6_VERSION
1435 else:
1436 raise errors.HypervisorError("SPICE: Unable to get an IP address"
1437 " for %s" % (spice_bind))
1438
1439 spice_address = addresses[spice_ip_version][0]
1440
1441 else:
1442
1443
1444 spice_address = spice_bind
1445
1446 spice_arg = "addr=%s" % spice_address
1447 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1448 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1449 (spice_arg, instance.network_port,
1450 pathutils.SPICE_CACERT_FILE))
1451 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1452 (spice_arg, pathutils.SPICE_CERT_FILE,
1453 pathutils.SPICE_CERT_FILE))
1454 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1455 if tls_ciphers:
1456 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1457 else:
1458 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1459
1460 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1461 spice_arg = "%s,disable-ticketing" % spice_arg
1462
1463 if spice_ip_version:
1464 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1465
1466
1467 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1468 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1469 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1470 if img_lossless:
1471 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1472 if img_jpeg:
1473 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1474 if img_zlib_glz:
1475 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1476
1477
1478 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1479 if video_streaming:
1480 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1481
1482
1483 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1484 spice_arg = "%s,playback-compression=off" % spice_arg
1485 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1486 spice_arg = "%s,agent-mouse=off" % spice_arg
1487 else:
1488
1489
1490 kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice"])
1491 kvm_cmd.extend([
1492 "-device",
1493 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1494 ])
1495 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1496
1497 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1498 kvm_cmd.extend(["-spice", spice_arg])
1499
1500 else:
1501
1502
1503 if self._DISPLAY_RE.search(kvmhelp):
1504 kvm_cmd.extend(["-display", "none"])
1505 else:
1506 kvm_cmd.extend(["-nographic"])
1507
1508 if hvp[constants.HV_USE_LOCALTIME]:
1509 kvm_cmd.extend(["-localtime"])
1510
1511 if hvp[constants.HV_KVM_USE_CHROOT]:
1512 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1513
1514
1515 if hvp[constants.HV_CPU_TYPE]:
1516 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1517
1518
1519
1520 if hvp[constants.HV_VGA]:
1521 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1522 elif spice_bind:
1523 kvm_cmd.extend(["-vga", "qxl"])
1524
1525
1526 if hvp[constants.HV_USB_DEVICES]:
1527 for dev in hvp[constants.HV_USB_DEVICES].split(","):
1528 kvm_cmd.extend(["-usbdevice", dev])
1529
1530
1531 if self._UUID_RE.search(kvmhelp):
1532 kvm_cmd.extend(["-uuid", instance.uuid])
1533
1534 if hvp[constants.HV_KVM_EXTRA]:
1535 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1536
1537 def _generate_kvm_device(dev_type, dev):
1538 """Helper for generating a kvm device out of a Ganeti device."""
1539 kvm_devid = _GenerateDeviceKVMId(dev_type, dev)
1540 hv_dev_type = _DEVICE_TYPE[dev_type](hvp)
1541 dev.hvinfo = _GenerateDeviceHVInfo(dev_type, kvm_devid,
1542 hv_dev_type, bus_slots)
1543
1544 kvm_disks = []
1545 for disk, link_name, uri in block_devices:
1546 _generate_kvm_device(constants.HOTPLUG_TARGET_DISK, disk)
1547 kvm_disks.append((disk, link_name, uri))
1548
1549 kvm_nics = []
1550 for nic in instance.nics:
1551 _generate_kvm_device(constants.HOTPLUG_TARGET_NIC, nic)
1552 kvm_nics.append(nic)
1553
1554 hvparams = hvp
1555
1556 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1557
1567
1569 """Read an instance's KVM runtime
1570
1571 """
1572 try:
1573 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1574 except EnvironmentError, err:
1575 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1576 return file_content
1577
1579 """Save an instance's KVM runtime
1580
1581 """
1582 kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1583
1584 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1585 serialized_disks = [(blk.ToDict(), link, uri)
1586 for blk, link, uri in kvm_disks]
1587 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1588 serialized_disks))
1589
1590 self._WriteKVMRuntime(instance.name, serialized_form)
1591
1593 """Load an instance's KVM runtime
1594
1595 """
1596 if not serialized_runtime:
1597 serialized_runtime = self._ReadKVMRuntime(instance.name)
1598
1599 return _AnalyzeSerializedRuntime(serialized_runtime)
1600
1601 - def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1602 """Run the KVM cmd and check for errors
1603
1604 @type name: string
1605 @param name: instance name
1606 @type kvm_cmd: list of strings
1607 @param kvm_cmd: runcmd input for kvm
1608 @type tap_fds: list of int
1609 @param tap_fds: fds of tap devices opened by Ganeti
1610
1611 """
1612 try:
1613 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1614 finally:
1615 for fd in tap_fds:
1616 utils_wrapper.CloseFdNoError(fd)
1617
1618 if result.failed:
1619 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1620 (name, result.fail_reason, result.output))
1621 if not self._InstancePidAlive(name)[2]:
1622 raise errors.HypervisorError("Failed to start instance %s" % name)
1623
1624 @staticmethod
1626 """Generate a TAP network interface name for a NIC.
1627
1628 See L{hv_base.GenerateTapName}.
1629
1630 For the case of the empty string, see L{OpenTap}
1631
1632 @type nic: ganeti.objects.NIC
1633 @param nic: NIC object for the name should be generated
1634
1635 @rtype: string
1636 @return: TAP network interface name, or the empty string if the
1637 NIC is not used in instance communication
1638
1639 """
1640 if nic.name is None or not \
1641 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
1642 return ""
1643
1644 return hv_base.GenerateTapName()
1645
1647 """Get network device options to properly enable supported features.
1648
1649 Return a dict of supported and enabled tap features with nic_model along
1650 with the extra strings to be appended to the --netdev and --device options.
1651 This function is called before opening a new tap device.
1652
1653 Currently the features_dict includes the following attributes:
1654 - vhost (boolean)
1655 - vnet_hdr (boolean)
1656 - mq (boolean, int)
1657
1658 @rtype: (dict, str, str) tuple
1659 @return: The supported features,
1660 the string to be appended to the --netdev option,
1661 the string to be appended to the --device option
1662
1663 """
1664 nic_type = up_hvp[constants.HV_NIC_TYPE]
1665 nic_extra_str = ""
1666 tap_extra_str = ""
1667 features = {
1668 "vhost": False,
1669 "vnet_hdr": False,
1670 "mq": (False, 1)
1671 }
1672 update_features = {}
1673 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1674 if self._DEVICE_DRIVER_SUPPORTED(self._VIRTIO_NET_PCI, devlist):
1675 nic_model = self._VIRTIO_NET_PCI
1676 update_features["vnet_hdr"] = up_hvp[constants.HV_VNET_HDR]
1677 else:
1678
1679
1680 nic_model = self._VIRTIO
1681
1682 if up_hvp[constants.HV_VHOST_NET]:
1683
1684 if self._VHOST_RE.search(kvmhelp):
1685 update_features["vhost"] = True
1686 tap_extra_str = ",vhost=on"
1687 else:
1688 raise errors.HypervisorError("vhost_net is configured"
1689 " but it is not available")
1690 virtio_net_queues = up_hvp.get(constants.HV_VIRTIO_NET_QUEUES, 1)
1691 if virtio_net_queues > 1:
1692
1693 if self._VIRTIO_NET_QUEUES_RE.search(kvmhelp):
1694
1695
1696
1697 nic_extra_str = ",mq=on,vectors=%d" % (2 * virtio_net_queues + 1)
1698 update_features["mq"] = (True, virtio_net_queues)
1699 else:
1700 raise errors.HypervisorError("virtio_net_queues is configured"
1701 " but it is not available")
1702 else:
1703 nic_model = nic_type
1704
1705 update_features["driver"] = nic_model
1706 features.update(update_features)
1707
1708 return features, tap_extra_str, nic_extra_str
1709
1710
1711
1713 """Execute a KVM cmd, after completing it with some last minute data.
1714
1715 @type instance: L{objects.Instance} object
1716 @param instance: the VM this command acts upon
1717 @type kvm_runtime: tuple of (list of str, list of L{objects.NIC} objects,
1718 dict of hypervisor options, list of tuples (L{objects.Disk}, str, str)
1719 @param kvm_runtime: (kvm command, NICs of the instance, options at startup
1720 of the instance, [(disk, link_name, uri)..])
1721 @type incoming: tuple of strings
1722 @param incoming: (target_host_ip, port) for migration.
1723 @type kvmhelp: string
1724 @param kvmhelp: output of kvm --help
1725
1726 """
1727
1728
1729
1730
1731
1732
1733
1734
1735 conf_hvp = instance.hvparams
1736 name = instance.name
1737 self._CheckDown(name)
1738
1739 self._ClearUserShutdown(instance.name)
1740 self._StartKvmd(instance.hvparams)
1741
1742 temp_files = []
1743
1744 kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1745
1746 kvm_path = kvm_cmd[0]
1747 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1748
1749
1750
1751 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1752 if security_model == constants.HT_SM_USER:
1753 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1754
1755 keymap = conf_hvp[constants.HV_KEYMAP]
1756 if keymap:
1757 keymap_path = self._InstanceKeymapFile(name)
1758
1759
1760
1761
1762 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1763 kvm_cmd.extend(["-k", keymap_path])
1764
1765
1766
1767
1768 tapfds = []
1769 taps = []
1770 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1771 if not kvm_nics:
1772 kvm_cmd.extend(["-net", "none"])
1773 else:
1774 features, tap_extra, nic_extra = \
1775 self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
1776 nic_model = features["driver"]
1777 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1778 for nic_seq, nic in enumerate(kvm_nics):
1779 tapname, nic_tapfds, nic_vhostfds = \
1780 OpenTap(features=features, name=self._GenerateKvmTapName(nic))
1781
1782 tapfds.extend(nic_tapfds)
1783 tapfds.extend(nic_vhostfds)
1784 taps.append(tapname)
1785 tapfd = "%s%s" % ("fds=" if len(nic_tapfds) > 1 else "fd=",
1786 ":".join(str(fd) for fd in nic_tapfds))
1787
1788 if nic_vhostfds:
1789 vhostfd = "%s%s" % (",vhostfds="
1790 if len(nic_vhostfds) > 1 else ",vhostfd=",
1791 ":".join(str(fd) for fd in nic_vhostfds))
1792 else:
1793 vhostfd = ""
1794
1795 if kvm_supports_netdev:
1796
1797 if "id" in nic.hvinfo:
1798 nic_val = _GenerateDeviceHVInfoStr(nic.hvinfo)
1799 netdev = nic.hvinfo["id"]
1800 else:
1801 nic_val = "%s" % nic_model
1802 netdev = "netdev%d" % nic_seq
1803 nic_val += (",netdev=%s,mac=%s%s" % (netdev, nic.mac, nic_extra))
1804 tap_val = ("type=tap,id=%s,%s%s%s" %
1805 (netdev, tapfd, vhostfd, tap_extra))
1806 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1807 else:
1808 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1809 nic.mac, nic_model)
1810 tap_val = "tap,vlan=%s,%s" % (nic_seq, tapfd)
1811 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1812
1813 if incoming:
1814 target, port = incoming
1815 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1816
1817
1818
1819
1820 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1821 vnc_pwd = None
1822 if vnc_pwd_file:
1823 try:
1824 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1825 except EnvironmentError, err:
1826 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1827 % (vnc_pwd_file, err))
1828
1829 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1830 utils.EnsureDirs([(self._InstanceChrootDir(name),
1831 constants.SECURE_DIR_MODE)])
1832
1833
1834 if self._QMP_RE.search(kvmhelp):
1835 logging.debug("Enabling QMP")
1836 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1837 self._InstanceQmpMonitor(instance.name)])
1838
1839 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1840 self._InstanceKvmdMonitor(instance.name)])
1841
1842
1843
1844
1845 for nic_seq, nic in enumerate(kvm_nics):
1846 if (incoming and
1847 nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED):
1848 continue
1849 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1850
1851 bdev_opts = self._GenerateKVMBlockDevicesOptions(up_hvp,
1852 kvm_disks,
1853 kvmhelp,
1854 devlist)
1855 kvm_cmd.extend(bdev_opts)
1856
1857
1858
1859 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1860 if start_kvm_paused:
1861 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1862
1863
1864
1865
1866 cpu_pinning = False
1867 if up_hvp.get(constants.HV_CPU_MASK, None):
1868 cpu_pinning = True
1869
1870 if security_model == constants.HT_SM_POOL:
1871 ss = ssconf.SimpleStore()
1872 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1873 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1874 uid = uidpool.RequestUnusedUid(all_uids)
1875 try:
1876 username = pwd.getpwuid(uid.GetUid()).pw_name
1877 kvm_cmd.extend(["-runas", username])
1878 self._RunKVMCmd(name, kvm_cmd, tapfds)
1879 except:
1880 uidpool.ReleaseUid(uid)
1881 raise
1882 else:
1883 uid.Unlock()
1884 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1885 else:
1886 self._RunKVMCmd(name, kvm_cmd, tapfds)
1887
1888 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1889 constants.RUN_DIRS_MODE)])
1890 for nic_seq, tap in enumerate(taps):
1891 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1892 data=tap)
1893
1894 if vnc_pwd:
1895 change_cmd = "change vnc password %s" % vnc_pwd
1896 self._CallMonitorCommand(instance.name, change_cmd)
1897
1898
1899
1900
1901
1902
1903 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1904 if spice_password_file:
1905 spice_pwd = ""
1906 try:
1907 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1908 except EnvironmentError, err:
1909 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1910 % (spice_password_file, err))
1911
1912 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1913 qmp.connect()
1914 arguments = {
1915 "protocol": "spice",
1916 "password": spice_pwd,
1917 }
1918 qmp.Execute("set_password", arguments)
1919
1920 for filename in temp_files:
1921 utils.RemoveFile(filename)
1922
1923
1924 if cpu_pinning:
1925 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1926
1927 start_memory = self._InstanceStartupMemory(instance)
1928 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1929 self.BalloonInstanceMemory(instance, start_memory)
1930
1931 if start_kvm_paused:
1932
1933
1934
1935 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1936
1937 @staticmethod
1954
1955 - def StartInstance(self, instance, block_devices, startup_paused):
1966
1967 @classmethod
1969 """Invoke a command on the instance monitor.
1970
1971 """
1972 if timeout is not None:
1973 timeout_cmd = "timeout %s" % (timeout, )
1974 else:
1975 timeout_cmd = ""
1976
1977
1978
1979
1980
1981
1982
1983
1984 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
1985 (utils.ShellQuote(command),
1986 timeout_cmd,
1987 constants.SOCAT_PATH,
1988 utils.ShellQuote(cls._InstanceMonitor(instance_name))))
1989 result = utils.RunCmd(socat)
1990 if result.failed:
1991 msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
1992 " output: %s" %
1993 (command, instance_name, result.fail_reason, result.output))
1994 raise errors.HypervisorError(msg)
1995
1996 return result
1997
1998 @_with_qmp
2023
2025 """Checks if hotplug is generally supported.
2026
2027 Hotplug is *not* supported in case of:
2028 - qemu versions < 1.7 (where all qmp related commands are supported)
2029 - for stopped instances
2030
2031 @raise errors.HypervisorError: in one of the previous cases
2032
2033 """
2034 try:
2035 output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2036 except errors.HypervisorError:
2037 raise errors.HotplugError("Instance is probably down")
2038
2039 match = self._INFO_VERSION_RE.search(output.stdout)
2040 if not match:
2041 raise errors.HotplugError("Cannot parse qemu version via monitor")
2042
2043
2044 v_major, v_min, _, _ = match.groups()
2045 if (int(v_major), int(v_min)) < (1, 7):
2046 raise errors.HotplugError("Hotplug not supported for qemu versions < 1.7")
2047
2049 """Helper function to get the slots of PCI and SCSI QEMU buses.
2050
2051 This will return the status of the first PCI and SCSI buses. By default
2052 QEMU boots with one PCI bus (pci.0) and occupies the first 3 PCI slots. If
2053 a SCSI disk is found then a SCSI controller is added on the PCI bus and a
2054 SCSI bus (scsi.0) is created.
2055
2056 During hotplug we could query QEMU via info qtree HMP command but parsing
2057 the result is too complicated. Instead we use the info stored in runtime
2058 files. We parse NIC and disk entries and based on their hvinfo we reserve
2059 the corresponding slots.
2060
2061 The runtime argument is a tuple as returned by _LoadKVMRuntime(). Obtain
2062 disks and NICs from it. In case a runtime file is not available (see
2063 _GenerateKVMRuntime()) we return the bus slots that QEMU boots with by
2064 default.
2065
2066 """
2067
2068 bus_slots = {
2069 _PCI_BUS: bitarray(self._DEFAULT_PCI_RESERVATIONS),
2070 _SCSI_BUS: bitarray(self._DEFAULT_SCSI_RESERVATIONS),
2071 }
2072
2073
2074 if hvp and constants.HV_KVM_PCI_RESERVATIONS in hvp:
2075 res = hvp[constants.HV_KVM_PCI_RESERVATIONS]
2076 pci = bitarray(constants.QEMU_PCI_SLOTS)
2077 pci.setall(False)
2078 pci[0:res:1] = True
2079 bus_slots[_PCI_BUS] = pci
2080
2081
2082 if runtime:
2083 _, nics, _, disks = runtime
2084 disks = [d for d, _, _ in disks]
2085 for d in disks + nics:
2086 if not d.hvinfo or "bus" not in d.hvinfo:
2087 continue
2088 bus = d.hvinfo["bus"]
2089 slots = bus_slots[bus]
2090 if bus == _PCI_BUS:
2091 slot = d.hvinfo["addr"]
2092 slots[int(slot, 16)] = True
2093 elif bus == _SCSI_BUS:
2094 slot = d.hvinfo["scsi-id"]
2095 slots[slot] = True
2096
2097 return bus_slots
2098
2099 @_with_qmp
2101 """Checks if a previous hotplug command has succeeded.
2102
2103 Depending on the should_exist value, verifies that an entry identified by
2104 device ID is present or not.
2105
2106 @raise errors.HypervisorError: if result is not the expected one
2107
2108 """
2109 for i in range(5):
2110 found = self.qmp.HasDevice(kvm_devid)
2111 logging.info("Verifying hotplug command (retry %s): %s", i, found)
2112 if found and should_exist:
2113 break
2114 if not found and not should_exist:
2115 break
2116 time.sleep(1)
2117
2118 if found and not should_exist:
2119 msg = "Device %s should have been removed but is still there" % kvm_devid
2120 raise errors.HypervisorError(msg)
2121
2122 if not found and should_exist:
2123 msg = "Device %s should have been added but is missing" % kvm_devid
2124 raise errors.HypervisorError(msg)
2125
2126 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
2127
2128 @_with_qmp
2129 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
2130 """ Helper method to hot-add a new device
2131
2132 It generates the device ID and hvinfo, and invokes the
2133 device-specific method.
2134
2135 """
2136 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2137 runtime = self._LoadKVMRuntime(instance)
2138 up_hvp = runtime[2]
2139 device_type = _DEVICE_TYPE[dev_type](up_hvp)
2140 bus_state = self._GetBusSlots(up_hvp, runtime)
2141
2142 if not device.hvinfo:
2143 device.hvinfo = _GenerateDeviceHVInfo(dev_type, kvm_devid,
2144 device_type, bus_state)
2145 if dev_type == constants.HOTPLUG_TARGET_DISK:
2146 uri = _GetDriveURI(device, extra[0], extra[1])
2147
2148 def drive_add_fn(filename):
2149 """Helper function that uses HMP to hot-add a drive."""
2150 cmd = "drive_add dummy file=%s,if=none,id=%s,format=raw" % \
2151 (filename, kvm_devid)
2152 self._CallMonitorCommand(instance.name, cmd)
2153
2154
2155
2156
2157
2158
2159
2160 self.qmp.HotAddDisk(device, kvm_devid, uri, drive_add_fn)
2161 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2162 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2163 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2164 devlist = self._GetKVMOutput(kvmpath, self._KVMOPT_DEVICELIST)
2165 features, _, _ = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
2166 (tap, tapfds, vhostfds) = OpenTap(features=features)
2167 self._ConfigureNIC(instance, seq, device, tap)
2168 self.qmp.HotAddNic(device, kvm_devid, tapfds, vhostfds, features)
2169 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2170
2171 self._VerifyHotplugCommand(instance, kvm_devid, True)
2172
2173 index = _DEVICE_RUNTIME_INDEX[dev_type]
2174 entry = _RUNTIME_ENTRY[dev_type](device, extra)
2175 runtime[index].append(entry)
2176 self._SaveKVMRuntime(instance, runtime)
2177
2178 @_with_qmp
2179 - def HotDelDevice(self, instance, dev_type, device, _, seq):
2204
2205 - def HotModDevice(self, instance, dev_type, device, _, seq):
2206 """ Helper method for hot-mod device
2207
2208 It gets device info from runtime file, generates the device name and
2209 invokes the device-specific method. Currently only NICs support hot-mod
2210
2211 """
2212 if dev_type == constants.HOTPLUG_TARGET_NIC:
2213
2214 device.hvinfo = self.HotDelDevice(instance, dev_type, device, _, seq)
2215 self.HotAddDevice(instance, dev_type, device, _, seq)
2216
2217 @classmethod
2219 """Parse the KVM version from the --help output.
2220
2221 @type text: string
2222 @param text: output of kvm --help
2223 @return: (version, v_maj, v_min, v_rev)
2224 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2225
2226 """
2227 match = cls._VERSION_RE.search(text.splitlines()[0])
2228 if not match:
2229 raise errors.HypervisorError("Unable to get KVM version")
2230
2231 v_all = match.group(0)
2232 v_maj = int(match.group(1))
2233 v_min = int(match.group(2))
2234 if match.group(4):
2235 v_rev = int(match.group(4))
2236 else:
2237 v_rev = 0
2238 return (v_all, v_maj, v_min, v_rev)
2239
2240 @classmethod
2242 """Return the output of a kvm invocation
2243
2244 @type kvm_path: string
2245 @param kvm_path: path to the kvm executable
2246 @type option: a key of _KVMOPTS_CMDS
2247 @param option: kvm option to fetch the output from
2248 @return: output a supported kvm invocation
2249 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2250
2251 """
2252 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2253
2254 optlist, can_fail = cls._KVMOPTS_CMDS[option]
2255
2256 result = utils.RunCmd([kvm_path] + optlist)
2257 if result.failed and not can_fail:
2258 raise errors.HypervisorError("Unable to get KVM %s output" %
2259 " ".join(optlist))
2260 return result.output
2261
2262 @classmethod
2264 """Return the installed KVM version.
2265
2266 @return: (version, v_maj, v_min, v_rev)
2267 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2268
2269 """
2270 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2271
2272 @classmethod
2283
2284 @classmethod
2285 - def _StopInstance(cls, instance, force=False, name=None, timeout=None):
2305
2306 - def StopInstance(self, instance, force=False, retry=False, name=None,
2307 timeout=None):
2312
2322
2345
2347 """Get instance information to perform a migration.
2348
2349 @type instance: L{objects.Instance}
2350 @param instance: instance to be migrated
2351 @rtype: string
2352 @return: content of the KVM runtime file
2353
2354 """
2355 return self._ReadKVMRuntime(instance.name)
2356
2358 """Prepare to accept an instance.
2359
2360 @type instance: L{objects.Instance}
2361 @param instance: instance to be accepted
2362 @type info: string
2363 @param info: content of the KVM runtime file on the source node
2364 @type target: string
2365 @param target: target host (usually ip), on this node
2366
2367 """
2368 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2369 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2370 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2371 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2372 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2373 incoming=incoming_address)
2374
2376 """Finalize the instance migration on the target node.
2377
2378 Stop the incoming mode KVM.
2379
2380 @type instance: L{objects.Instance}
2381 @param instance: instance whose migration is being finalized
2382
2383 """
2384 if success:
2385 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2386 kvm_nics = kvm_runtime[1]
2387
2388 for nic_seq, nic in enumerate(kvm_nics):
2389 if nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_ROUTED:
2390
2391 continue
2392 try:
2393 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2394 except EnvironmentError, err:
2395 logging.warning("Failed to find host interface for %s NIC #%d: %s",
2396 instance.name, nic_seq, str(err))
2397 continue
2398 try:
2399 self._ConfigureNIC(instance, nic_seq, nic, tap)
2400 except errors.HypervisorError, err:
2401 logging.warning(str(err))
2402
2403 self._WriteKVMRuntime(instance.name, info)
2404 else:
2405 self.StopInstance(instance, force=True)
2406
2448
2467
2469 """Get the migration status
2470
2471 @type instance: L{objects.Instance}
2472 @param instance: the instance that is being migrated
2473 @rtype: L{objects.MigrationStatus}
2474 @return: the status of the current migration (one of
2475 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2476 progress info that can be retrieved from the hypervisor
2477
2478 """
2479 info_command = "info migrate"
2480 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2481 result = self._CallMonitorCommand(instance.name, info_command)
2482 match = self._MIGRATION_STATUS_RE.search(result.stdout)
2483 if not match:
2484 if not result.stdout:
2485 logging.info("KVM: empty 'info migrate' result")
2486 else:
2487 logging.warning("KVM: unknown 'info migrate' result: %s",
2488 result.stdout)
2489 else:
2490 status = match.group(1)
2491 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2492 migration_status = objects.MigrationStatus(status=status)
2493 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2494 if match:
2495 migration_status.transferred_ram = match.group("transferred")
2496 migration_status.total_ram = match.group("total")
2497
2498 return migration_status
2499
2500 logging.warning("KVM: unknown migration status '%s'", status)
2501
2502 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2503
2504 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2505
2507 """Balloon an instance memory to a certain value.
2508
2509 @type instance: L{objects.Instance}
2510 @param instance: instance to be accepted
2511 @type mem: int
2512 @param mem: actual memory size to use for instance runtime
2513
2514 """
2515 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2516
2518 """Return information about the node.
2519
2520 @type hvparams: dict of strings
2521 @param hvparams: hypervisor parameters, not used in this class
2522
2523 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2524 the following keys:
2525 - hv_version: the hypervisor version in the form (major, minor,
2526 revision)
2527
2528 """
2529 result = self.GetLinuxNodeInfo()
2530 kvmpath = constants.KVM_PATH
2531 if hvparams is not None:
2532 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2533 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2534 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2535 return result
2536
2537 @classmethod
2538 - def GetInstanceConsole(cls, instance, primary_node, node_group,
2539 hvparams, beparams):
2540 """Return a command for connecting to the console of an instance.
2541
2542 """
2543 if hvparams[constants.HV_SERIAL_CONSOLE]:
2544 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2545 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2546 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2547 "STDIO,%s" % cls._SocatUnixConsoleParams(),
2548 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2549 ndparams = node_group.FillND(primary_node)
2550 return objects.InstanceConsole(instance=instance.name,
2551 kind=constants.CONS_SSH,
2552 host=primary_node.name,
2553 port=ndparams.get(constants.ND_SSH_PORT),
2554 user=constants.SSH_CONSOLE_USER,
2555 command=cmd)
2556
2557 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2558 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2559 display = instance.network_port - constants.VNC_BASE_PORT
2560 return objects.InstanceConsole(instance=instance.name,
2561 kind=constants.CONS_VNC,
2562 host=vnc_bind_address,
2563 port=instance.network_port,
2564 display=display)
2565
2566 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2567 if spice_bind:
2568 return objects.InstanceConsole(instance=instance.name,
2569 kind=constants.CONS_SPICE,
2570 host=spice_bind,
2571 port=instance.network_port)
2572
2573 return objects.InstanceConsole(instance=instance.name,
2574 kind=constants.CONS_MESSAGE,
2575 message=("No serial shell for instance %s" %
2576 instance.name))
2577
2578 - def Verify(self, hvparams=None):
2579 """Verify the hypervisor.
2580
2581 Check that the required binaries exist.
2582
2583 @type hvparams: dict of strings
2584 @param hvparams: hypervisor parameters to be verified against, not used here
2585
2586 @return: Problem description if something is wrong, C{None} otherwise
2587
2588 """
2589 msgs = []
2590 kvmpath = constants.KVM_PATH
2591 if hvparams is not None:
2592 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2593 if not os.path.exists(kvmpath):
2594 msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2595 if not os.path.exists(constants.SOCAT_PATH):
2596 msgs.append("The socat binary ('%s') does not exist" %
2597 constants.SOCAT_PATH)
2598
2599 return self._FormatVerifyResults(msgs)
2600
2601 @classmethod
2603 """Check the given parameters for validity.
2604
2605 @type hvparams: dict of strings
2606 @param hvparams: hypervisor parameters
2607 @raise errors.HypervisorError: when a parameter is not valid
2608
2609 """
2610 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2611
2612 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2613 if kernel_path:
2614 if not hvparams[constants.HV_ROOT_PATH]:
2615 raise errors.HypervisorError("Need a root partition for the instance,"
2616 " if a kernel is defined")
2617
2618 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2619 not hvparams[constants.HV_VNC_X509]):
2620 raise errors.HypervisorError("%s must be defined, if %s is" %
2621 (constants.HV_VNC_X509,
2622 constants.HV_VNC_X509_VERIFY))
2623
2624 if hvparams[constants.HV_SERIAL_CONSOLE]:
2625 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2626 valid_speeds = constants.VALID_SERIAL_SPEEDS
2627 if not serial_speed or serial_speed not in valid_speeds:
2628 raise errors.HypervisorError("Invalid serial console speed, must be"
2629 " one of: %s" %
2630 utils.CommaJoin(valid_speeds))
2631
2632 boot_order = hvparams[constants.HV_BOOT_ORDER]
2633 if (boot_order == constants.HT_BO_CDROM and
2634 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2635 raise errors.HypervisorError("Cannot boot from cdrom without an"
2636 " ISO path")
2637
2638 security_model = hvparams[constants.HV_SECURITY_MODEL]
2639 if security_model == constants.HT_SM_USER:
2640 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2641 raise errors.HypervisorError("A security domain (user to run kvm as)"
2642 " must be specified")
2643 elif (security_model == constants.HT_SM_NONE or
2644 security_model == constants.HT_SM_POOL):
2645 if hvparams[constants.HV_SECURITY_DOMAIN]:
2646 raise errors.HypervisorError("Cannot have a security domain when the"
2647 " security model is 'none' or 'pool'")
2648
2649 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2650 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2651 if spice_bind:
2652 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2653
2654
2655 if (netutils.IP4Address.IsValid(spice_bind) and
2656 spice_ip_version != constants.IP4_VERSION):
2657 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2658 " the specified IP version is %s" %
2659 (spice_bind, spice_ip_version))
2660
2661 if (netutils.IP6Address.IsValid(spice_bind) and
2662 spice_ip_version != constants.IP6_VERSION):
2663 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2664 " the specified IP version is %s" %
2665 (spice_bind, spice_ip_version))
2666 else:
2667
2668
2669 for param in _SPICE_ADDITIONAL_PARAMS:
2670 if hvparams[param]:
2671 raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2672 (param, constants.HV_KVM_SPICE_BIND))
2673
2674 @classmethod
2734
2735 @classmethod
2737 """KVM powercycle, just a wrapper over Linux powercycle.
2738
2739 @type hvparams: dict of strings
2740 @param hvparams: hypervisor parameters to be used on this node
2741
2742 """
2743 cls.LinuxPowercycle()
2744