4 # Copyright (C) 2008, 2009, 2010, 2011, 2012 Google Inc.
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.
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.
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
41 import affinity # pylint: disable=F0401
45 from ganeti import utils
46 from ganeti import constants
47 from ganeti import errors
48 from ganeti import serializer
49 from ganeti import objects
50 from ganeti import uidpool
51 from ganeti import ssconf
52 from ganeti import netutils
53 from ganeti import pathutils
54 from ganeti.hypervisor import hv_base
55 from ganeti.utils import wrapper as utils_wrapper
58 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
59 _KVM_START_PAUSED_FLAG = "-S"
61 # TUN/TAP driver constants, taken from <linux/if_tun.h>
62 # They are architecture-independent and already hardcoded in qemu-kvm source,
63 # so we can safely include them here.
64 TUNSETIFF = 0x400454ca
65 TUNGETIFF = 0x800454d2
66 TUNGETFEATURES = 0x800454cf
71 #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND}
72 _SPICE_ADDITIONAL_PARAMS = frozenset([
73 constants.HV_KVM_SPICE_IP_VERSION,
74 constants.HV_KVM_SPICE_PASSWORD_FILE,
75 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
76 constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
77 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
78 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
79 constants.HV_KVM_SPICE_USE_TLS,
83 def _ProbeTapVnetHdr(fd):
84 """Check whether to enable the IFF_VNET_HDR flag.
86 To do this, _all_ of the following conditions must be met:
87 1. TUNGETFEATURES ioctl() *must* be implemented
88 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
89 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
90 drivers/net/tun.c there is no way to test this until after the tap device
91 has been created using TUNSETIFF, and there is no way to change the
92 IFF_VNET_HDR flag after creating the interface, catch-22! However both
93 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
94 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
97 @param fd: the file descriptor of /dev/net/tun
100 req = struct.pack("I", 0)
102 res = fcntl.ioctl(fd, TUNGETFEATURES, req)
103 except EnvironmentError:
104 logging.warning("TUNGETFEATURES ioctl() not implemented")
107 tunflags = struct.unpack("I", res)[0]
108 if tunflags & IFF_VNET_HDR:
111 logging.warning("Host does not support IFF_VNET_HDR, not enabling")
115 def _OpenTap(vnet_hdr=True):
116 """Open a new tap device and return its file descriptor.
118 This is intended to be used by a qemu-type hypervisor together with the -net
119 tap,fd=<fd> command line parameter.
121 @type vnet_hdr: boolean
122 @param vnet_hdr: Enable the VNET Header
123 @return: (ifname, tapfd)
128 tapfd = os.open("/dev/net/tun", os.O_RDWR)
129 except EnvironmentError:
130 raise errors.HypervisorError("Failed to open /dev/net/tun")
132 flags = IFF_TAP | IFF_NO_PI
134 if vnet_hdr and _ProbeTapVnetHdr(tapfd):
135 flags |= IFF_VNET_HDR
137 # The struct ifreq ioctl request (see netdevice(7))
138 ifr = struct.pack("16sh", "", flags)
141 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
142 except EnvironmentError:
143 raise errors.HypervisorError("Failed to allocate a new TAP device")
145 # Get the interface name from the ioctl
146 ifname = struct.unpack("16sh", res)[0].strip("\x00")
147 return (ifname, tapfd)
150 def _BuildNetworkEnv(name, network, gateway, network6, gateway6,
151 network_type, mac_prefix, tags, env):
152 """Build environment variables concerning a Network.
156 env["NETWORK_NAME"] = name
158 env["NETWORK_SUBNET"] = network
160 env["NETWORK_GATEWAY"] = gateway
162 env["NETWORK_SUBNET6"] = network6
164 env["NETWORK_GATEWAY6"] = gateway6
166 env["NETWORK_MAC_PREFIX"] = mac_prefix
168 env["NETWORK_TYPE"] = network_type
170 env["NETWORK_TAGS"] = " ".join(tags)
176 """QEMU Messaging Protocol (QMP) message.
179 def __init__(self, data):
180 """Creates a new QMP message based on the passed data.
183 if not isinstance(data, dict):
184 raise TypeError("QmpMessage must be initialized with a dict")
188 def __getitem__(self, field_name):
189 """Get the value of the required field if present, or None.
191 Overrides the [] operator to provide access to the message data,
192 returning None if the required item is not in the message
193 @return: the value of the field_name field, or None if field_name
194 is not contained in the message
197 return self.data.get(field_name, None)
199 def __setitem__(self, field_name, field_value):
200 """Set the value of the required field_name to field_value.
203 self.data[field_name] = field_value
206 def BuildFromJsonString(json_string):
207 """Build a QmpMessage from a JSON encoded string.
209 @type json_string: str
210 @param json_string: JSON string representing the message
211 @rtype: L{QmpMessage}
212 @return: a L{QmpMessage} built from json_string
216 data = serializer.LoadJson(json_string)
217 return QmpMessage(data)
220 # The protocol expects the JSON object to be sent as a single line.
221 return serializer.DumpJson(self.data)
223 def __eq__(self, other):
224 # When comparing two QmpMessages, we are interested in comparing
225 # their internal representation of the message data
226 return self.data == other.data
230 """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
233 _FIRST_MESSAGE_KEY = "QMP"
236 _RETURN_KEY = RETURN_KEY = "return"
237 _ACTUAL_KEY = ACTUAL_KEY = "actual"
238 _ERROR_CLASS_KEY = "class"
239 _ERROR_DATA_KEY = "data"
240 _ERROR_DESC_KEY = "desc"
241 _EXECUTE_KEY = "execute"
242 _ARGUMENTS_KEY = "arguments"
243 _CAPABILITIES_COMMAND = "qmp_capabilities"
244 _MESSAGE_END_TOKEN = "\r\n"
247 def __init__(self, monitor_filename):
248 """Instantiates the QmpConnection object.
250 @type monitor_filename: string
251 @param monitor_filename: the filename of the UNIX raw socket on which the
252 QMP monitor is listening
255 self.monitor_filename = monitor_filename
256 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
257 # We want to fail if the server doesn't send a complete message
258 # in a reasonable amount of time
259 self.sock.settimeout(self._SOCKET_TIMEOUT)
260 self._connected = False
263 def _check_socket(self):
266 sock_stat = os.stat(self.monitor_filename)
267 except EnvironmentError, err:
268 if err.errno == errno.ENOENT:
269 raise errors.HypervisorError("No qmp socket found")
271 raise errors.HypervisorError("Error checking qmp socket: %s",
272 utils.ErrnoOrStr(err))
273 if not stat.S_ISSOCK(sock_stat.st_mode):
274 raise errors.HypervisorError("Qmp socket is not a socket")
276 def _check_connection(self):
277 """Make sure that the connection is established.
280 if not self._connected:
281 raise errors.ProgrammerError("To use a QmpConnection you need to first"
282 " invoke connect() on it")
285 """Connects to the QMP monitor.
287 Connects to the UNIX socket and makes sure that we can actually send and
288 receive data to the kvm instance via QMP.
290 @raise errors.HypervisorError: when there are communication errors
291 @raise errors.ProgrammerError: when there are data serialization errors
295 raise errors.ProgrammerError("Cannot connect twice")
299 # Check file existance/stuff
301 self.sock.connect(self.monitor_filename)
302 except EnvironmentError:
303 raise errors.HypervisorError("Can't connect to qmp socket")
304 self._connected = True
306 # Check if we receive a correct greeting message from the server
307 # (As per the QEMU Protocol Specification 0.1 - section 2.2)
308 greeting = self._Recv()
309 if not greeting[self._FIRST_MESSAGE_KEY]:
310 self._connected = False
311 raise errors.HypervisorError("kvm: qmp communication error (wrong"
314 # Let's put the monitor in command mode using the qmp_capabilities
315 # command, or else no command will be executable.
316 # (As per the QEMU Protocol Specification 0.1 - section 4)
317 self.Execute(self._CAPABILITIES_COMMAND)
319 def _ParseMessage(self, buf):
320 """Extract and parse a QMP message from the given buffer.
322 Seeks for a QMP message in the given buf. If found, it parses it and
323 returns it together with the rest of the characters in the buf.
324 If no message is found, returns None and the whole buffer.
326 @raise errors.ProgrammerError: when there are data serialization errors
330 # Check if we got the message end token (CRLF, as per the QEMU Protocol
331 # Specification 0.1 - Section 2.1.1)
332 pos = buf.find(self._MESSAGE_END_TOKEN)
335 message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
336 except Exception, err:
337 raise errors.ProgrammerError("QMP data serialization error: %s" % err)
340 return (message, buf)
343 """Receives a message from QMP and decodes the received JSON object.
346 @return: the received message
347 @raise errors.HypervisorError: when there are communication errors
348 @raise errors.ProgrammerError: when there are data serialization errors
351 self._check_connection()
353 # Check if there is already a message in the buffer
354 (message, self._buf) = self._ParseMessage(self._buf)
358 recv_buffer = StringIO.StringIO(self._buf)
359 recv_buffer.seek(len(self._buf))
362 data = self.sock.recv(4096)
365 recv_buffer.write(data)
367 (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
371 except socket.timeout, err:
372 raise errors.HypervisorError("Timeout while receiving a QMP message: "
374 except socket.error, err:
375 raise errors.HypervisorError("Unable to receive data from KVM using the"
376 " QMP protocol: %s" % err)
378 def _Send(self, message):
379 """Encodes and sends a message to KVM using QMP.
381 @type message: QmpMessage
382 @param message: message to send to KVM
383 @raise errors.HypervisorError: when there are communication errors
384 @raise errors.ProgrammerError: when there are data serialization errors
387 self._check_connection()
389 message_str = str(message)
390 except Exception, err:
391 raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
394 self.sock.sendall(message_str)
395 except socket.timeout, err:
396 raise errors.HypervisorError("Timeout while sending a QMP message: "
397 "%s (%s)" % (err.string, err.errno))
398 except socket.error, err:
399 raise errors.HypervisorError("Unable to send data from KVM using the"
400 " QMP protocol: %s" % err)
402 def Execute(self, command, arguments=None):
403 """Executes a QMP command and returns the response of the server.
406 @param command: the command to execute
407 @type arguments: dict
408 @param arguments: dictionary of arguments to be passed to the command
410 @return: dictionary representing the received JSON object
411 @raise errors.HypervisorError: when there are communication errors
412 @raise errors.ProgrammerError: when there are data serialization errors
415 self._check_connection()
416 message = QmpMessage({self._EXECUTE_KEY: command})
418 message[self._ARGUMENTS_KEY] = arguments
421 # Events can occur between the sending of the command and the reception
422 # of the response, so we need to filter out messages with the event key.
424 response = self._Recv()
425 err = response[self._ERROR_KEY]
427 raise errors.HypervisorError("kvm: error executing the %s"
428 " command: %s (%s, %s):" %
430 err[self._ERROR_DESC_KEY],
431 err[self._ERROR_CLASS_KEY],
432 err[self._ERROR_DATA_KEY]))
434 elif not response[self._EVENT_KEY]:
438 class KVMHypervisor(hv_base.BaseHypervisor):
439 """KVM hypervisor interface
444 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
445 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
446 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
447 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
448 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
449 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
450 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
451 # KVM instances with chroot enabled are started in empty chroot directories.
452 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
453 # After an instance is stopped, its chroot directory is removed.
454 # If the chroot directory is not empty, it can't be removed.
455 # A non-empty chroot directory indicates a possible security incident.
456 # To support forensics, the non-empty chroot directory is quarantined in
457 # a separate directory, called 'chroot-quarantine'.
458 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
459 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
460 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
463 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
464 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
465 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
466 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
467 constants.HV_ACPI: hv_base.NO_CHECK,
468 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
469 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
470 constants.HV_VNC_BIND_ADDRESS:
471 (False, lambda x: (netutils.IP4Address.IsValid(x) or
472 utils.IsNormAbsPath(x)),
473 "the VNC bind address must be either a valid IP address or an absolute"
474 " pathname", None, None),
475 constants.HV_VNC_TLS: hv_base.NO_CHECK,
476 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
477 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
478 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
479 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
480 constants.HV_KVM_SPICE_IP_VERSION:
481 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
482 x in constants.VALID_IP_VERSIONS),
483 "the SPICE IP version should be 4 or 6",
485 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
486 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
488 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
489 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
491 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
492 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
494 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
495 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
497 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
498 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
499 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
500 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
501 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
502 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
503 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
504 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
505 constants.HV_BOOT_ORDER:
506 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
507 constants.HV_NIC_TYPE:
508 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
509 constants.HV_DISK_TYPE:
510 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
511 constants.HV_KVM_CDROM_DISK_TYPE:
512 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
513 constants.HV_USB_MOUSE:
514 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
515 constants.HV_KEYMAP: hv_base.NO_CHECK,
516 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
517 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
518 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
519 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
520 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
521 constants.HV_DISK_CACHE:
522 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
523 constants.HV_SECURITY_MODEL:
524 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
525 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
526 constants.HV_KVM_FLAG:
527 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
528 constants.HV_VHOST_NET: hv_base.NO_CHECK,
529 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
530 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
531 constants.HV_REBOOT_BEHAVIOR:
532 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
533 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
534 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
535 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
536 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
537 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
540 _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
542 _MIGRATION_PROGRESS_RE = \
543 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
544 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
545 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
547 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
548 _MIGRATION_INFO_RETRY_DELAY = 2
550 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
552 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
553 _CPU_INFO_CMD = "info cpus"
556 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"(\S+).*\(default\)")
561 ANCILLARY_FILES_OPT = [
566 hv_base.BaseHypervisor.__init__(self)
567 # Let's make sure the directories we need exist, even if the RUN_DIR lives
568 # in a tmpfs filesystem or has been otherwise wiped out.
569 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
570 utils.EnsureDirs(dirs)
573 def _InstancePidFile(cls, instance_name):
574 """Returns the instance pidfile.
577 return utils.PathJoin(cls._PIDS_DIR, instance_name)
580 def _InstanceUidFile(cls, instance_name):
581 """Returns the instance uidfile.
584 return utils.PathJoin(cls._UIDS_DIR, instance_name)
587 def _InstancePidInfo(cls, pid):
588 """Check pid file for instance information.
590 Check that a pid file is associated with an instance, and retrieve
591 information from its command line.
593 @type pid: string or int
594 @param pid: process id of the instance to check
596 @return: (instance_name, memory, vcpus)
597 @raise errors.HypervisorError: when an instance cannot be found
600 alive = utils.IsProcessAlive(pid)
602 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
604 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
606 cmdline = utils.ReadFile(cmdline_file)
607 except EnvironmentError, err:
608 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
615 arg_list = cmdline.split("\x00")
617 arg = arg_list.pop(0)
619 instance = arg_list.pop(0)
621 memory = int(arg_list.pop(0))
623 vcpus = int(arg_list.pop(0).split(",")[0])
626 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
629 return (instance, memory, vcpus)
631 def _InstancePidAlive(self, instance_name):
632 """Returns the instance pidfile, pid, and liveness.
634 @type instance_name: string
635 @param instance_name: instance name
637 @return: (pid file name, pid, liveness)
640 pidfile = self._InstancePidFile(instance_name)
641 pid = utils.ReadPidFile(pidfile)
645 cmd_instance = self._InstancePidInfo(pid)[0]
646 alive = (cmd_instance == instance_name)
647 except errors.HypervisorError:
650 return (pidfile, pid, alive)
652 def _CheckDown(self, instance_name):
653 """Raises an error unless the given instance is down.
656 alive = self._InstancePidAlive(instance_name)[2]
658 raise errors.HypervisorError("Failed to start instance %s: %s" %
659 (instance_name, "already running"))
662 def _InstanceMonitor(cls, instance_name):
663 """Returns the instance monitor socket name
666 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
669 def _InstanceSerial(cls, instance_name):
670 """Returns the instance serial socket name
673 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
676 def _InstanceQmpMonitor(cls, instance_name):
677 """Returns the instance serial QMP socket name
680 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
683 def _SocatUnixConsoleParams():
684 """Returns the correct parameters for socat
686 If we have a new-enough socat we can use raw mode with an escape character.
689 if constants.SOCAT_USE_ESCAPE:
690 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
692 return "echo=0,icanon=0"
695 def _InstanceKVMRuntime(cls, instance_name):
696 """Returns the instance KVM runtime filename
699 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
702 def _InstanceChrootDir(cls, instance_name):
703 """Returns the name of the KVM chroot dir of the instance
706 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
709 def _InstanceNICDir(cls, instance_name):
710 """Returns the name of the directory holding the tap device files for a
714 return utils.PathJoin(cls._NICS_DIR, instance_name)
717 def _InstanceNICFile(cls, instance_name, seq):
718 """Returns the name of the file containing the tap device for a given NIC
721 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
724 def _InstanceKeymapFile(cls, instance_name):
725 """Returns the name of the file containing the keymap for a given instance
728 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
731 def _TryReadUidFile(cls, uid_file):
732 """Try to read a uid file
735 if os.path.exists(uid_file):
737 uid = int(utils.ReadOneLineFile(uid_file))
739 except EnvironmentError:
740 logging.warning("Can't read uid file", exc_info=True)
741 except (TypeError, ValueError):
742 logging.warning("Can't parse uid file contents", exc_info=True)
746 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
747 """Removes an instance's rutime sockets/files/dirs.
750 utils.RemoveFile(pidfile)
751 utils.RemoveFile(cls._InstanceMonitor(instance_name))
752 utils.RemoveFile(cls._InstanceSerial(instance_name))
753 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
754 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
755 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
756 uid_file = cls._InstanceUidFile(instance_name)
757 uid = cls._TryReadUidFile(uid_file)
758 utils.RemoveFile(uid_file)
760 uidpool.ReleaseUid(uid)
762 shutil.rmtree(cls._InstanceNICDir(instance_name))
764 if err.errno != errno.ENOENT:
767 chroot_dir = cls._InstanceChrootDir(instance_name)
768 utils.RemoveDir(chroot_dir)
770 if err.errno == errno.ENOTEMPTY:
771 # The chroot directory is expected to be empty, but it isn't.
772 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
775 utils.TimestampForFilename()))
776 logging.warning("The chroot directory of instance %s can not be"
777 " removed as it is not empty. Moving it to the"
778 " quarantine instead. Please investigate the"
779 " contents (%s) and clean up manually",
780 instance_name, new_chroot_dir)
781 utils.RenameFile(chroot_dir, new_chroot_dir)
786 def _ConfigureNIC(instance, seq, nic, tap):
787 """Run the network configuration script for a specified NIC
789 @param instance: instance we're acting on
790 @type instance: instance object
791 @param seq: nic sequence number
793 @param nic: nic we're acting on
794 @type nic: nic object
795 @param tap: the host's tap interface this NIC corresponds to
800 tags = " ".join(instance.tags)
805 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
806 "INSTANCE": instance.name,
808 "MODE": nic.nicparams[constants.NIC_MODE],
810 "INTERFACE_INDEX": str(seq),
817 if nic.nicparams[constants.NIC_LINK]:
818 env["LINK"] = nic.nicparams[constants.NIC_LINK]
821 n = objects.Network.FromDict(nic.netinfo)
822 _BuildNetworkEnv(nic.network, n.network, n.gateway,
823 n.network6, n.gateway6, n.network_type,
824 n.mac_prefix, n.tags, env)
826 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
827 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
829 result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
831 raise errors.HypervisorError("Failed to configure interface %s: %s."
832 " Network configuration script output: %s" %
833 (tap, result.fail_reason, result.output))
836 def _VerifyAffinityPackage():
838 raise errors.HypervisorError("affinity Python package not"
839 " found; cannot use CPU pinning under KVM")
842 def _BuildAffinityCpuMask(cpu_list):
843 """Create a CPU mask suitable for sched_setaffinity from a list of
846 See man taskset for more info on sched_setaffinity masks.
847 For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
849 @type cpu_list: list of int
850 @param cpu_list: list of physical CPU numbers to map to vCPUs in order
852 @return: a bit mask of CPU affinities
855 if cpu_list == constants.CPU_PINNING_OFF:
856 return constants.CPU_PINNING_ALL_KVM
858 return sum(2 ** cpu for cpu in cpu_list)
861 def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
862 """Change CPU affinity for running VM according to given CPU mask.
864 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
865 @type cpu_mask: string
866 @param process_id: process ID of KVM process. Used to pin entire VM
868 @type process_id: int
869 @param thread_dict: map of virtual CPUs to KVM thread IDs
870 @type thread_dict: dict int:int
873 # Convert the string CPU mask to a list of list of int's
874 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
876 if len(cpu_list) == 1:
877 all_cpu_mapping = cpu_list[0]
878 if all_cpu_mapping == constants.CPU_PINNING_OFF:
879 # If CPU pinning has 1 entry that's "all", then do nothing
882 # If CPU pinning has one non-all entry, map the entire VM to
883 # one set of physical CPUs
884 cls._VerifyAffinityPackage()
885 affinity.set_process_affinity_mask(
886 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
888 # The number of vCPUs mapped should match the number of vCPUs
889 # reported by KVM. This was already verified earlier, so
890 # here only as a sanity check.
891 assert len(thread_dict) == len(cpu_list)
892 cls._VerifyAffinityPackage()
894 # For each vCPU, map it to the proper list of physical CPUs
895 for vcpu, i in zip(cpu_list, range(len(cpu_list))):
896 affinity.set_process_affinity_mask(thread_dict[i],
897 cls._BuildAffinityCpuMask(vcpu))
899 def _GetVcpuThreadIds(self, instance_name):
900 """Get a mapping of vCPU no. to thread IDs for the instance
902 @type instance_name: string
903 @param instance_name: instance in question
904 @rtype: dictionary of int:int
905 @return: a dictionary mapping vCPU numbers to thread IDs
909 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
910 for line in output.stdout.splitlines():
911 match = self._CPU_INFO_RE.search(line)
914 grp = map(int, match.groups())
915 result[grp[0]] = grp[1]
919 def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
920 """Complete CPU pinning.
922 @type instance_name: string
923 @param instance_name: name of instance
924 @type cpu_mask: string
925 @param cpu_mask: CPU pinning mask as entered by user
928 # Get KVM process ID, to be used if need to pin entire VM
929 _, pid, _ = self._InstancePidAlive(instance_name)
930 # Get vCPU thread IDs, to be used if need to pin vCPUs separately
931 thread_dict = self._GetVcpuThreadIds(instance_name)
932 # Run CPU pinning, based on configured mask
933 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
935 def ListInstances(self):
936 """Get the list of running instances.
938 We can do this by listing our live instances directory and
939 checking whether the associated kvm process is still alive.
943 for name in os.listdir(self._PIDS_DIR):
944 if self._InstancePidAlive(name)[2]:
948 def GetInstanceInfo(self, instance_name):
949 """Get instance properties.
951 @type instance_name: string
952 @param instance_name: the instance name
953 @rtype: tuple of strings
954 @return: (name, id, memory, vcpus, stat, times)
957 _, pid, alive = self._InstancePidAlive(instance_name)
961 _, memory, vcpus = self._InstancePidInfo(pid)
966 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
968 vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
969 # Will fail if ballooning is not enabled, but we can then just resort to
971 mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
972 memory = mem_bytes / 1048576
973 except errors.HypervisorError:
976 return (instance_name, pid, memory, vcpus, istat, times)
978 def GetAllInstancesInfo(self):
979 """Get properties of all instances.
981 @return: list of tuples (name, id, memory, vcpus, stat, times)
985 for name in os.listdir(self._PIDS_DIR):
987 info = self.GetInstanceInfo(name)
988 except errors.HypervisorError:
989 # Ignore exceptions due to instances being shut down
995 def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
996 """Generate KVM information to start an instance.
998 @attention: this function must not have any side-effects; for
999 example, it must not write to the filesystem, or read values
1000 from the current system the are expected to differ between
1001 nodes, since it is only run once at instance startup;
1002 actions/kvm arguments that can vary between systems should be
1003 done in L{_ExecuteKVMRuntime}
1006 # pylint: disable=R0912,R0914,R0915
1007 _, v_major, v_min, _ = self._GetKVMVersion()
1008 hvp = instance.hvparams
1010 pidfile = self._InstancePidFile(instance.name)
1011 kvm = constants.KVM_PATH
1013 kvm_cmd.extend(["-M", self._GetDefaultMachineVersion()])
1014 # used just by the vnc server, if enabled
1015 kvm_cmd.extend(["-name", instance.name])
1016 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1018 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1019 if hvp[constants.HV_CPU_CORES]:
1020 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1021 if hvp[constants.HV_CPU_THREADS]:
1022 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1023 if hvp[constants.HV_CPU_SOCKETS]:
1024 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1026 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1028 kvm_cmd.extend(["-pidfile", pidfile])
1029 kvm_cmd.extend(["-balloon", "virtio"])
1030 kvm_cmd.extend(["-daemonize"])
1031 if not instance.hvparams[constants.HV_ACPI]:
1032 kvm_cmd.extend(["-no-acpi"])
1033 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1034 constants.INSTANCE_REBOOT_EXIT:
1035 kvm_cmd.extend(["-no-reboot"])
1037 kernel_path = hvp[constants.HV_KERNEL_PATH]
1039 boot_disk = boot_cdrom = boot_floppy = boot_network = False
1041 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1042 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1043 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1044 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1046 self.ValidateParameters(hvp)
1049 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1051 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
1052 kvm_cmd.extend(["-enable-kvm"])
1053 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
1054 kvm_cmd.extend(["-disable-kvm"])
1057 kvm_cmd.extend(["-boot", "n"])
1059 # whether this is an older KVM version that uses the boot=on flag
1061 needs_boot_flag = (v_major, v_min) < (0, 14)
1063 disk_type = hvp[constants.HV_DISK_TYPE]
1064 if disk_type == constants.HT_DISK_PARAVIRTUAL:
1065 if_val = ",if=virtio"
1067 if_val = ",if=%s" % disk_type
1069 disk_cache = hvp[constants.HV_DISK_CACHE]
1070 if instance.disk_template in constants.DTS_EXT_MIRROR:
1071 if disk_cache != "none":
1072 # TODO: make this a hard error, instead of a silent overwrite
1073 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1074 " to prevent shared storage corruption on migration",
1076 cache_val = ",cache=none"
1077 elif disk_cache != constants.HT_CACHE_DEFAULT:
1078 cache_val = ",cache=%s" % disk_cache
1081 for cfdev, dev_path in block_devices:
1082 if cfdev.mode != constants.DISK_RDWR:
1083 raise errors.HypervisorError("Instance has read-only disks which"
1084 " are not supported by KVM")
1085 # TODO: handle FD_LOOP and FD_BLKTAP (?)
1088 kvm_cmd.extend(["-boot", "c"])
1090 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1091 boot_val = ",boot=on"
1093 drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
1095 kvm_cmd.extend(["-drive", drive_val])
1097 #Now we can specify a different device type for CDROM devices.
1098 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1099 if not cdrom_disk_type:
1100 cdrom_disk_type = disk_type
1102 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1104 options = ",format=raw,media=cdrom"
1105 # set cdrom 'if' type
1107 actual_cdrom_type = constants.HT_DISK_IDE
1108 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1109 actual_cdrom_type = "virtio"
1111 actual_cdrom_type = cdrom_disk_type
1112 if_val = ",if=%s" % actual_cdrom_type
1113 # set boot flag, if needed
1116 kvm_cmd.extend(["-boot", "d"])
1118 boot_val = ",boot=on"
1119 # and finally build the entire '-drive' value
1120 drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1121 kvm_cmd.extend(["-drive", drive_val])
1123 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1125 options = ",format=raw,media=cdrom"
1126 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1127 if_val = ",if=virtio"
1129 if_val = ",if=%s" % cdrom_disk_type
1130 drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1131 kvm_cmd.extend(["-drive", drive_val])
1133 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1135 options = ",format=raw,media=disk"
1137 kvm_cmd.extend(["-boot", "a"])
1138 options = "%s,boot=on" % options
1139 if_val = ",if=floppy"
1140 options = "%s%s" % (options, if_val)
1141 drive_val = "file=%s%s" % (floppy_image, options)
1142 kvm_cmd.extend(["-drive", drive_val])
1145 kvm_cmd.extend(["-kernel", kernel_path])
1146 initrd_path = hvp[constants.HV_INITRD_PATH]
1148 kvm_cmd.extend(["-initrd", initrd_path])
1149 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1150 hvp[constants.HV_KERNEL_ARGS]]
1151 if hvp[constants.HV_SERIAL_CONSOLE]:
1152 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1153 root_append.append("console=ttyS0,%s" % serial_speed)
1154 kvm_cmd.extend(["-append", " ".join(root_append)])
1156 mem_path = hvp[constants.HV_MEM_PATH]
1158 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1160 monitor_dev = ("unix:%s,server,nowait" %
1161 self._InstanceMonitor(instance.name))
1162 kvm_cmd.extend(["-monitor", monitor_dev])
1163 if hvp[constants.HV_SERIAL_CONSOLE]:
1164 serial_dev = ("unix:%s,server,nowait" %
1165 self._InstanceSerial(instance.name))
1166 kvm_cmd.extend(["-serial", serial_dev])
1168 kvm_cmd.extend(["-serial", "none"])
1170 mouse_type = hvp[constants.HV_USB_MOUSE]
1171 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1172 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1173 spice_ip_version = None
1175 kvm_cmd.extend(["-usb"])
1178 kvm_cmd.extend(["-usbdevice", mouse_type])
1179 elif vnc_bind_address:
1180 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1182 if vnc_bind_address:
1183 if netutils.IP4Address.IsValid(vnc_bind_address):
1184 if instance.network_port > constants.VNC_BASE_PORT:
1185 display = instance.network_port - constants.VNC_BASE_PORT
1186 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1187 vnc_arg = ":%d" % (display)
1189 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1191 logging.error("Network port is not a valid VNC display (%d < %d)."
1192 " Not starting VNC", instance.network_port,
1193 constants.VNC_BASE_PORT)
1196 # Only allow tls and other option when not binding to a file, for now.
1197 # kvm/qemu gets confused otherwise about the filename to use.
1199 if hvp[constants.HV_VNC_TLS]:
1200 vnc_append = "%s,tls" % vnc_append
1201 if hvp[constants.HV_VNC_X509_VERIFY]:
1202 vnc_append = "%s,x509verify=%s" % (vnc_append,
1203 hvp[constants.HV_VNC_X509])
1204 elif hvp[constants.HV_VNC_X509]:
1205 vnc_append = "%s,x509=%s" % (vnc_append,
1206 hvp[constants.HV_VNC_X509])
1207 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1208 vnc_append = "%s,password" % vnc_append
1210 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1213 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1215 kvm_cmd.extend(["-vnc", vnc_arg])
1217 # FIXME: this is wrong here; the iface ip address differs
1218 # between systems, so it should be done in _ExecuteKVMRuntime
1219 if netutils.IsValidInterface(spice_bind):
1220 # The user specified a network interface, we have to figure out the IP
1222 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1223 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1225 # if the user specified an IP version and the interface does not
1226 # have that kind of IP addresses, throw an exception
1227 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1228 if not addresses[spice_ip_version]:
1229 raise errors.HypervisorError("spice: unable to get an IPv%s address"
1230 " for %s" % (spice_ip_version,
1233 # the user did not specify an IP version, we have to figure it out
1234 elif (addresses[constants.IP4_VERSION] and
1235 addresses[constants.IP6_VERSION]):
1236 # we have both ipv4 and ipv6, let's use the cluster default IP
1238 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1239 spice_ip_version = \
1240 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1241 elif addresses[constants.IP4_VERSION]:
1242 spice_ip_version = constants.IP4_VERSION
1243 elif addresses[constants.IP6_VERSION]:
1244 spice_ip_version = constants.IP6_VERSION
1246 raise errors.HypervisorError("spice: unable to get an IP address"
1247 " for %s" % (spice_bind))
1249 spice_address = addresses[spice_ip_version][0]
1252 # spice_bind is known to be a valid IP address, because
1253 # ValidateParameters checked it.
1254 spice_address = spice_bind
1256 spice_arg = "addr=%s" % spice_address
1257 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1258 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1259 (spice_arg, instance.network_port,
1260 pathutils.SPICE_CACERT_FILE))
1261 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1262 (spice_arg, pathutils.SPICE_CERT_FILE,
1263 pathutils.SPICE_CERT_FILE))
1264 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1266 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1268 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1270 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1271 spice_arg = "%s,disable-ticketing" % spice_arg
1273 if spice_ip_version:
1274 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1276 # Image compression options
1277 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1278 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1279 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1281 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1283 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1285 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1287 # Video stream detection
1288 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1290 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1292 # Audio compression, by default in qemu-kvm it is on
1293 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1294 spice_arg = "%s,playback-compression=off" % spice_arg
1295 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1296 spice_arg = "%s,agent-mouse=off" % spice_arg
1298 # Enable the spice agent communication channel between the host and the
1300 kvm_cmd.extend(["-device", "virtio-serial-pci"])
1301 kvm_cmd.extend(["-device", "virtserialport,chardev=spicechannel0,"
1302 "name=com.redhat.spice.0"])
1303 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1305 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1306 kvm_cmd.extend(["-spice", spice_arg])
1308 # Tell kvm to use the paravirtualized graphic card, optimized for SPICE
1309 kvm_cmd.extend(["-vga", "qxl"])
1312 kvm_cmd.extend(["-nographic"])
1314 if hvp[constants.HV_USE_LOCALTIME]:
1315 kvm_cmd.extend(["-localtime"])
1317 if hvp[constants.HV_KVM_USE_CHROOT]:
1318 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1320 # Add qemu-KVM -cpu param
1321 if hvp[constants.HV_CPU_TYPE]:
1322 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1324 # Save the current instance nics, but defer their expansion as parameters,
1325 # as we'll need to generate executable temp files for them.
1326 kvm_nics = instance.nics
1329 return (kvm_cmd, kvm_nics, hvparams)
1331 def _WriteKVMRuntime(self, instance_name, data):
1332 """Write an instance's KVM runtime
1336 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1338 except EnvironmentError, err:
1339 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1341 def _ReadKVMRuntime(self, instance_name):
1342 """Read an instance's KVM runtime
1346 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1347 except EnvironmentError, err:
1348 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1351 def _SaveKVMRuntime(self, instance, kvm_runtime):
1352 """Save an instance's KVM runtime
1355 kvm_cmd, kvm_nics, hvparams = kvm_runtime
1356 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1357 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1358 self._WriteKVMRuntime(instance.name, serialized_form)
1360 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1361 """Load an instance's KVM runtime
1364 if not serialized_runtime:
1365 serialized_runtime = self._ReadKVMRuntime(instance.name)
1366 loaded_runtime = serializer.Load(serialized_runtime)
1367 kvm_cmd, serialized_nics, hvparams = loaded_runtime
1368 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1369 return (kvm_cmd, kvm_nics, hvparams)
1371 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1372 """Run the KVM cmd and check for errors
1375 @param name: instance name
1376 @type kvm_cmd: list of strings
1377 @param kvm_cmd: runcmd input for kvm
1378 @type tap_fds: list of int
1379 @param tap_fds: fds of tap devices opened by Ganeti
1383 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1386 utils_wrapper.CloseFdNoError(fd)
1389 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1390 (name, result.fail_reason, result.output))
1391 if not self._InstancePidAlive(name)[2]:
1392 raise errors.HypervisorError("Failed to start instance %s" % name)
1394 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
1395 """Execute a KVM cmd, after completing it with some last minute data.
1397 @type incoming: tuple of strings
1398 @param incoming: (target_host_ip, port)
1401 # Small _ExecuteKVMRuntime hv parameters programming howto:
1402 # - conf_hvp contains the parameters as configured on ganeti. they might
1403 # have changed since the instance started; only use them if the change
1404 # won't affect the inside of the instance (which hasn't been rebooted).
1405 # - up_hvp contains the parameters as they were when the instance was
1406 # started, plus any new parameter which has been added between ganeti
1407 # versions: it is paramount that those default to a value which won't
1408 # affect the inside of the instance as well.
1409 conf_hvp = instance.hvparams
1410 name = instance.name
1411 self._CheckDown(name)
1415 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1416 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1418 _, v_major, v_min, _ = self._GetKVMVersion()
1420 # We know it's safe to run as a different user upon migration, so we'll use
1421 # the latest conf, from conf_hvp.
1422 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1423 if security_model == constants.HT_SM_USER:
1424 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1426 keymap = conf_hvp[constants.HV_KEYMAP]
1428 keymap_path = self._InstanceKeymapFile(name)
1429 # If a keymap file is specified, KVM won't use its internal defaults. By
1430 # first including the "en-us" layout, an error on loading the actual
1431 # layout (e.g. because it can't be found) won't lead to a non-functional
1432 # keyboard. A keyboard with incorrect keys is still better than none.
1433 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1434 kvm_cmd.extend(["-k", keymap_path])
1436 # We have reasons to believe changing something like the nic driver/type
1437 # upon migration won't exactly fly with the instance kernel, so for nic
1438 # related parameters we'll use up_hvp
1442 kvm_cmd.extend(["-net", "none"])
1446 nic_type = up_hvp[constants.HV_NIC_TYPE]
1447 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1448 # From version 0.12.0, kvm uses a new sintax for network configuration.
1449 if (v_major, v_min) >= (0, 12):
1450 nic_model = "virtio-net-pci"
1453 nic_model = "virtio"
1455 if up_hvp[constants.HV_VHOST_NET]:
1456 # vhost_net is only available from version 0.13.0 or newer
1457 if (v_major, v_min) >= (0, 13):
1458 tap_extra = ",vhost=on"
1460 raise errors.HypervisorError("vhost_net is configured"
1461 " but it is not available")
1463 nic_model = nic_type
1465 for nic_seq, nic in enumerate(kvm_nics):
1466 tapname, tapfd = _OpenTap(vnet_hdr)
1467 tapfds.append(tapfd)
1468 taps.append(tapname)
1469 if (v_major, v_min) >= (0, 12):
1470 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
1471 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
1472 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1474 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1476 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1477 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1480 target, port = incoming
1481 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1483 # Changing the vnc password doesn't bother the guest that much. At most it
1484 # will surprise people who connect to it. Whether positively or negatively
1486 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1490 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1491 except EnvironmentError, err:
1492 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1493 % (vnc_pwd_file, err))
1495 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1496 utils.EnsureDirs([(self._InstanceChrootDir(name),
1497 constants.SECURE_DIR_MODE)])
1499 # Automatically enable QMP if version is >= 0.14
1500 if (v_major, v_min) >= (0, 14):
1501 logging.debug("Enabling QMP")
1502 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1503 self._InstanceQmpMonitor(instance.name)])
1505 # Configure the network now for starting instances and bridged interfaces,
1506 # during FinalizeMigration for incoming instances' routed interfaces
1507 for nic_seq, nic in enumerate(kvm_nics):
1509 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1511 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1513 # CPU affinity requires kvm to start paused, so we set this flag if the
1514 # instance is not already paused and if we are not going to accept a
1515 # migrating instance. In the latter case, pausing is not needed.
1516 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1517 if start_kvm_paused:
1518 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1520 # Note: CPU pinning is using up_hvp since changes take effect
1521 # during instance startup anyway, and to avoid problems when soft
1522 # rebooting the instance.
1524 if up_hvp.get(constants.HV_CPU_MASK, None):
1527 if security_model == constants.HT_SM_POOL:
1528 ss = ssconf.SimpleStore()
1529 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1530 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1531 uid = uidpool.RequestUnusedUid(all_uids)
1533 username = pwd.getpwuid(uid.GetUid()).pw_name
1534 kvm_cmd.extend(["-runas", username])
1535 self._RunKVMCmd(name, kvm_cmd, tapfds)
1537 uidpool.ReleaseUid(uid)
1541 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1543 self._RunKVMCmd(name, kvm_cmd, tapfds)
1545 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1546 constants.RUN_DIRS_MODE)])
1547 for nic_seq, tap in enumerate(taps):
1548 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1552 change_cmd = "change vnc password %s" % vnc_pwd
1553 self._CallMonitorCommand(instance.name, change_cmd)
1555 # Setting SPICE password. We are not vulnerable to malicious passwordless
1556 # connection attempts because SPICE by default does not allow connections
1557 # if neither a password nor the "disable_ticketing" options are specified.
1558 # As soon as we send the password via QMP, that password is a valid ticket
1560 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1561 if spice_password_file:
1564 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1565 except EnvironmentError, err:
1566 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1567 % (spice_password_file, err))
1569 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1572 "protocol": "spice",
1573 "password": spice_pwd,
1575 qmp.Execute("set_password", arguments)
1577 for filename in temp_files:
1578 utils.RemoveFile(filename)
1580 # If requested, set CPU affinity and resume instance execution
1582 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1584 start_memory = self._InstanceStartupMemory(instance)
1585 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1586 self.BalloonInstanceMemory(instance, start_memory)
1588 if start_kvm_paused:
1589 # To control CPU pinning, ballooning, and vnc/spice passwords
1590 # the VM was started in a frozen state. If freezing was not
1591 # explicitly requested resume the vm status.
1592 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1594 def StartInstance(self, instance, block_devices, startup_paused):
1595 """Start an instance.
1598 self._CheckDown(instance.name)
1599 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1601 self._SaveKVMRuntime(instance, kvm_runtime)
1602 self._ExecuteKVMRuntime(instance, kvm_runtime)
1604 def _CallMonitorCommand(self, instance_name, command):
1605 """Invoke a command on the instance monitor.
1608 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1609 (utils.ShellQuote(command),
1610 constants.SOCAT_PATH,
1611 utils.ShellQuote(self._InstanceMonitor(instance_name))))
1612 result = utils.RunCmd(socat)
1614 msg = ("Failed to send command '%s' to instance %s."
1615 " output: %s, error: %s, fail_reason: %s" %
1616 (command, instance_name,
1617 result.stdout, result.stderr, result.fail_reason))
1618 raise errors.HypervisorError(msg)
1623 def _ParseKVMVersion(cls, text):
1624 """Parse the KVM version from the --help output.
1627 @param text: output of kvm --help
1628 @return: (version, v_maj, v_min, v_rev)
1629 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1632 match = cls._VERSION_RE.search(text.splitlines()[0])
1634 raise errors.HypervisorError("Unable to get KVM version")
1636 v_all = match.group(0)
1637 v_maj = int(match.group(1))
1638 v_min = int(match.group(2))
1640 v_rev = int(match.group(4))
1643 return (v_all, v_maj, v_min, v_rev)
1646 def _GetKVMHelpOutput(cls):
1647 """Return the KVM help output.
1649 @return: output of kvm --help
1650 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
1653 result = utils.RunCmd([constants.KVM_PATH, "--help"])
1655 raise errors.HypervisorError("Unable to get KVM help output")
1656 return result.output
1659 def _GetKVMVersion(cls):
1660 """Return the installed KVM version.
1662 @return: (version, v_maj, v_min, v_rev)
1663 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1666 return cls._ParseKVMVersion(cls._GetKVMHelpOutput())
1668 def StopInstance(self, instance, force=False, retry=False, name=None):
1669 """Stop an instance.
1672 if name is not None and not force:
1673 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1675 name = instance.name
1676 acpi = instance.hvparams[constants.HV_ACPI]
1679 _, pid, alive = self._InstancePidAlive(name)
1680 if pid > 0 and alive:
1681 if force or not acpi:
1682 utils.KillProcess(pid)
1684 self._CallMonitorCommand(name, "system_powerdown")
1687 def _GetDefaultMachineVersion(cls):
1688 """Return the default hardware revision (e.g. pc-1.1)
1691 result = utils.RunCmd([constants.KVM_PATH, "-M", "?"])
1693 raise errors.HypervisorError("Unable to get default hardware revision")
1694 for line in result.output.splitlines():
1695 match = cls._DEFAULT_MACHINE_VERSION_RE.match(line)
1697 return match.group(1)
1701 def CleanupInstance(self, instance_name):
1702 """Cleanup after a stopped instance
1705 pidfile, pid, alive = self._InstancePidAlive(instance_name)
1706 if pid > 0 and alive:
1707 raise errors.HypervisorError("Cannot cleanup a live instance")
1708 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1710 def RebootInstance(self, instance):
1711 """Reboot an instance.
1714 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1715 # socket the instance will stop, but now power up again. So we'll resort
1716 # to shutdown and restart.
1717 _, _, alive = self._InstancePidAlive(instance.name)
1719 raise errors.HypervisorError("Failed to reboot instance %s:"
1720 " not running" % instance.name)
1721 # StopInstance will delete the saved KVM runtime so:
1722 # ...first load it...
1723 kvm_runtime = self._LoadKVMRuntime(instance)
1724 # ...now we can safely call StopInstance...
1725 if not self.StopInstance(instance):
1726 self.StopInstance(instance, force=True)
1727 # ...and finally we can save it again, and execute it...
1728 self._SaveKVMRuntime(instance, kvm_runtime)
1729 self._ExecuteKVMRuntime(instance, kvm_runtime)
1731 def MigrationInfo(self, instance):
1732 """Get instance information to perform a migration.
1734 @type instance: L{objects.Instance}
1735 @param instance: instance to be migrated
1737 @return: content of the KVM runtime file
1740 return self._ReadKVMRuntime(instance.name)
1742 def AcceptInstance(self, instance, info, target):
1743 """Prepare to accept an instance.
1745 @type instance: L{objects.Instance}
1746 @param instance: instance to be accepted
1748 @param info: content of the KVM runtime file on the source node
1749 @type target: string
1750 @param target: target host (usually ip), on this node
1753 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1754 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1755 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1757 def FinalizeMigrationDst(self, instance, info, success):
1758 """Finalize the instance migration on the target node.
1760 Stop the incoming mode KVM.
1762 @type instance: L{objects.Instance}
1763 @param instance: instance whose migration is being finalized
1767 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1768 kvm_nics = kvm_runtime[1]
1770 for nic_seq, nic in enumerate(kvm_nics):
1771 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1772 # Bridged interfaces have already been configured
1775 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1776 except EnvironmentError, err:
1777 logging.warning("Failed to find host interface for %s NIC #%d: %s",
1778 instance.name, nic_seq, str(err))
1781 self._ConfigureNIC(instance, nic_seq, nic, tap)
1782 except errors.HypervisorError, err:
1783 logging.warning(str(err))
1785 self._WriteKVMRuntime(instance.name, info)
1787 self.StopInstance(instance, force=True)
1789 def MigrateInstance(self, instance, target, live):
1790 """Migrate an instance to a target node.
1792 The migration will not be attempted if the instance is not
1795 @type instance: L{objects.Instance}
1796 @param instance: the instance to be migrated
1797 @type target: string
1798 @param target: ip address of the target node
1800 @param live: perform a live migration
1803 instance_name = instance.name
1804 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1805 _, _, alive = self._InstancePidAlive(instance_name)
1807 raise errors.HypervisorError("Instance not running, cannot migrate")
1810 self._CallMonitorCommand(instance_name, "stop")
1812 migrate_command = ("migrate_set_speed %dm" %
1813 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1814 self._CallMonitorCommand(instance_name, migrate_command)
1816 migrate_command = ("migrate_set_downtime %dms" %
1817 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1818 self._CallMonitorCommand(instance_name, migrate_command)
1820 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1821 self._CallMonitorCommand(instance_name, migrate_command)
1823 def FinalizeMigrationSource(self, instance, success, live):
1824 """Finalize the instance migration on the source node.
1826 @type instance: L{objects.Instance}
1827 @param instance: the instance that was migrated
1829 @param success: whether the migration succeeded or not
1831 @param live: whether the user requested a live migration or not
1835 pidfile, pid, _ = self._InstancePidAlive(instance.name)
1836 utils.KillProcess(pid)
1837 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
1839 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1841 def GetMigrationStatus(self, instance):
1842 """Get the migration status
1844 @type instance: L{objects.Instance}
1845 @param instance: the instance that is being migrated
1846 @rtype: L{objects.MigrationStatus}
1847 @return: the status of the current migration (one of
1848 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1849 progress info that can be retrieved from the hypervisor
1852 info_command = "info migrate"
1853 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
1854 result = self._CallMonitorCommand(instance.name, info_command)
1855 match = self._MIGRATION_STATUS_RE.search(result.stdout)
1857 if not result.stdout:
1858 logging.info("KVM: empty 'info migrate' result")
1860 logging.warning("KVM: unknown 'info migrate' result: %s",
1863 status = match.group(1)
1864 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
1865 migration_status = objects.MigrationStatus(status=status)
1866 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
1868 migration_status.transferred_ram = match.group("transferred")
1869 migration_status.total_ram = match.group("total")
1871 return migration_status
1873 logging.warning("KVM: unknown migration status '%s'", status)
1875 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1877 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
1879 def BalloonInstanceMemory(self, instance, mem):
1880 """Balloon an instance memory to a certain value.
1882 @type instance: L{objects.Instance}
1883 @param instance: instance to be accepted
1885 @param mem: actual memory size to use for instance runtime
1888 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
1890 def GetNodeInfo(self):
1891 """Return information about the node.
1893 @return: a dict with the following keys (values in MiB):
1894 - memory_total: the total memory size on the node
1895 - memory_free: the available memory on the node for instances
1896 - memory_dom0: the memory used by the node itself, if available
1897 - hv_version: the hypervisor version in the form (major, minor,
1901 result = self.GetLinuxNodeInfo()
1902 _, v_major, v_min, v_rev = self._GetKVMVersion()
1903 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
1907 def GetInstanceConsole(cls, instance, hvparams, beparams):
1908 """Return a command for connecting to the console of an instance.
1911 if hvparams[constants.HV_SERIAL_CONSOLE]:
1912 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
1913 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1914 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1915 "STDIO,%s" % cls._SocatUnixConsoleParams(),
1916 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1917 return objects.InstanceConsole(instance=instance.name,
1918 kind=constants.CONS_SSH,
1919 host=instance.primary_node,
1920 user=constants.SSH_CONSOLE_USER,
1923 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1924 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1925 display = instance.network_port - constants.VNC_BASE_PORT
1926 return objects.InstanceConsole(instance=instance.name,
1927 kind=constants.CONS_VNC,
1928 host=vnc_bind_address,
1929 port=instance.network_port,
1932 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1934 return objects.InstanceConsole(instance=instance.name,
1935 kind=constants.CONS_SPICE,
1937 port=instance.network_port)
1939 return objects.InstanceConsole(instance=instance.name,
1940 kind=constants.CONS_MESSAGE,
1941 message=("No serial shell for instance %s" %
1945 """Verify the hypervisor.
1947 Check that the binary exists.
1950 if not os.path.exists(constants.KVM_PATH):
1951 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1952 if not os.path.exists(constants.SOCAT_PATH):
1953 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1956 def CheckParameterSyntax(cls, hvparams):
1957 """Check the given parameters for validity.
1959 @type hvparams: dict
1960 @param hvparams: dictionary with parameter names/value
1961 @raise errors.HypervisorError: when a parameter is not valid
1964 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1966 kernel_path = hvparams[constants.HV_KERNEL_PATH]
1968 if not hvparams[constants.HV_ROOT_PATH]:
1969 raise errors.HypervisorError("Need a root partition for the instance,"
1970 " if a kernel is defined")
1972 if (hvparams[constants.HV_VNC_X509_VERIFY] and
1973 not hvparams[constants.HV_VNC_X509]):
1974 raise errors.HypervisorError("%s must be defined, if %s is" %
1975 (constants.HV_VNC_X509,
1976 constants.HV_VNC_X509_VERIFY))
1978 if hvparams[constants.HV_SERIAL_CONSOLE]:
1979 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
1980 valid_speeds = constants.VALID_SERIAL_SPEEDS
1981 if not serial_speed or serial_speed not in valid_speeds:
1982 raise errors.HypervisorError("Invalid serial console speed, must be"
1984 utils.CommaJoin(valid_speeds))
1986 boot_order = hvparams[constants.HV_BOOT_ORDER]
1987 if (boot_order == constants.HT_BO_CDROM and
1988 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1989 raise errors.HypervisorError("Cannot boot from cdrom without an"
1992 security_model = hvparams[constants.HV_SECURITY_MODEL]
1993 if security_model == constants.HT_SM_USER:
1994 if not hvparams[constants.HV_SECURITY_DOMAIN]:
1995 raise errors.HypervisorError("A security domain (user to run kvm as)"
1996 " must be specified")
1997 elif (security_model == constants.HT_SM_NONE or
1998 security_model == constants.HT_SM_POOL):
1999 if hvparams[constants.HV_SECURITY_DOMAIN]:
2000 raise errors.HypervisorError("Cannot have a security domain when the"
2001 " security model is 'none' or 'pool'")
2003 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2004 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2006 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2007 # if an IP version is specified, the spice_bind parameter must be an
2009 if (netutils.IP4Address.IsValid(spice_bind) and
2010 spice_ip_version != constants.IP4_VERSION):
2011 raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
2012 " the specified IP version is %s" %
2013 (spice_bind, spice_ip_version))
2015 if (netutils.IP6Address.IsValid(spice_bind) and
2016 spice_ip_version != constants.IP6_VERSION):
2017 raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
2018 " the specified IP version is %s" %
2019 (spice_bind, spice_ip_version))
2021 # All the other SPICE parameters depend on spice_bind being set. Raise an
2022 # error if any of them is set without it.
2023 for param in _SPICE_ADDITIONAL_PARAMS:
2025 raise errors.HypervisorError("spice: %s requires %s to be set" %
2026 (param, constants.HV_KVM_SPICE_BIND))
2029 def ValidateParameters(cls, hvparams):
2030 """Check the given parameters for validity.
2032 @type hvparams: dict
2033 @param hvparams: dictionary with parameter names/value
2034 @raise errors.HypervisorError: when a parameter is not valid
2037 super(KVMHypervisor, cls).ValidateParameters(hvparams)
2039 security_model = hvparams[constants.HV_SECURITY_MODEL]
2040 if security_model == constants.HT_SM_USER:
2041 username = hvparams[constants.HV_SECURITY_DOMAIN]
2043 pwd.getpwnam(username)
2045 raise errors.HypervisorError("Unknown security domain user %s"
2048 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2050 # only one of VNC and SPICE can be used currently.
2051 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2052 raise errors.HypervisorError("both SPICE and VNC are configured, but"
2053 " only one of them can be used at a"
2056 # KVM version should be >= 0.14.0
2057 _, v_major, v_min, _ = cls._GetKVMVersion()
2058 if (v_major, v_min) < (0, 14):
2059 raise errors.HypervisorError("spice is configured, but it is not"
2060 " available in versions of KVM < 0.14")
2062 # if spice_bind is not an IP address, it must be a valid interface
2063 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
2064 or netutils.IP6Address.IsValid(spice_bind))
2065 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2066 raise errors.HypervisorError("spice: the %s parameter must be either"
2067 " a valid IP address or interface name" %
2068 constants.HV_KVM_SPICE_BIND)
2071 def PowercycleNode(cls):
2072 """KVM powercycle, just a wrapper over Linux powercycle.
2075 cls.LinuxPowercycle()