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"
555 ANCILLARY_FILES_OPT = [
560 hv_base.BaseHypervisor.__init__(self)
561 # Let's make sure the directories we need exist, even if the RUN_DIR lives
562 # in a tmpfs filesystem or has been otherwise wiped out.
563 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
564 utils.EnsureDirs(dirs)
567 def _InstancePidFile(cls, instance_name):
568 """Returns the instance pidfile.
571 return utils.PathJoin(cls._PIDS_DIR, instance_name)
574 def _InstanceUidFile(cls, instance_name):
575 """Returns the instance uidfile.
578 return utils.PathJoin(cls._UIDS_DIR, instance_name)
581 def _InstancePidInfo(cls, pid):
582 """Check pid file for instance information.
584 Check that a pid file is associated with an instance, and retrieve
585 information from its command line.
587 @type pid: string or int
588 @param pid: process id of the instance to check
590 @return: (instance_name, memory, vcpus)
591 @raise errors.HypervisorError: when an instance cannot be found
594 alive = utils.IsProcessAlive(pid)
596 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
598 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
600 cmdline = utils.ReadFile(cmdline_file)
601 except EnvironmentError, err:
602 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
609 arg_list = cmdline.split("\x00")
611 arg = arg_list.pop(0)
613 instance = arg_list.pop(0)
615 memory = int(arg_list.pop(0))
617 vcpus = int(arg_list.pop(0))
620 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
623 return (instance, memory, vcpus)
625 def _InstancePidAlive(self, instance_name):
626 """Returns the instance pidfile, pid, and liveness.
628 @type instance_name: string
629 @param instance_name: instance name
631 @return: (pid file name, pid, liveness)
634 pidfile = self._InstancePidFile(instance_name)
635 pid = utils.ReadPidFile(pidfile)
639 cmd_instance = self._InstancePidInfo(pid)[0]
640 alive = (cmd_instance == instance_name)
641 except errors.HypervisorError:
644 return (pidfile, pid, alive)
646 def _CheckDown(self, instance_name):
647 """Raises an error unless the given instance is down.
650 alive = self._InstancePidAlive(instance_name)[2]
652 raise errors.HypervisorError("Failed to start instance %s: %s" %
653 (instance_name, "already running"))
656 def _InstanceMonitor(cls, instance_name):
657 """Returns the instance monitor socket name
660 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
663 def _InstanceSerial(cls, instance_name):
664 """Returns the instance serial socket name
667 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
670 def _InstanceQmpMonitor(cls, instance_name):
671 """Returns the instance serial QMP socket name
674 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
677 def _SocatUnixConsoleParams():
678 """Returns the correct parameters for socat
680 If we have a new-enough socat we can use raw mode with an escape character.
683 if constants.SOCAT_USE_ESCAPE:
684 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
686 return "echo=0,icanon=0"
689 def _InstanceKVMRuntime(cls, instance_name):
690 """Returns the instance KVM runtime filename
693 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
696 def _InstanceChrootDir(cls, instance_name):
697 """Returns the name of the KVM chroot dir of the instance
700 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
703 def _InstanceNICDir(cls, instance_name):
704 """Returns the name of the directory holding the tap device files for a
708 return utils.PathJoin(cls._NICS_DIR, instance_name)
711 def _InstanceNICFile(cls, instance_name, seq):
712 """Returns the name of the file containing the tap device for a given NIC
715 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
718 def _InstanceKeymapFile(cls, instance_name):
719 """Returns the name of the file containing the keymap for a given instance
722 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
725 def _TryReadUidFile(cls, uid_file):
726 """Try to read a uid file
729 if os.path.exists(uid_file):
731 uid = int(utils.ReadOneLineFile(uid_file))
733 except EnvironmentError:
734 logging.warning("Can't read uid file", exc_info=True)
735 except (TypeError, ValueError):
736 logging.warning("Can't parse uid file contents", exc_info=True)
740 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
741 """Removes an instance's rutime sockets/files/dirs.
744 utils.RemoveFile(pidfile)
745 utils.RemoveFile(cls._InstanceMonitor(instance_name))
746 utils.RemoveFile(cls._InstanceSerial(instance_name))
747 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
748 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
749 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
750 uid_file = cls._InstanceUidFile(instance_name)
751 uid = cls._TryReadUidFile(uid_file)
752 utils.RemoveFile(uid_file)
754 uidpool.ReleaseUid(uid)
756 shutil.rmtree(cls._InstanceNICDir(instance_name))
758 if err.errno != errno.ENOENT:
761 chroot_dir = cls._InstanceChrootDir(instance_name)
762 utils.RemoveDir(chroot_dir)
764 if err.errno == errno.ENOTEMPTY:
765 # The chroot directory is expected to be empty, but it isn't.
766 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
769 utils.TimestampForFilename()))
770 logging.warning("The chroot directory of instance %s can not be"
771 " removed as it is not empty. Moving it to the"
772 " quarantine instead. Please investigate the"
773 " contents (%s) and clean up manually",
774 instance_name, new_chroot_dir)
775 utils.RenameFile(chroot_dir, new_chroot_dir)
780 def _ConfigureNIC(instance, seq, nic, tap):
781 """Run the network configuration script for a specified NIC
783 @param instance: instance we're acting on
784 @type instance: instance object
785 @param seq: nic sequence number
787 @param nic: nic we're acting on
788 @type nic: nic object
789 @param tap: the host's tap interface this NIC corresponds to
794 tags = " ".join(instance.tags)
799 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
800 "INSTANCE": instance.name,
802 "MODE": nic.nicparams[constants.NIC_MODE],
804 "INTERFACE_INDEX": str(seq),
811 if nic.nicparams[constants.NIC_LINK]:
812 env["LINK"] = nic.nicparams[constants.NIC_LINK]
815 n = objects.Network.FromDict(nic.netinfo)
816 _BuildNetworkEnv(nic.network, n.network, n.gateway,
817 n.network6, n.gateway6, n.network_type,
818 n.mac_prefix, n.tags, env)
820 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
821 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
823 result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
825 raise errors.HypervisorError("Failed to configure interface %s: %s."
826 " Network configuration script output: %s" %
827 (tap, result.fail_reason, result.output))
830 def _VerifyAffinityPackage():
832 raise errors.HypervisorError("affinity Python package not"
833 " found; cannot use CPU pinning under KVM")
836 def _BuildAffinityCpuMask(cpu_list):
837 """Create a CPU mask suitable for sched_setaffinity from a list of
840 See man taskset for more info on sched_setaffinity masks.
841 For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
843 @type cpu_list: list of int
844 @param cpu_list: list of physical CPU numbers to map to vCPUs in order
846 @return: a bit mask of CPU affinities
849 if cpu_list == constants.CPU_PINNING_OFF:
850 return constants.CPU_PINNING_ALL_KVM
852 return sum(2 ** cpu for cpu in cpu_list)
855 def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
856 """Change CPU affinity for running VM according to given CPU mask.
858 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
859 @type cpu_mask: string
860 @param process_id: process ID of KVM process. Used to pin entire VM
862 @type process_id: int
863 @param thread_dict: map of virtual CPUs to KVM thread IDs
864 @type thread_dict: dict int:int
867 # Convert the string CPU mask to a list of list of int's
868 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
870 if len(cpu_list) == 1:
871 all_cpu_mapping = cpu_list[0]
872 if all_cpu_mapping == constants.CPU_PINNING_OFF:
873 # If CPU pinning has 1 entry that's "all", then do nothing
876 # If CPU pinning has one non-all entry, map the entire VM to
877 # one set of physical CPUs
878 cls._VerifyAffinityPackage()
879 affinity.set_process_affinity_mask(
880 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
882 # The number of vCPUs mapped should match the number of vCPUs
883 # reported by KVM. This was already verified earlier, so
884 # here only as a sanity check.
885 assert len(thread_dict) == len(cpu_list)
886 cls._VerifyAffinityPackage()
888 # For each vCPU, map it to the proper list of physical CPUs
889 for vcpu, i in zip(cpu_list, range(len(cpu_list))):
890 affinity.set_process_affinity_mask(thread_dict[i],
891 cls._BuildAffinityCpuMask(vcpu))
893 def _GetVcpuThreadIds(self, instance_name):
894 """Get a mapping of vCPU no. to thread IDs for the instance
896 @type instance_name: string
897 @param instance_name: instance in question
898 @rtype: dictionary of int:int
899 @return: a dictionary mapping vCPU numbers to thread IDs
903 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
904 for line in output.stdout.splitlines():
905 match = self._CPU_INFO_RE.search(line)
908 grp = map(int, match.groups())
909 result[grp[0]] = grp[1]
913 def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
914 """Complete CPU pinning.
916 @type instance_name: string
917 @param instance_name: name of instance
918 @type cpu_mask: string
919 @param cpu_mask: CPU pinning mask as entered by user
922 # Get KVM process ID, to be used if need to pin entire VM
923 _, pid, _ = self._InstancePidAlive(instance_name)
924 # Get vCPU thread IDs, to be used if need to pin vCPUs separately
925 thread_dict = self._GetVcpuThreadIds(instance_name)
926 # Run CPU pinning, based on configured mask
927 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
929 def ListInstances(self):
930 """Get the list of running instances.
932 We can do this by listing our live instances directory and
933 checking whether the associated kvm process is still alive.
937 for name in os.listdir(self._PIDS_DIR):
938 if self._InstancePidAlive(name)[2]:
942 def GetInstanceInfo(self, instance_name):
943 """Get instance properties.
945 @type instance_name: string
946 @param instance_name: the instance name
947 @rtype: tuple of strings
948 @return: (name, id, memory, vcpus, stat, times)
951 _, pid, alive = self._InstancePidAlive(instance_name)
955 _, memory, vcpus = self._InstancePidInfo(pid)
960 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
962 vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
963 # Will fail if ballooning is not enabled, but we can then just resort to
965 mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
966 memory = mem_bytes / 1048576
967 except errors.HypervisorError:
970 return (instance_name, pid, memory, vcpus, istat, times)
972 def GetAllInstancesInfo(self):
973 """Get properties of all instances.
975 @return: list of tuples (name, id, memory, vcpus, stat, times)
979 for name in os.listdir(self._PIDS_DIR):
981 info = self.GetInstanceInfo(name)
982 except errors.HypervisorError:
983 # Ignore exceptions due to instances being shut down
989 def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
990 """Generate KVM information to start an instance.
992 @attention: this function must not have any side-effects; for
993 example, it must not write to the filesystem, or read values
994 from the current system the are expected to differ between
995 nodes, since it is only run once at instance startup;
996 actions/kvm arguments that can vary between systems should be
997 done in L{_ExecuteKVMRuntime}
1000 # pylint: disable=R0914,R0915
1001 _, v_major, v_min, _ = self._GetKVMVersion()
1003 pidfile = self._InstancePidFile(instance.name)
1004 kvm = constants.KVM_PATH
1006 # used just by the vnc server, if enabled
1007 kvm_cmd.extend(["-name", instance.name])
1008 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1009 kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
1010 kvm_cmd.extend(["-pidfile", pidfile])
1011 kvm_cmd.extend(["-balloon", "virtio"])
1012 kvm_cmd.extend(["-daemonize"])
1013 if not instance.hvparams[constants.HV_ACPI]:
1014 kvm_cmd.extend(["-no-acpi"])
1015 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1016 constants.INSTANCE_REBOOT_EXIT:
1017 kvm_cmd.extend(["-no-reboot"])
1019 hvp = instance.hvparams
1020 kernel_path = hvp[constants.HV_KERNEL_PATH]
1022 boot_disk = boot_cdrom = boot_floppy = boot_network = False
1024 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1025 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1026 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1027 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1029 self.ValidateParameters(hvp)
1032 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1034 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
1035 kvm_cmd.extend(["-enable-kvm"])
1036 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
1037 kvm_cmd.extend(["-disable-kvm"])
1040 kvm_cmd.extend(["-boot", "n"])
1042 # whether this is an older KVM version that uses the boot=on flag
1044 needs_boot_flag = (v_major, v_min) < (0, 14)
1046 disk_type = hvp[constants.HV_DISK_TYPE]
1047 if disk_type == constants.HT_DISK_PARAVIRTUAL:
1048 if_val = ",if=virtio"
1050 if_val = ",if=%s" % disk_type
1052 disk_cache = hvp[constants.HV_DISK_CACHE]
1053 if instance.disk_template in constants.DTS_EXT_MIRROR:
1054 if disk_cache != "none":
1055 # TODO: make this a hard error, instead of a silent overwrite
1056 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1057 " to prevent shared storage corruption on migration",
1059 cache_val = ",cache=none"
1060 elif disk_cache != constants.HT_CACHE_DEFAULT:
1061 cache_val = ",cache=%s" % disk_cache
1064 for cfdev, dev_path in block_devices:
1065 if cfdev.mode != constants.DISK_RDWR:
1066 raise errors.HypervisorError("Instance has read-only disks which"
1067 " are not supported by KVM")
1068 # TODO: handle FD_LOOP and FD_BLKTAP (?)
1071 kvm_cmd.extend(["-boot", "c"])
1073 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1074 boot_val = ",boot=on"
1076 drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
1078 kvm_cmd.extend(["-drive", drive_val])
1080 #Now we can specify a different device type for CDROM devices.
1081 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1082 if not cdrom_disk_type:
1083 cdrom_disk_type = disk_type
1085 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1087 options = ",format=raw,media=cdrom"
1088 # set cdrom 'if' type
1090 actual_cdrom_type = constants.HT_DISK_IDE
1091 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1092 actual_cdrom_type = "virtio"
1094 actual_cdrom_type = cdrom_disk_type
1095 if_val = ",if=%s" % actual_cdrom_type
1096 # set boot flag, if needed
1099 kvm_cmd.extend(["-boot", "d"])
1101 boot_val = ",boot=on"
1102 # and finally build the entire '-drive' value
1103 drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1104 kvm_cmd.extend(["-drive", drive_val])
1106 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1108 options = ",format=raw,media=cdrom"
1109 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1110 if_val = ",if=virtio"
1112 if_val = ",if=%s" % cdrom_disk_type
1113 drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1114 kvm_cmd.extend(["-drive", drive_val])
1116 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1118 options = ",format=raw,media=disk"
1120 kvm_cmd.extend(["-boot", "a"])
1121 options = "%s,boot=on" % options
1122 if_val = ",if=floppy"
1123 options = "%s%s" % (options, if_val)
1124 drive_val = "file=%s%s" % (floppy_image, options)
1125 kvm_cmd.extend(["-drive", drive_val])
1128 kvm_cmd.extend(["-kernel", kernel_path])
1129 initrd_path = hvp[constants.HV_INITRD_PATH]
1131 kvm_cmd.extend(["-initrd", initrd_path])
1132 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1133 hvp[constants.HV_KERNEL_ARGS]]
1134 if hvp[constants.HV_SERIAL_CONSOLE]:
1135 root_append.append("console=ttyS0,38400")
1136 kvm_cmd.extend(["-append", " ".join(root_append)])
1138 mem_path = hvp[constants.HV_MEM_PATH]
1140 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1142 monitor_dev = ("unix:%s,server,nowait" %
1143 self._InstanceMonitor(instance.name))
1144 kvm_cmd.extend(["-monitor", monitor_dev])
1145 if hvp[constants.HV_SERIAL_CONSOLE]:
1146 serial_dev = ("unix:%s,server,nowait" %
1147 self._InstanceSerial(instance.name))
1148 kvm_cmd.extend(["-serial", serial_dev])
1150 kvm_cmd.extend(["-serial", "none"])
1152 mouse_type = hvp[constants.HV_USB_MOUSE]
1153 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1154 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1155 spice_ip_version = None
1158 kvm_cmd.extend(["-usb"])
1159 kvm_cmd.extend(["-usbdevice", mouse_type])
1160 elif vnc_bind_address:
1161 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1163 if vnc_bind_address:
1164 if netutils.IP4Address.IsValid(vnc_bind_address):
1165 if instance.network_port > constants.VNC_BASE_PORT:
1166 display = instance.network_port - constants.VNC_BASE_PORT
1167 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1168 vnc_arg = ":%d" % (display)
1170 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1172 logging.error("Network port is not a valid VNC display (%d < %d)."
1173 " Not starting VNC", instance.network_port,
1174 constants.VNC_BASE_PORT)
1177 # Only allow tls and other option when not binding to a file, for now.
1178 # kvm/qemu gets confused otherwise about the filename to use.
1180 if hvp[constants.HV_VNC_TLS]:
1181 vnc_append = "%s,tls" % vnc_append
1182 if hvp[constants.HV_VNC_X509_VERIFY]:
1183 vnc_append = "%s,x509verify=%s" % (vnc_append,
1184 hvp[constants.HV_VNC_X509])
1185 elif hvp[constants.HV_VNC_X509]:
1186 vnc_append = "%s,x509=%s" % (vnc_append,
1187 hvp[constants.HV_VNC_X509])
1188 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1189 vnc_append = "%s,password" % vnc_append
1191 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1194 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1196 kvm_cmd.extend(["-vnc", vnc_arg])
1198 # FIXME: this is wrong here; the iface ip address differs
1199 # between systems, so it should be done in _ExecuteKVMRuntime
1200 if netutils.IsValidInterface(spice_bind):
1201 # The user specified a network interface, we have to figure out the IP
1203 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1204 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1206 # if the user specified an IP version and the interface does not
1207 # have that kind of IP addresses, throw an exception
1208 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1209 if not addresses[spice_ip_version]:
1210 raise errors.HypervisorError("spice: unable to get an IPv%s address"
1211 " for %s" % (spice_ip_version,
1214 # the user did not specify an IP version, we have to figure it out
1215 elif (addresses[constants.IP4_VERSION] and
1216 addresses[constants.IP6_VERSION]):
1217 # we have both ipv4 and ipv6, let's use the cluster default IP
1219 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1220 spice_ip_version = \
1221 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1222 elif addresses[constants.IP4_VERSION]:
1223 spice_ip_version = constants.IP4_VERSION
1224 elif addresses[constants.IP6_VERSION]:
1225 spice_ip_version = constants.IP6_VERSION
1227 raise errors.HypervisorError("spice: unable to get an IP address"
1228 " for %s" % (spice_bind))
1230 spice_address = addresses[spice_ip_version][0]
1233 # spice_bind is known to be a valid IP address, because
1234 # ValidateParameters checked it.
1235 spice_address = spice_bind
1237 spice_arg = "addr=%s" % spice_address
1238 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1239 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1240 (spice_arg, instance.network_port,
1241 pathutils.SPICE_CACERT_FILE))
1242 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1243 (spice_arg, pathutils.SPICE_CERT_FILE,
1244 pathutils.SPICE_CERT_FILE))
1245 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1247 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1249 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1251 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1252 spice_arg = "%s,disable-ticketing" % spice_arg
1254 if spice_ip_version:
1255 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1257 # Image compression options
1258 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1259 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1260 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1262 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1264 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1266 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1268 # Video stream detection
1269 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1271 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1273 # Audio compression, by default in qemu-kvm it is on
1274 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1275 spice_arg = "%s,playback-compression=off" % spice_arg
1276 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1277 spice_arg = "%s,agent-mouse=off" % spice_arg
1279 # Enable the spice agent communication channel between the host and the
1281 kvm_cmd.extend(["-device", "virtio-serial-pci"])
1282 kvm_cmd.extend(["-device", "virtserialport,chardev=spicechannel0,"
1283 "name=com.redhat.spice.0"])
1284 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1286 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1287 kvm_cmd.extend(["-spice", spice_arg])
1289 # Tell kvm to use the paravirtualized graphic card, optimized for SPICE
1290 kvm_cmd.extend(["-vga", "qxl"])
1293 kvm_cmd.extend(["-nographic"])
1295 if hvp[constants.HV_USE_LOCALTIME]:
1296 kvm_cmd.extend(["-localtime"])
1298 if hvp[constants.HV_KVM_USE_CHROOT]:
1299 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1301 # Add qemu-KVM -cpu param
1302 if hvp[constants.HV_CPU_TYPE]:
1303 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1305 # Save the current instance nics, but defer their expansion as parameters,
1306 # as we'll need to generate executable temp files for them.
1307 kvm_nics = instance.nics
1310 return (kvm_cmd, kvm_nics, hvparams)
1312 def _WriteKVMRuntime(self, instance_name, data):
1313 """Write an instance's KVM runtime
1317 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1319 except EnvironmentError, err:
1320 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1322 def _ReadKVMRuntime(self, instance_name):
1323 """Read an instance's KVM runtime
1327 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1328 except EnvironmentError, err:
1329 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1332 def _SaveKVMRuntime(self, instance, kvm_runtime):
1333 """Save an instance's KVM runtime
1336 kvm_cmd, kvm_nics, hvparams = kvm_runtime
1337 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1338 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1339 self._WriteKVMRuntime(instance.name, serialized_form)
1341 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1342 """Load an instance's KVM runtime
1345 if not serialized_runtime:
1346 serialized_runtime = self._ReadKVMRuntime(instance.name)
1347 loaded_runtime = serializer.Load(serialized_runtime)
1348 kvm_cmd, serialized_nics, hvparams = loaded_runtime
1349 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1350 return (kvm_cmd, kvm_nics, hvparams)
1352 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1353 """Run the KVM cmd and check for errors
1356 @param name: instance name
1357 @type kvm_cmd: list of strings
1358 @param kvm_cmd: runcmd input for kvm
1359 @type tap_fds: list of int
1360 @param tap_fds: fds of tap devices opened by Ganeti
1364 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1367 utils_wrapper.CloseFdNoError(fd)
1370 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1371 (name, result.fail_reason, result.output))
1372 if not self._InstancePidAlive(name)[2]:
1373 raise errors.HypervisorError("Failed to start instance %s" % name)
1375 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
1376 """Execute a KVM cmd, after completing it with some last minute data.
1378 @type incoming: tuple of strings
1379 @param incoming: (target_host_ip, port)
1382 # Small _ExecuteKVMRuntime hv parameters programming howto:
1383 # - conf_hvp contains the parameters as configured on ganeti. they might
1384 # have changed since the instance started; only use them if the change
1385 # won't affect the inside of the instance (which hasn't been rebooted).
1386 # - up_hvp contains the parameters as they were when the instance was
1387 # started, plus any new parameter which has been added between ganeti
1388 # versions: it is paramount that those default to a value which won't
1389 # affect the inside of the instance as well.
1390 conf_hvp = instance.hvparams
1391 name = instance.name
1392 self._CheckDown(name)
1396 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1397 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1399 _, v_major, v_min, _ = self._GetKVMVersion()
1401 # We know it's safe to run as a different user upon migration, so we'll use
1402 # the latest conf, from conf_hvp.
1403 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1404 if security_model == constants.HT_SM_USER:
1405 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1407 keymap = conf_hvp[constants.HV_KEYMAP]
1409 keymap_path = self._InstanceKeymapFile(name)
1410 # If a keymap file is specified, KVM won't use its internal defaults. By
1411 # first including the "en-us" layout, an error on loading the actual
1412 # layout (e.g. because it can't be found) won't lead to a non-functional
1413 # keyboard. A keyboard with incorrect keys is still better than none.
1414 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1415 kvm_cmd.extend(["-k", keymap_path])
1417 # We have reasons to believe changing something like the nic driver/type
1418 # upon migration won't exactly fly with the instance kernel, so for nic
1419 # related parameters we'll use up_hvp
1423 kvm_cmd.extend(["-net", "none"])
1427 nic_type = up_hvp[constants.HV_NIC_TYPE]
1428 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1429 # From version 0.12.0, kvm uses a new sintax for network configuration.
1430 if (v_major, v_min) >= (0, 12):
1431 nic_model = "virtio-net-pci"
1434 nic_model = "virtio"
1436 if up_hvp[constants.HV_VHOST_NET]:
1437 # vhost_net is only available from version 0.13.0 or newer
1438 if (v_major, v_min) >= (0, 13):
1439 tap_extra = ",vhost=on"
1441 raise errors.HypervisorError("vhost_net is configured"
1442 " but it is not available")
1444 nic_model = nic_type
1446 for nic_seq, nic in enumerate(kvm_nics):
1447 tapname, tapfd = _OpenTap(vnet_hdr)
1448 tapfds.append(tapfd)
1449 taps.append(tapname)
1450 if (v_major, v_min) >= (0, 12):
1451 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
1452 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
1453 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1455 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1457 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1458 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1461 target, port = incoming
1462 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1464 # Changing the vnc password doesn't bother the guest that much. At most it
1465 # will surprise people who connect to it. Whether positively or negatively
1467 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1471 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1472 except EnvironmentError, err:
1473 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1474 % (vnc_pwd_file, err))
1476 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1477 utils.EnsureDirs([(self._InstanceChrootDir(name),
1478 constants.SECURE_DIR_MODE)])
1480 # Automatically enable QMP if version is >= 0.14
1481 if (v_major, v_min) >= (0, 14):
1482 logging.debug("Enabling QMP")
1483 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1484 self._InstanceQmpMonitor(instance.name)])
1486 # Configure the network now for starting instances and bridged interfaces,
1487 # during FinalizeMigration for incoming instances' routed interfaces
1488 for nic_seq, nic in enumerate(kvm_nics):
1490 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1492 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1494 # CPU affinity requires kvm to start paused, so we set this flag if the
1495 # instance is not already paused and if we are not going to accept a
1496 # migrating instance. In the latter case, pausing is not needed.
1497 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1498 if start_kvm_paused:
1499 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1501 # Note: CPU pinning is using up_hvp since changes take effect
1502 # during instance startup anyway, and to avoid problems when soft
1503 # rebooting the instance.
1505 if up_hvp.get(constants.HV_CPU_MASK, None):
1508 if security_model == constants.HT_SM_POOL:
1509 ss = ssconf.SimpleStore()
1510 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1511 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1512 uid = uidpool.RequestUnusedUid(all_uids)
1514 username = pwd.getpwuid(uid.GetUid()).pw_name
1515 kvm_cmd.extend(["-runas", username])
1516 self._RunKVMCmd(name, kvm_cmd, tapfds)
1518 uidpool.ReleaseUid(uid)
1522 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1524 self._RunKVMCmd(name, kvm_cmd, tapfds)
1526 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1527 constants.RUN_DIRS_MODE)])
1528 for nic_seq, tap in enumerate(taps):
1529 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1533 change_cmd = "change vnc password %s" % vnc_pwd
1534 self._CallMonitorCommand(instance.name, change_cmd)
1536 # Setting SPICE password. We are not vulnerable to malicious passwordless
1537 # connection attempts because SPICE by default does not allow connections
1538 # if neither a password nor the "disable_ticketing" options are specified.
1539 # As soon as we send the password via QMP, that password is a valid ticket
1541 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1542 if spice_password_file:
1545 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1546 except EnvironmentError, err:
1547 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1548 % (spice_password_file, err))
1550 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1553 "protocol": "spice",
1554 "password": spice_pwd,
1556 qmp.Execute("set_password", arguments)
1558 for filename in temp_files:
1559 utils.RemoveFile(filename)
1561 # If requested, set CPU affinity and resume instance execution
1563 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1565 start_memory = self._InstanceStartupMemory(instance)
1566 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1567 self.BalloonInstanceMemory(instance, start_memory)
1569 if start_kvm_paused:
1570 # To control CPU pinning, ballooning, and vnc/spice passwords
1571 # the VM was started in a frozen state. If freezing was not
1572 # explicitly requested resume the vm status.
1573 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1575 def StartInstance(self, instance, block_devices, startup_paused):
1576 """Start an instance.
1579 self._CheckDown(instance.name)
1580 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1582 self._SaveKVMRuntime(instance, kvm_runtime)
1583 self._ExecuteKVMRuntime(instance, kvm_runtime)
1585 def _CallMonitorCommand(self, instance_name, command):
1586 """Invoke a command on the instance monitor.
1589 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1590 (utils.ShellQuote(command),
1591 constants.SOCAT_PATH,
1592 utils.ShellQuote(self._InstanceMonitor(instance_name))))
1593 result = utils.RunCmd(socat)
1595 msg = ("Failed to send command '%s' to instance %s."
1596 " output: %s, error: %s, fail_reason: %s" %
1597 (command, instance_name,
1598 result.stdout, result.stderr, result.fail_reason))
1599 raise errors.HypervisorError(msg)
1604 def _ParseKVMVersion(cls, text):
1605 """Parse the KVM version from the --help output.
1608 @param text: output of kvm --help
1609 @return: (version, v_maj, v_min, v_rev)
1610 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1613 match = cls._VERSION_RE.search(text.splitlines()[0])
1615 raise errors.HypervisorError("Unable to get KVM version")
1617 v_all = match.group(0)
1618 v_maj = int(match.group(1))
1619 v_min = int(match.group(2))
1621 v_rev = int(match.group(4))
1624 return (v_all, v_maj, v_min, v_rev)
1627 def _GetKVMVersion(cls):
1628 """Return the installed KVM version.
1630 @return: (version, v_maj, v_min, v_rev)
1631 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1634 result = utils.RunCmd([constants.KVM_PATH, "--help"])
1636 raise errors.HypervisorError("Unable to get KVM version")
1637 return cls._ParseKVMVersion(result.output)
1639 def StopInstance(self, instance, force=False, retry=False, name=None):
1640 """Stop an instance.
1643 if name is not None and not force:
1644 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1646 name = instance.name
1647 acpi = instance.hvparams[constants.HV_ACPI]
1650 _, pid, alive = self._InstancePidAlive(name)
1651 if pid > 0 and alive:
1652 if force or not acpi:
1653 utils.KillProcess(pid)
1655 self._CallMonitorCommand(name, "system_powerdown")
1657 def CleanupInstance(self, instance_name):
1658 """Cleanup after a stopped instance
1661 pidfile, pid, alive = self._InstancePidAlive(instance_name)
1662 if pid > 0 and alive:
1663 raise errors.HypervisorError("Cannot cleanup a live instance")
1664 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1666 def RebootInstance(self, instance):
1667 """Reboot an instance.
1670 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1671 # socket the instance will stop, but now power up again. So we'll resort
1672 # to shutdown and restart.
1673 _, _, alive = self._InstancePidAlive(instance.name)
1675 raise errors.HypervisorError("Failed to reboot instance %s:"
1676 " not running" % instance.name)
1677 # StopInstance will delete the saved KVM runtime so:
1678 # ...first load it...
1679 kvm_runtime = self._LoadKVMRuntime(instance)
1680 # ...now we can safely call StopInstance...
1681 if not self.StopInstance(instance):
1682 self.StopInstance(instance, force=True)
1683 # ...and finally we can save it again, and execute it...
1684 self._SaveKVMRuntime(instance, kvm_runtime)
1685 self._ExecuteKVMRuntime(instance, kvm_runtime)
1687 def MigrationInfo(self, instance):
1688 """Get instance information to perform a migration.
1690 @type instance: L{objects.Instance}
1691 @param instance: instance to be migrated
1693 @return: content of the KVM runtime file
1696 return self._ReadKVMRuntime(instance.name)
1698 def AcceptInstance(self, instance, info, target):
1699 """Prepare to accept an instance.
1701 @type instance: L{objects.Instance}
1702 @param instance: instance to be accepted
1704 @param info: content of the KVM runtime file on the source node
1705 @type target: string
1706 @param target: target host (usually ip), on this node
1709 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1710 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1711 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1713 def FinalizeMigrationDst(self, instance, info, success):
1714 """Finalize the instance migration on the target node.
1716 Stop the incoming mode KVM.
1718 @type instance: L{objects.Instance}
1719 @param instance: instance whose migration is being finalized
1723 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1724 kvm_nics = kvm_runtime[1]
1726 for nic_seq, nic in enumerate(kvm_nics):
1727 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1728 # Bridged interfaces have already been configured
1731 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1732 except EnvironmentError, err:
1733 logging.warning("Failed to find host interface for %s NIC #%d: %s",
1734 instance.name, nic_seq, str(err))
1737 self._ConfigureNIC(instance, nic_seq, nic, tap)
1738 except errors.HypervisorError, err:
1739 logging.warning(str(err))
1741 self._WriteKVMRuntime(instance.name, info)
1743 self.StopInstance(instance, force=True)
1745 def MigrateInstance(self, instance, target, live):
1746 """Migrate an instance to a target node.
1748 The migration will not be attempted if the instance is not
1751 @type instance: L{objects.Instance}
1752 @param instance: the instance to be migrated
1753 @type target: string
1754 @param target: ip address of the target node
1756 @param live: perform a live migration
1759 instance_name = instance.name
1760 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1761 _, _, alive = self._InstancePidAlive(instance_name)
1763 raise errors.HypervisorError("Instance not running, cannot migrate")
1766 self._CallMonitorCommand(instance_name, "stop")
1768 migrate_command = ("migrate_set_speed %dm" %
1769 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1770 self._CallMonitorCommand(instance_name, migrate_command)
1772 migrate_command = ("migrate_set_downtime %dms" %
1773 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1774 self._CallMonitorCommand(instance_name, migrate_command)
1776 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1777 self._CallMonitorCommand(instance_name, migrate_command)
1779 def FinalizeMigrationSource(self, instance, success, live):
1780 """Finalize the instance migration on the source node.
1782 @type instance: L{objects.Instance}
1783 @param instance: the instance that was migrated
1785 @param success: whether the migration succeeded or not
1787 @param live: whether the user requested a live migration or not
1791 pidfile, pid, _ = self._InstancePidAlive(instance.name)
1792 utils.KillProcess(pid)
1793 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
1795 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1797 def GetMigrationStatus(self, instance):
1798 """Get the migration status
1800 @type instance: L{objects.Instance}
1801 @param instance: the instance that is being migrated
1802 @rtype: L{objects.MigrationStatus}
1803 @return: the status of the current migration (one of
1804 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1805 progress info that can be retrieved from the hypervisor
1808 info_command = "info migrate"
1809 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
1810 result = self._CallMonitorCommand(instance.name, info_command)
1811 match = self._MIGRATION_STATUS_RE.search(result.stdout)
1813 if not result.stdout:
1814 logging.info("KVM: empty 'info migrate' result")
1816 logging.warning("KVM: unknown 'info migrate' result: %s",
1819 status = match.group(1)
1820 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
1821 migration_status = objects.MigrationStatus(status=status)
1822 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
1824 migration_status.transferred_ram = match.group("transferred")
1825 migration_status.total_ram = match.group("total")
1827 return migration_status
1829 logging.warning("KVM: unknown migration status '%s'", status)
1831 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1833 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
1835 def BalloonInstanceMemory(self, instance, mem):
1836 """Balloon an instance memory to a certain value.
1838 @type instance: L{objects.Instance}
1839 @param instance: instance to be accepted
1841 @param mem: actual memory size to use for instance runtime
1844 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
1846 def GetNodeInfo(self):
1847 """Return information about the node.
1849 @return: a dict with the following keys (values in MiB):
1850 - memory_total: the total memory size on the node
1851 - memory_free: the available memory on the node for instances
1852 - memory_dom0: the memory used by the node itself, if available
1853 - hv_version: the hypervisor version in the form (major, minor,
1857 result = self.GetLinuxNodeInfo()
1858 _, v_major, v_min, v_rev = self._GetKVMVersion()
1859 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
1863 def GetInstanceConsole(cls, instance, hvparams, beparams):
1864 """Return a command for connecting to the console of an instance.
1867 if hvparams[constants.HV_SERIAL_CONSOLE]:
1868 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
1869 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1870 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1871 "STDIO,%s" % cls._SocatUnixConsoleParams(),
1872 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1873 return objects.InstanceConsole(instance=instance.name,
1874 kind=constants.CONS_SSH,
1875 host=instance.primary_node,
1876 user=constants.SSH_CONSOLE_USER,
1879 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1880 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1881 display = instance.network_port - constants.VNC_BASE_PORT
1882 return objects.InstanceConsole(instance=instance.name,
1883 kind=constants.CONS_VNC,
1884 host=vnc_bind_address,
1885 port=instance.network_port,
1888 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1890 return objects.InstanceConsole(instance=instance.name,
1891 kind=constants.CONS_SPICE,
1893 port=instance.network_port)
1895 return objects.InstanceConsole(instance=instance.name,
1896 kind=constants.CONS_MESSAGE,
1897 message=("No serial shell for instance %s" %
1901 """Verify the hypervisor.
1903 Check that the binary exists.
1906 if not os.path.exists(constants.KVM_PATH):
1907 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1908 if not os.path.exists(constants.SOCAT_PATH):
1909 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1912 def CheckParameterSyntax(cls, hvparams):
1913 """Check the given parameters for validity.
1915 @type hvparams: dict
1916 @param hvparams: dictionary with parameter names/value
1917 @raise errors.HypervisorError: when a parameter is not valid
1920 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1922 kernel_path = hvparams[constants.HV_KERNEL_PATH]
1924 if not hvparams[constants.HV_ROOT_PATH]:
1925 raise errors.HypervisorError("Need a root partition for the instance,"
1926 " if a kernel is defined")
1928 if (hvparams[constants.HV_VNC_X509_VERIFY] and
1929 not hvparams[constants.HV_VNC_X509]):
1930 raise errors.HypervisorError("%s must be defined, if %s is" %
1931 (constants.HV_VNC_X509,
1932 constants.HV_VNC_X509_VERIFY))
1934 boot_order = hvparams[constants.HV_BOOT_ORDER]
1935 if (boot_order == constants.HT_BO_CDROM and
1936 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1937 raise errors.HypervisorError("Cannot boot from cdrom without an"
1940 security_model = hvparams[constants.HV_SECURITY_MODEL]
1941 if security_model == constants.HT_SM_USER:
1942 if not hvparams[constants.HV_SECURITY_DOMAIN]:
1943 raise errors.HypervisorError("A security domain (user to run kvm as)"
1944 " must be specified")
1945 elif (security_model == constants.HT_SM_NONE or
1946 security_model == constants.HT_SM_POOL):
1947 if hvparams[constants.HV_SECURITY_DOMAIN]:
1948 raise errors.HypervisorError("Cannot have a security domain when the"
1949 " security model is 'none' or 'pool'")
1951 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1952 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
1954 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1955 # if an IP version is specified, the spice_bind parameter must be an
1957 if (netutils.IP4Address.IsValid(spice_bind) and
1958 spice_ip_version != constants.IP4_VERSION):
1959 raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
1960 " the specified IP version is %s" %
1961 (spice_bind, spice_ip_version))
1963 if (netutils.IP6Address.IsValid(spice_bind) and
1964 spice_ip_version != constants.IP6_VERSION):
1965 raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
1966 " the specified IP version is %s" %
1967 (spice_bind, spice_ip_version))
1969 # All the other SPICE parameters depend on spice_bind being set. Raise an
1970 # error if any of them is set without it.
1971 for param in _SPICE_ADDITIONAL_PARAMS:
1973 raise errors.HypervisorError("spice: %s requires %s to be set" %
1974 (param, constants.HV_KVM_SPICE_BIND))
1977 def ValidateParameters(cls, hvparams):
1978 """Check the given parameters for validity.
1980 @type hvparams: dict
1981 @param hvparams: dictionary with parameter names/value
1982 @raise errors.HypervisorError: when a parameter is not valid
1985 super(KVMHypervisor, cls).ValidateParameters(hvparams)
1987 security_model = hvparams[constants.HV_SECURITY_MODEL]
1988 if security_model == constants.HT_SM_USER:
1989 username = hvparams[constants.HV_SECURITY_DOMAIN]
1991 pwd.getpwnam(username)
1993 raise errors.HypervisorError("Unknown security domain user %s"
1996 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1998 # only one of VNC and SPICE can be used currently.
1999 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2000 raise errors.HypervisorError("both SPICE and VNC are configured, but"
2001 " only one of them can be used at a"
2004 # KVM version should be >= 0.14.0
2005 _, v_major, v_min, _ = cls._GetKVMVersion()
2006 if (v_major, v_min) < (0, 14):
2007 raise errors.HypervisorError("spice is configured, but it is not"
2008 " available in versions of KVM < 0.14")
2010 # if spice_bind is not an IP address, it must be a valid interface
2011 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
2012 or netutils.IP6Address.IsValid(spice_bind))
2013 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2014 raise errors.HypervisorError("spice: the %s parameter must be either"
2015 " a valid IP address or interface name" %
2016 constants.HV_KVM_SPICE_BIND)
2019 def PowercycleNode(cls):
2020 """KVM powercycle, just a wrapper over Linux powercycle.
2023 cls.LinuxPowercycle()