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 _AVAILABLE_PCI_SLOT = bitarray("0")
89
90
91
92
93
94 _KVM_NICS_RUNTIME_INDEX = 1
95 _KVM_DISKS_RUNTIME_INDEX = 3
96 _DEVICE_RUNTIME_INDEX = {
97 constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX,
98 constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX
99 }
100 _FIND_RUNTIME_ENTRY = {
101 constants.HOTPLUG_TARGET_NIC:
102 lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid],
103 constants.HOTPLUG_TARGET_DISK:
104 lambda disk, kvm_disks: [(d, l, u) for (d, l, u) in kvm_disks
105 if d.uuid == disk.uuid]
106 }
107 _RUNTIME_DEVICE = {
108 constants.HOTPLUG_TARGET_NIC: lambda d: d,
109 constants.HOTPLUG_TARGET_DISK: lambda (d, e, _): d
110 }
111 _RUNTIME_ENTRY = {
112 constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
113 constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e[0], e[1])
114 }
115
116 _MIGRATION_CAPS_DELIM = ":"
120 """Helper function to get the drive uri to be used in --drive kvm option
121
122 @type disk: L{objects.Disk}
123 @param disk: A disk configuration object
124 @type link: string
125 @param link: The device link as returned by _SymlinkBlockDev()
126 @type uri: string
127 @param uri: The drive uri as returned by _CalculateDeviceURI()
128
129 """
130 access_mode = disk.params.get(constants.LDP_ACCESS,
131 constants.DISK_KERNELSPACE)
132 if (uri and access_mode == constants.DISK_USERSPACE):
133 drive_uri = uri
134 else:
135 drive_uri = link
136
137 return drive_uri
138
141 """Helper function to generate a unique device name used by KVM
142
143 QEMU monitor commands use names to identify devices. Here we use their pci
144 slot and a part of their UUID to name them. dev.pci might be None for old
145 devices in the cluster.
146
147 @type dev_type: sting
148 @param dev_type: device type of param dev
149 @type dev: L{objects.Disk} or L{objects.NIC}
150 @param dev: the device object for which we generate a kvm name
151 @raise errors.HotplugError: in case a device has no pci slot (old devices)
152
153 """
154
155 if not dev.pci:
156 raise errors.HotplugError("Hotplug is not supported for %s with UUID %s" %
157 (dev_type, dev.uuid))
158
159 return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
160
163 """Helper method to get first available slot in a bitarray
164
165 @type slots: bitarray
166 @param slots: the bitarray to operate on
167 @type slot: integer
168 @param slot: if given we check whether the slot is free
169 @type reserve: boolean
170 @param reserve: whether to reserve the first available slot or not
171 @return: the idx of the (first) available slot
172 @raise errors.HotplugError: If all slots in a bitarray are occupied
173 or the given slot is not free.
174
175 """
176 if slot is not None:
177 assert slot < len(slots)
178 if slots[slot]:
179 raise errors.HypervisorError("Slots %d occupied" % slot)
180
181 else:
182 avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
183 if not avail:
184 raise errors.HypervisorError("All slots occupied")
185
186 slot = int(avail[0])
187
188 if reserve:
189 slots[slot] = True
190
191 return slot
192
195 """Helper function to get an existing device inside the runtime file
196
197 Used when an instance is running. Load kvm runtime file and search
198 for a device based on its type and uuid.
199
200 @type dev_type: sting
201 @param dev_type: device type of param dev
202 @type device: L{objects.Disk} or L{objects.NIC}
203 @param device: the device object for which we generate a kvm name
204 @type runtime: tuple (cmd, nics, hvparams, disks)
205 @param runtime: the runtime data to search for the device
206 @raise errors.HotplugError: in case the requested device does not
207 exist (e.g. device has been added without --hotplug option) or
208 device info has not pci slot (e.g. old devices in the cluster)
209
210 """
211 index = _DEVICE_RUNTIME_INDEX[dev_type]
212 found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
213 if not found:
214 raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
215 (dev_type, device.uuid))
216
217 return found[0]
218
221 """Upgrade runtime data
222
223 Remove any deprecated fields or change the format of the data.
224 The runtime files are not upgraded when Ganeti is upgraded, so the required
225 modification have to be performed here.
226
227 @type serialized_runtime: string
228 @param serialized_runtime: raw text data read from actual runtime file
229 @return: (cmd, nic dicts, hvparams, bdev dicts)
230 @rtype: tuple
231
232 """
233 loaded_runtime = serializer.Load(serialized_runtime)
234 kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
235 if len(loaded_runtime) >= 4:
236 serialized_disks = loaded_runtime[3]
237 else:
238 serialized_disks = []
239
240 for nic in serialized_nics:
241
242 if "uuid" not in nic:
243 nic["uuid"] = utils.NewUUID()
244
245 return kvm_cmd, serialized_nics, hvparams, serialized_disks
246
249 """Return runtime entries for a serialized runtime file
250
251 @type serialized_runtime: string
252 @param serialized_runtime: raw text data read from actual runtime file
253 @return: (cmd, nics, hvparams, bdevs)
254 @rtype: tuple
255
256 """
257 kvm_cmd, serialized_nics, hvparams, serialized_disks = \
258 _UpgradeSerializedRuntime(serialized_runtime)
259 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
260 kvm_disks = [(objects.Disk.FromDict(sdisk), link, uri)
261 for sdisk, link, uri in serialized_disks]
262
263 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
264
269
272 """Check if a given URL exists on the server
273
274 """
275 try:
276 urllib2.urlopen(HeadRequest(url))
277 return True
278 except urllib2.URLError:
279 return False
280
283 """KVM hypervisor interface
284
285 """
286 CAN_MIGRATE = True
287
288 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
289 _PIDS_DIR = _ROOT_DIR + "/pid"
290 _UIDS_DIR = _ROOT_DIR + "/uid"
291 _CTRL_DIR = _ROOT_DIR + "/ctrl"
292 _CONF_DIR = _ROOT_DIR + "/conf"
293 _NICS_DIR = _ROOT_DIR + "/nic"
294 _KEYMAP_DIR = _ROOT_DIR + "/keymap"
295
296 _CHROOT_DIR = _ROOT_DIR + "/chroot"
297
298
299
300
301
302 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
303 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
304 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
305
306 PARAMETERS = {
307 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
308 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
309 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
310 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
311 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
312 constants.HV_ACPI: hv_base.NO_CHECK,
313 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
314 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
315 constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK,
316 constants.HV_VNC_TLS: hv_base.NO_CHECK,
317 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
318 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
319 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
320 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK,
321 constants.HV_KVM_SPICE_IP_VERSION:
322 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
323 x in constants.VALID_IP_VERSIONS),
324 "The SPICE IP version should be 4 or 6",
325 None, None),
326 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
327 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
328 hv_base.ParamInSet(
329 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
330 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
331 hv_base.ParamInSet(
332 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
333 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
334 hv_base.ParamInSet(
335 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
336 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
337 hv_base.ParamInSet(
338 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
339 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
340 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
341 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
342 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
343 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
344 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
345 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
346 constants.HV_BOOT_ORDER:
347 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
348 constants.HV_NIC_TYPE:
349 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
350 constants.HV_DISK_TYPE:
351 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
352 constants.HV_KVM_CDROM_DISK_TYPE:
353 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
354 constants.HV_USB_MOUSE:
355 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
356 constants.HV_KEYMAP: hv_base.NO_CHECK,
357 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
358 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
359 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
360 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
361 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
362 constants.HV_DISK_CACHE:
363 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
364 constants.HV_KVM_DISK_AIO:
365 hv_base.ParamInSet(False, constants.HT_KVM_VALID_AIO_TYPES),
366 constants.HV_SECURITY_MODEL:
367 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
368 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
369 constants.HV_KVM_FLAG:
370 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
371 constants.HV_VHOST_NET: hv_base.NO_CHECK,
372 constants.HV_VIRTIO_NET_QUEUES: hv_base.OPT_VIRTIO_NET_QUEUES_CHECK,
373 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
374 constants.HV_KVM_USER_SHUTDOWN: hv_base.NO_CHECK,
375 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
376 constants.HV_REBOOT_BEHAVIOR:
377 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
378 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
379 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
380 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
381 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
382 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
383 constants.HV_SOUNDHW: hv_base.NO_CHECK,
384 constants.HV_USB_DEVICES: hv_base.NO_CHECK,
385 constants.HV_VGA: hv_base.NO_CHECK,
386 constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
387 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
388 constants.HV_KVM_MIGRATION_CAPS: hv_base.NO_CHECK,
389 constants.HV_VNET_HDR: hv_base.NO_CHECK,
390 }
391
392 _VIRTIO = "virtio"
393 _VIRTIO_NET_PCI = "virtio-net-pci"
394 _VIRTIO_BLK_PCI = "virtio-blk-pci"
395
396 _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)",
397 re.M | re.I)
398 _MIGRATION_PROGRESS_RE = \
399 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
400 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
401 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
402
403 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
404 _MIGRATION_INFO_RETRY_DELAY = 2
405
406 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
407
408 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
409 _CPU_INFO_CMD = "info cpus"
410 _CONT_CMD = "cont"
411
412 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
413 _CHECK_MACHINE_VERSION_RE = \
414 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
415
416 _QMP_RE = re.compile(r"^-qmp\s", re.M)
417 _SPICE_RE = re.compile(r"^-spice\s", re.M)
418 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
419 _VIRTIO_NET_QUEUES_RE = re.compile(r"^-net\s.*,fds=x:y:...:z", re.M)
420 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
421 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
422 _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
423 _DISPLAY_RE = re.compile(r"^-display\s", re.M)
424 _MACHINE_RE = re.compile(r"^-machine\s", re.M)
425 _VIRTIO_NET_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M)
426 _VIRTIO_BLK_RE = re.compile(r"^name \"%s\"" % _VIRTIO_BLK_PCI, re.M)
427
428
429
430 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
431 _UUID_RE = re.compile(r"^-uuid\s", re.M)
432
433 _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
434 _INFO_PCI_CMD = "info pci"
435 _FIND_PCI_DEVICE_RE = \
436 staticmethod(
437 lambda pci, devid: re.compile(r'Bus.*device[ ]*%d,(.*\n){5,6}.*id "%s"' %
438 (pci, devid), re.M))
439
440 _INFO_VERSION_RE = \
441 re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
442 _INFO_VERSION_CMD = "info version"
443
444
445 _DEFAULT_PCI_RESERVATIONS = "11100000000000000000000000000000"
446 _SOUNDHW_WITH_PCI_SLOT = ["ac97", "es1370", "hda"]
447
448 ANCILLARY_FILES = [
449 _KVM_NETWORK_SCRIPT,
450 ]
451 ANCILLARY_FILES_OPT = [
452 _KVM_NETWORK_SCRIPT,
453 ]
454
455
456 _KVMOPT_HELP = "help"
457 _KVMOPT_MLIST = "mlist"
458 _KVMOPT_DEVICELIST = "devicelist"
459
460
461
462 _KVMOPTS_CMDS = {
463 _KVMOPT_HELP: (["--help"], False),
464 _KVMOPT_MLIST: (["-M", "?"], False),
465 _KVMOPT_DEVICELIST: (["-device", "?"], True),
466 }
467
474
475 @classmethod
477 """Returns the instance pidfile.
478
479 """
480 return utils.PathJoin(cls._PIDS_DIR, instance_name)
481
482 @classmethod
484 """Returns the instance uidfile.
485
486 """
487 return utils.PathJoin(cls._UIDS_DIR, instance_name)
488
489 @classmethod
491 """Check pid file for instance information.
492
493 Check that a pid file is associated with an instance, and retrieve
494 information from its command line.
495
496 @type pid: string or int
497 @param pid: process id of the instance to check
498 @rtype: tuple
499 @return: (instance_name, memory, vcpus)
500 @raise errors.HypervisorError: when an instance cannot be found
501
502 """
503 alive = utils.IsProcessAlive(pid)
504 if not alive:
505 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
506
507 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
508 try:
509 cmdline = utils.ReadFile(cmdline_file)
510 except EnvironmentError, err:
511 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
512 (pid, err))
513
514 instance = None
515 memory = 0
516 vcpus = 0
517
518 arg_list = cmdline.split("\x00")
519 while arg_list:
520 arg = arg_list.pop(0)
521 if arg == "-name":
522 instance = arg_list.pop(0)
523 elif arg == "-m":
524 memory = int(arg_list.pop(0))
525 elif arg == "-smp":
526 vcpus = int(arg_list.pop(0).split(",")[0])
527
528 if instance is None:
529 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
530 " instance" % pid)
531
532 return (instance, memory, vcpus)
533
534 @classmethod
536 """Returns the instance pidfile, pid, and liveness.
537
538 @type instance_name: string
539 @param instance_name: instance name
540 @rtype: tuple
541 @return: (pid file name, pid, liveness)
542
543 """
544 pidfile = cls._InstancePidFile(instance_name)
545 pid = utils.ReadPidFile(pidfile)
546
547 alive = False
548 try:
549 cmd_instance = cls._InstancePidInfo(pid)[0]
550 alive = (cmd_instance == instance_name)
551 except errors.HypervisorError:
552 pass
553
554 return (pidfile, pid, alive)
555
556 @classmethod
558 """Raises an error unless the given instance is down.
559
560 """
561 alive = cls._InstancePidAlive(instance_name)[2]
562 if alive:
563 raise errors.HypervisorError("Failed to start instance %s: %s" %
564 (instance_name, "already running"))
565
566 @classmethod
568 """Returns the instance monitor socket name
569
570 """
571 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
572
573 @classmethod
575 """Returns the instance serial socket name
576
577 """
578 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
579
580 @classmethod
582 """Returns the instance serial QMP socket name
583
584 """
585 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
586
587 @classmethod
589 """Returns the instance kvm daemon socket name
590
591 """
592 return utils.PathJoin(cls._CTRL_DIR, "%s.kvmd" % instance_name)
593
594 @classmethod
596 """Returns the instance QMP output filename
597
598 """
599 return utils.PathJoin(cls._CTRL_DIR, "%s.shutdown" % instance_name)
600
601 @staticmethod
603 """Returns the correct parameters for socat
604
605 If we have a new-enough socat we can use raw mode with an escape character.
606
607 """
608 if constants.SOCAT_USE_ESCAPE:
609 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
610 else:
611 return "echo=0,icanon=0"
612
613 @classmethod
615 """Returns the instance KVM runtime filename
616
617 """
618 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
619
620 @classmethod
622 """Returns the name of the KVM chroot dir of the instance
623
624 """
625 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
626
627 @classmethod
629 """Returns the name of the directory holding the tap device files for a
630 given instance.
631
632 """
633 return utils.PathJoin(cls._NICS_DIR, instance_name)
634
635 @classmethod
637 """Returns the name of the file containing the tap device for a given NIC
638
639 """
640 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
641
642 @classmethod
644 """Returns the name of the file containing the keymap for a given instance
645
646 """
647 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
648
649 @classmethod
651 """Try to read a uid file
652
653 """
654 if os.path.exists(uid_file):
655 try:
656 uid = int(utils.ReadOneLineFile(uid_file))
657 return uid
658 except EnvironmentError:
659 logging.warning("Can't read uid file", exc_info=True)
660 except (TypeError, ValueError):
661 logging.warning("Can't parse uid file contents", exc_info=True)
662 return None
663
664 @classmethod
666 """Removes an instance's rutime sockets/files/dirs.
667
668 """
669 utils.RemoveFile(pidfile)
670 utils.RemoveFile(cls._InstanceMonitor(instance_name))
671 utils.RemoveFile(cls._InstanceSerial(instance_name))
672 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
673 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
674 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
675 uid_file = cls._InstanceUidFile(instance_name)
676 uid = cls._TryReadUidFile(uid_file)
677 utils.RemoveFile(uid_file)
678 if uid is not None:
679 uidpool.ReleaseUid(uid)
680 try:
681 shutil.rmtree(cls._InstanceNICDir(instance_name))
682 except OSError, err:
683 if err.errno != errno.ENOENT:
684 raise
685 try:
686 chroot_dir = cls._InstanceChrootDir(instance_name)
687 utils.RemoveDir(chroot_dir)
688 except OSError, err:
689 if err.errno == errno.ENOTEMPTY:
690
691 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
692 prefix="%s-%s-" %
693 (instance_name,
694 utils.TimestampForFilename()))
695 logging.warning("The chroot directory of instance %s can not be"
696 " removed as it is not empty. Moving it to the"
697 " quarantine instead. Please investigate the"
698 " contents (%s) and clean up manually",
699 instance_name, new_chroot_dir)
700 utils.RenameFile(chroot_dir, new_chroot_dir)
701 else:
702 raise
703
704 @staticmethod
721
722 @classmethod
724 """Sets the affinity of a process to the given CPUs.
725
726 @type process_id: int
727 @type cpus: list of int
728 @param cpus: The list of CPUs the process ID may use.
729
730 """
731 if psutil is None:
732 raise errors.HypervisorError("psutil Python package not"
733 " found; cannot use CPU pinning under KVM")
734
735 target_process = psutil.Process(process_id)
736 if cpus == constants.CPU_PINNING_OFF:
737 target_process.set_cpu_affinity(range(psutil.cpu_count()))
738 else:
739 target_process.set_cpu_affinity(cpus)
740
741 @classmethod
743 """Change CPU affinity for running VM according to given CPU mask.
744
745 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
746 @type cpu_mask: string
747 @param process_id: process ID of KVM process. Used to pin entire VM
748 to physical CPUs.
749 @type process_id: int
750 @param thread_dict: map of virtual CPUs to KVM thread IDs
751 @type thread_dict: dict int:int
752
753 """
754
755 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
756
757 if len(cpu_list) == 1:
758 all_cpu_mapping = cpu_list[0]
759 if all_cpu_mapping == constants.CPU_PINNING_OFF:
760
761 pass
762 else:
763
764
765 cls._SetProcessAffinity(process_id, all_cpu_mapping)
766 else:
767
768
769
770 assert len(thread_dict) == len(cpu_list)
771
772
773 for i, vcpu in enumerate(cpu_list):
774 cls._SetProcessAffinity(thread_dict[i], vcpu)
775
777 """Get a mapping of vCPU no. to thread IDs for the instance
778
779 @type instance_name: string
780 @param instance_name: instance in question
781 @rtype: dictionary of int:int
782 @return: a dictionary mapping vCPU numbers to thread IDs
783
784 """
785 result = {}
786 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
787 for line in output.stdout.splitlines():
788 match = self._CPU_INFO_RE.search(line)
789 if not match:
790 continue
791 grp = map(int, match.groups())
792 result[grp[0]] = grp[1]
793
794 return result
795
797 """Complete CPU pinning.
798
799 @type instance_name: string
800 @param instance_name: name of instance
801 @type cpu_mask: string
802 @param cpu_mask: CPU pinning mask as entered by user
803
804 """
805
806 _, pid, _ = self._InstancePidAlive(instance_name)
807
808 thread_dict = self._GetVcpuThreadIds(instance_name)
809
810 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
811
813 """Get the list of running instances.
814
815 We can do this by listing our live instances directory and
816 checking whether the associated kvm process is still alive.
817
818 """
819 result = []
820 for name in os.listdir(self._PIDS_DIR):
821 if self._InstancePidAlive(name)[2]:
822 result.append(name)
823 return result
824
825 @classmethod
828
829 @classmethod
832
834 """Get instance properties.
835
836 @type instance_name: string
837 @param instance_name: the instance name
838 @type hvparams: dict of strings
839 @param hvparams: hypervisor parameters to be used with this instance
840 @rtype: tuple of strings
841 @return: (name, id, memory, vcpus, stat, times)
842
843 """
844 _, pid, alive = self._InstancePidAlive(instance_name)
845 if not alive:
846 if self._IsUserShutdown(instance_name):
847 return (instance_name, -1, 0, 0, hv_base.HvInstanceState.SHUTDOWN, 0)
848 else:
849 return None
850
851 _, memory, vcpus = self._InstancePidInfo(pid)
852 istat = hv_base.HvInstanceState.RUNNING
853 times = 0
854
855 try:
856 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
857 qmp.connect()
858 vcpus = len(qmp.Execute("query-cpus"))
859
860
861 mem_bytes = qmp.Execute("query-balloon")[qmp.ACTUAL_KEY]
862 memory = mem_bytes / 1048576
863 except errors.HypervisorError:
864 pass
865
866 return (instance_name, pid, memory, vcpus, istat, times)
867
869 """Get properties of all instances.
870
871 @type hvparams: dict of strings
872 @param hvparams: hypervisor parameters
873 @return: list of tuples (name, id, memory, vcpus, stat, times)
874
875 """
876 data = []
877 for name in os.listdir(self._PIDS_DIR):
878 try:
879 info = self.GetInstanceInfo(name)
880 except errors.HypervisorError:
881
882 continue
883 if info:
884 data.append(info)
885 return data
886
889 """Generate KVM options regarding instance's block devices.
890
891 @type instance: L{objects.Instance}
892 @param instance: the instance object
893 @type up_hvp: dict
894 @param up_hvp: the instance's runtime hypervisor parameters
895 @type kvm_disks: list of tuples
896 @param kvm_disks: list of tuples [(disk, link_name, uri)..]
897 @type kvmhelp: string
898 @param kvmhelp: output of kvm --help
899 @type devlist: string
900 @param devlist: output of kvm -device ?
901 @rtype: list
902 @return: list of command line options eventually used by kvm executable
903
904 """
905 kernel_path = up_hvp[constants.HV_KERNEL_PATH]
906 if kernel_path:
907 boot_disk = False
908 else:
909 boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
910
911
912
913 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
914
915 dev_opts = []
916 device_driver = None
917 disk_type = up_hvp[constants.HV_DISK_TYPE]
918 if disk_type == constants.HT_DISK_PARAVIRTUAL:
919 if_val = ",if=%s" % self._VIRTIO
920 try:
921 if self._VIRTIO_BLK_RE.search(devlist):
922 if_val = ",if=none"
923
924 device_driver = self._VIRTIO_BLK_PCI
925 except errors.HypervisorError, _:
926 pass
927 else:
928 if_val = ",if=%s" % disk_type
929
930 aio_mode = up_hvp[constants.HV_KVM_DISK_AIO]
931 if aio_mode == constants.HT_KVM_AIO_NATIVE:
932 aio_val = ",aio=%s" % aio_mode
933 else:
934 aio_val = ""
935
936 disk_cache = up_hvp[constants.HV_DISK_CACHE]
937 if instance.disk_template in constants.DTS_EXT_MIRROR:
938 if disk_cache != "none":
939
940 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
941 " to prevent shared storage corruption on migration",
942 disk_cache)
943 cache_val = ",cache=none"
944 elif disk_cache != constants.HT_CACHE_DEFAULT:
945 cache_val = ",cache=%s" % disk_cache
946 else:
947 cache_val = ""
948 for cfdev, link_name, uri in kvm_disks:
949 if cfdev.mode != constants.DISK_RDWR:
950 raise errors.HypervisorError("Instance has read-only disks which"
951 " are not supported by KVM")
952
953 boot_val = ""
954 if boot_disk:
955 dev_opts.extend(["-boot", "c"])
956 boot_disk = False
957 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
958 boot_val = ",boot=on"
959
960 drive_uri = _GetDriveURI(cfdev, link_name, uri)
961
962 drive_val = "file=%s,format=raw%s%s%s%s" % \
963 (drive_uri, if_val, boot_val, cache_val, aio_val)
964
965 if device_driver:
966
967
968
969 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK, cfdev)
970 drive_val += (",id=%s" % kvm_devid)
971 drive_val += (",bus=0,unit=%d" % cfdev.pci)
972 dev_val = ("%s,drive=%s,id=%s" %
973 (device_driver, kvm_devid, kvm_devid))
974 dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
975 dev_opts.extend(["-device", dev_val])
976
977 dev_opts.extend(["-drive", drive_val])
978
979 return dev_opts
980
981 @staticmethod
982 - def _CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image, cdrom_boot,
983 needs_boot_flag):
984 """Extends L{kvm_cmd} with the '-drive' option for a cdrom, and
985 optionally the '-boot' option.
986
987 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide -boot d
988
989 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide,boot=on
990
991 Example: -drive file=http://hostname.com/cdrom.iso,media=cdrom
992
993 @type kvm_cmd: string
994 @param kvm_cmd: KVM command line
995
996 @type cdrom_disk_type:
997 @param cdrom_disk_type:
998
999 @type cdrom_image:
1000 @param cdrom_image:
1001
1002 @type cdrom_boot:
1003 @param cdrom_boot:
1004
1005 @type needs_boot_flag:
1006 @param needs_boot_flag:
1007
1008 """
1009
1010
1011 if utils.IsUrl(cdrom_image) and not _CheckUrl(cdrom_image):
1012 raise errors.HypervisorError("Cdrom ISO image '%s' is not accessible" %
1013 cdrom_image)
1014
1015
1016 if utils.IsUrl(cdrom_image):
1017 options = ",media=cdrom"
1018 else:
1019 options = ",media=cdrom,format=raw"
1020
1021
1022 if cdrom_boot:
1023 if_val = ",if=" + constants.HT_DISK_IDE
1024 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1025 if_val = ",if=virtio"
1026 else:
1027 if_val = ",if=" + cdrom_disk_type
1028
1029
1030 boot_val = ""
1031 if cdrom_boot:
1032 kvm_cmd.extend(["-boot", "d"])
1033
1034
1035
1036 if needs_boot_flag:
1037 boot_val = ",boot=on"
1038
1039
1040 drive_val = "file=%s%s%s%s" % (cdrom_image, options, if_val, boot_val)
1041 kvm_cmd.extend(["-drive", drive_val])
1042
1045 """Generate KVM information to start an instance.
1046
1047 @type kvmhelp: string
1048 @param kvmhelp: output of kvm --help
1049 @attention: this function must not have any side-effects; for
1050 example, it must not write to the filesystem, or read values
1051 from the current system the are expected to differ between
1052 nodes, since it is only run once at instance startup;
1053 actions/kvm arguments that can vary between systems should be
1054 done in L{_ExecuteKVMRuntime}
1055
1056 """
1057
1058 hvp = instance.hvparams
1059 self.ValidateParameters(hvp)
1060
1061 pidfile = self._InstancePidFile(instance.name)
1062 kvm = hvp[constants.HV_KVM_PATH]
1063 kvm_cmd = [kvm]
1064
1065 kvm_cmd.extend(["-name", instance.name])
1066 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1067
1068 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1069 if hvp[constants.HV_CPU_CORES]:
1070 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1071 if hvp[constants.HV_CPU_THREADS]:
1072 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1073 if hvp[constants.HV_CPU_SOCKETS]:
1074 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1075
1076 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1077
1078 kvm_cmd.extend(["-pidfile", pidfile])
1079
1080 pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1081
1082
1083 if hvp[constants.HV_SOUNDHW]:
1084 soundhw = hvp[constants.HV_SOUNDHW]
1085
1086
1087
1088 if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
1089 _ = _GetFreeSlot(pci_reservations, reserve=True)
1090 kvm_cmd.extend(["-soundhw", soundhw])
1091
1092 if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
1093
1094 _ = _GetFreeSlot(pci_reservations, reserve=True)
1095
1096
1097 addr = _GetFreeSlot(pci_reservations, reserve=True)
1098 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1099 kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
1100 kvm_cmd.extend(["-daemonize"])
1101 if not instance.hvparams[constants.HV_ACPI]:
1102 kvm_cmd.extend(["-no-acpi"])
1103 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1104 constants.INSTANCE_REBOOT_EXIT:
1105 kvm_cmd.extend(["-no-reboot"])
1106
1107 mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1108 if not mversion:
1109 mversion = self._GetDefaultMachineVersion(kvm)
1110 if self._MACHINE_RE.search(kvmhelp):
1111
1112
1113
1114 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1115 specprop = ",accel=kvm"
1116 else:
1117 specprop = ""
1118 machinespec = "%s%s" % (mversion, specprop)
1119 kvm_cmd.extend(["-machine", machinespec])
1120 else:
1121 kvm_cmd.extend(["-M", mversion])
1122 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1123 self._ENABLE_KVM_RE.search(kvmhelp)):
1124 kvm_cmd.extend(["-enable-kvm"])
1125 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1126 self._DISABLE_KVM_RE.search(kvmhelp)):
1127 kvm_cmd.extend(["-disable-kvm"])
1128
1129 kernel_path = hvp[constants.HV_KERNEL_PATH]
1130 if kernel_path:
1131 boot_cdrom = boot_floppy = boot_network = False
1132 else:
1133 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1134 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1135 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1136
1137 if startup_paused:
1138 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1139
1140 if boot_network:
1141 kvm_cmd.extend(["-boot", "n"])
1142
1143 disk_type = hvp[constants.HV_DISK_TYPE]
1144
1145
1146 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1147 if not cdrom_disk_type:
1148 cdrom_disk_type = disk_type
1149
1150 cdrom_image1 = hvp[constants.HV_CDROM_IMAGE_PATH]
1151 if cdrom_image1:
1152 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1153 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image1, boot_cdrom,
1154 needs_boot_flag)
1155
1156 cdrom_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1157 if cdrom_image2:
1158 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image2, False, False)
1159
1160 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1161 if floppy_image:
1162 options = ",format=raw,media=disk"
1163 if boot_floppy:
1164 kvm_cmd.extend(["-boot", "a"])
1165 options = "%s,boot=on" % options
1166 if_val = ",if=floppy"
1167 options = "%s%s" % (options, if_val)
1168 drive_val = "file=%s%s" % (floppy_image, options)
1169 kvm_cmd.extend(["-drive", drive_val])
1170
1171 if kernel_path:
1172 kvm_cmd.extend(["-kernel", kernel_path])
1173 initrd_path = hvp[constants.HV_INITRD_PATH]
1174 if initrd_path:
1175 kvm_cmd.extend(["-initrd", initrd_path])
1176 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1177 hvp[constants.HV_KERNEL_ARGS]]
1178 if hvp[constants.HV_SERIAL_CONSOLE]:
1179 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1180 root_append.append("console=ttyS0,%s" % serial_speed)
1181 kvm_cmd.extend(["-append", " ".join(root_append)])
1182
1183 mem_path = hvp[constants.HV_MEM_PATH]
1184 if mem_path:
1185 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1186
1187 monitor_dev = ("unix:%s,server,nowait" %
1188 self._InstanceMonitor(instance.name))
1189 kvm_cmd.extend(["-monitor", monitor_dev])
1190 if hvp[constants.HV_SERIAL_CONSOLE]:
1191 serial_dev = ("unix:%s,server,nowait" %
1192 self._InstanceSerial(instance.name))
1193 kvm_cmd.extend(["-serial", serial_dev])
1194 else:
1195 kvm_cmd.extend(["-serial", "none"])
1196
1197 mouse_type = hvp[constants.HV_USB_MOUSE]
1198 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1199 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1200 spice_ip_version = None
1201
1202 kvm_cmd.extend(["-usb"])
1203
1204 if mouse_type:
1205 kvm_cmd.extend(["-usbdevice", mouse_type])
1206 elif vnc_bind_address:
1207 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1208
1209 if vnc_bind_address:
1210 if netutils.IsValidInterface(vnc_bind_address):
1211 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1212 if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1213 if len(if_ip4_addresses) < 1:
1214 logging.error("Could not determine IPv4 address of interface %s",
1215 vnc_bind_address)
1216 else:
1217 vnc_bind_address = if_ip4_addresses[0]
1218 if netutils.IP4Address.IsValid(vnc_bind_address):
1219 if instance.network_port > constants.VNC_BASE_PORT:
1220 display = instance.network_port - constants.VNC_BASE_PORT
1221 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1222 vnc_arg = ":%d" % (display)
1223 else:
1224 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1225 else:
1226 logging.error("Network port is not a valid VNC display (%d < %d),"
1227 " not starting VNC",
1228 instance.network_port, constants.VNC_BASE_PORT)
1229 vnc_arg = "none"
1230
1231
1232
1233 vnc_append = ""
1234 if hvp[constants.HV_VNC_TLS]:
1235 vnc_append = "%s,tls" % vnc_append
1236 if hvp[constants.HV_VNC_X509_VERIFY]:
1237 vnc_append = "%s,x509verify=%s" % (vnc_append,
1238 hvp[constants.HV_VNC_X509])
1239 elif hvp[constants.HV_VNC_X509]:
1240 vnc_append = "%s,x509=%s" % (vnc_append,
1241 hvp[constants.HV_VNC_X509])
1242 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1243 vnc_append = "%s,password" % vnc_append
1244
1245 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1246
1247 else:
1248 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1249
1250 kvm_cmd.extend(["-vnc", vnc_arg])
1251 elif spice_bind:
1252
1253
1254 if netutils.IsValidInterface(spice_bind):
1255
1256
1257 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1258 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1259
1260
1261
1262 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1263 if not addresses[spice_ip_version]:
1264 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1265 " for %s" % (spice_ip_version,
1266 spice_bind))
1267
1268
1269 elif (addresses[constants.IP4_VERSION] and
1270 addresses[constants.IP6_VERSION]):
1271
1272
1273 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1274 spice_ip_version = \
1275 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1276 elif addresses[constants.IP4_VERSION]:
1277 spice_ip_version = constants.IP4_VERSION
1278 elif addresses[constants.IP6_VERSION]:
1279 spice_ip_version = constants.IP6_VERSION
1280 else:
1281 raise errors.HypervisorError("SPICE: Unable to get an IP address"
1282 " for %s" % (spice_bind))
1283
1284 spice_address = addresses[spice_ip_version][0]
1285
1286 else:
1287
1288
1289 spice_address = spice_bind
1290
1291 spice_arg = "addr=%s" % spice_address
1292 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1293 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1294 (spice_arg, instance.network_port,
1295 pathutils.SPICE_CACERT_FILE))
1296 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1297 (spice_arg, pathutils.SPICE_CERT_FILE,
1298 pathutils.SPICE_CERT_FILE))
1299 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1300 if tls_ciphers:
1301 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1302 else:
1303 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1304
1305 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1306 spice_arg = "%s,disable-ticketing" % spice_arg
1307
1308 if spice_ip_version:
1309 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1310
1311
1312 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1313 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1314 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1315 if img_lossless:
1316 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1317 if img_jpeg:
1318 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1319 if img_zlib_glz:
1320 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1321
1322
1323 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1324 if video_streaming:
1325 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1326
1327
1328 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1329 spice_arg = "%s,playback-compression=off" % spice_arg
1330 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1331 spice_arg = "%s,agent-mouse=off" % spice_arg
1332 else:
1333
1334
1335 addr = _GetFreeSlot(pci_reservations, reserve=True)
1336 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1337 kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
1338 kvm_cmd.extend([
1339 "-device",
1340 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1341 ])
1342 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1343
1344 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1345 kvm_cmd.extend(["-spice", spice_arg])
1346
1347 else:
1348
1349
1350 if self._DISPLAY_RE.search(kvmhelp):
1351 kvm_cmd.extend(["-display", "none"])
1352 else:
1353 kvm_cmd.extend(["-nographic"])
1354
1355 if hvp[constants.HV_USE_LOCALTIME]:
1356 kvm_cmd.extend(["-localtime"])
1357
1358 if hvp[constants.HV_KVM_USE_CHROOT]:
1359 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1360
1361
1362 if hvp[constants.HV_CPU_TYPE]:
1363 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1364
1365
1366
1367 if hvp[constants.HV_VGA]:
1368 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1369 elif spice_bind:
1370 kvm_cmd.extend(["-vga", "qxl"])
1371
1372
1373 if hvp[constants.HV_USB_DEVICES]:
1374 for dev in hvp[constants.HV_USB_DEVICES].split(","):
1375 kvm_cmd.extend(["-usbdevice", dev])
1376
1377
1378 if self._UUID_RE.search(kvmhelp):
1379 kvm_cmd.extend(["-uuid", instance.uuid])
1380
1381 if hvp[constants.HV_KVM_EXTRA]:
1382 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1383
1384 kvm_disks = []
1385 for disk, link_name, uri in block_devices:
1386 disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
1387 kvm_disks.append((disk, link_name, uri))
1388
1389 kvm_nics = []
1390 for nic in instance.nics:
1391 nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True)
1392 kvm_nics.append(nic)
1393
1394 hvparams = hvp
1395
1396 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1397
1407
1409 """Read an instance's KVM runtime
1410
1411 """
1412 try:
1413 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1414 except EnvironmentError, err:
1415 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1416 return file_content
1417
1419 """Save an instance's KVM runtime
1420
1421 """
1422 kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1423
1424 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1425 serialized_disks = [(blk.ToDict(), link, uri)
1426 for blk, link, uri in kvm_disks]
1427 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1428 serialized_disks))
1429
1430 self._WriteKVMRuntime(instance.name, serialized_form)
1431
1433 """Load an instance's KVM runtime
1434
1435 """
1436 if not serialized_runtime:
1437 serialized_runtime = self._ReadKVMRuntime(instance.name)
1438
1439 return _AnalyzeSerializedRuntime(serialized_runtime)
1440
1441 - def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1442 """Run the KVM cmd and check for errors
1443
1444 @type name: string
1445 @param name: instance name
1446 @type kvm_cmd: list of strings
1447 @param kvm_cmd: runcmd input for kvm
1448 @type tap_fds: list of int
1449 @param tap_fds: fds of tap devices opened by Ganeti
1450
1451 """
1452 try:
1453 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1454 finally:
1455 for fd in tap_fds:
1456 utils_wrapper.CloseFdNoError(fd)
1457
1458 if result.failed:
1459 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1460 (name, result.fail_reason, result.output))
1461 if not self._InstancePidAlive(name)[2]:
1462 raise errors.HypervisorError("Failed to start instance %s" % name)
1463
1464 @staticmethod
1466 """Generate a TAP network interface name for a NIC.
1467
1468 See L{hv_base.GenerateTapName}.
1469
1470 For the case of the empty string, see L{OpenTap}
1471
1472 @type nic: ganeti.objects.NIC
1473 @param nic: NIC object for the name should be generated
1474
1475 @rtype: string
1476 @return: TAP network interface name, or the empty string if the
1477 NIC is not used in instance communication
1478
1479 """
1480 if nic.name is None or not \
1481 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
1482 return ""
1483
1484 return hv_base.GenerateTapName()
1485
1487 """Get network device options to properly enable supported features.
1488
1489 Return tuple of supported and enabled tap features with nic_model.
1490 This function is called before opening a new tap device.
1491
1492 @return: (nic_model, vnet_hdr, virtio_net_queues, tap_extra, nic_extra)
1493 @rtype: tuple
1494
1495 """
1496 virtio_net_queues = 1
1497 nic_extra = ""
1498 nic_type = up_hvp[constants.HV_NIC_TYPE]
1499 tap_extra = ""
1500 vnet_hdr = False
1501 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1502 nic_model = self._VIRTIO
1503 try:
1504 if self._VIRTIO_NET_RE.search(devlist):
1505 nic_model = self._VIRTIO_NET_PCI
1506 vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1507 except errors.HypervisorError, _:
1508
1509
1510 pass
1511
1512 if up_hvp[constants.HV_VHOST_NET]:
1513
1514 if self._VHOST_RE.search(kvmhelp):
1515 tap_extra = ",vhost=on"
1516 else:
1517 raise errors.HypervisorError("vhost_net is configured"
1518 " but it is not available")
1519 if up_hvp[constants.HV_VIRTIO_NET_QUEUES] > 1:
1520
1521 if self._VIRTIO_NET_QUEUES_RE.search(kvmhelp):
1522 virtio_net_queues = up_hvp[constants.HV_VIRTIO_NET_QUEUES]
1523
1524
1525
1526 nic_extra = ",mq=on,vectors=%d" % (2 * virtio_net_queues + 1)
1527 else:
1528 raise errors.HypervisorError("virtio_net_queues is configured"
1529 " but it is not available")
1530 else:
1531 nic_model = nic_type
1532
1533 return (nic_model, vnet_hdr, virtio_net_queues, tap_extra, nic_extra)
1534
1535
1536
1538 """Execute a KVM cmd, after completing it with some last minute data.
1539
1540 @type incoming: tuple of strings
1541 @param incoming: (target_host_ip, port)
1542 @type kvmhelp: string
1543 @param kvmhelp: output of kvm --help
1544
1545 """
1546
1547
1548
1549
1550
1551
1552
1553
1554 conf_hvp = instance.hvparams
1555 name = instance.name
1556 self._CheckDown(name)
1557
1558 self._ClearUserShutdown(instance.name)
1559 self._StartKvmd(instance.hvparams)
1560
1561 temp_files = []
1562
1563 kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1564
1565 kvm_path = kvm_cmd[0]
1566 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1567
1568
1569
1570 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1571 if security_model == constants.HT_SM_USER:
1572 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1573
1574 keymap = conf_hvp[constants.HV_KEYMAP]
1575 if keymap:
1576 keymap_path = self._InstanceKeymapFile(name)
1577
1578
1579
1580
1581 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1582 kvm_cmd.extend(["-k", keymap_path])
1583
1584
1585
1586
1587 tapfds = []
1588 taps = []
1589 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1590 if not kvm_nics:
1591 kvm_cmd.extend(["-net", "none"])
1592 else:
1593 (nic_model, vnet_hdr,
1594 virtio_net_queues, tap_extra,
1595 nic_extra) = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
1596 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1597 for nic_seq, nic in enumerate(kvm_nics):
1598 tapname, nic_tapfds = OpenTap(vnet_hdr=vnet_hdr,
1599 virtio_net_queues=virtio_net_queues,
1600 name=self._GenerateKvmTapName(nic))
1601 tapfds.extend(nic_tapfds)
1602 taps.append(tapname)
1603 tapfd = "%s%s" % ("fds=" if len(nic_tapfds) > 1 else "fd=",
1604 ":".join(str(fd) for fd in nic_tapfds))
1605 if kvm_supports_netdev:
1606 nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1607 try:
1608
1609
1610 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1611 netdev = kvm_devid
1612 nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1613 except errors.HotplugError:
1614 netdev = "netdev%d" % nic_seq
1615 nic_val += (",netdev=%s%s" % (netdev, nic_extra))
1616 tap_val = ("type=tap,id=%s,%s%s" %
1617 (netdev, tapfd, tap_extra))
1618 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1619 else:
1620 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1621 nic.mac, nic_model)
1622 tap_val = "tap,vlan=%s,%s" % (nic_seq, tapfd)
1623 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1624
1625 if incoming:
1626 target, port = incoming
1627 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1628
1629
1630
1631
1632 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1633 vnc_pwd = None
1634 if vnc_pwd_file:
1635 try:
1636 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1637 except EnvironmentError, err:
1638 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1639 % (vnc_pwd_file, err))
1640
1641 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1642 utils.EnsureDirs([(self._InstanceChrootDir(name),
1643 constants.SECURE_DIR_MODE)])
1644
1645
1646 if self._QMP_RE.search(kvmhelp):
1647 logging.debug("Enabling QMP")
1648 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1649 self._InstanceQmpMonitor(instance.name)])
1650
1651 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1652 self._InstanceKvmdMonitor(instance.name)])
1653
1654
1655
1656
1657 for nic_seq, nic in enumerate(kvm_nics):
1658 if (incoming and
1659 nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED):
1660 continue
1661 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1662
1663 bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1664 up_hvp,
1665 kvm_disks,
1666 kvmhelp,
1667 devlist)
1668 kvm_cmd.extend(bdev_opts)
1669
1670
1671
1672 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1673 if start_kvm_paused:
1674 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1675
1676
1677
1678
1679 cpu_pinning = False
1680 if up_hvp.get(constants.HV_CPU_MASK, None):
1681 cpu_pinning = True
1682
1683 if security_model == constants.HT_SM_POOL:
1684 ss = ssconf.SimpleStore()
1685 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1686 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1687 uid = uidpool.RequestUnusedUid(all_uids)
1688 try:
1689 username = pwd.getpwuid(uid.GetUid()).pw_name
1690 kvm_cmd.extend(["-runas", username])
1691 self._RunKVMCmd(name, kvm_cmd, tapfds)
1692 except:
1693 uidpool.ReleaseUid(uid)
1694 raise
1695 else:
1696 uid.Unlock()
1697 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1698 else:
1699 self._RunKVMCmd(name, kvm_cmd, tapfds)
1700
1701 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1702 constants.RUN_DIRS_MODE)])
1703 for nic_seq, tap in enumerate(taps):
1704 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1705 data=tap)
1706
1707 if vnc_pwd:
1708 change_cmd = "change vnc password %s" % vnc_pwd
1709 self._CallMonitorCommand(instance.name, change_cmd)
1710
1711
1712
1713
1714
1715
1716 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1717 if spice_password_file:
1718 spice_pwd = ""
1719 try:
1720 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1721 except EnvironmentError, err:
1722 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1723 % (spice_password_file, err))
1724
1725 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1726 qmp.connect()
1727 arguments = {
1728 "protocol": "spice",
1729 "password": spice_pwd,
1730 }
1731 qmp.Execute("set_password", arguments)
1732
1733 for filename in temp_files:
1734 utils.RemoveFile(filename)
1735
1736
1737 if cpu_pinning:
1738 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1739
1740 start_memory = self._InstanceStartupMemory(instance)
1741 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1742 self.BalloonInstanceMemory(instance, start_memory)
1743
1744 if start_kvm_paused:
1745
1746
1747
1748 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1749
1750 @staticmethod
1767
1768 - def StartInstance(self, instance, block_devices, startup_paused):
1779
1780 @classmethod
1782 """Invoke a command on the instance monitor.
1783
1784 """
1785 if timeout is not None:
1786 timeout_cmd = "timeout %s" % (timeout, )
1787 else:
1788 timeout_cmd = ""
1789
1790
1791
1792
1793
1794
1795
1796
1797 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
1798 (utils.ShellQuote(command),
1799 timeout_cmd,
1800 constants.SOCAT_PATH,
1801 utils.ShellQuote(cls._InstanceMonitor(instance_name))))
1802 result = utils.RunCmd(socat)
1803 if result.failed:
1804 msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
1805 " output: %s" %
1806 (command, instance_name, result.fail_reason, result.output))
1807 raise errors.HypervisorError(msg)
1808
1809 return result
1810
1812 """Get the first available pci slot of a runnung instance.
1813
1814 """
1815 slots = bitarray(32)
1816 slots.setall(False)
1817 output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
1818 for line in output.stdout.splitlines():
1819 match = self._INFO_PCI_RE.search(line)
1820 if match:
1821 slot = int(match.group(1))
1822 slots[slot] = True
1823
1824 dev.pci = _GetFreeSlot(slots)
1825
1852
1876
1881
1884 """Checks if a previous hotplug command has succeeded.
1885
1886 It issues info pci monitor command and checks depending on should_exist
1887 value if an entry with PCI slot and device ID is found or not.
1888
1889 @raise errors.HypervisorError: if result is not the expected one
1890
1891 """
1892 output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
1893 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
1894 match = \
1895 self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
1896 if match and not should_exist:
1897 msg = "Device %s should have been removed but is still there" % kvm_devid
1898 raise errors.HypervisorError(msg)
1899
1900 if not match and should_exist:
1901 msg = "Device %s should have been added but is missing" % kvm_devid
1902 raise errors.HypervisorError(msg)
1903
1904 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
1905
1906 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
1907 """ Helper method to hot-add a new device
1908
1909 It gets free pci slot generates the device name and invokes the
1910 device specific method.
1911
1912 """
1913
1914 if device.pci is None:
1915 self._GetFreePCISlot(instance, device)
1916 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
1917 runtime = self._LoadKVMRuntime(instance)
1918 if dev_type == constants.HOTPLUG_TARGET_DISK:
1919 drive_uri = _GetDriveURI(device, extra[0], extra[1])
1920 cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
1921 (drive_uri, kvm_devid)]
1922 cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
1923 (hex(device.pci), kvm_devid, kvm_devid)]
1924 elif dev_type == constants.HOTPLUG_TARGET_NIC:
1925 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
1926 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
1927 devlist = self._GetKVMOutput(kvmpath, self._KVMOPT_DEVICELIST)
1928 up_hvp = runtime[2]
1929 (_, vnet_hdr,
1930 virtio_net_queues, tap_extra,
1931 nic_extra) = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
1932 (tap, fds) = OpenTap(vnet_hdr=vnet_hdr,
1933 virtio_net_queues=virtio_net_queues)
1934
1935
1936 tapfd = ",".join(["fd=%s" % fd for fd in fds])
1937 self._ConfigureNIC(instance, seq, device, tap)
1938 self._PassTapFd(instance, fds, device)
1939 cmds = ["netdev_add tap,id=%s,%s%s" % (kvm_devid, tapfd, tap_extra)]
1940 args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s%s" % \
1941 (hex(device.pci), device.mac, kvm_devid, kvm_devid, nic_extra)
1942 cmds += ["device_add %s" % args]
1943 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
1944
1945 self._CallHotplugCommands(instance.name, cmds)
1946 self._VerifyHotplugCommand(instance.name, device, dev_type, True)
1947
1948 index = _DEVICE_RUNTIME_INDEX[dev_type]
1949 entry = _RUNTIME_ENTRY[dev_type](device, extra)
1950 runtime[index].append(entry)
1951 self._SaveKVMRuntime(instance, runtime)
1952
1953 - def HotDelDevice(self, instance, dev_type, device, _, seq):
1954 """ Helper method for hot-del device
1955
1956 It gets device info from runtime file, generates the device name and
1957 invokes the device specific method.
1958
1959 """
1960 runtime = self._LoadKVMRuntime(instance)
1961 entry = _GetExistingDeviceInfo(dev_type, device, runtime)
1962 kvm_device = _RUNTIME_DEVICE[dev_type](entry)
1963 kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
1964 if dev_type == constants.HOTPLUG_TARGET_DISK:
1965 cmds = ["device_del %s" % kvm_devid]
1966 cmds += ["drive_del %s" % kvm_devid]
1967 elif dev_type == constants.HOTPLUG_TARGET_NIC:
1968 cmds = ["device_del %s" % kvm_devid]
1969 cmds += ["netdev_del %s" % kvm_devid]
1970 utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
1971 self._CallHotplugCommands(instance.name, cmds)
1972 self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
1973 index = _DEVICE_RUNTIME_INDEX[dev_type]
1974 runtime[index].remove(entry)
1975 self._SaveKVMRuntime(instance, runtime)
1976
1977 return kvm_device.pci
1978
1979 - def HotModDevice(self, instance, dev_type, device, _, seq):
1980 """ Helper method for hot-mod device
1981
1982 It gets device info from runtime file, generates the device name and
1983 invokes the device specific method. Currently only NICs support hot-mod
1984
1985 """
1986 if dev_type == constants.HOTPLUG_TARGET_NIC:
1987
1988 device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
1989 self.HotAddDevice(instance, dev_type, device, _, seq)
1990
1992 """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
1993
1994 """
1995
1996
1997 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1998 command = "getfd %s\n" % kvm_devid
1999 logging.info("%s", fds)
2000 try:
2001 monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2002 monsock.connect()
2003 fdsend.sendfds(monsock.sock, command, fds=fds)
2004 finally:
2005 monsock.close()
2006
2007 @classmethod
2009 """Parse the KVM version from the --help output.
2010
2011 @type text: string
2012 @param text: output of kvm --help
2013 @return: (version, v_maj, v_min, v_rev)
2014 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2015
2016 """
2017 match = cls._VERSION_RE.search(text.splitlines()[0])
2018 if not match:
2019 raise errors.HypervisorError("Unable to get KVM version")
2020
2021 v_all = match.group(0)
2022 v_maj = int(match.group(1))
2023 v_min = int(match.group(2))
2024 if match.group(4):
2025 v_rev = int(match.group(4))
2026 else:
2027 v_rev = 0
2028 return (v_all, v_maj, v_min, v_rev)
2029
2030 @classmethod
2032 """Return the output of a kvm invocation
2033
2034 @type kvm_path: string
2035 @param kvm_path: path to the kvm executable
2036 @type option: a key of _KVMOPTS_CMDS
2037 @param option: kvm option to fetch the output from
2038 @return: output a supported kvm invocation
2039 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2040
2041 """
2042 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2043
2044 optlist, can_fail = cls._KVMOPTS_CMDS[option]
2045
2046 result = utils.RunCmd([kvm_path] + optlist)
2047 if result.failed and not can_fail:
2048 raise errors.HypervisorError("Unable to get KVM %s output" %
2049 " ".join(optlist))
2050 return result.output
2051
2052 @classmethod
2054 """Return the installed KVM version.
2055
2056 @return: (version, v_maj, v_min, v_rev)
2057 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2058
2059 """
2060 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2061
2062 @classmethod
2073
2074 @classmethod
2075 - def _StopInstance(cls, instance, force=False, name=None, timeout=None):
2095
2096 - def StopInstance(self, instance, force=False, retry=False, name=None,
2097 timeout=None):
2102
2112
2135
2137 """Get instance information to perform a migration.
2138
2139 @type instance: L{objects.Instance}
2140 @param instance: instance to be migrated
2141 @rtype: string
2142 @return: content of the KVM runtime file
2143
2144 """
2145 return self._ReadKVMRuntime(instance.name)
2146
2148 """Prepare to accept an instance.
2149
2150 @type instance: L{objects.Instance}
2151 @param instance: instance to be accepted
2152 @type info: string
2153 @param info: content of the KVM runtime file on the source node
2154 @type target: string
2155 @param target: target host (usually ip), on this node
2156
2157 """
2158 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2159 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2160 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2161 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2162 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2163 incoming=incoming_address)
2164
2166 """Finalize the instance migration on the target node.
2167
2168 Stop the incoming mode KVM.
2169
2170 @type instance: L{objects.Instance}
2171 @param instance: instance whose migration is being finalized
2172
2173 """
2174 if success:
2175 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2176 kvm_nics = kvm_runtime[1]
2177
2178 for nic_seq, nic in enumerate(kvm_nics):
2179 if nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_ROUTED:
2180
2181 continue
2182 try:
2183 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2184 except EnvironmentError, err:
2185 logging.warning("Failed to find host interface for %s NIC #%d: %s",
2186 instance.name, nic_seq, str(err))
2187 continue
2188 try:
2189 self._ConfigureNIC(instance, nic_seq, nic, tap)
2190 except errors.HypervisorError, err:
2191 logging.warning(str(err))
2192
2193 self._WriteKVMRuntime(instance.name, info)
2194 else:
2195 self.StopInstance(instance, force=True)
2196
2238
2257
2259 """Get the migration status
2260
2261 @type instance: L{objects.Instance}
2262 @param instance: the instance that is being migrated
2263 @rtype: L{objects.MigrationStatus}
2264 @return: the status of the current migration (one of
2265 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2266 progress info that can be retrieved from the hypervisor
2267
2268 """
2269 info_command = "info migrate"
2270 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2271 result = self._CallMonitorCommand(instance.name, info_command)
2272 match = self._MIGRATION_STATUS_RE.search(result.stdout)
2273 if not match:
2274 if not result.stdout:
2275 logging.info("KVM: empty 'info migrate' result")
2276 else:
2277 logging.warning("KVM: unknown 'info migrate' result: %s",
2278 result.stdout)
2279 else:
2280 status = match.group(1)
2281 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2282 migration_status = objects.MigrationStatus(status=status)
2283 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2284 if match:
2285 migration_status.transferred_ram = match.group("transferred")
2286 migration_status.total_ram = match.group("total")
2287
2288 return migration_status
2289
2290 logging.warning("KVM: unknown migration status '%s'", status)
2291
2292 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2293
2294 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2295
2297 """Balloon an instance memory to a certain value.
2298
2299 @type instance: L{objects.Instance}
2300 @param instance: instance to be accepted
2301 @type mem: int
2302 @param mem: actual memory size to use for instance runtime
2303
2304 """
2305 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2306
2308 """Return information about the node.
2309
2310 @type hvparams: dict of strings
2311 @param hvparams: hypervisor parameters, not used in this class
2312
2313 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2314 the following keys:
2315 - hv_version: the hypervisor version in the form (major, minor,
2316 revision)
2317
2318 """
2319 result = self.GetLinuxNodeInfo()
2320 kvmpath = constants.KVM_PATH
2321 if hvparams is not None:
2322 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2323 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2324 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2325 return result
2326
2327 @classmethod
2328 - def GetInstanceConsole(cls, instance, primary_node, node_group,
2329 hvparams, beparams):
2330 """Return a command for connecting to the console of an instance.
2331
2332 """
2333 if hvparams[constants.HV_SERIAL_CONSOLE]:
2334 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2335 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2336 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2337 "STDIO,%s" % cls._SocatUnixConsoleParams(),
2338 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2339 ndparams = node_group.FillND(primary_node)
2340 return objects.InstanceConsole(instance=instance.name,
2341 kind=constants.CONS_SSH,
2342 host=primary_node.name,
2343 port=ndparams.get(constants.ND_SSH_PORT),
2344 user=constants.SSH_CONSOLE_USER,
2345 command=cmd)
2346
2347 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2348 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2349 display = instance.network_port - constants.VNC_BASE_PORT
2350 return objects.InstanceConsole(instance=instance.name,
2351 kind=constants.CONS_VNC,
2352 host=vnc_bind_address,
2353 port=instance.network_port,
2354 display=display)
2355
2356 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2357 if spice_bind:
2358 return objects.InstanceConsole(instance=instance.name,
2359 kind=constants.CONS_SPICE,
2360 host=spice_bind,
2361 port=instance.network_port)
2362
2363 return objects.InstanceConsole(instance=instance.name,
2364 kind=constants.CONS_MESSAGE,
2365 message=("No serial shell for instance %s" %
2366 instance.name))
2367
2368 - def Verify(self, hvparams=None):
2369 """Verify the hypervisor.
2370
2371 Check that the required binaries exist.
2372
2373 @type hvparams: dict of strings
2374 @param hvparams: hypervisor parameters to be verified against, not used here
2375
2376 @return: Problem description if something is wrong, C{None} otherwise
2377
2378 """
2379 msgs = []
2380 kvmpath = constants.KVM_PATH
2381 if hvparams is not None:
2382 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2383 if not os.path.exists(kvmpath):
2384 msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2385 if not os.path.exists(constants.SOCAT_PATH):
2386 msgs.append("The socat binary ('%s') does not exist" %
2387 constants.SOCAT_PATH)
2388
2389 return self._FormatVerifyResults(msgs)
2390
2391 @classmethod
2393 """Check the given parameters for validity.
2394
2395 @type hvparams: dict of strings
2396 @param hvparams: hypervisor parameters
2397 @raise errors.HypervisorError: when a parameter is not valid
2398
2399 """
2400 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2401
2402 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2403 if kernel_path:
2404 if not hvparams[constants.HV_ROOT_PATH]:
2405 raise errors.HypervisorError("Need a root partition for the instance,"
2406 " if a kernel is defined")
2407
2408 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2409 not hvparams[constants.HV_VNC_X509]):
2410 raise errors.HypervisorError("%s must be defined, if %s is" %
2411 (constants.HV_VNC_X509,
2412 constants.HV_VNC_X509_VERIFY))
2413
2414 if hvparams[constants.HV_SERIAL_CONSOLE]:
2415 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2416 valid_speeds = constants.VALID_SERIAL_SPEEDS
2417 if not serial_speed or serial_speed not in valid_speeds:
2418 raise errors.HypervisorError("Invalid serial console speed, must be"
2419 " one of: %s" %
2420 utils.CommaJoin(valid_speeds))
2421
2422 boot_order = hvparams[constants.HV_BOOT_ORDER]
2423 if (boot_order == constants.HT_BO_CDROM and
2424 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2425 raise errors.HypervisorError("Cannot boot from cdrom without an"
2426 " ISO path")
2427
2428 security_model = hvparams[constants.HV_SECURITY_MODEL]
2429 if security_model == constants.HT_SM_USER:
2430 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2431 raise errors.HypervisorError("A security domain (user to run kvm as)"
2432 " must be specified")
2433 elif (security_model == constants.HT_SM_NONE or
2434 security_model == constants.HT_SM_POOL):
2435 if hvparams[constants.HV_SECURITY_DOMAIN]:
2436 raise errors.HypervisorError("Cannot have a security domain when the"
2437 " security model is 'none' or 'pool'")
2438
2439 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2440 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2441 if spice_bind:
2442 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2443
2444
2445 if (netutils.IP4Address.IsValid(spice_bind) and
2446 spice_ip_version != constants.IP4_VERSION):
2447 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2448 " the specified IP version is %s" %
2449 (spice_bind, spice_ip_version))
2450
2451 if (netutils.IP6Address.IsValid(spice_bind) and
2452 spice_ip_version != constants.IP6_VERSION):
2453 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2454 " the specified IP version is %s" %
2455 (spice_bind, spice_ip_version))
2456 else:
2457
2458
2459 for param in _SPICE_ADDITIONAL_PARAMS:
2460 if hvparams[param]:
2461 raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2462 (param, constants.HV_KVM_SPICE_BIND))
2463
2464 @classmethod
2524
2525 @classmethod
2527 """KVM powercycle, just a wrapper over Linux powercycle.
2528
2529 @type hvparams: dict of strings
2530 @param hvparams: hypervisor parameters to be used on this node
2531
2532 """
2533 cls.LinuxPowercycle()
2534