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