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_VNC_BIND_ADDRESS:
470 (False, lambda x: (netutils.IP4Address.IsValid(x) or
471 utils.IsNormAbsPath(x)),
472 "the VNC bind address must be either a valid IP address or an absolute"
473 " pathname", None, None),
474 constants.HV_VNC_TLS: hv_base.NO_CHECK,
475 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
476 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
477 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
478 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
479 constants.HV_KVM_SPICE_IP_VERSION:
480 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
481 x in constants.VALID_IP_VERSIONS),
482 "the SPICE IP version should be 4 or 6",
484 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
485 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
487 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
488 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
490 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
491 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
493 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
494 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
496 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
497 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
498 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
499 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
500 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
501 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
502 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
503 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
504 constants.HV_BOOT_ORDER:
505 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
506 constants.HV_NIC_TYPE:
507 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
508 constants.HV_DISK_TYPE:
509 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
510 constants.HV_KVM_CDROM_DISK_TYPE:
511 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
512 constants.HV_USB_MOUSE:
513 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
514 constants.HV_KEYMAP: hv_base.NO_CHECK,
515 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
516 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
517 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
518 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
519 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
520 constants.HV_DISK_CACHE:
521 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
522 constants.HV_SECURITY_MODEL:
523 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
524 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
525 constants.HV_KVM_FLAG:
526 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
527 constants.HV_VHOST_NET: hv_base.NO_CHECK,
528 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
529 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
530 constants.HV_REBOOT_BEHAVIOR:
531 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
532 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
533 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
536 _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
538 _MIGRATION_PROGRESS_RE = \
539 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
540 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
541 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
543 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
544 _MIGRATION_INFO_RETRY_DELAY = 2
546 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
548 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
549 _CPU_INFO_CMD = "info cpus"
552 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"(\S+).*\(default\)")
557 ANCILLARY_FILES_OPT = [
562 hv_base.BaseHypervisor.__init__(self)
563 # Let's make sure the directories we need exist, even if the RUN_DIR lives
564 # in a tmpfs filesystem or has been otherwise wiped out.
565 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
566 utils.EnsureDirs(dirs)
569 def _InstancePidFile(cls, instance_name):
570 """Returns the instance pidfile.
573 return utils.PathJoin(cls._PIDS_DIR, instance_name)
576 def _InstanceUidFile(cls, instance_name):
577 """Returns the instance uidfile.
580 return utils.PathJoin(cls._UIDS_DIR, instance_name)
583 def _InstancePidInfo(cls, pid):
584 """Check pid file for instance information.
586 Check that a pid file is associated with an instance, and retrieve
587 information from its command line.
589 @type pid: string or int
590 @param pid: process id of the instance to check
592 @return: (instance_name, memory, vcpus)
593 @raise errors.HypervisorError: when an instance cannot be found
596 alive = utils.IsProcessAlive(pid)
598 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
600 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
602 cmdline = utils.ReadFile(cmdline_file)
603 except EnvironmentError, err:
604 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
611 arg_list = cmdline.split("\x00")
613 arg = arg_list.pop(0)
615 instance = arg_list.pop(0)
617 memory = int(arg_list.pop(0))
619 vcpus = int(arg_list.pop(0))
622 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
625 return (instance, memory, vcpus)
627 def _InstancePidAlive(self, instance_name):
628 """Returns the instance pidfile, pid, and liveness.
630 @type instance_name: string
631 @param instance_name: instance name
633 @return: (pid file name, pid, liveness)
636 pidfile = self._InstancePidFile(instance_name)
637 pid = utils.ReadPidFile(pidfile)
641 cmd_instance = self._InstancePidInfo(pid)[0]
642 alive = (cmd_instance == instance_name)
643 except errors.HypervisorError:
646 return (pidfile, pid, alive)
648 def _CheckDown(self, instance_name):
649 """Raises an error unless the given instance is down.
652 alive = self._InstancePidAlive(instance_name)[2]
654 raise errors.HypervisorError("Failed to start instance %s: %s" %
655 (instance_name, "already running"))
658 def _InstanceMonitor(cls, instance_name):
659 """Returns the instance monitor socket name
662 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
665 def _InstanceSerial(cls, instance_name):
666 """Returns the instance serial socket name
669 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
672 def _InstanceQmpMonitor(cls, instance_name):
673 """Returns the instance serial QMP socket name
676 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
679 def _SocatUnixConsoleParams():
680 """Returns the correct parameters for socat
682 If we have a new-enough socat we can use raw mode with an escape character.
685 if constants.SOCAT_USE_ESCAPE:
686 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
688 return "echo=0,icanon=0"
691 def _InstanceKVMRuntime(cls, instance_name):
692 """Returns the instance KVM runtime filename
695 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
698 def _InstanceChrootDir(cls, instance_name):
699 """Returns the name of the KVM chroot dir of the instance
702 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
705 def _InstanceNICDir(cls, instance_name):
706 """Returns the name of the directory holding the tap device files for a
710 return utils.PathJoin(cls._NICS_DIR, instance_name)
713 def _InstanceNICFile(cls, instance_name, seq):
714 """Returns the name of the file containing the tap device for a given NIC
717 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
720 def _InstanceKeymapFile(cls, instance_name):
721 """Returns the name of the file containing the keymap for a given instance
724 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
727 def _TryReadUidFile(cls, uid_file):
728 """Try to read a uid file
731 if os.path.exists(uid_file):
733 uid = int(utils.ReadOneLineFile(uid_file))
735 except EnvironmentError:
736 logging.warning("Can't read uid file", exc_info=True)
737 except (TypeError, ValueError):
738 logging.warning("Can't parse uid file contents", exc_info=True)
742 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
743 """Removes an instance's rutime sockets/files/dirs.
746 utils.RemoveFile(pidfile)
747 utils.RemoveFile(cls._InstanceMonitor(instance_name))
748 utils.RemoveFile(cls._InstanceSerial(instance_name))
749 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
750 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
751 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
752 uid_file = cls._InstanceUidFile(instance_name)
753 uid = cls._TryReadUidFile(uid_file)
754 utils.RemoveFile(uid_file)
756 uidpool.ReleaseUid(uid)
758 shutil.rmtree(cls._InstanceNICDir(instance_name))
760 if err.errno != errno.ENOENT:
763 chroot_dir = cls._InstanceChrootDir(instance_name)
764 utils.RemoveDir(chroot_dir)
766 if err.errno == errno.ENOTEMPTY:
767 # The chroot directory is expected to be empty, but it isn't.
768 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
771 utils.TimestampForFilename()))
772 logging.warning("The chroot directory of instance %s can not be"
773 " removed as it is not empty. Moving it to the"
774 " quarantine instead. Please investigate the"
775 " contents (%s) and clean up manually",
776 instance_name, new_chroot_dir)
777 utils.RenameFile(chroot_dir, new_chroot_dir)
782 def _ConfigureNIC(instance, seq, nic, tap):
783 """Run the network configuration script for a specified NIC
785 @param instance: instance we're acting on
786 @type instance: instance object
787 @param seq: nic sequence number
789 @param nic: nic we're acting on
790 @type nic: nic object
791 @param tap: the host's tap interface this NIC corresponds to
796 tags = " ".join(instance.tags)
801 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
802 "INSTANCE": instance.name,
804 "MODE": nic.nicparams[constants.NIC_MODE],
806 "INTERFACE_INDEX": str(seq),
813 if nic.nicparams[constants.NIC_LINK]:
814 env["LINK"] = nic.nicparams[constants.NIC_LINK]
817 n = objects.Network.FromDict(nic.netinfo)
818 _BuildNetworkEnv(nic.network, n.network, n.gateway,
819 n.network6, n.gateway6, n.network_type,
820 n.mac_prefix, n.tags, env)
822 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
823 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
825 result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
827 raise errors.HypervisorError("Failed to configure interface %s: %s."
828 " Network configuration script output: %s" %
829 (tap, result.fail_reason, result.output))
832 def _VerifyAffinityPackage():
834 raise errors.HypervisorError("affinity Python package not"
835 " found; cannot use CPU pinning under KVM")
838 def _BuildAffinityCpuMask(cpu_list):
839 """Create a CPU mask suitable for sched_setaffinity from a list of
842 See man taskset for more info on sched_setaffinity masks.
843 For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
845 @type cpu_list: list of int
846 @param cpu_list: list of physical CPU numbers to map to vCPUs in order
848 @return: a bit mask of CPU affinities
851 if cpu_list == constants.CPU_PINNING_OFF:
852 return constants.CPU_PINNING_ALL_KVM
854 return sum(2 ** cpu for cpu in cpu_list)
857 def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
858 """Change CPU affinity for running VM according to given CPU mask.
860 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
861 @type cpu_mask: string
862 @param process_id: process ID of KVM process. Used to pin entire VM
864 @type process_id: int
865 @param thread_dict: map of virtual CPUs to KVM thread IDs
866 @type thread_dict: dict int:int
869 # Convert the string CPU mask to a list of list of int's
870 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
872 if len(cpu_list) == 1:
873 all_cpu_mapping = cpu_list[0]
874 if all_cpu_mapping == constants.CPU_PINNING_OFF:
875 # If CPU pinning has 1 entry that's "all", then do nothing
878 # If CPU pinning has one non-all entry, map the entire VM to
879 # one set of physical CPUs
880 cls._VerifyAffinityPackage()
881 affinity.set_process_affinity_mask(
882 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
884 # The number of vCPUs mapped should match the number of vCPUs
885 # reported by KVM. This was already verified earlier, so
886 # here only as a sanity check.
887 assert len(thread_dict) == len(cpu_list)
888 cls._VerifyAffinityPackage()
890 # For each vCPU, map it to the proper list of physical CPUs
891 for vcpu, i in zip(cpu_list, range(len(cpu_list))):
892 affinity.set_process_affinity_mask(thread_dict[i],
893 cls._BuildAffinityCpuMask(vcpu))
895 def _GetVcpuThreadIds(self, instance_name):
896 """Get a mapping of vCPU no. to thread IDs for the instance
898 @type instance_name: string
899 @param instance_name: instance in question
900 @rtype: dictionary of int:int
901 @return: a dictionary mapping vCPU numbers to thread IDs
905 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
906 for line in output.stdout.splitlines():
907 match = self._CPU_INFO_RE.search(line)
910 grp = map(int, match.groups())
911 result[grp[0]] = grp[1]
915 def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
916 """Complete CPU pinning.
918 @type instance_name: string
919 @param instance_name: name of instance
920 @type cpu_mask: string
921 @param cpu_mask: CPU pinning mask as entered by user
924 # Get KVM process ID, to be used if need to pin entire VM
925 _, pid, _ = self._InstancePidAlive(instance_name)
926 # Get vCPU thread IDs, to be used if need to pin vCPUs separately
927 thread_dict = self._GetVcpuThreadIds(instance_name)
928 # Run CPU pinning, based on configured mask
929 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
931 def ListInstances(self):
932 """Get the list of running instances.
934 We can do this by listing our live instances directory and
935 checking whether the associated kvm process is still alive.
939 for name in os.listdir(self._PIDS_DIR):
940 if self._InstancePidAlive(name)[2]:
944 def GetInstanceInfo(self, instance_name):
945 """Get instance properties.
947 @type instance_name: string
948 @param instance_name: the instance name
949 @rtype: tuple of strings
950 @return: (name, id, memory, vcpus, stat, times)
953 _, pid, alive = self._InstancePidAlive(instance_name)
957 _, memory, vcpus = self._InstancePidInfo(pid)
962 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
964 vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
965 # Will fail if ballooning is not enabled, but we can then just resort to
967 mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
968 memory = mem_bytes / 1048576
969 except errors.HypervisorError:
972 return (instance_name, pid, memory, vcpus, istat, times)
974 def GetAllInstancesInfo(self):
975 """Get properties of all instances.
977 @return: list of tuples (name, id, memory, vcpus, stat, times)
981 for name in os.listdir(self._PIDS_DIR):
983 info = self.GetInstanceInfo(name)
984 except errors.HypervisorError:
985 # Ignore exceptions due to instances being shut down
991 def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
992 """Generate KVM information to start an instance.
994 @attention: this function must not have any side-effects; for
995 example, it must not write to the filesystem, or read values
996 from the current system the are expected to differ between
997 nodes, since it is only run once at instance startup;
998 actions/kvm arguments that can vary between systems should be
999 done in L{_ExecuteKVMRuntime}
1002 # pylint: disable=R0914,R0915
1003 _, v_major, v_min, _ = self._GetKVMVersion()
1005 pidfile = self._InstancePidFile(instance.name)
1006 kvm = constants.KVM_PATH
1008 kvm_cmd.extend(["-M", self._GetDefaultMachineVersion()])
1009 # used just by the vnc server, if enabled
1010 kvm_cmd.extend(["-name", instance.name])
1011 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1012 kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
1013 kvm_cmd.extend(["-pidfile", pidfile])
1014 kvm_cmd.extend(["-balloon", "virtio"])
1015 kvm_cmd.extend(["-daemonize"])
1016 if not instance.hvparams[constants.HV_ACPI]:
1017 kvm_cmd.extend(["-no-acpi"])
1018 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1019 constants.INSTANCE_REBOOT_EXIT:
1020 kvm_cmd.extend(["-no-reboot"])
1022 hvp = instance.hvparams
1023 kernel_path = hvp[constants.HV_KERNEL_PATH]
1025 boot_disk = boot_cdrom = boot_floppy = boot_network = False
1027 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1028 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1029 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1030 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1032 self.ValidateParameters(hvp)
1035 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1037 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
1038 kvm_cmd.extend(["-enable-kvm"])
1039 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
1040 kvm_cmd.extend(["-disable-kvm"])
1043 kvm_cmd.extend(["-boot", "n"])
1045 # whether this is an older KVM version that uses the boot=on flag
1047 needs_boot_flag = (v_major, v_min) < (0, 14)
1049 disk_type = hvp[constants.HV_DISK_TYPE]
1050 if disk_type == constants.HT_DISK_PARAVIRTUAL:
1051 if_val = ",if=virtio"
1053 if_val = ",if=%s" % disk_type
1055 disk_cache = hvp[constants.HV_DISK_CACHE]
1056 if instance.disk_template in constants.DTS_EXT_MIRROR:
1057 if disk_cache != "none":
1058 # TODO: make this a hard error, instead of a silent overwrite
1059 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1060 " to prevent shared storage corruption on migration",
1062 cache_val = ",cache=none"
1063 elif disk_cache != constants.HT_CACHE_DEFAULT:
1064 cache_val = ",cache=%s" % disk_cache
1067 for cfdev, dev_path in block_devices:
1068 if cfdev.mode != constants.DISK_RDWR:
1069 raise errors.HypervisorError("Instance has read-only disks which"
1070 " are not supported by KVM")
1071 # TODO: handle FD_LOOP and FD_BLKTAP (?)
1074 kvm_cmd.extend(["-boot", "c"])
1076 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1077 boot_val = ",boot=on"
1079 drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
1081 kvm_cmd.extend(["-drive", drive_val])
1083 #Now we can specify a different device type for CDROM devices.
1084 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1085 if not cdrom_disk_type:
1086 cdrom_disk_type = disk_type
1088 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1090 options = ",format=raw,media=cdrom"
1091 # set cdrom 'if' type
1093 actual_cdrom_type = constants.HT_DISK_IDE
1094 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1095 actual_cdrom_type = "virtio"
1097 actual_cdrom_type = cdrom_disk_type
1098 if_val = ",if=%s" % actual_cdrom_type
1099 # set boot flag, if needed
1102 kvm_cmd.extend(["-boot", "d"])
1104 boot_val = ",boot=on"
1105 # and finally build the entire '-drive' value
1106 drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1107 kvm_cmd.extend(["-drive", drive_val])
1109 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1111 options = ",format=raw,media=cdrom"
1112 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1113 if_val = ",if=virtio"
1115 if_val = ",if=%s" % cdrom_disk_type
1116 drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1117 kvm_cmd.extend(["-drive", drive_val])
1119 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1121 options = ",format=raw,media=disk"
1123 kvm_cmd.extend(["-boot", "a"])
1124 options = "%s,boot=on" % options
1125 if_val = ",if=floppy"
1126 options = "%s%s" % (options, if_val)
1127 drive_val = "file=%s%s" % (floppy_image, options)
1128 kvm_cmd.extend(["-drive", drive_val])
1131 kvm_cmd.extend(["-kernel", kernel_path])
1132 initrd_path = hvp[constants.HV_INITRD_PATH]
1134 kvm_cmd.extend(["-initrd", initrd_path])
1135 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1136 hvp[constants.HV_KERNEL_ARGS]]
1137 if hvp[constants.HV_SERIAL_CONSOLE]:
1138 root_append.append("console=ttyS0,38400")
1139 kvm_cmd.extend(["-append", " ".join(root_append)])
1141 mem_path = hvp[constants.HV_MEM_PATH]
1143 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1145 monitor_dev = ("unix:%s,server,nowait" %
1146 self._InstanceMonitor(instance.name))
1147 kvm_cmd.extend(["-monitor", monitor_dev])
1148 if hvp[constants.HV_SERIAL_CONSOLE]:
1149 serial_dev = ("unix:%s,server,nowait" %
1150 self._InstanceSerial(instance.name))
1151 kvm_cmd.extend(["-serial", serial_dev])
1153 kvm_cmd.extend(["-serial", "none"])
1155 mouse_type = hvp[constants.HV_USB_MOUSE]
1156 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1157 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1158 spice_ip_version = None
1161 kvm_cmd.extend(["-usb"])
1162 kvm_cmd.extend(["-usbdevice", mouse_type])
1163 elif vnc_bind_address:
1164 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1166 if vnc_bind_address:
1167 if netutils.IP4Address.IsValid(vnc_bind_address):
1168 if instance.network_port > constants.VNC_BASE_PORT:
1169 display = instance.network_port - constants.VNC_BASE_PORT
1170 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1171 vnc_arg = ":%d" % (display)
1173 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1175 logging.error("Network port is not a valid VNC display (%d < %d)."
1176 " Not starting VNC", instance.network_port,
1177 constants.VNC_BASE_PORT)
1180 # Only allow tls and other option when not binding to a file, for now.
1181 # kvm/qemu gets confused otherwise about the filename to use.
1183 if hvp[constants.HV_VNC_TLS]:
1184 vnc_append = "%s,tls" % vnc_append
1185 if hvp[constants.HV_VNC_X509_VERIFY]:
1186 vnc_append = "%s,x509verify=%s" % (vnc_append,
1187 hvp[constants.HV_VNC_X509])
1188 elif hvp[constants.HV_VNC_X509]:
1189 vnc_append = "%s,x509=%s" % (vnc_append,
1190 hvp[constants.HV_VNC_X509])
1191 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1192 vnc_append = "%s,password" % vnc_append
1194 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1197 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1199 kvm_cmd.extend(["-vnc", vnc_arg])
1201 # FIXME: this is wrong here; the iface ip address differs
1202 # between systems, so it should be done in _ExecuteKVMRuntime
1203 if netutils.IsValidInterface(spice_bind):
1204 # The user specified a network interface, we have to figure out the IP
1206 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1207 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1209 # if the user specified an IP version and the interface does not
1210 # have that kind of IP addresses, throw an exception
1211 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1212 if not addresses[spice_ip_version]:
1213 raise errors.HypervisorError("spice: unable to get an IPv%s address"
1214 " for %s" % (spice_ip_version,
1217 # the user did not specify an IP version, we have to figure it out
1218 elif (addresses[constants.IP4_VERSION] and
1219 addresses[constants.IP6_VERSION]):
1220 # we have both ipv4 and ipv6, let's use the cluster default IP
1222 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1223 spice_ip_version = \
1224 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1225 elif addresses[constants.IP4_VERSION]:
1226 spice_ip_version = constants.IP4_VERSION
1227 elif addresses[constants.IP6_VERSION]:
1228 spice_ip_version = constants.IP6_VERSION
1230 raise errors.HypervisorError("spice: unable to get an IP address"
1231 " for %s" % (spice_bind))
1233 spice_address = addresses[spice_ip_version][0]
1236 # spice_bind is known to be a valid IP address, because
1237 # ValidateParameters checked it.
1238 spice_address = spice_bind
1240 spice_arg = "addr=%s" % spice_address
1241 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1242 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1243 (spice_arg, instance.network_port,
1244 pathutils.SPICE_CACERT_FILE))
1245 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1246 (spice_arg, pathutils.SPICE_CERT_FILE,
1247 pathutils.SPICE_CERT_FILE))
1248 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1250 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1252 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1254 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1255 spice_arg = "%s,disable-ticketing" % spice_arg
1257 if spice_ip_version:
1258 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1260 # Image compression options
1261 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1262 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1263 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1265 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1267 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1269 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1271 # Video stream detection
1272 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1274 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1276 # Audio compression, by default in qemu-kvm it is on
1277 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1278 spice_arg = "%s,playback-compression=off" % spice_arg
1279 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1280 spice_arg = "%s,agent-mouse=off" % spice_arg
1282 # Enable the spice agent communication channel between the host and the
1284 kvm_cmd.extend(["-device", "virtio-serial-pci"])
1285 kvm_cmd.extend(["-device", "virtserialport,chardev=spicechannel0,"
1286 "name=com.redhat.spice.0"])
1287 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1289 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1290 kvm_cmd.extend(["-spice", spice_arg])
1292 # Tell kvm to use the paravirtualized graphic card, optimized for SPICE
1293 kvm_cmd.extend(["-vga", "qxl"])
1296 kvm_cmd.extend(["-nographic"])
1298 if hvp[constants.HV_USE_LOCALTIME]:
1299 kvm_cmd.extend(["-localtime"])
1301 if hvp[constants.HV_KVM_USE_CHROOT]:
1302 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1304 # Add qemu-KVM -cpu param
1305 if hvp[constants.HV_CPU_TYPE]:
1306 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1308 # Save the current instance nics, but defer their expansion as parameters,
1309 # as we'll need to generate executable temp files for them.
1310 kvm_nics = instance.nics
1313 return (kvm_cmd, kvm_nics, hvparams)
1315 def _WriteKVMRuntime(self, instance_name, data):
1316 """Write an instance's KVM runtime
1320 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1322 except EnvironmentError, err:
1323 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1325 def _ReadKVMRuntime(self, instance_name):
1326 """Read an instance's KVM runtime
1330 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1331 except EnvironmentError, err:
1332 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1335 def _SaveKVMRuntime(self, instance, kvm_runtime):
1336 """Save an instance's KVM runtime
1339 kvm_cmd, kvm_nics, hvparams = kvm_runtime
1340 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1341 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1342 self._WriteKVMRuntime(instance.name, serialized_form)
1344 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1345 """Load an instance's KVM runtime
1348 if not serialized_runtime:
1349 serialized_runtime = self._ReadKVMRuntime(instance.name)
1350 loaded_runtime = serializer.Load(serialized_runtime)
1351 kvm_cmd, serialized_nics, hvparams = loaded_runtime
1352 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1353 return (kvm_cmd, kvm_nics, hvparams)
1355 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1356 """Run the KVM cmd and check for errors
1359 @param name: instance name
1360 @type kvm_cmd: list of strings
1361 @param kvm_cmd: runcmd input for kvm
1362 @type tap_fds: list of int
1363 @param tap_fds: fds of tap devices opened by Ganeti
1367 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1370 utils_wrapper.CloseFdNoError(fd)
1373 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1374 (name, result.fail_reason, result.output))
1375 if not self._InstancePidAlive(name)[2]:
1376 raise errors.HypervisorError("Failed to start instance %s" % name)
1378 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
1379 """Execute a KVM cmd, after completing it with some last minute data.
1381 @type incoming: tuple of strings
1382 @param incoming: (target_host_ip, port)
1385 # Small _ExecuteKVMRuntime hv parameters programming howto:
1386 # - conf_hvp contains the parameters as configured on ganeti. they might
1387 # have changed since the instance started; only use them if the change
1388 # won't affect the inside of the instance (which hasn't been rebooted).
1389 # - up_hvp contains the parameters as they were when the instance was
1390 # started, plus any new parameter which has been added between ganeti
1391 # versions: it is paramount that those default to a value which won't
1392 # affect the inside of the instance as well.
1393 conf_hvp = instance.hvparams
1394 name = instance.name
1395 self._CheckDown(name)
1399 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1400 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1402 _, v_major, v_min, _ = self._GetKVMVersion()
1404 # We know it's safe to run as a different user upon migration, so we'll use
1405 # the latest conf, from conf_hvp.
1406 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1407 if security_model == constants.HT_SM_USER:
1408 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1410 keymap = conf_hvp[constants.HV_KEYMAP]
1412 keymap_path = self._InstanceKeymapFile(name)
1413 # If a keymap file is specified, KVM won't use its internal defaults. By
1414 # first including the "en-us" layout, an error on loading the actual
1415 # layout (e.g. because it can't be found) won't lead to a non-functional
1416 # keyboard. A keyboard with incorrect keys is still better than none.
1417 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1418 kvm_cmd.extend(["-k", keymap_path])
1420 # We have reasons to believe changing something like the nic driver/type
1421 # upon migration won't exactly fly with the instance kernel, so for nic
1422 # related parameters we'll use up_hvp
1426 kvm_cmd.extend(["-net", "none"])
1430 nic_type = up_hvp[constants.HV_NIC_TYPE]
1431 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1432 # From version 0.12.0, kvm uses a new sintax for network configuration.
1433 if (v_major, v_min) >= (0, 12):
1434 nic_model = "virtio-net-pci"
1437 nic_model = "virtio"
1439 if up_hvp[constants.HV_VHOST_NET]:
1440 # vhost_net is only available from version 0.13.0 or newer
1441 if (v_major, v_min) >= (0, 13):
1442 tap_extra = ",vhost=on"
1444 raise errors.HypervisorError("vhost_net is configured"
1445 " but it is not available")
1447 nic_model = nic_type
1449 for nic_seq, nic in enumerate(kvm_nics):
1450 tapname, tapfd = _OpenTap(vnet_hdr)
1451 tapfds.append(tapfd)
1452 taps.append(tapname)
1453 if (v_major, v_min) >= (0, 12):
1454 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
1455 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
1456 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1458 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1460 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1461 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1464 target, port = incoming
1465 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1467 # Changing the vnc password doesn't bother the guest that much. At most it
1468 # will surprise people who connect to it. Whether positively or negatively
1470 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1474 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1475 except EnvironmentError, err:
1476 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1477 % (vnc_pwd_file, err))
1479 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1480 utils.EnsureDirs([(self._InstanceChrootDir(name),
1481 constants.SECURE_DIR_MODE)])
1483 # Automatically enable QMP if version is >= 0.14
1484 if (v_major, v_min) >= (0, 14):
1485 logging.debug("Enabling QMP")
1486 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1487 self._InstanceQmpMonitor(instance.name)])
1489 # Configure the network now for starting instances and bridged interfaces,
1490 # during FinalizeMigration for incoming instances' routed interfaces
1491 for nic_seq, nic in enumerate(kvm_nics):
1493 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1495 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1497 # CPU affinity requires kvm to start paused, so we set this flag if the
1498 # instance is not already paused and if we are not going to accept a
1499 # migrating instance. In the latter case, pausing is not needed.
1500 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1501 if start_kvm_paused:
1502 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1504 # Note: CPU pinning is using up_hvp since changes take effect
1505 # during instance startup anyway, and to avoid problems when soft
1506 # rebooting the instance.
1508 if up_hvp.get(constants.HV_CPU_MASK, None):
1511 if security_model == constants.HT_SM_POOL:
1512 ss = ssconf.SimpleStore()
1513 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1514 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1515 uid = uidpool.RequestUnusedUid(all_uids)
1517 username = pwd.getpwuid(uid.GetUid()).pw_name
1518 kvm_cmd.extend(["-runas", username])
1519 self._RunKVMCmd(name, kvm_cmd, tapfds)
1521 uidpool.ReleaseUid(uid)
1525 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1527 self._RunKVMCmd(name, kvm_cmd, tapfds)
1529 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1530 constants.RUN_DIRS_MODE)])
1531 for nic_seq, tap in enumerate(taps):
1532 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1536 change_cmd = "change vnc password %s" % vnc_pwd
1537 self._CallMonitorCommand(instance.name, change_cmd)
1539 # Setting SPICE password. We are not vulnerable to malicious passwordless
1540 # connection attempts because SPICE by default does not allow connections
1541 # if neither a password nor the "disable_ticketing" options are specified.
1542 # As soon as we send the password via QMP, that password is a valid ticket
1544 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1545 if spice_password_file:
1548 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1549 except EnvironmentError, err:
1550 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1551 % (spice_password_file, err))
1553 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1556 "protocol": "spice",
1557 "password": spice_pwd,
1559 qmp.Execute("set_password", arguments)
1561 for filename in temp_files:
1562 utils.RemoveFile(filename)
1564 # If requested, set CPU affinity and resume instance execution
1566 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1568 start_memory = self._InstanceStartupMemory(instance)
1569 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1570 self.BalloonInstanceMemory(instance, start_memory)
1572 if start_kvm_paused:
1573 # To control CPU pinning, ballooning, and vnc/spice passwords
1574 # the VM was started in a frozen state. If freezing was not
1575 # explicitly requested resume the vm status.
1576 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1578 def StartInstance(self, instance, block_devices, startup_paused):
1579 """Start an instance.
1582 self._CheckDown(instance.name)
1583 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1585 self._SaveKVMRuntime(instance, kvm_runtime)
1586 self._ExecuteKVMRuntime(instance, kvm_runtime)
1588 def _CallMonitorCommand(self, instance_name, command):
1589 """Invoke a command on the instance monitor.
1592 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1593 (utils.ShellQuote(command),
1594 constants.SOCAT_PATH,
1595 utils.ShellQuote(self._InstanceMonitor(instance_name))))
1596 result = utils.RunCmd(socat)
1598 msg = ("Failed to send command '%s' to instance %s."
1599 " output: %s, error: %s, fail_reason: %s" %
1600 (command, instance_name,
1601 result.stdout, result.stderr, result.fail_reason))
1602 raise errors.HypervisorError(msg)
1607 def _ParseKVMVersion(cls, text):
1608 """Parse the KVM version from the --help output.
1611 @param text: output of kvm --help
1612 @return: (version, v_maj, v_min, v_rev)
1613 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1616 match = cls._VERSION_RE.search(text.splitlines()[0])
1618 raise errors.HypervisorError("Unable to get KVM version")
1620 v_all = match.group(0)
1621 v_maj = int(match.group(1))
1622 v_min = int(match.group(2))
1624 v_rev = int(match.group(4))
1627 return (v_all, v_maj, v_min, v_rev)
1630 def _GetKVMVersion(cls):
1631 """Return the installed KVM version.
1633 @return: (version, v_maj, v_min, v_rev)
1634 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1637 result = utils.RunCmd([constants.KVM_PATH, "--help"])
1639 raise errors.HypervisorError("Unable to get KVM version")
1640 return cls._ParseKVMVersion(result.output)
1642 def StopInstance(self, instance, force=False, retry=False, name=None):
1643 """Stop an instance.
1646 if name is not None and not force:
1647 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1649 name = instance.name
1650 acpi = instance.hvparams[constants.HV_ACPI]
1653 _, pid, alive = self._InstancePidAlive(name)
1654 if pid > 0 and alive:
1655 if force or not acpi:
1656 utils.KillProcess(pid)
1658 self._CallMonitorCommand(name, "system_powerdown")
1661 def _GetDefaultMachineVersion(cls):
1662 """Return the default hardware revision (e.g. pc-1.1)
1665 result = utils.RunCmd([constants.KVM_PATH, "-M", "?"])
1667 raise errors.HypervisorError("Unable to get default hardware revision")
1668 for line in result.output.splitlines():
1669 match = cls._DEFAULT_MACHINE_VERSION_RE.match(line)
1671 return match.group(1)
1675 def CleanupInstance(self, instance_name):
1676 """Cleanup after a stopped instance
1679 pidfile, pid, alive = self._InstancePidAlive(instance_name)
1680 if pid > 0 and alive:
1681 raise errors.HypervisorError("Cannot cleanup a live instance")
1682 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1684 def RebootInstance(self, instance):
1685 """Reboot an instance.
1688 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1689 # socket the instance will stop, but now power up again. So we'll resort
1690 # to shutdown and restart.
1691 _, _, alive = self._InstancePidAlive(instance.name)
1693 raise errors.HypervisorError("Failed to reboot instance %s:"
1694 " not running" % instance.name)
1695 # StopInstance will delete the saved KVM runtime so:
1696 # ...first load it...
1697 kvm_runtime = self._LoadKVMRuntime(instance)
1698 # ...now we can safely call StopInstance...
1699 if not self.StopInstance(instance):
1700 self.StopInstance(instance, force=True)
1701 # ...and finally we can save it again, and execute it...
1702 self._SaveKVMRuntime(instance, kvm_runtime)
1703 self._ExecuteKVMRuntime(instance, kvm_runtime)
1705 def MigrationInfo(self, instance):
1706 """Get instance information to perform a migration.
1708 @type instance: L{objects.Instance}
1709 @param instance: instance to be migrated
1711 @return: content of the KVM runtime file
1714 return self._ReadKVMRuntime(instance.name)
1716 def AcceptInstance(self, instance, info, target):
1717 """Prepare to accept an instance.
1719 @type instance: L{objects.Instance}
1720 @param instance: instance to be accepted
1722 @param info: content of the KVM runtime file on the source node
1723 @type target: string
1724 @param target: target host (usually ip), on this node
1727 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1728 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1729 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1731 def FinalizeMigrationDst(self, instance, info, success):
1732 """Finalize the instance migration on the target node.
1734 Stop the incoming mode KVM.
1736 @type instance: L{objects.Instance}
1737 @param instance: instance whose migration is being finalized
1741 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1742 kvm_nics = kvm_runtime[1]
1744 for nic_seq, nic in enumerate(kvm_nics):
1745 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1746 # Bridged interfaces have already been configured
1749 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1750 except EnvironmentError, err:
1751 logging.warning("Failed to find host interface for %s NIC #%d: %s",
1752 instance.name, nic_seq, str(err))
1755 self._ConfigureNIC(instance, nic_seq, nic, tap)
1756 except errors.HypervisorError, err:
1757 logging.warning(str(err))
1759 self._WriteKVMRuntime(instance.name, info)
1761 self.StopInstance(instance, force=True)
1763 def MigrateInstance(self, instance, target, live):
1764 """Migrate an instance to a target node.
1766 The migration will not be attempted if the instance is not
1769 @type instance: L{objects.Instance}
1770 @param instance: the instance to be migrated
1771 @type target: string
1772 @param target: ip address of the target node
1774 @param live: perform a live migration
1777 instance_name = instance.name
1778 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1779 _, _, alive = self._InstancePidAlive(instance_name)
1781 raise errors.HypervisorError("Instance not running, cannot migrate")
1784 self._CallMonitorCommand(instance_name, "stop")
1786 migrate_command = ("migrate_set_speed %dm" %
1787 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1788 self._CallMonitorCommand(instance_name, migrate_command)
1790 migrate_command = ("migrate_set_downtime %dms" %
1791 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1792 self._CallMonitorCommand(instance_name, migrate_command)
1794 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1795 self._CallMonitorCommand(instance_name, migrate_command)
1797 def FinalizeMigrationSource(self, instance, success, live):
1798 """Finalize the instance migration on the source node.
1800 @type instance: L{objects.Instance}
1801 @param instance: the instance that was migrated
1803 @param success: whether the migration succeeded or not
1805 @param live: whether the user requested a live migration or not
1809 pidfile, pid, _ = self._InstancePidAlive(instance.name)
1810 utils.KillProcess(pid)
1811 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
1813 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1815 def GetMigrationStatus(self, instance):
1816 """Get the migration status
1818 @type instance: L{objects.Instance}
1819 @param instance: the instance that is being migrated
1820 @rtype: L{objects.MigrationStatus}
1821 @return: the status of the current migration (one of
1822 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1823 progress info that can be retrieved from the hypervisor
1826 info_command = "info migrate"
1827 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
1828 result = self._CallMonitorCommand(instance.name, info_command)
1829 match = self._MIGRATION_STATUS_RE.search(result.stdout)
1831 if not result.stdout:
1832 logging.info("KVM: empty 'info migrate' result")
1834 logging.warning("KVM: unknown 'info migrate' result: %s",
1837 status = match.group(1)
1838 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
1839 migration_status = objects.MigrationStatus(status=status)
1840 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
1842 migration_status.transferred_ram = match.group("transferred")
1843 migration_status.total_ram = match.group("total")
1845 return migration_status
1847 logging.warning("KVM: unknown migration status '%s'", status)
1849 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1851 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
1853 def BalloonInstanceMemory(self, instance, mem):
1854 """Balloon an instance memory to a certain value.
1856 @type instance: L{objects.Instance}
1857 @param instance: instance to be accepted
1859 @param mem: actual memory size to use for instance runtime
1862 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
1864 def GetNodeInfo(self):
1865 """Return information about the node.
1867 @return: a dict with the following keys (values in MiB):
1868 - memory_total: the total memory size on the node
1869 - memory_free: the available memory on the node for instances
1870 - memory_dom0: the memory used by the node itself, if available
1871 - hv_version: the hypervisor version in the form (major, minor,
1875 result = self.GetLinuxNodeInfo()
1876 _, v_major, v_min, v_rev = self._GetKVMVersion()
1877 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
1881 def GetInstanceConsole(cls, instance, hvparams, beparams):
1882 """Return a command for connecting to the console of an instance.
1885 if hvparams[constants.HV_SERIAL_CONSOLE]:
1886 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
1887 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1888 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1889 "STDIO,%s" % cls._SocatUnixConsoleParams(),
1890 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1891 return objects.InstanceConsole(instance=instance.name,
1892 kind=constants.CONS_SSH,
1893 host=instance.primary_node,
1894 user=constants.SSH_CONSOLE_USER,
1897 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1898 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1899 display = instance.network_port - constants.VNC_BASE_PORT
1900 return objects.InstanceConsole(instance=instance.name,
1901 kind=constants.CONS_VNC,
1902 host=vnc_bind_address,
1903 port=instance.network_port,
1906 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1908 return objects.InstanceConsole(instance=instance.name,
1909 kind=constants.CONS_SPICE,
1911 port=instance.network_port)
1913 return objects.InstanceConsole(instance=instance.name,
1914 kind=constants.CONS_MESSAGE,
1915 message=("No serial shell for instance %s" %
1919 """Verify the hypervisor.
1921 Check that the binary exists.
1924 if not os.path.exists(constants.KVM_PATH):
1925 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1926 if not os.path.exists(constants.SOCAT_PATH):
1927 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1930 def CheckParameterSyntax(cls, hvparams):
1931 """Check the given parameters for validity.
1933 @type hvparams: dict
1934 @param hvparams: dictionary with parameter names/value
1935 @raise errors.HypervisorError: when a parameter is not valid
1938 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1940 kernel_path = hvparams[constants.HV_KERNEL_PATH]
1942 if not hvparams[constants.HV_ROOT_PATH]:
1943 raise errors.HypervisorError("Need a root partition for the instance,"
1944 " if a kernel is defined")
1946 if (hvparams[constants.HV_VNC_X509_VERIFY] and
1947 not hvparams[constants.HV_VNC_X509]):
1948 raise errors.HypervisorError("%s must be defined, if %s is" %
1949 (constants.HV_VNC_X509,
1950 constants.HV_VNC_X509_VERIFY))
1952 boot_order = hvparams[constants.HV_BOOT_ORDER]
1953 if (boot_order == constants.HT_BO_CDROM and
1954 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1955 raise errors.HypervisorError("Cannot boot from cdrom without an"
1958 security_model = hvparams[constants.HV_SECURITY_MODEL]
1959 if security_model == constants.HT_SM_USER:
1960 if not hvparams[constants.HV_SECURITY_DOMAIN]:
1961 raise errors.HypervisorError("A security domain (user to run kvm as)"
1962 " must be specified")
1963 elif (security_model == constants.HT_SM_NONE or
1964 security_model == constants.HT_SM_POOL):
1965 if hvparams[constants.HV_SECURITY_DOMAIN]:
1966 raise errors.HypervisorError("Cannot have a security domain when the"
1967 " security model is 'none' or 'pool'")
1969 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1970 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
1972 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1973 # if an IP version is specified, the spice_bind parameter must be an
1975 if (netutils.IP4Address.IsValid(spice_bind) and
1976 spice_ip_version != constants.IP4_VERSION):
1977 raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
1978 " the specified IP version is %s" %
1979 (spice_bind, spice_ip_version))
1981 if (netutils.IP6Address.IsValid(spice_bind) and
1982 spice_ip_version != constants.IP6_VERSION):
1983 raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
1984 " the specified IP version is %s" %
1985 (spice_bind, spice_ip_version))
1987 # All the other SPICE parameters depend on spice_bind being set. Raise an
1988 # error if any of them is set without it.
1989 for param in _SPICE_ADDITIONAL_PARAMS:
1991 raise errors.HypervisorError("spice: %s requires %s to be set" %
1992 (param, constants.HV_KVM_SPICE_BIND))
1995 def ValidateParameters(cls, hvparams):
1996 """Check the given parameters for validity.
1998 @type hvparams: dict
1999 @param hvparams: dictionary with parameter names/value
2000 @raise errors.HypervisorError: when a parameter is not valid
2003 super(KVMHypervisor, cls).ValidateParameters(hvparams)
2005 security_model = hvparams[constants.HV_SECURITY_MODEL]
2006 if security_model == constants.HT_SM_USER:
2007 username = hvparams[constants.HV_SECURITY_DOMAIN]
2009 pwd.getpwnam(username)
2011 raise errors.HypervisorError("Unknown security domain user %s"
2014 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2016 # only one of VNC and SPICE can be used currently.
2017 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2018 raise errors.HypervisorError("both SPICE and VNC are configured, but"
2019 " only one of them can be used at a"
2022 # KVM version should be >= 0.14.0
2023 _, v_major, v_min, _ = cls._GetKVMVersion()
2024 if (v_major, v_min) < (0, 14):
2025 raise errors.HypervisorError("spice is configured, but it is not"
2026 " available in versions of KVM < 0.14")
2028 # if spice_bind is not an IP address, it must be a valid interface
2029 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
2030 or netutils.IP6Address.IsValid(spice_bind))
2031 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2032 raise errors.HypervisorError("spice: the %s parameter must be either"
2033 " a valid IP address or interface name" %
2034 constants.HV_KVM_SPICE_BIND)
2037 def PowercycleNode(cls):
2038 """KVM powercycle, just a wrapper over Linux powercycle.
2041 cls.LinuxPowercycle()