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 up_hvp: dict
879 @param up_hvp: the instance's runtime hypervisor parameters
880 @type kvm_disks: list of tuples
881 @param kvm_disks: list of tuples [(disk, link_name, uri)..]
882 @type kvmhelp: string
883 @param kvmhelp: output of kvm --help
884 @type devlist: string
885 @param devlist: output of kvm -device ?
886 @rtype: list
887 @return: list of command line options eventually used by kvm executable
888
889 """
890 kernel_path = up_hvp[constants.HV_KERNEL_PATH]
891 if kernel_path:
892 boot_disk = False
893 else:
894 boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
895
896
897
898 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
899
900 dev_opts = []
901 device_driver = None
902 disk_type = up_hvp[constants.HV_DISK_TYPE]
903 if disk_type == constants.HT_DISK_PARAVIRTUAL:
904 if_val = ",if=%s" % self._VIRTIO
905 try:
906 if self._VIRTIO_BLK_RE.search(devlist):
907 if_val = ",if=none"
908
909 device_driver = self._VIRTIO_BLK_PCI
910 except errors.HypervisorError, _:
911 pass
912 else:
913 if_val = ",if=%s" % disk_type
914
915 aio_mode = up_hvp[constants.HV_KVM_DISK_AIO]
916 if aio_mode == constants.HT_KVM_AIO_NATIVE:
917 aio_val = ",aio=%s" % aio_mode
918 else:
919 aio_val = ""
920
921 disk_cache = up_hvp[constants.HV_DISK_CACHE]
922 for cfdev, link_name, uri in kvm_disks:
923 if cfdev.dev_type in constants.DTS_EXT_MIRROR:
924 if disk_cache != "none":
925
926 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
927 " to prevent shared storage corruption on migration",
928 disk_cache)
929 cache_val = ",cache=none"
930 elif disk_cache != constants.HT_CACHE_DEFAULT:
931 cache_val = ",cache=%s" % disk_cache
932 else:
933 cache_val = ""
934 if cfdev.mode != constants.DISK_RDWR:
935 raise errors.HypervisorError("Instance has read-only disks which"
936 " are not supported by KVM")
937
938 boot_val = ""
939 if boot_disk:
940 dev_opts.extend(["-boot", "c"])
941 boot_disk = False
942 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
943 boot_val = ",boot=on"
944
945 drive_uri = _GetDriveURI(cfdev, link_name, uri)
946
947 drive_val = "file=%s,format=raw%s%s%s%s" % \
948 (drive_uri, if_val, boot_val, cache_val, aio_val)
949
950 if device_driver:
951
952
953
954 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK, cfdev)
955 drive_val += (",id=%s" % kvm_devid)
956 drive_val += (",bus=0,unit=%d" % cfdev.pci)
957 dev_val = ("%s,drive=%s,id=%s" %
958 (device_driver, kvm_devid, kvm_devid))
959 dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
960 dev_opts.extend(["-device", dev_val])
961
962 dev_opts.extend(["-drive", drive_val])
963
964 return dev_opts
965
966 @staticmethod
967 - def _CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image, cdrom_boot,
968 needs_boot_flag):
969 """Extends L{kvm_cmd} with the '-drive' option for a cdrom, and
970 optionally the '-boot' option.
971
972 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide -boot d
973
974 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide,boot=on
975
976 Example: -drive file=http://hostname.com/cdrom.iso,media=cdrom
977
978 @type kvm_cmd: string
979 @param kvm_cmd: KVM command line
980
981 @type cdrom_disk_type:
982 @param cdrom_disk_type:
983
984 @type cdrom_image:
985 @param cdrom_image:
986
987 @type cdrom_boot:
988 @param cdrom_boot:
989
990 @type needs_boot_flag:
991 @param needs_boot_flag:
992
993 """
994
995
996 if utils.IsUrl(cdrom_image) and not _CheckUrl(cdrom_image):
997 raise errors.HypervisorError("Cdrom ISO image '%s' is not accessible" %
998 cdrom_image)
999
1000
1001 if utils.IsUrl(cdrom_image):
1002 options = ",media=cdrom"
1003 else:
1004 options = ",media=cdrom,format=raw"
1005
1006
1007 if cdrom_boot:
1008 if_val = ",if=" + constants.HT_DISK_IDE
1009 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1010 if_val = ",if=virtio"
1011 else:
1012 if_val = ",if=" + cdrom_disk_type
1013
1014
1015 boot_val = ""
1016 if cdrom_boot:
1017 kvm_cmd.extend(["-boot", "d"])
1018
1019
1020
1021 if needs_boot_flag:
1022 boot_val = ",boot=on"
1023
1024
1025 drive_val = "file=%s%s%s%s" % (cdrom_image, options, if_val, boot_val)
1026 kvm_cmd.extend(["-drive", drive_val])
1027
1030 """Generate KVM information to start an instance.
1031
1032 @type kvmhelp: string
1033 @param kvmhelp: output of kvm --help
1034 @attention: this function must not have any side-effects; for
1035 example, it must not write to the filesystem, or read values
1036 from the current system the are expected to differ between
1037 nodes, since it is only run once at instance startup;
1038 actions/kvm arguments that can vary between systems should be
1039 done in L{_ExecuteKVMRuntime}
1040
1041 """
1042
1043 hvp = instance.hvparams
1044 self.ValidateParameters(hvp)
1045
1046 pidfile = self._InstancePidFile(instance.name)
1047 kvm = hvp[constants.HV_KVM_PATH]
1048 kvm_cmd = [kvm]
1049
1050 kvm_cmd.extend(["-name", instance.name])
1051 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1052
1053 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1054 if hvp[constants.HV_CPU_CORES]:
1055 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1056 if hvp[constants.HV_CPU_THREADS]:
1057 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1058 if hvp[constants.HV_CPU_SOCKETS]:
1059 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1060
1061 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1062
1063 kvm_cmd.extend(["-pidfile", pidfile])
1064
1065 pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1066
1067
1068 if hvp[constants.HV_SOUNDHW]:
1069 soundhw = hvp[constants.HV_SOUNDHW]
1070
1071
1072
1073 if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
1074 _ = utils.GetFreeSlot(pci_reservations, reserve=True)
1075 kvm_cmd.extend(["-soundhw", soundhw])
1076
1077 if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
1078
1079 _ = utils.GetFreeSlot(pci_reservations, reserve=True)
1080
1081
1082 addr = utils.GetFreeSlot(pci_reservations, reserve=True)
1083 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1084 kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
1085 kvm_cmd.extend(["-daemonize"])
1086 if not instance.hvparams[constants.HV_ACPI]:
1087 kvm_cmd.extend(["-no-acpi"])
1088 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1089 constants.INSTANCE_REBOOT_EXIT:
1090 kvm_cmd.extend(["-no-reboot"])
1091
1092 mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1093 if not mversion:
1094 mversion = self._GetDefaultMachineVersion(kvm)
1095 if self._MACHINE_RE.search(kvmhelp):
1096
1097
1098
1099 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1100 specprop = ",accel=kvm"
1101 else:
1102 specprop = ""
1103 machinespec = "%s%s" % (mversion, specprop)
1104 kvm_cmd.extend(["-machine", machinespec])
1105 else:
1106 kvm_cmd.extend(["-M", mversion])
1107 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1108 self._ENABLE_KVM_RE.search(kvmhelp)):
1109 kvm_cmd.extend(["-enable-kvm"])
1110 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1111 self._DISABLE_KVM_RE.search(kvmhelp)):
1112 kvm_cmd.extend(["-disable-kvm"])
1113
1114 kernel_path = hvp[constants.HV_KERNEL_PATH]
1115 if kernel_path:
1116 boot_cdrom = boot_floppy = boot_network = False
1117 else:
1118 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1119 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1120 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1121
1122 if startup_paused:
1123 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1124
1125 if boot_network:
1126 kvm_cmd.extend(["-boot", "n"])
1127
1128 disk_type = hvp[constants.HV_DISK_TYPE]
1129
1130
1131 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1132 if not cdrom_disk_type:
1133 cdrom_disk_type = disk_type
1134
1135 cdrom_image1 = hvp[constants.HV_CDROM_IMAGE_PATH]
1136 if cdrom_image1:
1137 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1138 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image1, boot_cdrom,
1139 needs_boot_flag)
1140
1141 cdrom_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1142 if cdrom_image2:
1143 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image2, False, False)
1144
1145 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1146 if floppy_image:
1147 options = ",format=raw,media=disk"
1148 if boot_floppy:
1149 kvm_cmd.extend(["-boot", "a"])
1150 options = "%s,boot=on" % options
1151 if_val = ",if=floppy"
1152 options = "%s%s" % (options, if_val)
1153 drive_val = "file=%s%s" % (floppy_image, options)
1154 kvm_cmd.extend(["-drive", drive_val])
1155
1156 if kernel_path:
1157 kvm_cmd.extend(["-kernel", kernel_path])
1158 initrd_path = hvp[constants.HV_INITRD_PATH]
1159 if initrd_path:
1160 kvm_cmd.extend(["-initrd", initrd_path])
1161 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1162 hvp[constants.HV_KERNEL_ARGS]]
1163 if hvp[constants.HV_SERIAL_CONSOLE]:
1164 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1165 root_append.append("console=ttyS0,%s" % serial_speed)
1166 kvm_cmd.extend(["-append", " ".join(root_append)])
1167
1168 mem_path = hvp[constants.HV_MEM_PATH]
1169 if mem_path:
1170 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1171
1172 monitor_dev = ("unix:%s,server,nowait" %
1173 self._InstanceMonitor(instance.name))
1174 kvm_cmd.extend(["-monitor", monitor_dev])
1175 if hvp[constants.HV_SERIAL_CONSOLE]:
1176 serial_dev = ("unix:%s,server,nowait" %
1177 self._InstanceSerial(instance.name))
1178 kvm_cmd.extend(["-serial", serial_dev])
1179 else:
1180 kvm_cmd.extend(["-serial", "none"])
1181
1182 mouse_type = hvp[constants.HV_USB_MOUSE]
1183 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1184 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1185 spice_ip_version = None
1186
1187 kvm_cmd.extend(["-usb"])
1188
1189 if mouse_type:
1190 kvm_cmd.extend(["-usbdevice", mouse_type])
1191 elif vnc_bind_address:
1192 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1193
1194 if vnc_bind_address:
1195 if netutils.IsValidInterface(vnc_bind_address):
1196 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1197 if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1198 if len(if_ip4_addresses) < 1:
1199 logging.error("Could not determine IPv4 address of interface %s",
1200 vnc_bind_address)
1201 else:
1202 vnc_bind_address = if_ip4_addresses[0]
1203 if netutils.IP4Address.IsValid(vnc_bind_address):
1204 if instance.network_port > constants.VNC_BASE_PORT:
1205 display = instance.network_port - constants.VNC_BASE_PORT
1206 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1207 vnc_arg = ":%d" % (display)
1208 else:
1209 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1210 else:
1211 logging.error("Network port is not a valid VNC display (%d < %d),"
1212 " not starting VNC",
1213 instance.network_port, constants.VNC_BASE_PORT)
1214 vnc_arg = "none"
1215
1216
1217
1218 vnc_append = ""
1219 if hvp[constants.HV_VNC_TLS]:
1220 vnc_append = "%s,tls" % vnc_append
1221 if hvp[constants.HV_VNC_X509_VERIFY]:
1222 vnc_append = "%s,x509verify=%s" % (vnc_append,
1223 hvp[constants.HV_VNC_X509])
1224 elif hvp[constants.HV_VNC_X509]:
1225 vnc_append = "%s,x509=%s" % (vnc_append,
1226 hvp[constants.HV_VNC_X509])
1227 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1228 vnc_append = "%s,password" % vnc_append
1229
1230 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1231
1232 else:
1233 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1234
1235 kvm_cmd.extend(["-vnc", vnc_arg])
1236 elif spice_bind:
1237
1238
1239 if netutils.IsValidInterface(spice_bind):
1240
1241
1242 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1243 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1244
1245
1246
1247 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1248 if not addresses[spice_ip_version]:
1249 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1250 " for %s" % (spice_ip_version,
1251 spice_bind))
1252
1253
1254 elif (addresses[constants.IP4_VERSION] and
1255 addresses[constants.IP6_VERSION]):
1256
1257
1258 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1259 spice_ip_version = \
1260 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1261 elif addresses[constants.IP4_VERSION]:
1262 spice_ip_version = constants.IP4_VERSION
1263 elif addresses[constants.IP6_VERSION]:
1264 spice_ip_version = constants.IP6_VERSION
1265 else:
1266 raise errors.HypervisorError("SPICE: Unable to get an IP address"
1267 " for %s" % (spice_bind))
1268
1269 spice_address = addresses[spice_ip_version][0]
1270
1271 else:
1272
1273
1274 spice_address = spice_bind
1275
1276 spice_arg = "addr=%s" % spice_address
1277 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1278 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1279 (spice_arg, instance.network_port,
1280 pathutils.SPICE_CACERT_FILE))
1281 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1282 (spice_arg, pathutils.SPICE_CERT_FILE,
1283 pathutils.SPICE_CERT_FILE))
1284 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1285 if tls_ciphers:
1286 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1287 else:
1288 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1289
1290 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1291 spice_arg = "%s,disable-ticketing" % spice_arg
1292
1293 if spice_ip_version:
1294 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1295
1296
1297 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1298 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1299 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1300 if img_lossless:
1301 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1302 if img_jpeg:
1303 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1304 if img_zlib_glz:
1305 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1306
1307
1308 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1309 if video_streaming:
1310 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1311
1312
1313 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1314 spice_arg = "%s,playback-compression=off" % spice_arg
1315 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1316 spice_arg = "%s,agent-mouse=off" % spice_arg
1317 else:
1318
1319
1320 addr = utils.GetFreeSlot(pci_reservations, reserve=True)
1321 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1322 kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
1323 kvm_cmd.extend([
1324 "-device",
1325 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1326 ])
1327 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1328
1329 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1330 kvm_cmd.extend(["-spice", spice_arg])
1331
1332 else:
1333
1334
1335 if self._DISPLAY_RE.search(kvmhelp):
1336 kvm_cmd.extend(["-display", "none"])
1337 else:
1338 kvm_cmd.extend(["-nographic"])
1339
1340 if hvp[constants.HV_USE_LOCALTIME]:
1341 kvm_cmd.extend(["-localtime"])
1342
1343 if hvp[constants.HV_KVM_USE_CHROOT]:
1344 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1345
1346
1347 if hvp[constants.HV_CPU_TYPE]:
1348 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1349
1350
1351
1352 if hvp[constants.HV_VGA]:
1353 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1354 elif spice_bind:
1355 kvm_cmd.extend(["-vga", "qxl"])
1356
1357
1358 if hvp[constants.HV_USB_DEVICES]:
1359 for dev in hvp[constants.HV_USB_DEVICES].split(","):
1360 kvm_cmd.extend(["-usbdevice", dev])
1361
1362
1363 if self._UUID_RE.search(kvmhelp):
1364 kvm_cmd.extend(["-uuid", instance.uuid])
1365
1366 if hvp[constants.HV_KVM_EXTRA]:
1367 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1368
1369 kvm_disks = []
1370 for disk, link_name, uri in block_devices:
1371 disk.pci = utils.GetFreeSlot(pci_reservations, disk.pci, True)
1372 kvm_disks.append((disk, link_name, uri))
1373
1374 kvm_nics = []
1375 for nic in instance.nics:
1376 nic.pci = utils.GetFreeSlot(pci_reservations, nic.pci, True)
1377 kvm_nics.append(nic)
1378
1379 hvparams = hvp
1380
1381 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1382
1392
1394 """Read an instance's KVM runtime
1395
1396 """
1397 try:
1398 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1399 except EnvironmentError, err:
1400 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1401 return file_content
1402
1404 """Save an instance's KVM runtime
1405
1406 """
1407 kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1408
1409 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1410 serialized_disks = [(blk.ToDict(), link, uri)
1411 for blk, link, uri in kvm_disks]
1412 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1413 serialized_disks))
1414
1415 self._WriteKVMRuntime(instance.name, serialized_form)
1416
1418 """Load an instance's KVM runtime
1419
1420 """
1421 if not serialized_runtime:
1422 serialized_runtime = self._ReadKVMRuntime(instance.name)
1423
1424 return _AnalyzeSerializedRuntime(serialized_runtime)
1425
1426 - def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1427 """Run the KVM cmd and check for errors
1428
1429 @type name: string
1430 @param name: instance name
1431 @type kvm_cmd: list of strings
1432 @param kvm_cmd: runcmd input for kvm
1433 @type tap_fds: list of int
1434 @param tap_fds: fds of tap devices opened by Ganeti
1435
1436 """
1437 try:
1438 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1439 finally:
1440 for fd in tap_fds:
1441 utils_wrapper.CloseFdNoError(fd)
1442
1443 if result.failed:
1444 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1445 (name, result.fail_reason, result.output))
1446 if not self._InstancePidAlive(name)[2]:
1447 raise errors.HypervisorError("Failed to start instance %s" % name)
1448
1449 @staticmethod
1451 """Generate a TAP network interface name for a NIC.
1452
1453 See L{hv_base.GenerateTapName}.
1454
1455 For the case of the empty string, see L{OpenTap}
1456
1457 @type nic: ganeti.objects.NIC
1458 @param nic: NIC object for the name should be generated
1459
1460 @rtype: string
1461 @return: TAP network interface name, or the empty string if the
1462 NIC is not used in instance communication
1463
1464 """
1465 if nic.name is None or not \
1466 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
1467 return ""
1468
1469 return hv_base.GenerateTapName()
1470
1472 """Get network device options to properly enable supported features.
1473
1474 Return a dict of supported and enabled tap features with nic_model along
1475 with the extra strings to be appended to the --netdev and --device options.
1476 This function is called before opening a new tap device.
1477
1478 Currently the features_dict includes the following attributes:
1479 - vhost (boolean)
1480 - vnet_hdr (boolean)
1481 - mq (boolean, int)
1482
1483 @rtype: (dict, str, str) tuple
1484 @return: The supported features,
1485 the string to be appended to the --netdev option,
1486 the string to be appended to the --device option
1487
1488 """
1489 nic_type = up_hvp[constants.HV_NIC_TYPE]
1490 nic_extra_str = ""
1491 tap_extra_str = ""
1492 features = {
1493 "vhost": False,
1494 "vnet_hdr": False,
1495 "mq": (False, 1)
1496 }
1497 update_features = {}
1498 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1499 nic_model = self._VIRTIO
1500 try:
1501 if self._VIRTIO_NET_RE.search(devlist):
1502 nic_model = self._VIRTIO_NET_PCI
1503 update_features["vnet_hdr"] = up_hvp[constants.HV_VNET_HDR]
1504 except errors.HypervisorError, _:
1505
1506
1507 pass
1508
1509 if up_hvp[constants.HV_VHOST_NET]:
1510
1511 if self._VHOST_RE.search(kvmhelp):
1512 update_features["vhost"] = True
1513 tap_extra_str = ",vhost=on"
1514 else:
1515 raise errors.HypervisorError("vhost_net is configured"
1516 " but it is not available")
1517 virtio_net_queues = up_hvp.get(constants.HV_VIRTIO_NET_QUEUES, 1)
1518 if virtio_net_queues > 1:
1519
1520 if self._VIRTIO_NET_QUEUES_RE.search(kvmhelp):
1521
1522
1523
1524 nic_extra_str = ",mq=on,vectors=%d" % (2 * virtio_net_queues + 1)
1525 update_features["mq"] = (True, virtio_net_queues)
1526 else:
1527 raise errors.HypervisorError("virtio_net_queues is configured"
1528 " but it is not available")
1529 else:
1530 nic_model = nic_type
1531
1532 update_features["driver"] = nic_model
1533 features.update(update_features)
1534
1535 return features, tap_extra_str, nic_extra_str
1536
1537
1538
1540 """Execute a KVM cmd, after completing it with some last minute data.
1541
1542 @type instance: L{objects.Instance} object
1543 @param instance: the VM this command acts upon
1544 @type kvm_runtime: tuple of (list of str, list of L{objects.NIC} objects,
1545 dict of hypervisor options, list of tuples (L{objects.Disk}, str, str)
1546 @param kvm_runtime: (kvm command, NICs of the instance, options at startup
1547 of the instance, [(disk, link_name, uri)..])
1548 @type incoming: tuple of strings
1549 @param incoming: (target_host_ip, port) for migration.
1550 @type kvmhelp: string
1551 @param kvmhelp: output of kvm --help
1552
1553 """
1554
1555
1556
1557
1558
1559
1560
1561
1562 conf_hvp = instance.hvparams
1563 name = instance.name
1564 self._CheckDown(name)
1565
1566 self._ClearUserShutdown(instance.name)
1567 self._StartKvmd(instance.hvparams)
1568
1569 temp_files = []
1570
1571 kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1572
1573 kvm_path = kvm_cmd[0]
1574 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1575
1576
1577
1578 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1579 if security_model == constants.HT_SM_USER:
1580 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1581
1582 keymap = conf_hvp[constants.HV_KEYMAP]
1583 if keymap:
1584 keymap_path = self._InstanceKeymapFile(name)
1585
1586
1587
1588
1589 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1590 kvm_cmd.extend(["-k", keymap_path])
1591
1592
1593
1594
1595 tapfds = []
1596 taps = []
1597 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1598 if not kvm_nics:
1599 kvm_cmd.extend(["-net", "none"])
1600 else:
1601 features, tap_extra, nic_extra = \
1602 self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
1603 nic_model = features["driver"]
1604 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1605 for nic_seq, nic in enumerate(kvm_nics):
1606 tapname, nic_tapfds, nic_vhostfds = \
1607 OpenTap(features=features, name=self._GenerateKvmTapName(nic))
1608
1609 tapfds.extend(nic_tapfds)
1610 tapfds.extend(nic_vhostfds)
1611 taps.append(tapname)
1612 tapfd = "%s%s" % ("fds=" if len(nic_tapfds) > 1 else "fd=",
1613 ":".join(str(fd) for fd in nic_tapfds))
1614
1615 if nic_vhostfds:
1616 vhostfd = "%s%s" % (",vhostfds="
1617 if len(nic_vhostfds) > 1 else ",vhostfd=",
1618 ":".join(str(fd) for fd in nic_vhostfds))
1619 else:
1620 vhostfd = ""
1621
1622 if kvm_supports_netdev:
1623 nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1624 try:
1625
1626
1627 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1628 netdev = kvm_devid
1629 nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1630 except errors.HotplugError:
1631 netdev = "netdev%d" % nic_seq
1632 nic_val += (",netdev=%s%s" % (netdev, nic_extra))
1633 tap_val = ("type=tap,id=%s,%s%s%s" %
1634 (netdev, tapfd, vhostfd, tap_extra))
1635 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1636 else:
1637 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1638 nic.mac, nic_model)
1639 tap_val = "tap,vlan=%s,%s" % (nic_seq, tapfd)
1640 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1641
1642 if incoming:
1643 target, port = incoming
1644 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1645
1646
1647
1648
1649 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1650 vnc_pwd = None
1651 if vnc_pwd_file:
1652 try:
1653 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1654 except EnvironmentError, err:
1655 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1656 % (vnc_pwd_file, err))
1657
1658 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1659 utils.EnsureDirs([(self._InstanceChrootDir(name),
1660 constants.SECURE_DIR_MODE)])
1661
1662
1663 if self._QMP_RE.search(kvmhelp):
1664 logging.debug("Enabling QMP")
1665 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1666 self._InstanceQmpMonitor(instance.name)])
1667
1668 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1669 self._InstanceKvmdMonitor(instance.name)])
1670
1671
1672
1673
1674 for nic_seq, nic in enumerate(kvm_nics):
1675 if (incoming and
1676 nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED):
1677 continue
1678 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1679
1680 bdev_opts = self._GenerateKVMBlockDevicesOptions(up_hvp,
1681 kvm_disks,
1682 kvmhelp,
1683 devlist)
1684 kvm_cmd.extend(bdev_opts)
1685
1686
1687
1688 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1689 if start_kvm_paused:
1690 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1691
1692
1693
1694
1695 cpu_pinning = False
1696 if up_hvp.get(constants.HV_CPU_MASK, None):
1697 cpu_pinning = True
1698
1699 if security_model == constants.HT_SM_POOL:
1700 ss = ssconf.SimpleStore()
1701 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1702 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1703 uid = uidpool.RequestUnusedUid(all_uids)
1704 try:
1705 username = pwd.getpwuid(uid.GetUid()).pw_name
1706 kvm_cmd.extend(["-runas", username])
1707 self._RunKVMCmd(name, kvm_cmd, tapfds)
1708 except:
1709 uidpool.ReleaseUid(uid)
1710 raise
1711 else:
1712 uid.Unlock()
1713 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1714 else:
1715 self._RunKVMCmd(name, kvm_cmd, tapfds)
1716
1717 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1718 constants.RUN_DIRS_MODE)])
1719 for nic_seq, tap in enumerate(taps):
1720 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1721 data=tap)
1722
1723 if vnc_pwd:
1724 change_cmd = "change vnc password %s" % vnc_pwd
1725 self._CallMonitorCommand(instance.name, change_cmd)
1726
1727
1728
1729
1730
1731
1732 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1733 if spice_password_file:
1734 spice_pwd = ""
1735 try:
1736 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1737 except EnvironmentError, err:
1738 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1739 % (spice_password_file, err))
1740
1741 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1742 qmp.connect()
1743 arguments = {
1744 "protocol": "spice",
1745 "password": spice_pwd,
1746 }
1747 qmp.Execute("set_password", arguments)
1748
1749 for filename in temp_files:
1750 utils.RemoveFile(filename)
1751
1752
1753 if cpu_pinning:
1754 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1755
1756 start_memory = self._InstanceStartupMemory(instance)
1757 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1758 self.BalloonInstanceMemory(instance, start_memory)
1759
1760 if start_kvm_paused:
1761
1762
1763
1764 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1765
1766 @staticmethod
1783
1784 - def StartInstance(self, instance, block_devices, startup_paused):
1795
1796 @classmethod
1798 """Invoke a command on the instance monitor.
1799
1800 """
1801 if timeout is not None:
1802 timeout_cmd = "timeout %s" % (timeout, )
1803 else:
1804 timeout_cmd = ""
1805
1806
1807
1808
1809
1810
1811
1812
1813 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
1814 (utils.ShellQuote(command),
1815 timeout_cmd,
1816 constants.SOCAT_PATH,
1817 utils.ShellQuote(cls._InstanceMonitor(instance_name))))
1818 result = utils.RunCmd(socat)
1819 if result.failed:
1820 msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
1821 " output: %s" %
1822 (command, instance_name, result.fail_reason, result.output))
1823 raise errors.HypervisorError(msg)
1824
1825 return result
1826
1827 @_with_qmp
1840
1842 """Checks if hotplug is generally supported.
1843
1844 Hotplug is *not* supported in case of:
1845 - qemu versions < 1.7 (where all qmp related commands are supported)
1846 - for stopped instances
1847
1848 @raise errors.HypervisorError: in one of the previous cases
1849
1850 """
1851 try:
1852 output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
1853 except errors.HypervisorError:
1854 raise errors.HotplugError("Instance is probably down")
1855
1856 match = self._INFO_VERSION_RE.search(output.stdout)
1857 if not match:
1858 raise errors.HotplugError("Cannot parse qemu version via monitor")
1859
1860
1861 v_major, v_min, _, _ = match.groups()
1862 if (int(v_major), int(v_min)) < (1, 7):
1863 raise errors.HotplugError("Hotplug not supported for qemu versions < 1.7")
1864
1865 @_with_qmp
1868 """Checks if a previous hotplug command has succeeded.
1869
1870 Depending on the should_exist value, verifies that an entry identified by
1871 the PCI slot and device ID is present or not.
1872
1873 @raise errors.HypervisorError: if result is not the expected one
1874
1875 """
1876 for i in range(5):
1877 found = self.qmp.HasPCIDevice(device, kvm_devid)
1878 logging.info("Verifying hotplug command (retry %s): %s", i, found)
1879 if found and should_exist:
1880 break
1881 if not found and not should_exist:
1882 break
1883 time.sleep(1)
1884
1885 if found and not should_exist:
1886 msg = "Device %s should have been removed but is still there" % kvm_devid
1887 raise errors.HypervisorError(msg)
1888
1889 if not found and should_exist:
1890 msg = "Device %s should have been added but is missing" % kvm_devid
1891 raise errors.HypervisorError(msg)
1892
1893 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
1894
1895 @_with_qmp
1896 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
1897 """ Helper method to hot-add a new device
1898
1899 It gets free pci slot generates the device name and invokes the
1900 device specific method.
1901
1902 """
1903
1904 if device.pci is None:
1905 device.pci = self.qmp.GetFreePCISlot()
1906 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
1907 runtime = self._LoadKVMRuntime(instance)
1908 if dev_type == constants.HOTPLUG_TARGET_DISK:
1909 uri = _GetDriveURI(device, extra[0], extra[1])
1910 self.qmp.HotAddDisk(device, kvm_devid, uri)
1911 elif dev_type == constants.HOTPLUG_TARGET_NIC:
1912 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
1913 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
1914 devlist = self._GetKVMOutput(kvmpath, self._KVMOPT_DEVICELIST)
1915 up_hvp = runtime[2]
1916 features, _, _ = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
1917 (tap, tapfds, vhostfds) = OpenTap(features=features)
1918 self._ConfigureNIC(instance, seq, device, tap)
1919 self.qmp.HotAddNic(device, kvm_devid, tapfds, vhostfds, features)
1920 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
1921
1922 self._VerifyHotplugCommand(instance, device, kvm_devid, True)
1923
1924 index = _DEVICE_RUNTIME_INDEX[dev_type]
1925 entry = _RUNTIME_ENTRY[dev_type](device, extra)
1926 runtime[index].append(entry)
1927 self._SaveKVMRuntime(instance, runtime)
1928
1929 @_with_qmp
1930 - def HotDelDevice(self, instance, dev_type, device, _, seq):
1931 """ Helper method for hot-del device
1932
1933 It gets device info from runtime file, generates the device name and
1934 invokes the device specific method.
1935
1936 """
1937 runtime = self._LoadKVMRuntime(instance)
1938 entry = _GetExistingDeviceInfo(dev_type, device, runtime)
1939 kvm_device = _RUNTIME_DEVICE[dev_type](entry)
1940 kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
1941 if dev_type == constants.HOTPLUG_TARGET_DISK:
1942 self.qmp.HotDelDisk(kvm_devid)
1943
1944 command = "drive_del %s\n" % kvm_devid
1945 self._CallMonitorCommand(instance.name, command)
1946 elif dev_type == constants.HOTPLUG_TARGET_NIC:
1947 self.qmp.HotDelNic(kvm_devid)
1948 utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
1949 self._VerifyHotplugCommand(instance, kvm_device, kvm_devid, False)
1950 index = _DEVICE_RUNTIME_INDEX[dev_type]
1951 runtime[index].remove(entry)
1952 self._SaveKVMRuntime(instance, runtime)
1953
1954 return kvm_device.pci
1955
1956 - def HotModDevice(self, instance, dev_type, device, _, seq):
1957 """ Helper method for hot-mod device
1958
1959 It gets device info from runtime file, generates the device name and
1960 invokes the device specific method. Currently only NICs support hot-mod
1961
1962 """
1963 if dev_type == constants.HOTPLUG_TARGET_NIC:
1964
1965 device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
1966 self.HotAddDevice(instance, dev_type, device, _, seq)
1967
1968 @classmethod
1970 """Parse the KVM version from the --help output.
1971
1972 @type text: string
1973 @param text: output of kvm --help
1974 @return: (version, v_maj, v_min, v_rev)
1975 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1976
1977 """
1978 match = cls._VERSION_RE.search(text.splitlines()[0])
1979 if not match:
1980 raise errors.HypervisorError("Unable to get KVM version")
1981
1982 v_all = match.group(0)
1983 v_maj = int(match.group(1))
1984 v_min = int(match.group(2))
1985 if match.group(4):
1986 v_rev = int(match.group(4))
1987 else:
1988 v_rev = 0
1989 return (v_all, v_maj, v_min, v_rev)
1990
1991 @classmethod
1993 """Return the output of a kvm invocation
1994
1995 @type kvm_path: string
1996 @param kvm_path: path to the kvm executable
1997 @type option: a key of _KVMOPTS_CMDS
1998 @param option: kvm option to fetch the output from
1999 @return: output a supported kvm invocation
2000 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2001
2002 """
2003 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2004
2005 optlist, can_fail = cls._KVMOPTS_CMDS[option]
2006
2007 result = utils.RunCmd([kvm_path] + optlist)
2008 if result.failed and not can_fail:
2009 raise errors.HypervisorError("Unable to get KVM %s output" %
2010 " ".join(optlist))
2011 return result.output
2012
2013 @classmethod
2015 """Return the installed KVM version.
2016
2017 @return: (version, v_maj, v_min, v_rev)
2018 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2019
2020 """
2021 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2022
2023 @classmethod
2034
2035 @classmethod
2036 - def _StopInstance(cls, instance, force=False, name=None, timeout=None):
2056
2057 - def StopInstance(self, instance, force=False, retry=False, name=None,
2058 timeout=None):
2063
2073
2096
2098 """Get instance information to perform a migration.
2099
2100 @type instance: L{objects.Instance}
2101 @param instance: instance to be migrated
2102 @rtype: string
2103 @return: content of the KVM runtime file
2104
2105 """
2106 return self._ReadKVMRuntime(instance.name)
2107
2109 """Prepare to accept an instance.
2110
2111 @type instance: L{objects.Instance}
2112 @param instance: instance to be accepted
2113 @type info: string
2114 @param info: content of the KVM runtime file on the source node
2115 @type target: string
2116 @param target: target host (usually ip), on this node
2117
2118 """
2119 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2120 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2121 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2122 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2123 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2124 incoming=incoming_address)
2125
2127 """Finalize the instance migration on the target node.
2128
2129 Stop the incoming mode KVM.
2130
2131 @type instance: L{objects.Instance}
2132 @param instance: instance whose migration is being finalized
2133
2134 """
2135 if success:
2136 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2137 kvm_nics = kvm_runtime[1]
2138
2139 for nic_seq, nic in enumerate(kvm_nics):
2140 if nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_ROUTED:
2141
2142 continue
2143 try:
2144 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2145 except EnvironmentError, err:
2146 logging.warning("Failed to find host interface for %s NIC #%d: %s",
2147 instance.name, nic_seq, str(err))
2148 continue
2149 try:
2150 self._ConfigureNIC(instance, nic_seq, nic, tap)
2151 except errors.HypervisorError, err:
2152 logging.warning(str(err))
2153
2154 self._WriteKVMRuntime(instance.name, info)
2155 else:
2156 self.StopInstance(instance, force=True)
2157
2199
2218
2220 """Get the migration status
2221
2222 @type instance: L{objects.Instance}
2223 @param instance: the instance that is being migrated
2224 @rtype: L{objects.MigrationStatus}
2225 @return: the status of the current migration (one of
2226 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2227 progress info that can be retrieved from the hypervisor
2228
2229 """
2230 info_command = "info migrate"
2231 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2232 result = self._CallMonitorCommand(instance.name, info_command)
2233 match = self._MIGRATION_STATUS_RE.search(result.stdout)
2234 if not match:
2235 if not result.stdout:
2236 logging.info("KVM: empty 'info migrate' result")
2237 else:
2238 logging.warning("KVM: unknown 'info migrate' result: %s",
2239 result.stdout)
2240 else:
2241 status = match.group(1)
2242 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2243 migration_status = objects.MigrationStatus(status=status)
2244 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2245 if match:
2246 migration_status.transferred_ram = match.group("transferred")
2247 migration_status.total_ram = match.group("total")
2248
2249 return migration_status
2250
2251 logging.warning("KVM: unknown migration status '%s'", status)
2252
2253 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2254
2255 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2256
2258 """Balloon an instance memory to a certain value.
2259
2260 @type instance: L{objects.Instance}
2261 @param instance: instance to be accepted
2262 @type mem: int
2263 @param mem: actual memory size to use for instance runtime
2264
2265 """
2266 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2267
2269 """Return information about the node.
2270
2271 @type hvparams: dict of strings
2272 @param hvparams: hypervisor parameters, not used in this class
2273
2274 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2275 the following keys:
2276 - hv_version: the hypervisor version in the form (major, minor,
2277 revision)
2278
2279 """
2280 result = self.GetLinuxNodeInfo()
2281 kvmpath = constants.KVM_PATH
2282 if hvparams is not None:
2283 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2284 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2285 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2286 return result
2287
2288 @classmethod
2289 - def GetInstanceConsole(cls, instance, primary_node, node_group,
2290 hvparams, beparams):
2291 """Return a command for connecting to the console of an instance.
2292
2293 """
2294 if hvparams[constants.HV_SERIAL_CONSOLE]:
2295 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2296 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2297 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2298 "STDIO,%s" % cls._SocatUnixConsoleParams(),
2299 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2300 ndparams = node_group.FillND(primary_node)
2301 return objects.InstanceConsole(instance=instance.name,
2302 kind=constants.CONS_SSH,
2303 host=primary_node.name,
2304 port=ndparams.get(constants.ND_SSH_PORT),
2305 user=constants.SSH_CONSOLE_USER,
2306 command=cmd)
2307
2308 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2309 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2310 display = instance.network_port - constants.VNC_BASE_PORT
2311 return objects.InstanceConsole(instance=instance.name,
2312 kind=constants.CONS_VNC,
2313 host=vnc_bind_address,
2314 port=instance.network_port,
2315 display=display)
2316
2317 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2318 if spice_bind:
2319 return objects.InstanceConsole(instance=instance.name,
2320 kind=constants.CONS_SPICE,
2321 host=spice_bind,
2322 port=instance.network_port)
2323
2324 return objects.InstanceConsole(instance=instance.name,
2325 kind=constants.CONS_MESSAGE,
2326 message=("No serial shell for instance %s" %
2327 instance.name))
2328
2329 - def Verify(self, hvparams=None):
2330 """Verify the hypervisor.
2331
2332 Check that the required binaries exist.
2333
2334 @type hvparams: dict of strings
2335 @param hvparams: hypervisor parameters to be verified against, not used here
2336
2337 @return: Problem description if something is wrong, C{None} otherwise
2338
2339 """
2340 msgs = []
2341 kvmpath = constants.KVM_PATH
2342 if hvparams is not None:
2343 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2344 if not os.path.exists(kvmpath):
2345 msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2346 if not os.path.exists(constants.SOCAT_PATH):
2347 msgs.append("The socat binary ('%s') does not exist" %
2348 constants.SOCAT_PATH)
2349
2350 return self._FormatVerifyResults(msgs)
2351
2352 @classmethod
2354 """Check the given parameters for validity.
2355
2356 @type hvparams: dict of strings
2357 @param hvparams: hypervisor parameters
2358 @raise errors.HypervisorError: when a parameter is not valid
2359
2360 """
2361 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2362
2363 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2364 if kernel_path:
2365 if not hvparams[constants.HV_ROOT_PATH]:
2366 raise errors.HypervisorError("Need a root partition for the instance,"
2367 " if a kernel is defined")
2368
2369 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2370 not hvparams[constants.HV_VNC_X509]):
2371 raise errors.HypervisorError("%s must be defined, if %s is" %
2372 (constants.HV_VNC_X509,
2373 constants.HV_VNC_X509_VERIFY))
2374
2375 if hvparams[constants.HV_SERIAL_CONSOLE]:
2376 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2377 valid_speeds = constants.VALID_SERIAL_SPEEDS
2378 if not serial_speed or serial_speed not in valid_speeds:
2379 raise errors.HypervisorError("Invalid serial console speed, must be"
2380 " one of: %s" %
2381 utils.CommaJoin(valid_speeds))
2382
2383 boot_order = hvparams[constants.HV_BOOT_ORDER]
2384 if (boot_order == constants.HT_BO_CDROM and
2385 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2386 raise errors.HypervisorError("Cannot boot from cdrom without an"
2387 " ISO path")
2388
2389 security_model = hvparams[constants.HV_SECURITY_MODEL]
2390 if security_model == constants.HT_SM_USER:
2391 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2392 raise errors.HypervisorError("A security domain (user to run kvm as)"
2393 " must be specified")
2394 elif (security_model == constants.HT_SM_NONE or
2395 security_model == constants.HT_SM_POOL):
2396 if hvparams[constants.HV_SECURITY_DOMAIN]:
2397 raise errors.HypervisorError("Cannot have a security domain when the"
2398 " security model is 'none' or 'pool'")
2399
2400 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2401 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2402 if spice_bind:
2403 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2404
2405
2406 if (netutils.IP4Address.IsValid(spice_bind) and
2407 spice_ip_version != constants.IP4_VERSION):
2408 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2409 " the specified IP version is %s" %
2410 (spice_bind, spice_ip_version))
2411
2412 if (netutils.IP6Address.IsValid(spice_bind) and
2413 spice_ip_version != constants.IP6_VERSION):
2414 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2415 " the specified IP version is %s" %
2416 (spice_bind, spice_ip_version))
2417 else:
2418
2419
2420 for param in _SPICE_ADDITIONAL_PARAMS:
2421 if hvparams[param]:
2422 raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2423 (param, constants.HV_KVM_SPICE_BIND))
2424
2425 @classmethod
2485
2486 @classmethod
2488 """KVM powercycle, just a wrapper over Linux powercycle.
2489
2490 @type hvparams: dict of strings
2491 @param hvparams: hypervisor parameters to be used on this node
2492
2493 """
2494 cls.LinuxPowercycle()
2495