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