Move 'ConfigureNIC' to 'hv_base'
[ganeti-github.git] / lib / hypervisor / hv_kvm / __init__.py
1 #
2 #
3
4 # Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """KVM hypervisor
23
24 """
25
26 import errno
27 import os
28 import os.path
29 import re
30 import tempfile
31 import time
32 import logging
33 import pwd
34 import shutil
35 import urllib2
36 from bitarray import bitarray
37 try:
38 import affinity # pylint: disable=F0401
39 except ImportError:
40 affinity = None
41 try:
42 import fdsend # pylint: disable=F0401
43 except ImportError:
44 fdsend = None
45
46 from ganeti import utils
47 from ganeti import constants
48 from ganeti import errors
49 from ganeti import serializer
50 from ganeti import objects
51 from ganeti import uidpool
52 from ganeti import ssconf
53 from ganeti import netutils
54 from ganeti import pathutils
55 from ganeti.hypervisor import hv_base
56 from ganeti.utils import wrapper as utils_wrapper
57
58 from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \
59 MonitorSocket
60 from ganeti.hypervisor.hv_kvm.netdev import OpenTap
61
62
63 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
64 _KVM_START_PAUSED_FLAG = "-S"
65
66 #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND}
67 _SPICE_ADDITIONAL_PARAMS = frozenset([
68 constants.HV_KVM_SPICE_IP_VERSION,
69 constants.HV_KVM_SPICE_PASSWORD_FILE,
70 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
71 constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
72 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
73 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
74 constants.HV_KVM_SPICE_USE_TLS,
75 ])
76
77 # Constant bitarray that reflects to a free pci slot
78 # Use it with bitarray.search()
79 _AVAILABLE_PCI_SLOT = bitarray("0")
80
81 # below constants show the format of runtime file
82 # the nics are in second possition, while the disks in 4th (last)
83 # moreover disk entries are stored as a list of in tuples
84 # (L{objects.Disk}, link_name, uri)
85 _KVM_NICS_RUNTIME_INDEX = 1
86 _KVM_DISKS_RUNTIME_INDEX = 3
87 _DEVICE_RUNTIME_INDEX = {
88 constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX,
89 constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX
90 }
91 _FIND_RUNTIME_ENTRY = {
92 constants.HOTPLUG_TARGET_NIC:
93 lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid],
94 constants.HOTPLUG_TARGET_DISK:
95 lambda disk, kvm_disks: [(d, l, u) for (d, l, u) in kvm_disks
96 if d.uuid == disk.uuid]
97 }
98 _RUNTIME_DEVICE = {
99 constants.HOTPLUG_TARGET_NIC: lambda d: d,
100 constants.HOTPLUG_TARGET_DISK: lambda (d, e, _): d
101 }
102 _RUNTIME_ENTRY = {
103 constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
104 constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e, None)
105 }
106
107 _MIGRATION_CAPS_DELIM = ":"
108
109
110 def _GenerateDeviceKVMId(dev_type, dev):
111 """Helper function to generate a unique device name used by KVM
112
113 QEMU monitor commands use names to identify devices. Here we use their pci
114 slot and a part of their UUID to name them. dev.pci might be None for old
115 devices in the cluster.
116
117 @type dev_type: sting
118 @param dev_type: device type of param dev
119 @type dev: L{objects.Disk} or L{objects.NIC}
120 @param dev: the device object for which we generate a kvm name
121 @raise errors.HotplugError: in case a device has no pci slot (old devices)
122
123 """
124
125 if not dev.pci:
126 raise errors.HotplugError("Hotplug is not supported for %s with UUID %s" %
127 (dev_type, dev.uuid))
128
129 return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
130
131
132 def _GetFreeSlot(slots, slot=None, reserve=False):
133 """Helper method to get first available slot in a bitarray
134
135 @type slots: bitarray
136 @param slots: the bitarray to operate on
137 @type slot: integer
138 @param slot: if given we check whether the slot is free
139 @type reserve: boolean
140 @param reserve: whether to reserve the first available slot or not
141 @return: the idx of the (first) available slot
142 @raise errors.HotplugError: If all slots in a bitarray are occupied
143 or the given slot is not free.
144
145 """
146 if slot is not None:
147 assert slot < len(slots)
148 if slots[slot]:
149 raise errors.HypervisorError("Slots %d occupied" % slot)
150
151 else:
152 avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
153 if not avail:
154 raise errors.HypervisorError("All slots occupied")
155
156 slot = int(avail[0])
157
158 if reserve:
159 slots[slot] = True
160
161 return slot
162
163
164 def _GetExistingDeviceInfo(dev_type, device, runtime):
165 """Helper function to get an existing device inside the runtime file
166
167 Used when an instance is running. Load kvm runtime file and search
168 for a device based on its type and uuid.
169
170 @type dev_type: sting
171 @param dev_type: device type of param dev
172 @type device: L{objects.Disk} or L{objects.NIC}
173 @param device: the device object for which we generate a kvm name
174 @type runtime: tuple (cmd, nics, hvparams, disks)
175 @param runtime: the runtime data to search for the device
176 @raise errors.HotplugError: in case the requested device does not
177 exist (e.g. device has been added without --hotplug option) or
178 device info has not pci slot (e.g. old devices in the cluster)
179
180 """
181 index = _DEVICE_RUNTIME_INDEX[dev_type]
182 found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
183 if not found:
184 raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
185 (dev_type, device.uuid))
186
187 return found[0]
188
189
190 def _UpgradeSerializedRuntime(serialized_runtime):
191 """Upgrade runtime data
192
193 Remove any deprecated fields or change the format of the data.
194 The runtime files are not upgraded when Ganeti is upgraded, so the required
195 modification have to be performed here.
196
197 @type serialized_runtime: string
198 @param serialized_runtime: raw text data read from actual runtime file
199 @return: (cmd, nic dicts, hvparams, bdev dicts)
200 @rtype: tuple
201
202 """
203 loaded_runtime = serializer.Load(serialized_runtime)
204 kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
205 if len(loaded_runtime) >= 4:
206 serialized_disks = loaded_runtime[3]
207 else:
208 serialized_disks = []
209
210 for nic in serialized_nics:
211 # Add a dummy uuid slot if an pre-2.8 NIC is found
212 if "uuid" not in nic:
213 nic["uuid"] = utils.NewUUID()
214
215 return kvm_cmd, serialized_nics, hvparams, serialized_disks
216
217
218 def _AnalyzeSerializedRuntime(serialized_runtime):
219 """Return runtime entries for a serialized runtime file
220
221 @type serialized_runtime: string
222 @param serialized_runtime: raw text data read from actual runtime file
223 @return: (cmd, nics, hvparams, bdevs)
224 @rtype: tuple
225
226 """
227 kvm_cmd, serialized_nics, hvparams, serialized_disks = \
228 _UpgradeSerializedRuntime(serialized_runtime)
229 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
230 kvm_disks = [(objects.Disk.FromDict(sdisk), link, uri)
231 for sdisk, link, uri in serialized_disks]
232
233 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
234
235
236 class HeadRequest(urllib2.Request):
237 def get_method(self):
238 return "HEAD"
239
240
241 def _CheckUrl(url):
242 """Check if a given URL exists on the server
243
244 """
245 try:
246 urllib2.urlopen(HeadRequest(url))
247 return True
248 except urllib2.URLError:
249 return False
250
251
252 class KVMHypervisor(hv_base.BaseHypervisor):
253 """KVM hypervisor interface
254
255 """
256 CAN_MIGRATE = True
257
258 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
259 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
260 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
261 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
262 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
263 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
264 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
265 # KVM instances with chroot enabled are started in empty chroot directories.
266 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
267 # After an instance is stopped, its chroot directory is removed.
268 # If the chroot directory is not empty, it can't be removed.
269 # A non-empty chroot directory indicates a possible security incident.
270 # To support forensics, the non-empty chroot directory is quarantined in
271 # a separate directory, called 'chroot-quarantine'.
272 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
273 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
274 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
275
276 PARAMETERS = {
277 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
278 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
279 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
280 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
281 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
282 constants.HV_ACPI: hv_base.NO_CHECK,
283 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
284 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
285 constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK, # will be checked later
286 constants.HV_VNC_TLS: hv_base.NO_CHECK,
287 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
288 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
289 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
290 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
291 constants.HV_KVM_SPICE_IP_VERSION:
292 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
293 x in constants.VALID_IP_VERSIONS),
294 "The SPICE IP version should be 4 or 6",
295 None, None),
296 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
297 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
298 hv_base.ParamInSet(
299 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
300 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
301 hv_base.ParamInSet(
302 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
303 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
304 hv_base.ParamInSet(
305 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
306 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
307 hv_base.ParamInSet(
308 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
309 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
310 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
311 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
312 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
313 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
314 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
315 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
316 constants.HV_BOOT_ORDER:
317 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
318 constants.HV_NIC_TYPE:
319 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
320 constants.HV_DISK_TYPE:
321 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
322 constants.HV_KVM_CDROM_DISK_TYPE:
323 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
324 constants.HV_USB_MOUSE:
325 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
326 constants.HV_KEYMAP: hv_base.NO_CHECK,
327 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
328 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
329 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
330 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
331 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
332 constants.HV_DISK_CACHE:
333 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
334 constants.HV_SECURITY_MODEL:
335 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
336 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
337 constants.HV_KVM_FLAG:
338 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
339 constants.HV_VHOST_NET: hv_base.NO_CHECK,
340 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
341 constants.HV_KVM_USER_SHUTDOWN: hv_base.NO_CHECK,
342 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
343 constants.HV_REBOOT_BEHAVIOR:
344 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
345 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
346 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
347 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
348 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
349 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
350 constants.HV_SOUNDHW: hv_base.NO_CHECK,
351 constants.HV_USB_DEVICES: hv_base.NO_CHECK,
352 constants.HV_VGA: hv_base.NO_CHECK,
353 constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
354 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
355 constants.HV_KVM_MIGRATION_CAPS: hv_base.NO_CHECK,
356 constants.HV_VNET_HDR: hv_base.NO_CHECK,
357 }
358
359 _VIRTIO = "virtio"
360 _VIRTIO_NET_PCI = "virtio-net-pci"
361 _VIRTIO_BLK_PCI = "virtio-blk-pci"
362
363 _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)",
364 re.M | re.I)
365 _MIGRATION_PROGRESS_RE = \
366 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
367 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
368 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
369
370 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
371 _MIGRATION_INFO_RETRY_DELAY = 2
372
373 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
374
375 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
376 _CPU_INFO_CMD = "info cpus"
377 _CONT_CMD = "cont"
378
379 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
380 _CHECK_MACHINE_VERSION_RE = \
381 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
382
383 _QMP_RE = re.compile(r"^-qmp\s", re.M)
384 _SPICE_RE = re.compile(r"^-spice\s", re.M)
385 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
386 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
387 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
388 _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
389 _DISPLAY_RE = re.compile(r"^-display\s", re.M)
390 _MACHINE_RE = re.compile(r"^-machine\s", re.M)
391 _VIRTIO_NET_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M)
392 _VIRTIO_BLK_RE = re.compile(r"^name \"%s\"" % _VIRTIO_BLK_PCI, re.M)
393 # match -drive.*boot=on|off on different lines, but in between accept only
394 # dashes not preceeded by a new line (which would mean another option
395 # different than -drive is starting)
396 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
397 _UUID_RE = re.compile(r"^-uuid\s", re.M)
398
399 _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
400 _INFO_PCI_CMD = "info pci"
401 _FIND_PCI_DEVICE_RE = \
402 staticmethod(
403 lambda pci, devid: re.compile(r'Bus.*device[ ]*%d,(.*\n){5,6}.*id "%s"' %
404 (pci, devid), re.M))
405
406 _INFO_VERSION_RE = \
407 re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
408 _INFO_VERSION_CMD = "info version"
409
410 # Slot 0 for Host bridge, Slot 1 for ISA bridge, Slot 2 for VGA controller
411 _DEFAULT_PCI_RESERVATIONS = "11100000000000000000000000000000"
412 _SOUNDHW_WITH_PCI_SLOT = ["ac97", "es1370", "hda"]
413
414 ANCILLARY_FILES = [
415 _KVM_NETWORK_SCRIPT,
416 ]
417 ANCILLARY_FILES_OPT = [
418 _KVM_NETWORK_SCRIPT,
419 ]
420
421 # Supported kvm options to get output from
422 _KVMOPT_HELP = "help"
423 _KVMOPT_MLIST = "mlist"
424 _KVMOPT_DEVICELIST = "devicelist"
425
426 # Command to execute to get the output from kvm, and whether to
427 # accept the output even on failure.
428 _KVMOPTS_CMDS = {
429 _KVMOPT_HELP: (["--help"], False),
430 _KVMOPT_MLIST: (["-M", "?"], False),
431 _KVMOPT_DEVICELIST: (["-device", "?"], True),
432 }
433
434 def __init__(self):
435 hv_base.BaseHypervisor.__init__(self)
436 # Let's make sure the directories we need exist, even if the RUN_DIR lives
437 # in a tmpfs filesystem or has been otherwise wiped out.
438 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
439 utils.EnsureDirs(dirs)
440
441 @classmethod
442 def _InstancePidFile(cls, instance_name):
443 """Returns the instance pidfile.
444
445 """
446 return utils.PathJoin(cls._PIDS_DIR, instance_name)
447
448 @classmethod
449 def _InstanceUidFile(cls, instance_name):
450 """Returns the instance uidfile.
451
452 """
453 return utils.PathJoin(cls._UIDS_DIR, instance_name)
454
455 @classmethod
456 def _InstancePidInfo(cls, pid):
457 """Check pid file for instance information.
458
459 Check that a pid file is associated with an instance, and retrieve
460 information from its command line.
461
462 @type pid: string or int
463 @param pid: process id of the instance to check
464 @rtype: tuple
465 @return: (instance_name, memory, vcpus)
466 @raise errors.HypervisorError: when an instance cannot be found
467
468 """
469 alive = utils.IsProcessAlive(pid)
470 if not alive:
471 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
472
473 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
474 try:
475 cmdline = utils.ReadFile(cmdline_file)
476 except EnvironmentError, err:
477 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
478 (pid, err))
479
480 instance = None
481 memory = 0
482 vcpus = 0
483
484 arg_list = cmdline.split("\x00")
485 while arg_list:
486 arg = arg_list.pop(0)
487 if arg == "-name":
488 instance = arg_list.pop(0)
489 elif arg == "-m":
490 memory = int(arg_list.pop(0))
491 elif arg == "-smp":
492 vcpus = int(arg_list.pop(0).split(",")[0])
493
494 if instance is None:
495 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
496 " instance" % pid)
497
498 return (instance, memory, vcpus)
499
500 @classmethod
501 def _InstancePidAlive(cls, instance_name):
502 """Returns the instance pidfile, pid, and liveness.
503
504 @type instance_name: string
505 @param instance_name: instance name
506 @rtype: tuple
507 @return: (pid file name, pid, liveness)
508
509 """
510 pidfile = cls._InstancePidFile(instance_name)
511 pid = utils.ReadPidFile(pidfile)
512
513 alive = False
514 try:
515 cmd_instance = cls._InstancePidInfo(pid)[0]
516 alive = (cmd_instance == instance_name)
517 except errors.HypervisorError:
518 pass
519
520 return (pidfile, pid, alive)
521
522 @classmethod
523 def _CheckDown(cls, instance_name):
524 """Raises an error unless the given instance is down.
525
526 """
527 alive = cls._InstancePidAlive(instance_name)[2]
528 if alive:
529 raise errors.HypervisorError("Failed to start instance %s: %s" %
530 (instance_name, "already running"))
531
532 @classmethod
533 def _InstanceMonitor(cls, instance_name):
534 """Returns the instance monitor socket name
535
536 """
537 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
538
539 @classmethod
540 def _InstanceSerial(cls, instance_name):
541 """Returns the instance serial socket name
542
543 """
544 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
545
546 @classmethod
547 def _InstanceQmpMonitor(cls, instance_name):
548 """Returns the instance serial QMP socket name
549
550 """
551 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
552
553 @classmethod
554 def _InstanceShutdownMonitor(cls, instance_name):
555 """Returns the instance QMP output filename
556
557 """
558 return utils.PathJoin(cls._CTRL_DIR, "%s.shutdown" % instance_name)
559
560 @staticmethod
561 def _SocatUnixConsoleParams():
562 """Returns the correct parameters for socat
563
564 If we have a new-enough socat we can use raw mode with an escape character.
565
566 """
567 if constants.SOCAT_USE_ESCAPE:
568 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
569 else:
570 return "echo=0,icanon=0"
571
572 @classmethod
573 def _InstanceKVMRuntime(cls, instance_name):
574 """Returns the instance KVM runtime filename
575
576 """
577 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
578
579 @classmethod
580 def _InstanceChrootDir(cls, instance_name):
581 """Returns the name of the KVM chroot dir of the instance
582
583 """
584 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
585
586 @classmethod
587 def _InstanceNICDir(cls, instance_name):
588 """Returns the name of the directory holding the tap device files for a
589 given instance.
590
591 """
592 return utils.PathJoin(cls._NICS_DIR, instance_name)
593
594 @classmethod
595 def _InstanceNICFile(cls, instance_name, seq):
596 """Returns the name of the file containing the tap device for a given NIC
597
598 """
599 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
600
601 @classmethod
602 def _InstanceKeymapFile(cls, instance_name):
603 """Returns the name of the file containing the keymap for a given instance
604
605 """
606 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
607
608 @classmethod
609 def _TryReadUidFile(cls, uid_file):
610 """Try to read a uid file
611
612 """
613 if os.path.exists(uid_file):
614 try:
615 uid = int(utils.ReadOneLineFile(uid_file))
616 return uid
617 except EnvironmentError:
618 logging.warning("Can't read uid file", exc_info=True)
619 except (TypeError, ValueError):
620 logging.warning("Can't parse uid file contents", exc_info=True)
621 return None
622
623 @classmethod
624 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
625 """Removes an instance's rutime sockets/files/dirs.
626
627 """
628 utils.RemoveFile(pidfile)
629 utils.RemoveFile(cls._InstanceMonitor(instance_name))
630 utils.RemoveFile(cls._InstanceSerial(instance_name))
631 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
632 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
633 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
634 uid_file = cls._InstanceUidFile(instance_name)
635 uid = cls._TryReadUidFile(uid_file)
636 utils.RemoveFile(uid_file)
637 if uid is not None:
638 uidpool.ReleaseUid(uid)
639 try:
640 shutil.rmtree(cls._InstanceNICDir(instance_name))
641 except OSError, err:
642 if err.errno != errno.ENOENT:
643 raise
644 try:
645 chroot_dir = cls._InstanceChrootDir(instance_name)
646 utils.RemoveDir(chroot_dir)
647 except OSError, err:
648 if err.errno == errno.ENOTEMPTY:
649 # The chroot directory is expected to be empty, but it isn't.
650 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
651 prefix="%s-%s-" %
652 (instance_name,
653 utils.TimestampForFilename()))
654 logging.warning("The chroot directory of instance %s can not be"
655 " removed as it is not empty. Moving it to the"
656 " quarantine instead. Please investigate the"
657 " contents (%s) and clean up manually",
658 instance_name, new_chroot_dir)
659 utils.RenameFile(chroot_dir, new_chroot_dir)
660 else:
661 raise
662
663 @staticmethod
664 def _ConfigureNIC(instance, seq, nic, tap):
665 """Run the network configuration script for a specified NIC
666
667 See L{hv_base.ConfigureNIC}.
668
669 @param instance: instance we're acting on
670 @type instance: instance object
671 @param seq: nic sequence number
672 @type seq: int
673 @param nic: nic we're acting on
674 @type nic: nic object
675 @param tap: the host's tap interface this NIC corresponds to
676 @type tap: str
677
678 """
679 hv_base.ConfigureNIC([pathutils.KVM_IFUP, tap], instance, seq, nic, tap)
680
681 @staticmethod
682 def _VerifyAffinityPackage():
683 if affinity is None:
684 raise errors.HypervisorError("affinity Python package not"
685 " found; cannot use CPU pinning under KVM")
686
687 @staticmethod
688 def _BuildAffinityCpuMask(cpu_list):
689 """Create a CPU mask suitable for sched_setaffinity from a list of
690 CPUs.
691
692 See man taskset for more info on sched_setaffinity masks.
693 For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
694
695 @type cpu_list: list of int
696 @param cpu_list: list of physical CPU numbers to map to vCPUs in order
697 @rtype: int
698 @return: a bit mask of CPU affinities
699
700 """
701 if cpu_list == constants.CPU_PINNING_OFF:
702 return constants.CPU_PINNING_ALL_KVM
703 else:
704 return sum(2 ** cpu for cpu in cpu_list)
705
706 @classmethod
707 def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
708 """Change CPU affinity for running VM according to given CPU mask.
709
710 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
711 @type cpu_mask: string
712 @param process_id: process ID of KVM process. Used to pin entire VM
713 to physical CPUs.
714 @type process_id: int
715 @param thread_dict: map of virtual CPUs to KVM thread IDs
716 @type thread_dict: dict int:int
717
718 """
719 # Convert the string CPU mask to a list of list of int's
720 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
721
722 if len(cpu_list) == 1:
723 all_cpu_mapping = cpu_list[0]
724 if all_cpu_mapping == constants.CPU_PINNING_OFF:
725 # If CPU pinning has 1 entry that's "all", then do nothing
726 pass
727 else:
728 # If CPU pinning has one non-all entry, map the entire VM to
729 # one set of physical CPUs
730 cls._VerifyAffinityPackage()
731 affinity.set_process_affinity_mask(
732 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
733 else:
734 # The number of vCPUs mapped should match the number of vCPUs
735 # reported by KVM. This was already verified earlier, so
736 # here only as a sanity check.
737 assert len(thread_dict) == len(cpu_list)
738 cls._VerifyAffinityPackage()
739
740 # For each vCPU, map it to the proper list of physical CPUs
741 for vcpu, i in zip(cpu_list, range(len(cpu_list))):
742 affinity.set_process_affinity_mask(thread_dict[i],
743 cls._BuildAffinityCpuMask(vcpu))
744
745 def _GetVcpuThreadIds(self, instance_name):
746 """Get a mapping of vCPU no. to thread IDs for the instance
747
748 @type instance_name: string
749 @param instance_name: instance in question
750 @rtype: dictionary of int:int
751 @return: a dictionary mapping vCPU numbers to thread IDs
752
753 """
754 result = {}
755 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
756 for line in output.stdout.splitlines():
757 match = self._CPU_INFO_RE.search(line)
758 if not match:
759 continue
760 grp = map(int, match.groups())
761 result[grp[0]] = grp[1]
762
763 return result
764
765 def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
766 """Complete CPU pinning.
767
768 @type instance_name: string
769 @param instance_name: name of instance
770 @type cpu_mask: string
771 @param cpu_mask: CPU pinning mask as entered by user
772
773 """
774 # Get KVM process ID, to be used if need to pin entire VM
775 _, pid, _ = self._InstancePidAlive(instance_name)
776 # Get vCPU thread IDs, to be used if need to pin vCPUs separately
777 thread_dict = self._GetVcpuThreadIds(instance_name)
778 # Run CPU pinning, based on configured mask
779 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
780
781 def ListInstances(self, hvparams=None):
782 """Get the list of running instances.
783
784 We can do this by listing our live instances directory and
785 checking whether the associated kvm process is still alive.
786
787 """
788 result = []
789 for name in os.listdir(self._PIDS_DIR):
790 if self._InstancePidAlive(name)[2] or self._IsUserShutdown(name):
791 result.append(name)
792 return result
793
794 @classmethod
795 def _IsUserShutdown(cls, instance_name):
796 return os.path.exists(cls._InstanceShutdownMonitor(instance_name))
797
798 @classmethod
799 def _ClearUserShutdown(cls, instance_name):
800 utils.RemoveFile(cls._InstanceShutdownMonitor(instance_name))
801
802 def GetInstanceInfo(self, instance_name, hvparams=None):
803 """Get instance properties.
804
805 @type instance_name: string
806 @param instance_name: the instance name
807 @type hvparams: dict of strings
808 @param hvparams: hvparams to be used with this instance
809 @rtype: tuple of strings
810 @return: (name, id, memory, vcpus, stat, times)
811
812 """
813 _, pid, alive = self._InstancePidAlive(instance_name)
814 if not alive:
815 if self._IsUserShutdown(instance_name):
816 return (instance_name, -1, 0, 0, hv_base.HvInstanceState.SHUTDOWN, 0)
817 else:
818 return None
819
820 _, memory, vcpus = self._InstancePidInfo(pid)
821 istat = hv_base.HvInstanceState.RUNNING
822 times = 0
823
824 try:
825 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
826 qmp.connect()
827 vcpus = len(qmp.Execute("query-cpus"))
828 # Will fail if ballooning is not enabled, but we can then just resort to
829 # the value above.
830 mem_bytes = qmp.Execute("query-balloon")[qmp.ACTUAL_KEY]
831 memory = mem_bytes / 1048576
832 except errors.HypervisorError:
833 pass
834
835 return (instance_name, pid, memory, vcpus, istat, times)
836
837 def GetAllInstancesInfo(self, hvparams=None):
838 """Get properties of all instances.
839
840 @type hvparams: dict of strings
841 @param hvparams: hypervisor parameter
842 @return: list of tuples (name, id, memory, vcpus, stat, times)
843
844 """
845 data = []
846 for name in os.listdir(self._PIDS_DIR):
847 try:
848 info = self.GetInstanceInfo(name)
849 except errors.HypervisorError:
850 # Ignore exceptions due to instances being shut down
851 continue
852 if info:
853 data.append(info)
854 return data
855
856 def _GenerateKVMBlockDevicesOptions(self, instance, up_hvp, kvm_disks,
857 kvmhelp, devlist):
858 """Generate KVM options regarding instance's block devices.
859
860 @type instance: L{objects.Instance}
861 @param instance: the instance object
862 @type up_hvp: dict
863 @param up_hvp: the instance's runtime hypervisor parameters
864 @type kvm_disks: list of tuples
865 @param kvm_disks: list of tuples [(disk, link_name, uri)..]
866 @type kvmhelp: string
867 @param kvmhelp: output of kvm --help
868 @type devlist: string
869 @param devlist: output of kvm -device ?
870 @rtype: list
871 @return: list of command line options eventually used by kvm executable
872
873 """
874 kernel_path = up_hvp[constants.HV_KERNEL_PATH]
875 if kernel_path:
876 boot_disk = False
877 else:
878 boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
879
880 # whether this is an older KVM version that uses the boot=on flag
881 # on devices
882 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
883
884 dev_opts = []
885 device_driver = None
886 disk_type = up_hvp[constants.HV_DISK_TYPE]
887 if disk_type == constants.HT_DISK_PARAVIRTUAL:
888 if_val = ",if=%s" % self._VIRTIO
889 try:
890 if self._VIRTIO_BLK_RE.search(devlist):
891 if_val = ",if=none"
892 # will be passed in -device option as driver
893 device_driver = self._VIRTIO_BLK_PCI
894 except errors.HypervisorError, _:
895 pass
896 else:
897 if_val = ",if=%s" % disk_type
898 # Cache mode
899 disk_cache = up_hvp[constants.HV_DISK_CACHE]
900 if instance.disk_template in constants.DTS_EXT_MIRROR:
901 if disk_cache != "none":
902 # TODO: make this a hard error, instead of a silent overwrite
903 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
904 " to prevent shared storage corruption on migration",
905 disk_cache)
906 cache_val = ",cache=none"
907 elif disk_cache != constants.HT_CACHE_DEFAULT:
908 cache_val = ",cache=%s" % disk_cache
909 else:
910 cache_val = ""
911 for cfdev, link_name, uri in kvm_disks:
912 if cfdev.mode != constants.DISK_RDWR:
913 raise errors.HypervisorError("Instance has read-only disks which"
914 " are not supported by KVM")
915 # TODO: handle FD_LOOP and FD_BLKTAP (?)
916 boot_val = ""
917 if boot_disk:
918 dev_opts.extend(["-boot", "c"])
919 boot_disk = False
920 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
921 boot_val = ",boot=on"
922
923 access_mode = cfdev.params.get(constants.LDP_ACCESS,
924 constants.DISK_KERNELSPACE)
925 if (uri and access_mode == constants.DISK_USERSPACE):
926 drive_uri = uri
927 else:
928 drive_uri = link_name
929
930 drive_val = "file=%s,format=raw%s%s%s" % \
931 (drive_uri, if_val, boot_val, cache_val)
932
933 if device_driver:
934 # kvm_disks are the 4th entry of runtime file that did not exist in
935 # the past. That means that cfdev should always have pci slot and
936 # _GenerateDeviceKVMId() will not raise a exception.
937 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK, cfdev)
938 drive_val += (",id=%s" % kvm_devid)
939 drive_val += (",bus=0,unit=%d" % cfdev.pci)
940 dev_val = ("%s,drive=%s,id=%s" %
941 (device_driver, kvm_devid, kvm_devid))
942 dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
943 dev_opts.extend(["-device", dev_val])
944
945 dev_opts.extend(["-drive", drive_val])
946
947 return dev_opts
948
949 @staticmethod
950 def _CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image, cdrom_boot,
951 needs_boot_flag):
952 """Extends L{kvm_cmd} with the '-drive' option for a cdrom, and
953 optionally the '-boot' option.
954
955 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide -boot d
956
957 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide,boot=on
958
959 Example: -drive file=http://hostname.com/cdrom.iso,media=cdrom
960
961 @type kvm_cmd: string
962 @param kvm_cmd: KVM command line
963
964 @type cdrom_disk_type:
965 @param cdrom_disk_type:
966
967 @type cdrom_image:
968 @param cdrom_image:
969
970 @type cdrom_boot:
971 @param cdrom_boot:
972
973 @type needs_boot_flag:
974 @param needs_boot_flag:
975
976 """
977 # Check that the ISO image is accessible
978 # See https://bugs.launchpad.net/qemu/+bug/597575
979 if utils.IsUrl(cdrom_image) and not _CheckUrl(cdrom_image):
980 raise errors.HypervisorError("Cdrom ISO image '%s' is not accessible" %
981 cdrom_image)
982
983 # set cdrom 'media' and 'format', if needed
984 if utils.IsUrl(cdrom_image):
985 options = ",media=cdrom"
986 else:
987 options = ",media=cdrom,format=raw"
988
989 # set cdrom 'if' type
990 if cdrom_boot:
991 if_val = ",if=" + constants.HT_DISK_IDE
992 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
993 if_val = ",if=virtio"
994 else:
995 if_val = ",if=" + cdrom_disk_type
996
997 # set boot flag, if needed
998 boot_val = ""
999 if cdrom_boot:
1000 kvm_cmd.extend(["-boot", "d"])
1001
1002 # whether this is an older KVM version that requires the 'boot=on' flag
1003 # on devices
1004 if needs_boot_flag:
1005 boot_val = ",boot=on"
1006
1007 # build '-drive' option
1008 drive_val = "file=%s%s%s%s" % (cdrom_image, options, if_val, boot_val)
1009 kvm_cmd.extend(["-drive", drive_val])
1010
1011 def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1012 kvmhelp):
1013 """Generate KVM information to start an instance.
1014
1015 @type kvmhelp: string
1016 @param kvmhelp: output of kvm --help
1017 @attention: this function must not have any side-effects; for
1018 example, it must not write to the filesystem, or read values
1019 from the current system the are expected to differ between
1020 nodes, since it is only run once at instance startup;
1021 actions/kvm arguments that can vary between systems should be
1022 done in L{_ExecuteKVMRuntime}
1023
1024 """
1025 # pylint: disable=R0912,R0914,R0915
1026 hvp = instance.hvparams
1027 self.ValidateParameters(hvp)
1028
1029 pidfile = self._InstancePidFile(instance.name)
1030 kvm = hvp[constants.HV_KVM_PATH]
1031 kvm_cmd = [kvm]
1032 # used just by the vnc server, if enabled
1033 kvm_cmd.extend(["-name", instance.name])
1034 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1035
1036 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1037 if hvp[constants.HV_CPU_CORES]:
1038 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1039 if hvp[constants.HV_CPU_THREADS]:
1040 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1041 if hvp[constants.HV_CPU_SOCKETS]:
1042 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1043
1044 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1045
1046 kvm_cmd.extend(["-pidfile", pidfile])
1047
1048 pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1049
1050 # As requested by music lovers
1051 if hvp[constants.HV_SOUNDHW]:
1052 soundhw = hvp[constants.HV_SOUNDHW]
1053 # For some reason only few sound devices require a PCI slot
1054 # while the Audio controller *must* be in slot 3.
1055 # That's why we bridge this option early in command line
1056 if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
1057 _ = _GetFreeSlot(pci_reservations, reserve=True)
1058 kvm_cmd.extend(["-soundhw", soundhw])
1059
1060 if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
1061 # The SCSI controller requires another PCI slot.
1062 _ = _GetFreeSlot(pci_reservations, reserve=True)
1063
1064 # Add id to ballon and place to the first available slot (3 or 4)
1065 addr = _GetFreeSlot(pci_reservations, reserve=True)
1066 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1067 kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
1068 kvm_cmd.extend(["-daemonize"])
1069 if not instance.hvparams[constants.HV_ACPI]:
1070 kvm_cmd.extend(["-no-acpi"])
1071 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1072 constants.INSTANCE_REBOOT_EXIT:
1073 kvm_cmd.extend(["-no-reboot"])
1074
1075 mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1076 if not mversion:
1077 mversion = self._GetDefaultMachineVersion(kvm)
1078 if self._MACHINE_RE.search(kvmhelp):
1079 # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
1080 # extra hypervisor parameters. We should also investigate whether and how
1081 # shadow_mem should be considered for the resource model.
1082 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1083 specprop = ",accel=kvm"
1084 else:
1085 specprop = ""
1086 machinespec = "%s%s" % (mversion, specprop)
1087 kvm_cmd.extend(["-machine", machinespec])
1088 else:
1089 kvm_cmd.extend(["-M", mversion])
1090 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1091 self._ENABLE_KVM_RE.search(kvmhelp)):
1092 kvm_cmd.extend(["-enable-kvm"])
1093 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1094 self._DISABLE_KVM_RE.search(kvmhelp)):
1095 kvm_cmd.extend(["-disable-kvm"])
1096
1097 kernel_path = hvp[constants.HV_KERNEL_PATH]
1098 if kernel_path:
1099 boot_cdrom = boot_floppy = boot_network = False
1100 else:
1101 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1102 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1103 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1104
1105 if startup_paused:
1106 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1107
1108 if boot_network:
1109 kvm_cmd.extend(["-boot", "n"])
1110
1111 disk_type = hvp[constants.HV_DISK_TYPE]
1112
1113 # Now we can specify a different device type for CDROM devices.
1114 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1115 if not cdrom_disk_type:
1116 cdrom_disk_type = disk_type
1117
1118 cdrom_image1 = hvp[constants.HV_CDROM_IMAGE_PATH]
1119 if cdrom_image1:
1120 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1121 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image1, boot_cdrom,
1122 needs_boot_flag)
1123
1124 cdrom_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1125 if cdrom_image2:
1126 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image2, False, False)
1127
1128 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1129 if floppy_image:
1130 options = ",format=raw,media=disk"
1131 if boot_floppy:
1132 kvm_cmd.extend(["-boot", "a"])
1133 options = "%s,boot=on" % options
1134 if_val = ",if=floppy"
1135 options = "%s%s" % (options, if_val)
1136 drive_val = "file=%s%s" % (floppy_image, options)
1137 kvm_cmd.extend(["-drive", drive_val])
1138
1139 if kernel_path:
1140 kvm_cmd.extend(["-kernel", kernel_path])
1141 initrd_path = hvp[constants.HV_INITRD_PATH]
1142 if initrd_path:
1143 kvm_cmd.extend(["-initrd", initrd_path])
1144 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1145 hvp[constants.HV_KERNEL_ARGS]]
1146 if hvp[constants.HV_SERIAL_CONSOLE]:
1147 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1148 root_append.append("console=ttyS0,%s" % serial_speed)
1149 kvm_cmd.extend(["-append", " ".join(root_append)])
1150
1151 mem_path = hvp[constants.HV_MEM_PATH]
1152 if mem_path:
1153 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1154
1155 monitor_dev = ("unix:%s,server,nowait" %
1156 self._InstanceMonitor(instance.name))
1157 kvm_cmd.extend(["-monitor", monitor_dev])
1158 if hvp[constants.HV_SERIAL_CONSOLE]:
1159 serial_dev = ("unix:%s,server,nowait" %
1160 self._InstanceSerial(instance.name))
1161 kvm_cmd.extend(["-serial", serial_dev])
1162 else:
1163 kvm_cmd.extend(["-serial", "none"])
1164
1165 mouse_type = hvp[constants.HV_USB_MOUSE]
1166 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1167 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1168 spice_ip_version = None
1169
1170 kvm_cmd.extend(["-usb"])
1171
1172 if mouse_type:
1173 kvm_cmd.extend(["-usbdevice", mouse_type])
1174 elif vnc_bind_address:
1175 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1176
1177 if vnc_bind_address:
1178 if netutils.IsValidInterface(vnc_bind_address):
1179 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1180 if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1181 if len(if_ip4_addresses) < 1:
1182 logging.error("Could not determine IPv4 address of interface %s",
1183 vnc_bind_address)
1184 else:
1185 vnc_bind_address = if_ip4_addresses[0]
1186 if netutils.IP4Address.IsValid(vnc_bind_address):
1187 if instance.network_port > constants.VNC_BASE_PORT:
1188 display = instance.network_port - constants.VNC_BASE_PORT
1189 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1190 vnc_arg = ":%d" % (display)
1191 else:
1192 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1193 else:
1194 logging.error("Network port is not a valid VNC display (%d < %d),"
1195 " not starting VNC",
1196 instance.network_port, constants.VNC_BASE_PORT)
1197 vnc_arg = "none"
1198
1199 # Only allow tls and other option when not binding to a file, for now.
1200 # kvm/qemu gets confused otherwise about the filename to use.
1201 vnc_append = ""
1202 if hvp[constants.HV_VNC_TLS]:
1203 vnc_append = "%s,tls" % vnc_append
1204 if hvp[constants.HV_VNC_X509_VERIFY]:
1205 vnc_append = "%s,x509verify=%s" % (vnc_append,
1206 hvp[constants.HV_VNC_X509])
1207 elif hvp[constants.HV_VNC_X509]:
1208 vnc_append = "%s,x509=%s" % (vnc_append,
1209 hvp[constants.HV_VNC_X509])
1210 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1211 vnc_append = "%s,password" % vnc_append
1212
1213 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1214
1215 else:
1216 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1217
1218 kvm_cmd.extend(["-vnc", vnc_arg])
1219 elif spice_bind:
1220 # FIXME: this is wrong here; the iface ip address differs
1221 # between systems, so it should be done in _ExecuteKVMRuntime
1222 if netutils.IsValidInterface(spice_bind):
1223 # The user specified a network interface, we have to figure out the IP
1224 # address.
1225 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1226 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1227
1228 # if the user specified an IP version and the interface does not
1229 # have that kind of IP addresses, throw an exception
1230 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1231 if not addresses[spice_ip_version]:
1232 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1233 " for %s" % (spice_ip_version,
1234 spice_bind))
1235
1236 # the user did not specify an IP version, we have to figure it out
1237 elif (addresses[constants.IP4_VERSION] and
1238 addresses[constants.IP6_VERSION]):
1239 # we have both ipv4 and ipv6, let's use the cluster default IP
1240 # version
1241 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1242 spice_ip_version = \
1243 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1244 elif addresses[constants.IP4_VERSION]:
1245 spice_ip_version = constants.IP4_VERSION
1246 elif addresses[constants.IP6_VERSION]:
1247 spice_ip_version = constants.IP6_VERSION
1248 else:
1249 raise errors.HypervisorError("SPICE: Unable to get an IP address"
1250 " for %s" % (spice_bind))
1251
1252 spice_address = addresses[spice_ip_version][0]
1253
1254 else:
1255 # spice_bind is known to be a valid IP address, because
1256 # ValidateParameters checked it.
1257 spice_address = spice_bind
1258
1259 spice_arg = "addr=%s" % spice_address
1260 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1261 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1262 (spice_arg, instance.network_port,
1263 pathutils.SPICE_CACERT_FILE))
1264 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1265 (spice_arg, pathutils.SPICE_CERT_FILE,
1266 pathutils.SPICE_CERT_FILE))
1267 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1268 if tls_ciphers:
1269 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1270 else:
1271 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1272
1273 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1274 spice_arg = "%s,disable-ticketing" % spice_arg
1275
1276 if spice_ip_version:
1277 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1278
1279 # Image compression options
1280 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1281 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1282 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1283 if img_lossless:
1284 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1285 if img_jpeg:
1286 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1287 if img_zlib_glz:
1288 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1289
1290 # Video stream detection
1291 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1292 if video_streaming:
1293 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1294
1295 # Audio compression, by default in qemu-kvm it is on
1296 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1297 spice_arg = "%s,playback-compression=off" % spice_arg
1298 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1299 spice_arg = "%s,agent-mouse=off" % spice_arg
1300 else:
1301 # Enable the spice agent communication channel between the host and the
1302 # agent.
1303 addr = _GetFreeSlot(pci_reservations, reserve=True)
1304 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1305 kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
1306 kvm_cmd.extend([
1307 "-device",
1308 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1309 ])
1310 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1311
1312 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1313 kvm_cmd.extend(["-spice", spice_arg])
1314
1315 else:
1316 # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1317 # also works in earlier versions though (tested with 1.1 and 1.3)
1318 if self._DISPLAY_RE.search(kvmhelp):
1319 kvm_cmd.extend(["-display", "none"])
1320 else:
1321 kvm_cmd.extend(["-nographic"])
1322
1323 if hvp[constants.HV_USE_LOCALTIME]:
1324 kvm_cmd.extend(["-localtime"])
1325
1326 if hvp[constants.HV_KVM_USE_CHROOT]:
1327 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1328
1329 # Add qemu-KVM -cpu param
1330 if hvp[constants.HV_CPU_TYPE]:
1331 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1332
1333 # Pass a -vga option if requested, or if spice is used, for backwards
1334 # compatibility.
1335 if hvp[constants.HV_VGA]:
1336 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1337 elif spice_bind:
1338 kvm_cmd.extend(["-vga", "qxl"])
1339
1340 # Various types of usb devices, comma separated
1341 if hvp[constants.HV_USB_DEVICES]:
1342 for dev in hvp[constants.HV_USB_DEVICES].split(","):
1343 kvm_cmd.extend(["-usbdevice", dev])
1344
1345 # Set system UUID to instance UUID
1346 if self._UUID_RE.search(kvmhelp):
1347 kvm_cmd.extend(["-uuid", instance.uuid])
1348
1349 if hvp[constants.HV_KVM_EXTRA]:
1350 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1351
1352 kvm_disks = []
1353 for disk, link_name, uri in block_devices:
1354 disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
1355 kvm_disks.append((disk, link_name, uri))
1356
1357 kvm_nics = []
1358 for nic in instance.nics:
1359 nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True)
1360 kvm_nics.append(nic)
1361
1362 hvparams = hvp
1363
1364 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1365
1366 def _WriteKVMRuntime(self, instance_name, data):
1367 """Write an instance's KVM runtime
1368
1369 """
1370 try:
1371 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1372 data=data)
1373 except EnvironmentError, err:
1374 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1375
1376 def _ReadKVMRuntime(self, instance_name):
1377 """Read an instance's KVM runtime
1378
1379 """
1380 try:
1381 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1382 except EnvironmentError, err:
1383 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1384 return file_content
1385
1386 def _SaveKVMRuntime(self, instance, kvm_runtime):
1387 """Save an instance's KVM runtime
1388
1389 """
1390 kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1391
1392 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1393 serialized_disks = [(blk.ToDict(), link, uri)
1394 for blk, link, uri in kvm_disks]
1395 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1396 serialized_disks))
1397
1398 self._WriteKVMRuntime(instance.name, serialized_form)
1399
1400 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1401 """Load an instance's KVM runtime
1402
1403 """
1404 if not serialized_runtime:
1405 serialized_runtime = self._ReadKVMRuntime(instance.name)
1406
1407 return _AnalyzeSerializedRuntime(serialized_runtime)
1408
1409 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1410 """Run the KVM cmd and check for errors
1411
1412 @type name: string
1413 @param name: instance name
1414 @type kvm_cmd: list of strings
1415 @param kvm_cmd: runcmd input for kvm
1416 @type tap_fds: list of int
1417 @param tap_fds: fds of tap devices opened by Ganeti
1418
1419 """
1420 try:
1421 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1422 finally:
1423 for fd in tap_fds:
1424 utils_wrapper.CloseFdNoError(fd)
1425
1426 if result.failed:
1427 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1428 (name, result.fail_reason, result.output))
1429 if not self._InstancePidAlive(name)[2]:
1430 raise errors.HypervisorError("Failed to start instance %s" % name)
1431
1432 @staticmethod
1433 def _GenerateKvmTapName(nic):
1434 """Generate a TAP network interface name for a NIC.
1435
1436 See L{hv_base.GenerateTapName}.
1437
1438 For the case of the empty string, see L{OpenTap}
1439
1440 @type nic: ganeti.objects.NIC
1441 @param nic: NIC object for the name should be generated
1442
1443 @rtype: string
1444 @return: TAP network interface name, or the empty string if the
1445 NIC is not used in instance communication
1446
1447 """
1448 if nic.name is None or not \
1449 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
1450 return ""
1451
1452 return hv_base.GenerateTapName()
1453
1454 # too many local variables
1455 # pylint: disable=R0914
1456 def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1457 """Execute a KVM cmd, after completing it with some last minute data.
1458
1459 @type incoming: tuple of strings
1460 @param incoming: (target_host_ip, port)
1461 @type kvmhelp: string
1462 @param kvmhelp: output of kvm --help
1463
1464 """
1465 # Small _ExecuteKVMRuntime hv parameters programming howto:
1466 # - conf_hvp contains the parameters as configured on ganeti. they might
1467 # have changed since the instance started; only use them if the change
1468 # won't affect the inside of the instance (which hasn't been rebooted).
1469 # - up_hvp contains the parameters as they were when the instance was
1470 # started, plus any new parameter which has been added between ganeti
1471 # versions: it is paramount that those default to a value which won't
1472 # affect the inside of the instance as well.
1473 conf_hvp = instance.hvparams
1474 name = instance.name
1475 self._CheckDown(name)
1476
1477 self._ClearUserShutdown(instance.name)
1478 self._StartKvmd(instance.hvparams)
1479
1480 temp_files = []
1481
1482 kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1483 # the first element of kvm_cmd is always the path to the kvm binary
1484 kvm_path = kvm_cmd[0]
1485 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1486
1487 # We know it's safe to run as a different user upon migration, so we'll use
1488 # the latest conf, from conf_hvp.
1489 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1490 if security_model == constants.HT_SM_USER:
1491 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1492
1493 keymap = conf_hvp[constants.HV_KEYMAP]
1494 if keymap:
1495 keymap_path = self._InstanceKeymapFile(name)
1496 # If a keymap file is specified, KVM won't use its internal defaults. By
1497 # first including the "en-us" layout, an error on loading the actual
1498 # layout (e.g. because it can't be found) won't lead to a non-functional
1499 # keyboard. A keyboard with incorrect keys is still better than none.
1500 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1501 kvm_cmd.extend(["-k", keymap_path])
1502
1503 # We have reasons to believe changing something like the nic driver/type
1504 # upon migration won't exactly fly with the instance kernel, so for nic
1505 # related parameters we'll use up_hvp
1506 tapfds = []
1507 taps = []
1508 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1509 if not kvm_nics:
1510 kvm_cmd.extend(["-net", "none"])
1511 else:
1512 vnet_hdr = False
1513 tap_extra = ""
1514 nic_type = up_hvp[constants.HV_NIC_TYPE]
1515 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1516 nic_model = self._VIRTIO
1517 try:
1518 if self._VIRTIO_NET_RE.search(devlist):
1519 nic_model = self._VIRTIO_NET_PCI
1520 vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1521 except errors.HypervisorError, _:
1522 # Older versions of kvm don't support DEVICE_LIST, but they don't
1523 # have new virtio syntax either.
1524 pass
1525
1526 if up_hvp[constants.HV_VHOST_NET]:
1527 # check for vhost_net support
1528 if self._VHOST_RE.search(kvmhelp):
1529 tap_extra = ",vhost=on"
1530 else:
1531 raise errors.HypervisorError("vhost_net is configured"
1532 " but it is not available")
1533 else:
1534 nic_model = nic_type
1535
1536 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1537
1538 for nic_seq, nic in enumerate(kvm_nics):
1539 tapname, tapfd = OpenTap(vnet_hdr=vnet_hdr,
1540 name=self._GenerateKvmTapName(nic))
1541 tapfds.append(tapfd)
1542 taps.append(tapname)
1543 if kvm_supports_netdev:
1544 nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1545 try:
1546 # kvm_nics already exist in old runtime files and thus there might
1547 # be some entries without pci slot (therefore try: except:)
1548 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1549 netdev = kvm_devid
1550 nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1551 except errors.HotplugError:
1552 netdev = "netdev%d" % nic_seq
1553 nic_val += (",netdev=%s" % netdev)
1554 tap_val = ("type=tap,id=%s,fd=%d%s" %
1555 (netdev, tapfd, tap_extra))
1556 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1557 else:
1558 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1559 nic.mac, nic_model)
1560 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1561 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1562
1563 if incoming:
1564 target, port = incoming
1565 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1566
1567 # Changing the vnc password doesn't bother the guest that much. At most it
1568 # will surprise people who connect to it. Whether positively or negatively
1569 # it's debatable.
1570 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1571 vnc_pwd = None
1572 if vnc_pwd_file:
1573 try:
1574 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1575 except EnvironmentError, err:
1576 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1577 % (vnc_pwd_file, err))
1578
1579 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1580 utils.EnsureDirs([(self._InstanceChrootDir(name),
1581 constants.SECURE_DIR_MODE)])
1582
1583 # Automatically enable QMP if version is >= 0.14
1584 if self._QMP_RE.search(kvmhelp):
1585 logging.debug("Enabling QMP")
1586 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1587 self._InstanceQmpMonitor(instance.name)])
1588
1589 # Configure the network now for starting instances and bridged interfaces,
1590 # during FinalizeMigration for incoming instances' routed interfaces
1591 for nic_seq, nic in enumerate(kvm_nics):
1592 if (incoming and
1593 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1594 continue
1595 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1596
1597 bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1598 up_hvp,
1599 kvm_disks,
1600 kvmhelp,
1601 devlist)
1602 kvm_cmd.extend(bdev_opts)
1603 # CPU affinity requires kvm to start paused, so we set this flag if the
1604 # instance is not already paused and if we are not going to accept a
1605 # migrating instance. In the latter case, pausing is not needed.
1606 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1607 if start_kvm_paused:
1608 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1609
1610 # Note: CPU pinning is using up_hvp since changes take effect
1611 # during instance startup anyway, and to avoid problems when soft
1612 # rebooting the instance.
1613 cpu_pinning = False
1614 if up_hvp.get(constants.HV_CPU_MASK, None):
1615 cpu_pinning = True
1616
1617 if security_model == constants.HT_SM_POOL:
1618 ss = ssconf.SimpleStore()
1619 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1620 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1621 uid = uidpool.RequestUnusedUid(all_uids)
1622 try:
1623 username = pwd.getpwuid(uid.GetUid()).pw_name
1624 kvm_cmd.extend(["-runas", username])
1625 self._RunKVMCmd(name, kvm_cmd, tapfds)
1626 except:
1627 uidpool.ReleaseUid(uid)
1628 raise
1629 else:
1630 uid.Unlock()
1631 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1632 else:
1633 self._RunKVMCmd(name, kvm_cmd, tapfds)
1634
1635 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1636 constants.RUN_DIRS_MODE)])
1637 for nic_seq, tap in enumerate(taps):
1638 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1639 data=tap)
1640
1641 if vnc_pwd:
1642 change_cmd = "change vnc password %s" % vnc_pwd
1643 self._CallMonitorCommand(instance.name, change_cmd)
1644
1645 # Setting SPICE password. We are not vulnerable to malicious passwordless
1646 # connection attempts because SPICE by default does not allow connections
1647 # if neither a password nor the "disable_ticketing" options are specified.
1648 # As soon as we send the password via QMP, that password is a valid ticket
1649 # for connection.
1650 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1651 if spice_password_file:
1652 spice_pwd = ""
1653 try:
1654 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1655 except EnvironmentError, err:
1656 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1657 % (spice_password_file, err))
1658
1659 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1660 qmp.connect()
1661 arguments = {
1662 "protocol": "spice",
1663 "password": spice_pwd,
1664 }
1665 qmp.Execute("set_password", arguments)
1666
1667 for filename in temp_files:
1668 utils.RemoveFile(filename)
1669
1670 # If requested, set CPU affinity and resume instance execution
1671 if cpu_pinning:
1672 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1673
1674 start_memory = self._InstanceStartupMemory(instance)
1675 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1676 self.BalloonInstanceMemory(instance, start_memory)
1677
1678 if start_kvm_paused:
1679 # To control CPU pinning, ballooning, and vnc/spice passwords
1680 # the VM was started in a frozen state. If freezing was not
1681 # explicitly requested resume the vm status.
1682 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1683
1684 @staticmethod
1685 def _StartKvmd(hvparams):
1686 """Ensure that the Kvm daemon is running.
1687
1688 """
1689 if hvparams is None \
1690 or not hvparams[constants.HV_KVM_USER_SHUTDOWN] \
1691 or utils.IsDaemonAlive(constants.KVMD):
1692 return
1693
1694 result = utils.RunCmd(constants.KVMD)
1695
1696 if result.failed:
1697 raise errors.HypervisorError("Failed to start KVM daemon")
1698
1699 def StartInstance(self, instance, block_devices, startup_paused):
1700 """Start an instance.
1701
1702 """
1703 self._CheckDown(instance.name)
1704 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
1705 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
1706 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1707 startup_paused, kvmhelp)
1708 self._SaveKVMRuntime(instance, kvm_runtime)
1709 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
1710
1711 @classmethod
1712 def _CallMonitorCommand(cls, instance_name, command, timeout=None):
1713 """Invoke a command on the instance monitor.
1714
1715 """
1716 if timeout is not None:
1717 timeout_cmd = "timeout %s" % (timeout, )
1718 else:
1719 timeout_cmd = ""
1720
1721 # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
1722 # version. The monitor protocol is designed for human consumption, whereas
1723 # QMP is made for programmatic usage. In the worst case QMP can also
1724 # execute monitor commands. As it is, all calls to socat take at least
1725 # 500ms and likely more: socat can't detect the end of the reply and waits
1726 # for 500ms of no data received before exiting (500 ms is the default for
1727 # the "-t" parameter).
1728 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
1729 (utils.ShellQuote(command),
1730 timeout_cmd,
1731 constants.SOCAT_PATH,
1732 utils.ShellQuote(cls._InstanceMonitor(instance_name))))
1733 result = utils.RunCmd(socat)
1734 if result.failed:
1735 msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
1736 " output: %s" %
1737 (command, instance_name, result.fail_reason, result.output))
1738 raise errors.HypervisorError(msg)
1739
1740 return result
1741
1742 def _GetFreePCISlot(self, instance, dev):
1743 """Get the first available pci slot of a runnung instance.
1744
1745 """
1746 slots = bitarray(32)
1747 slots.setall(False) # pylint: disable=E1101
1748 output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
1749 for line in output.stdout.splitlines():
1750 match = self._INFO_PCI_RE.search(line)
1751 if match:
1752 slot = int(match.group(1))
1753 slots[slot] = True
1754
1755 dev.pci = _GetFreeSlot(slots)
1756
1757 def VerifyHotplugSupport(self, instance, action, dev_type):
1758 """Verifies that hotplug is supported.
1759
1760 Hotplug is *not* supported in case of:
1761 - security models and chroot (disk hotplug)
1762 - fdsend module is missing (nic hot-add)
1763
1764 @raise errors.HypervisorError: in one of the previous cases
1765
1766 """
1767 if dev_type == constants.HOTPLUG_TARGET_DISK:
1768 hvp = instance.hvparams
1769 security_model = hvp[constants.HV_SECURITY_MODEL]
1770 use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
1771 if use_chroot:
1772 raise errors.HotplugError("Disk hotplug is not supported"
1773 " in case of chroot.")
1774 if security_model != constants.HT_SM_NONE:
1775 raise errors.HotplugError("Disk Hotplug is not supported in case"
1776 " security models are used.")
1777
1778 if (dev_type == constants.HOTPLUG_TARGET_NIC and
1779 action == constants.HOTPLUG_ACTION_ADD and not fdsend):
1780 raise errors.HotplugError("Cannot hot-add NIC."
1781 " fdsend python module is missing.")
1782
1783 def HotplugSupported(self, instance):
1784 """Checks if hotplug is generally supported.
1785
1786 Hotplug is *not* supported in case of:
1787 - qemu versions < 1.0
1788 - for stopped instances
1789
1790 @raise errors.HypervisorError: in one of the previous cases
1791
1792 """
1793 try:
1794 output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
1795 except errors.HypervisorError:
1796 raise errors.HotplugError("Instance is probably down")
1797
1798 # TODO: search for netdev_add, drive_add, device_add.....
1799 match = self._INFO_VERSION_RE.search(output.stdout)
1800 if not match:
1801 raise errors.HotplugError("Cannot parse qemu version via monitor")
1802
1803 v_major, v_min, _, _ = match.groups()
1804 if (int(v_major), int(v_min)) < (1, 0):
1805 raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
1806
1807 def _CallHotplugCommands(self, name, cmds):
1808 for c in cmds:
1809 self._CallMonitorCommand(name, c)
1810 time.sleep(1)
1811
1812 def _VerifyHotplugCommand(self, instance_name, device, dev_type,
1813 should_exist):
1814 """Checks if a previous hotplug command has succeeded.
1815
1816 It issues info pci monitor command and checks depending on should_exist
1817 value if an entry with PCI slot and device ID is found or not.
1818
1819 @raise errors.HypervisorError: if result is not the expected one
1820
1821 """
1822 output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
1823 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
1824 match = \
1825 self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
1826 if match and not should_exist:
1827 msg = "Device %s should have been removed but is still there" % kvm_devid
1828 raise errors.HypervisorError(msg)
1829
1830 if not match and should_exist:
1831 msg = "Device %s should have been added but is missing" % kvm_devid
1832 raise errors.HypervisorError(msg)
1833
1834 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
1835
1836 def HotAddDevice(self, instance, dev_type, device, extra, seq):
1837 """ Helper method to hot-add a new device
1838
1839 It gets free pci slot generates the device name and invokes the
1840 device specific method.
1841
1842 """
1843 # in case of hot-mod this is given
1844 if device.pci is None:
1845 self._GetFreePCISlot(instance, device)
1846 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
1847 runtime = self._LoadKVMRuntime(instance)
1848 if dev_type == constants.HOTPLUG_TARGET_DISK:
1849 cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
1850 (extra, kvm_devid)]
1851 cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
1852 (hex(device.pci), kvm_devid, kvm_devid)]
1853 elif dev_type == constants.HOTPLUG_TARGET_NIC:
1854 (tap, fd) = OpenTap()
1855 self._ConfigureNIC(instance, seq, device, tap)
1856 self._PassTapFd(instance, fd, device)
1857 cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
1858 args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
1859 (hex(device.pci), device.mac, kvm_devid, kvm_devid)
1860 cmds += ["device_add %s" % args]
1861 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
1862
1863 self._CallHotplugCommands(instance.name, cmds)
1864 self._VerifyHotplugCommand(instance.name, device, dev_type, True)
1865 # update relevant entries in runtime file
1866 index = _DEVICE_RUNTIME_INDEX[dev_type]
1867 entry = _RUNTIME_ENTRY[dev_type](device, extra)
1868 runtime[index].append(entry)
1869 self._SaveKVMRuntime(instance, runtime)
1870
1871 def HotDelDevice(self, instance, dev_type, device, _, seq):
1872 """ Helper method for hot-del device
1873
1874 It gets device info from runtime file, generates the device name and
1875 invokes the device specific method.
1876
1877 """
1878 runtime = self._LoadKVMRuntime(instance)
1879 entry = _GetExistingDeviceInfo(dev_type, device, runtime)
1880 kvm_device = _RUNTIME_DEVICE[dev_type](entry)
1881 kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
1882 if dev_type == constants.HOTPLUG_TARGET_DISK:
1883 cmds = ["device_del %s" % kvm_devid]
1884 cmds += ["drive_del %s" % kvm_devid]
1885 elif dev_type == constants.HOTPLUG_TARGET_NIC:
1886 cmds = ["device_del %s" % kvm_devid]
1887 cmds += ["netdev_del %s" % kvm_devid]
1888 utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
1889 self._CallHotplugCommands(instance.name, cmds)
1890 self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
1891 index = _DEVICE_RUNTIME_INDEX[dev_type]
1892 runtime[index].remove(entry)
1893 self._SaveKVMRuntime(instance, runtime)
1894
1895 return kvm_device.pci
1896
1897 def HotModDevice(self, instance, dev_type, device, _, seq):
1898 """ Helper method for hot-mod device
1899
1900 It gets device info from runtime file, generates the device name and
1901 invokes the device specific method. Currently only NICs support hot-mod
1902
1903 """
1904 if dev_type == constants.HOTPLUG_TARGET_NIC:
1905 # putting it back in the same pci slot
1906 device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
1907 self.HotAddDevice(instance, dev_type, device, _, seq)
1908
1909 def _PassTapFd(self, instance, fd, nic):
1910 """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
1911
1912 """
1913 # TODO: factor out code related to unix sockets.
1914 # squash common parts between monitor and qmp
1915 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1916 command = "getfd %s\n" % kvm_devid
1917 fds = [fd]
1918 logging.info("%s", fds)
1919 try:
1920 monsock = MonitorSocket(self._InstanceMonitor(instance.name))
1921 monsock.connect()
1922 fdsend.sendfds(monsock.sock, command, fds=fds)
1923 finally:
1924 monsock.close()
1925
1926 @classmethod
1927 def _ParseKVMVersion(cls, text):
1928 """Parse the KVM version from the --help output.
1929
1930 @type text: string
1931 @param text: output of kvm --help
1932 @return: (version, v_maj, v_min, v_rev)
1933 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1934
1935 """
1936 match = cls._VERSION_RE.search(text.splitlines()[0])
1937 if not match:
1938 raise errors.HypervisorError("Unable to get KVM version")
1939
1940 v_all = match.group(0)
1941 v_maj = int(match.group(1))
1942 v_min = int(match.group(2))
1943 if match.group(4):
1944 v_rev = int(match.group(4))
1945 else:
1946 v_rev = 0
1947 return (v_all, v_maj, v_min, v_rev)
1948
1949 @classmethod
1950 def _GetKVMOutput(cls, kvm_path, option):
1951 """Return the output of a kvm invocation
1952
1953 @type kvm_path: string
1954 @param kvm_path: path to the kvm executable
1955 @type option: a key of _KVMOPTS_CMDS
1956 @param option: kvm option to fetch the output from
1957 @return: output a supported kvm invocation
1958 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
1959
1960 """
1961 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
1962
1963 optlist, can_fail = cls._KVMOPTS_CMDS[option]
1964
1965 result = utils.RunCmd([kvm_path] + optlist)
1966 if result.failed and not can_fail:
1967 raise errors.HypervisorError("Unable to get KVM %s output" %
1968 " ".join(optlist))
1969 return result.output
1970
1971 @classmethod
1972 def _GetKVMVersion(cls, kvm_path):
1973 """Return the installed KVM version.
1974
1975 @return: (version, v_maj, v_min, v_rev)
1976 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1977
1978 """
1979 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
1980
1981 @classmethod
1982 def _GetDefaultMachineVersion(cls, kvm_path):
1983 """Return the default hardware revision (e.g. pc-1.1)
1984
1985 """
1986 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
1987 match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
1988 if match:
1989 return match.group(1)
1990 else:
1991 return "pc"
1992
1993 @classmethod
1994 def _StopInstance(cls, instance, force=False, name=None, timeout=None):
1995 """Stop an instance.
1996
1997 """
1998 assert(timeout is None or force is not None)
1999
2000 if name is not None and not force:
2001 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2002 if name is None:
2003 name = instance.name
2004 acpi = instance.hvparams[constants.HV_ACPI]
2005 else:
2006 acpi = False
2007 _, pid, alive = cls._InstancePidAlive(name)
2008 if pid > 0 and alive:
2009 if force or not acpi:
2010 utils.KillProcess(pid)
2011 else:
2012 cls._CallMonitorCommand(name, "system_powerdown", timeout)
2013 cls._ClearUserShutdown(instance.name)
2014
2015 def StopInstance(self, instance, force=False, retry=False, name=None,
2016 timeout=None):
2017 """Stop an instance.
2018
2019 """
2020 self._StopInstance(instance, force, name=name, timeout=timeout)
2021
2022 def CleanupInstance(self, instance_name):
2023 """Cleanup after a stopped instance
2024
2025 """
2026 pidfile, pid, alive = self._InstancePidAlive(instance_name)
2027 if pid > 0 and alive:
2028 raise errors.HypervisorError("Cannot cleanup a live instance")
2029 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2030 self._ClearUserShutdown(instance_name)
2031
2032 def RebootInstance(self, instance):
2033 """Reboot an instance.
2034
2035 """
2036 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2037 # socket the instance will stop, but now power up again. So we'll resort
2038 # to shutdown and restart.
2039 _, _, alive = self._InstancePidAlive(instance.name)
2040 if not alive:
2041 raise errors.HypervisorError("Failed to reboot instance %s:"
2042 " not running" % instance.name)
2043 # StopInstance will delete the saved KVM runtime so:
2044 # ...first load it...
2045 kvm_runtime = self._LoadKVMRuntime(instance)
2046 # ...now we can safely call StopInstance...
2047 if not self.StopInstance(instance):
2048 self.StopInstance(instance, force=True)
2049 # ...and finally we can save it again, and execute it...
2050 self._SaveKVMRuntime(instance, kvm_runtime)
2051 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2052 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2053 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2054
2055 def MigrationInfo(self, instance):
2056 """Get instance information to perform a migration.
2057
2058 @type instance: L{objects.Instance}
2059 @param instance: instance to be migrated
2060 @rtype: string
2061 @return: content of the KVM runtime file
2062
2063 """
2064 return self._ReadKVMRuntime(instance.name)
2065
2066 def AcceptInstance(self, instance, info, target):
2067 """Prepare to accept an instance.
2068
2069 @type instance: L{objects.Instance}
2070 @param instance: instance to be accepted
2071 @type info: string
2072 @param info: content of the KVM runtime file on the source node
2073 @type target: string
2074 @param target: target host (usually ip), on this node
2075
2076 """
2077 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2078 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2079 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2080 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2081 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2082 incoming=incoming_address)
2083
2084 def FinalizeMigrationDst(self, instance, info, success):
2085 """Finalize the instance migration on the target node.
2086
2087 Stop the incoming mode KVM.
2088
2089 @type instance: L{objects.Instance}
2090 @param instance: instance whose migration is being finalized
2091
2092 """
2093 if success:
2094 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2095 kvm_nics = kvm_runtime[1]
2096
2097 for nic_seq, nic in enumerate(kvm_nics):
2098 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2099 # Bridged interfaces have already been configured
2100 continue
2101 try:
2102 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2103 except EnvironmentError, err:
2104 logging.warning("Failed to find host interface for %s NIC #%d: %s",
2105 instance.name, nic_seq, str(err))
2106 continue
2107 try:
2108 self._ConfigureNIC(instance, nic_seq, nic, tap)
2109 except errors.HypervisorError, err:
2110 logging.warning(str(err))
2111
2112 self._WriteKVMRuntime(instance.name, info)
2113 else:
2114 self.StopInstance(instance, force=True)
2115
2116 def MigrateInstance(self, cluster_name, instance, target, live):
2117 """Migrate an instance to a target node.
2118
2119 The migration will not be attempted if the instance is not
2120 currently running.
2121
2122 @type cluster_name: string
2123 @param cluster_name: name of the cluster
2124 @type instance: L{objects.Instance}
2125 @param instance: the instance to be migrated
2126 @type target: string
2127 @param target: ip address of the target node
2128 @type live: boolean
2129 @param live: perform a live migration
2130
2131 """
2132 instance_name = instance.name
2133 port = instance.hvparams[constants.HV_MIGRATION_PORT]
2134 _, _, alive = self._InstancePidAlive(instance_name)
2135 if not alive:
2136 raise errors.HypervisorError("Instance not running, cannot migrate")
2137
2138 if not live:
2139 self._CallMonitorCommand(instance_name, "stop")
2140
2141 migrate_command = ("migrate_set_speed %dm" %
2142 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2143 self._CallMonitorCommand(instance_name, migrate_command)
2144
2145 migrate_command = ("migrate_set_downtime %dms" %
2146 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2147 self._CallMonitorCommand(instance_name, migrate_command)
2148
2149 migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS]
2150 if migration_caps:
2151 for c in migration_caps.split(_MIGRATION_CAPS_DELIM):
2152 migrate_command = ("migrate_set_capability %s on" % c)
2153 self._CallMonitorCommand(instance_name, migrate_command)
2154
2155 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2156 self._CallMonitorCommand(instance_name, migrate_command)
2157
2158 def FinalizeMigrationSource(self, instance, success, live):
2159 """Finalize the instance migration on the source node.
2160
2161 @type instance: L{objects.Instance}
2162 @param instance: the instance that was migrated
2163 @type success: bool
2164 @param success: whether the migration succeeded or not
2165 @type live: bool
2166 @param live: whether the user requested a live migration or not
2167
2168 """
2169 if success:
2170 pidfile, pid, _ = self._InstancePidAlive(instance.name)
2171 utils.KillProcess(pid)
2172 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2173 elif live:
2174 self._CallMonitorCommand(instance.name, self._CONT_CMD)
2175 self._ClearUserShutdown(instance.name)
2176
2177 def GetMigrationStatus(self, instance):
2178 """Get the migration status
2179
2180 @type instance: L{objects.Instance}
2181 @param instance: the instance that is being migrated
2182 @rtype: L{objects.MigrationStatus}
2183 @return: the status of the current migration (one of
2184 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2185 progress info that can be retrieved from the hypervisor
2186
2187 """
2188 info_command = "info migrate"
2189 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2190 result = self._CallMonitorCommand(instance.name, info_command)
2191 match = self._MIGRATION_STATUS_RE.search(result.stdout)
2192 if not match:
2193 if not result.stdout:
2194 logging.info("KVM: empty 'info migrate' result")
2195 else:
2196 logging.warning("KVM: unknown 'info migrate' result: %s",
2197 result.stdout)
2198 else:
2199 status = match.group(1)
2200 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2201 migration_status = objects.MigrationStatus(status=status)
2202 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2203 if match:
2204 migration_status.transferred_ram = match.group("transferred")
2205 migration_status.total_ram = match.group("total")
2206
2207 return migration_status
2208
2209 logging.warning("KVM: unknown migration status '%s'", status)
2210
2211 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2212
2213 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2214
2215 def BalloonInstanceMemory(self, instance, mem):
2216 """Balloon an instance memory to a certain value.
2217
2218 @type instance: L{objects.Instance}
2219 @param instance: instance to be accepted
2220 @type mem: int
2221 @param mem: actual memory size to use for instance runtime
2222
2223 """
2224 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2225
2226 def GetNodeInfo(self, hvparams=None):
2227 """Return information about the node.
2228
2229 @type hvparams: dict of strings
2230 @param hvparams: hypervisor parameters, not used in this class
2231
2232 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2233 the following keys:
2234 - hv_version: the hypervisor version in the form (major, minor,
2235 revision)
2236
2237 """
2238 result = self.GetLinuxNodeInfo()
2239 kvmpath = constants.KVM_PATH
2240 if hvparams is not None:
2241 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2242 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2243 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2244 return result
2245
2246 @classmethod
2247 def GetInstanceConsole(cls, instance, primary_node, node_group,
2248 hvparams, beparams):
2249 """Return a command for connecting to the console of an instance.
2250
2251 """
2252 if hvparams[constants.HV_SERIAL_CONSOLE]:
2253 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2254 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2255 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2256 "STDIO,%s" % cls._SocatUnixConsoleParams(),
2257 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2258 ndparams = node_group.FillND(primary_node)
2259 return objects.InstanceConsole(instance=instance.name,
2260 kind=constants.CONS_SSH,
2261 host=primary_node.name,
2262 port=ndparams.get(constants.ND_SSH_PORT),
2263 user=constants.SSH_CONSOLE_USER,
2264 command=cmd)
2265
2266 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2267 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2268 display = instance.network_port - constants.VNC_BASE_PORT
2269 return objects.InstanceConsole(instance=instance.name,
2270 kind=constants.CONS_VNC,
2271 host=vnc_bind_address,
2272 port=instance.network_port,
2273 display=display)
2274
2275 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2276 if spice_bind:
2277 return objects.InstanceConsole(instance=instance.name,
2278 kind=constants.CONS_SPICE,
2279 host=spice_bind,
2280 port=instance.network_port)
2281
2282 return objects.InstanceConsole(instance=instance.name,
2283 kind=constants.CONS_MESSAGE,
2284 message=("No serial shell for instance %s" %
2285 instance.name))
2286
2287 def Verify(self, hvparams=None):
2288 """Verify the hypervisor.
2289
2290 Check that the required binaries exist.
2291
2292 @type hvparams: dict of strings
2293 @param hvparams: hypervisor parameters to be verified against, not used here
2294
2295 @return: Problem description if something is wrong, C{None} otherwise
2296
2297 """
2298 msgs = []
2299 kvmpath = constants.KVM_PATH
2300 if hvparams is not None:
2301 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2302 if not os.path.exists(kvmpath):
2303 msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2304 if not os.path.exists(constants.SOCAT_PATH):
2305 msgs.append("The socat binary ('%s') does not exist" %
2306 constants.SOCAT_PATH)
2307
2308 return self._FormatVerifyResults(msgs)
2309
2310 @classmethod
2311 def CheckParameterSyntax(cls, hvparams):
2312 """Check the given parameters for validity.
2313
2314 @type hvparams: dict
2315 @param hvparams: dictionary with parameter names/value
2316 @raise errors.HypervisorError: when a parameter is not valid
2317
2318 """
2319 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2320
2321 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2322 if kernel_path:
2323 if not hvparams[constants.HV_ROOT_PATH]:
2324 raise errors.HypervisorError("Need a root partition for the instance,"
2325 " if a kernel is defined")
2326
2327 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2328 not hvparams[constants.HV_VNC_X509]):
2329 raise errors.HypervisorError("%s must be defined, if %s is" %
2330 (constants.HV_VNC_X509,
2331 constants.HV_VNC_X509_VERIFY))
2332
2333 if hvparams[constants.HV_SERIAL_CONSOLE]:
2334 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2335 valid_speeds = constants.VALID_SERIAL_SPEEDS
2336 if not serial_speed or serial_speed not in valid_speeds:
2337 raise errors.HypervisorError("Invalid serial console speed, must be"
2338 " one of: %s" %
2339 utils.CommaJoin(valid_speeds))
2340
2341 boot_order = hvparams[constants.HV_BOOT_ORDER]
2342 if (boot_order == constants.HT_BO_CDROM and
2343 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2344 raise errors.HypervisorError("Cannot boot from cdrom without an"
2345 " ISO path")
2346
2347 security_model = hvparams[constants.HV_SECURITY_MODEL]
2348 if security_model == constants.HT_SM_USER:
2349 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2350 raise errors.HypervisorError("A security domain (user to run kvm as)"
2351 " must be specified")
2352 elif (security_model == constants.HT_SM_NONE or
2353 security_model == constants.HT_SM_POOL):
2354 if hvparams[constants.HV_SECURITY_DOMAIN]:
2355 raise errors.HypervisorError("Cannot have a security domain when the"
2356 " security model is 'none' or 'pool'")
2357
2358 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2359 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2360 if spice_bind:
2361 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2362 # if an IP version is specified, the spice_bind parameter must be an
2363 # IP of that family
2364 if (netutils.IP4Address.IsValid(spice_bind) and
2365 spice_ip_version != constants.IP4_VERSION):
2366 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2367 " the specified IP version is %s" %
2368 (spice_bind, spice_ip_version))
2369
2370 if (netutils.IP6Address.IsValid(spice_bind) and
2371 spice_ip_version != constants.IP6_VERSION):
2372 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2373 " the specified IP version is %s" %
2374 (spice_bind, spice_ip_version))
2375 else:
2376 # All the other SPICE parameters depend on spice_bind being set. Raise an
2377 # error if any of them is set without it.
2378 for param in _SPICE_ADDITIONAL_PARAMS:
2379 if hvparams[param]:
2380 raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2381 (param, constants.HV_KVM_SPICE_BIND))
2382
2383 @classmethod
2384 def ValidateParameters(cls, hvparams):
2385 """Check the given parameters for validity.
2386
2387 @type hvparams: dict
2388 @param hvparams: dictionary with parameter names/value
2389 @raise errors.HypervisorError: when a parameter is not valid
2390
2391 """
2392 super(KVMHypervisor, cls).ValidateParameters(hvparams)
2393
2394 kvm_path = hvparams[constants.HV_KVM_PATH]
2395
2396 security_model = hvparams[constants.HV_SECURITY_MODEL]
2397 if security_model == constants.HT_SM_USER:
2398 username = hvparams[constants.HV_SECURITY_DOMAIN]
2399 try:
2400 pwd.getpwnam(username)
2401 except KeyError:
2402 raise errors.HypervisorError("Unknown security domain user %s"
2403 % username)
2404 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2405 if vnc_bind_address:
2406 bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address)
2407 is_interface = netutils.IsValidInterface(vnc_bind_address)
2408 is_path = utils.IsNormAbsPath(vnc_bind_address)
2409 if not bound_to_addr and not is_interface and not is_path:
2410 raise errors.HypervisorError("VNC: The %s parameter must be either"
2411 " a valid IP address, an interface name,"
2412 " or an absolute path" %
2413 constants.HV_KVM_SPICE_BIND)
2414
2415 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2416 if spice_bind:
2417 # only one of VNC and SPICE can be used currently.
2418 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2419 raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2420 " only one of them can be used at a"
2421 " given time")
2422
2423 # check that KVM supports SPICE
2424 kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2425 if not cls._SPICE_RE.search(kvmhelp):
2426 raise errors.HypervisorError("SPICE is configured, but it is not"
2427 " supported according to 'kvm --help'")
2428
2429 # if spice_bind is not an IP address, it must be a valid interface
2430 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2431 netutils.IP6Address.IsValid(spice_bind))
2432 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2433 raise errors.HypervisorError("SPICE: The %s parameter must be either"
2434 " a valid IP address or interface name" %
2435 constants.HV_KVM_SPICE_BIND)
2436
2437 machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2438 if machine_version:
2439 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2440 if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2441 raise errors.HypervisorError("Unsupported machine version: %s" %
2442 machine_version)
2443
2444 @classmethod
2445 def PowercycleNode(cls, hvparams=None):
2446 """KVM powercycle, just a wrapper over Linux powercycle.
2447
2448 @type hvparams: dict of strings
2449 @param hvparams: hypervisor params to be used on this node
2450
2451 """
2452 cls.LinuxPowercycle()