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
38 from ganeti import utils
39 from ganeti import constants
40 from ganeti import errors
41 from ganeti import serializer
42 from ganeti import objects
43 from ganeti import uidpool
44 from ganeti import ssconf
45 from ganeti.hypervisor import hv_base
46 from ganeti import netutils
47 from ganeti.utils import wrapper as utils_wrapper
50 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
52 # TUN/TAP driver constants, taken from <linux/if_tun.h>
53 # They are architecture-independent and already hardcoded in qemu-kvm source,
54 # so we can safely include them here.
55 TUNSETIFF = 0x400454ca
56 TUNGETIFF = 0x800454d2
57 TUNGETFEATURES = 0x800454cf
63 def _ProbeTapVnetHdr(fd):
64 """Check whether to enable the IFF_VNET_HDR flag.
66 To do this, _all_ of the following conditions must be met:
67 1. TUNGETFEATURES ioctl() *must* be implemented
68 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
69 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
70 drivers/net/tun.c there is no way to test this until after the tap device
71 has been created using TUNSETIFF, and there is no way to change the
72 IFF_VNET_HDR flag after creating the interface, catch-22! However both
73 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
74 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
77 @param fd: the file descriptor of /dev/net/tun
80 req = struct.pack("I", 0)
82 res = fcntl.ioctl(fd, TUNGETFEATURES, req)
83 except EnvironmentError:
84 logging.warning("TUNGETFEATURES ioctl() not implemented")
87 tunflags = struct.unpack("I", res)[0]
88 if tunflags & IFF_VNET_HDR:
91 logging.warning("Host does not support IFF_VNET_HDR, not enabling")
95 def _OpenTap(vnet_hdr=True):
96 """Open a new tap device and return its file descriptor.
98 This is intended to be used by a qemu-type hypervisor together with the -net
99 tap,fd=<fd> command line parameter.
101 @type vnet_hdr: boolean
102 @param vnet_hdr: Enable the VNET Header
103 @return: (ifname, tapfd)
108 tapfd = os.open("/dev/net/tun", os.O_RDWR)
109 except EnvironmentError:
110 raise errors.HypervisorError("Failed to open /dev/net/tun")
112 flags = IFF_TAP | IFF_NO_PI
114 if vnet_hdr and _ProbeTapVnetHdr(tapfd):
115 flags |= IFF_VNET_HDR
117 # The struct ifreq ioctl request (see netdevice(7))
118 ifr = struct.pack("16sh", "", flags)
121 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
122 except EnvironmentError:
123 raise errors.HypervisorError("Failed to allocate a new TAP device")
125 # Get the interface name from the ioctl
126 ifname = struct.unpack("16sh", res)[0].strip("\x00")
127 return (ifname, tapfd)
130 class KVMHypervisor(hv_base.BaseHypervisor):
131 """KVM hypervisor interface"""
134 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
135 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
136 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
137 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
138 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
139 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
140 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
141 # KVM instances with chroot enabled are started in empty chroot directories.
142 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
143 # After an instance is stopped, its chroot directory is removed.
144 # If the chroot directory is not empty, it can't be removed.
145 # A non-empty chroot directory indicates a possible security incident.
146 # To support forensics, the non-empty chroot directory is quarantined in
147 # a separate directory, called 'chroot-quarantine'.
148 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
149 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
150 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
153 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
154 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
155 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
156 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
157 constants.HV_ACPI: hv_base.NO_CHECK,
158 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
159 constants.HV_VNC_BIND_ADDRESS:
160 (False, lambda x: (netutils.IP4Address.IsValid(x) or
161 utils.IsNormAbsPath(x)),
162 "the VNC bind address must be either a valid IP address or an absolute"
163 " pathname", None, None),
164 constants.HV_VNC_TLS: hv_base.NO_CHECK,
165 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
166 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
167 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
168 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
169 constants.HV_KVM_SPICE_IP_VERSION:
170 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
171 x in constants.VALID_IP_VERSIONS),
172 "the SPICE IP version should be 4 or 6",
174 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
175 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
176 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
177 constants.HV_BOOT_ORDER:
178 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
179 constants.HV_NIC_TYPE:
180 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
181 constants.HV_DISK_TYPE:
182 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
183 constants.HV_KVM_CDROM_DISK_TYPE:
184 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
185 constants.HV_USB_MOUSE:
186 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
187 constants.HV_KEYMAP: hv_base.NO_CHECK,
188 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
189 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
190 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
191 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
192 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
193 constants.HV_DISK_CACHE:
194 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
195 constants.HV_SECURITY_MODEL:
196 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
197 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
198 constants.HV_KVM_FLAG:
199 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
200 constants.HV_VHOST_NET: hv_base.NO_CHECK,
201 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
202 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
203 constants.HV_REBOOT_BEHAVIOR:
204 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
207 _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
209 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
210 _MIGRATION_INFO_RETRY_DELAY = 2
212 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
219 hv_base.BaseHypervisor.__init__(self)
220 # Let's make sure the directories we need exist, even if the RUN_DIR lives
221 # in a tmpfs filesystem or has been otherwise wiped out.
222 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
223 utils.EnsureDirs(dirs)
226 def _InstancePidFile(cls, instance_name):
227 """Returns the instance pidfile.
230 return utils.PathJoin(cls._PIDS_DIR, instance_name)
233 def _InstanceUidFile(cls, instance_name):
234 """Returns the instance uidfile.
237 return utils.PathJoin(cls._UIDS_DIR, instance_name)
240 def _InstancePidInfo(cls, pid):
241 """Check pid file for instance information.
243 Check that a pid file is associated with an instance, and retrieve
244 information from its command line.
246 @type pid: string or int
247 @param pid: process id of the instance to check
249 @return: (instance_name, memory, vcpus)
250 @raise errors.HypervisorError: when an instance cannot be found
253 alive = utils.IsProcessAlive(pid)
255 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
257 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
259 cmdline = utils.ReadFile(cmdline_file)
260 except EnvironmentError, err:
261 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
268 arg_list = cmdline.split("\x00")
270 arg = arg_list.pop(0)
272 instance = arg_list.pop(0)
274 memory = int(arg_list.pop(0))
276 vcpus = int(arg_list.pop(0))
279 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
282 return (instance, memory, vcpus)
284 def _InstancePidAlive(self, instance_name):
285 """Returns the instance pidfile, pid, and liveness.
287 @type instance_name: string
288 @param instance_name: instance name
290 @return: (pid file name, pid, liveness)
293 pidfile = self._InstancePidFile(instance_name)
294 pid = utils.ReadPidFile(pidfile)
298 cmd_instance = self._InstancePidInfo(pid)[0]
299 alive = (cmd_instance == instance_name)
300 except errors.HypervisorError:
303 return (pidfile, pid, alive)
305 def _CheckDown(self, instance_name):
306 """Raises an error unless the given instance is down.
309 alive = self._InstancePidAlive(instance_name)[2]
311 raise errors.HypervisorError("Failed to start instance %s: %s" %
312 (instance_name, "already running"))
315 def _InstanceMonitor(cls, instance_name):
316 """Returns the instance monitor socket name
319 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
322 def _InstanceSerial(cls, instance_name):
323 """Returns the instance serial socket name
326 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
329 def _SocatUnixConsoleParams():
330 """Returns the correct parameters for socat
332 If we have a new-enough socat we can use raw mode with an escape character.
335 if constants.SOCAT_USE_ESCAPE:
336 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
338 return "echo=0,icanon=0"
341 def _InstanceKVMRuntime(cls, instance_name):
342 """Returns the instance KVM runtime filename
345 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
348 def _InstanceChrootDir(cls, instance_name):
349 """Returns the name of the KVM chroot dir of the instance
352 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
355 def _InstanceNICDir(cls, instance_name):
356 """Returns the name of the directory holding the tap device files for a
360 return utils.PathJoin(cls._NICS_DIR, instance_name)
363 def _InstanceNICFile(cls, instance_name, seq):
364 """Returns the name of the file containing the tap device for a given NIC
367 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
370 def _InstanceKeymapFile(cls, instance_name):
371 """Returns the name of the file containing the keymap for a given instance
374 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
377 def _TryReadUidFile(cls, uid_file):
378 """Try to read a uid file
381 if os.path.exists(uid_file):
383 uid = int(utils.ReadOneLineFile(uid_file))
385 except EnvironmentError:
386 logging.warning("Can't read uid file", exc_info=True)
387 except (TypeError, ValueError):
388 logging.warning("Can't parse uid file contents", exc_info=True)
392 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
393 """Removes an instance's rutime sockets/files/dirs.
396 utils.RemoveFile(pidfile)
397 utils.RemoveFile(cls._InstanceMonitor(instance_name))
398 utils.RemoveFile(cls._InstanceSerial(instance_name))
399 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
400 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
401 uid_file = cls._InstanceUidFile(instance_name)
402 uid = cls._TryReadUidFile(uid_file)
403 utils.RemoveFile(uid_file)
405 uidpool.ReleaseUid(uid)
407 shutil.rmtree(cls._InstanceNICDir(instance_name))
409 if err.errno != errno.ENOENT:
412 chroot_dir = cls._InstanceChrootDir(instance_name)
413 utils.RemoveDir(chroot_dir)
415 if err.errno == errno.ENOTEMPTY:
416 # The chroot directory is expected to be empty, but it isn't.
417 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
420 utils.TimestampForFilename()))
421 logging.warning("The chroot directory of instance %s can not be"
422 " removed as it is not empty. Moving it to the"
423 " quarantine instead. Please investigate the"
424 " contents (%s) and clean up manually",
425 instance_name, new_chroot_dir)
426 utils.RenameFile(chroot_dir, new_chroot_dir)
431 def _ConfigureNIC(instance, seq, nic, tap):
432 """Run the network configuration script for a specified NIC
434 @param instance: instance we're acting on
435 @type instance: instance object
436 @param seq: nic sequence number
438 @param nic: nic we're acting on
439 @type nic: nic object
440 @param tap: the host's tap interface this NIC corresponds to
446 tags = " ".join(instance.tags)
451 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
452 "INSTANCE": instance.name,
454 "MODE": nic.nicparams[constants.NIC_MODE],
456 "INTERFACE_INDEX": str(seq),
463 if nic.nicparams[constants.NIC_LINK]:
464 env["LINK"] = nic.nicparams[constants.NIC_LINK]
466 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
467 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
469 result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
471 raise errors.HypervisorError("Failed to configure interface %s: %s."
472 " Network configuration script output: %s" %
473 (tap, result.fail_reason, result.output))
475 def ListInstances(self):
476 """Get the list of running instances.
478 We can do this by listing our live instances directory and
479 checking whether the associated kvm process is still alive.
483 for name in os.listdir(self._PIDS_DIR):
484 if self._InstancePidAlive(name)[2]:
488 def GetInstanceInfo(self, instance_name):
489 """Get instance properties.
491 @type instance_name: string
492 @param instance_name: the instance name
493 @rtype: tuple of strings
494 @return: (name, id, memory, vcpus, stat, times)
497 _, pid, alive = self._InstancePidAlive(instance_name)
501 _, memory, vcpus = self._InstancePidInfo(pid)
505 return (instance_name, pid, memory, vcpus, stat, times)
507 def GetAllInstancesInfo(self):
508 """Get properties of all instances.
510 @return: list of tuples (name, id, memory, vcpus, stat, times)
514 for name in os.listdir(self._PIDS_DIR):
516 info = self.GetInstanceInfo(name)
517 except errors.HypervisorError:
523 def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
524 """Generate KVM information to start an instance.
526 @attention: this function must not have any side-effects; for
527 example, it must not write to the filesystem, or read values
528 from the current system the are expected to differ between
529 nodes, since it is only run once at instance startup;
530 actions/kvm arguments that can vary between systems should be
531 done in L{_ExecuteKVMRuntime}
534 _, v_major, v_min, _ = self._GetKVMVersion()
536 pidfile = self._InstancePidFile(instance.name)
537 kvm = constants.KVM_PATH
539 # used just by the vnc server, if enabled
540 kvm_cmd.extend(["-name", instance.name])
541 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MEMORY]])
542 kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
543 kvm_cmd.extend(["-pidfile", pidfile])
544 kvm_cmd.extend(["-daemonize"])
545 if not instance.hvparams[constants.HV_ACPI]:
546 kvm_cmd.extend(["-no-acpi"])
548 kvm_cmd.extend(["-S"])
549 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
550 constants.INSTANCE_REBOOT_EXIT:
551 kvm_cmd.extend(["-no-reboot"])
553 hvp = instance.hvparams
554 kernel_path = hvp[constants.HV_KERNEL_PATH]
556 boot_disk = boot_cdrom = boot_floppy = boot_network = False
558 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
559 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
560 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
561 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
563 self.ValidateParameters(hvp)
565 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
566 kvm_cmd.extend(["-enable-kvm"])
567 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
568 kvm_cmd.extend(["-disable-kvm"])
571 kvm_cmd.extend(["-boot", "n"])
573 disk_type = hvp[constants.HV_DISK_TYPE]
574 if disk_type == constants.HT_DISK_PARAVIRTUAL:
575 if_val = ",if=virtio"
577 if_val = ",if=%s" % disk_type
579 disk_cache = hvp[constants.HV_DISK_CACHE]
580 if instance.disk_template in constants.DTS_EXT_MIRROR:
581 if disk_cache != "none":
582 # TODO: make this a hard error, instead of a silent overwrite
583 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
584 " to prevent shared storage corruption on migration",
586 cache_val = ",cache=none"
587 elif disk_cache != constants.HT_CACHE_DEFAULT:
588 cache_val = ",cache=%s" % disk_cache
591 for cfdev, dev_path in block_devices:
592 if cfdev.mode != constants.DISK_RDWR:
593 raise errors.HypervisorError("Instance has read-only disks which"
594 " are not supported by KVM")
595 # TODO: handle FD_LOOP and FD_BLKTAP (?)
598 kvm_cmd.extend(["-boot", "c"])
600 if (v_major, v_min) < (0, 14) and disk_type != constants.HT_DISK_IDE:
601 boot_val = ",boot=on"
603 drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
605 kvm_cmd.extend(["-drive", drive_val])
607 #Now we can specify a different device type for CDROM devices.
608 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
609 if not cdrom_disk_type:
610 cdrom_disk_type = disk_type
612 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
614 options = ",format=raw,media=cdrom"
616 kvm_cmd.extend(["-boot", "d"])
617 if cdrom_disk_type != constants.HT_DISK_IDE:
618 options = "%s,boot=on,if=%s" % (options, constants.HT_DISK_IDE)
620 options = "%s,boot=on" % options
622 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
623 if_val = ",if=virtio"
625 if_val = ",if=%s" % cdrom_disk_type
626 options = "%s%s" % (options, if_val)
627 drive_val = "file=%s%s" % (iso_image, options)
628 kvm_cmd.extend(["-drive", drive_val])
630 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
632 options = ",format=raw,media=cdrom"
633 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
634 if_val = ",if=virtio"
636 if_val = ",if=%s" % cdrom_disk_type
637 options = "%s%s" % (options, if_val)
638 drive_val = "file=%s%s" % (iso_image2, options)
639 kvm_cmd.extend(["-drive", drive_val])
641 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
643 options = ",format=raw,media=disk"
645 kvm_cmd.extend(["-boot", "a"])
646 options = "%s,boot=on" % options
647 if_val = ",if=floppy"
648 options = "%s%s" % (options, if_val)
649 drive_val = "file=%s%s" % (floppy_image, options)
650 kvm_cmd.extend(["-drive", drive_val])
653 kvm_cmd.extend(["-kernel", kernel_path])
654 initrd_path = hvp[constants.HV_INITRD_PATH]
656 kvm_cmd.extend(["-initrd", initrd_path])
657 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
658 hvp[constants.HV_KERNEL_ARGS]]
659 if hvp[constants.HV_SERIAL_CONSOLE]:
660 root_append.append("console=ttyS0,38400")
661 kvm_cmd.extend(["-append", " ".join(root_append)])
663 mem_path = hvp[constants.HV_MEM_PATH]
665 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
667 monitor_dev = ("unix:%s,server,nowait" %
668 self._InstanceMonitor(instance.name))
669 kvm_cmd.extend(["-monitor", monitor_dev])
670 if hvp[constants.HV_SERIAL_CONSOLE]:
671 serial_dev = ("unix:%s,server,nowait" %
672 self._InstanceSerial(instance.name))
673 kvm_cmd.extend(["-serial", serial_dev])
675 kvm_cmd.extend(["-serial", "none"])
677 mouse_type = hvp[constants.HV_USB_MOUSE]
678 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
679 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
680 spice_ip_version = None
683 kvm_cmd.extend(["-usb"])
684 kvm_cmd.extend(["-usbdevice", mouse_type])
685 elif vnc_bind_address:
686 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
689 if netutils.IP4Address.IsValid(vnc_bind_address):
690 if instance.network_port > constants.VNC_BASE_PORT:
691 display = instance.network_port - constants.VNC_BASE_PORT
692 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
693 vnc_arg = ":%d" % (display)
695 vnc_arg = "%s:%d" % (vnc_bind_address, display)
697 logging.error("Network port is not a valid VNC display (%d < %d)."
698 " Not starting VNC", instance.network_port,
699 constants.VNC_BASE_PORT)
702 # Only allow tls and other option when not binding to a file, for now.
703 # kvm/qemu gets confused otherwise about the filename to use.
705 if hvp[constants.HV_VNC_TLS]:
706 vnc_append = "%s,tls" % vnc_append
707 if hvp[constants.HV_VNC_X509_VERIFY]:
708 vnc_append = "%s,x509verify=%s" % (vnc_append,
709 hvp[constants.HV_VNC_X509])
710 elif hvp[constants.HV_VNC_X509]:
711 vnc_append = "%s,x509=%s" % (vnc_append,
712 hvp[constants.HV_VNC_X509])
713 if hvp[constants.HV_VNC_PASSWORD_FILE]:
714 vnc_append = "%s,password" % vnc_append
716 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
719 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
721 kvm_cmd.extend(["-vnc", vnc_arg])
723 # FIXME: this is wrong here; the iface ip address differs
724 # between systems, so it should be done in _ExecuteKVMRuntime
725 if netutils.IsValidInterface(spice_bind):
726 # The user specified a network interface, we have to figure out the IP
728 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
729 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
731 # if the user specified an IP version and the interface does not
732 # have that kind of IP addresses, throw an exception
733 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
734 if not addresses[spice_ip_version]:
735 raise errors.HypervisorError("spice: unable to get an IPv%s address"
736 " for %s" % (spice_ip_version,
739 # the user did not specify an IP version, we have to figure it out
740 elif (addresses[constants.IP4_VERSION] and
741 addresses[constants.IP6_VERSION]):
742 # we have both ipv4 and ipv6, let's use the cluster default IP
744 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
745 spice_ip_version = netutils.IPAddress.GetVersionFromAddressFamily(
747 elif addresses[constants.IP4_VERSION]:
748 spice_ip_version = constants.IP4_VERSION
749 elif addresses[constants.IP6_VERSION]:
750 spice_ip_version = constants.IP6_VERSION
752 raise errors.HypervisorError("spice: unable to get an IP address"
753 " for %s" % (spice_bind))
755 spice_address = addresses[spice_ip_version][0]
758 # spice_bind is known to be a valid IP address, because
759 # ValidateParameters checked it.
760 spice_address = spice_bind
762 spice_arg = "addr=%s,port=%s,disable-ticketing" % (spice_address,
763 instance.network_port)
765 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
767 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
768 kvm_cmd.extend(["-spice", spice_arg])
771 kvm_cmd.extend(["-nographic"])
773 if hvp[constants.HV_USE_LOCALTIME]:
774 kvm_cmd.extend(["-localtime"])
776 if hvp[constants.HV_KVM_USE_CHROOT]:
777 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
779 # Save the current instance nics, but defer their expansion as parameters,
780 # as we'll need to generate executable temp files for them.
781 kvm_nics = instance.nics
784 return (kvm_cmd, kvm_nics, hvparams)
786 def _WriteKVMRuntime(self, instance_name, data):
787 """Write an instance's KVM runtime
791 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
793 except EnvironmentError, err:
794 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
796 def _ReadKVMRuntime(self, instance_name):
797 """Read an instance's KVM runtime
801 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
802 except EnvironmentError, err:
803 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
806 def _SaveKVMRuntime(self, instance, kvm_runtime):
807 """Save an instance's KVM runtime
810 kvm_cmd, kvm_nics, hvparams = kvm_runtime
811 serialized_nics = [nic.ToDict() for nic in kvm_nics]
812 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
813 self._WriteKVMRuntime(instance.name, serialized_form)
815 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
816 """Load an instance's KVM runtime
819 if not serialized_runtime:
820 serialized_runtime = self._ReadKVMRuntime(instance.name)
821 loaded_runtime = serializer.Load(serialized_runtime)
822 kvm_cmd, serialized_nics, hvparams = loaded_runtime
823 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
824 return (kvm_cmd, kvm_nics, hvparams)
826 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
827 """Run the KVM cmd and check for errors
830 @param name: instance name
831 @type kvm_cmd: list of strings
832 @param kvm_cmd: runcmd input for kvm
833 @type tap_fds: list of int
834 @param tap_fds: fds of tap devices opened by Ganeti
838 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
841 utils_wrapper.CloseFdNoError(fd)
844 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
845 (name, result.fail_reason, result.output))
846 if not self._InstancePidAlive(name)[2]:
847 raise errors.HypervisorError("Failed to start instance %s" % name)
849 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
850 """Execute a KVM cmd, after completing it with some last minute data.
852 @type incoming: tuple of strings
853 @param incoming: (target_host_ip, port)
856 # Small _ExecuteKVMRuntime hv parameters programming howto:
857 # - conf_hvp contains the parameters as configured on ganeti. they might
858 # have changed since the instance started; only use them if the change
859 # won't affect the inside of the instance (which hasn't been rebooted).
860 # - up_hvp contains the parameters as they were when the instance was
861 # started, plus any new parameter which has been added between ganeti
862 # versions: it is paramount that those default to a value which won't
863 # affect the inside of the instance as well.
864 conf_hvp = instance.hvparams
866 self._CheckDown(name)
870 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
871 up_hvp = objects.FillDict(conf_hvp, up_hvp)
873 _, v_major, v_min, _ = self._GetKVMVersion()
875 # We know it's safe to run as a different user upon migration, so we'll use
876 # the latest conf, from conf_hvp.
877 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
878 if security_model == constants.HT_SM_USER:
879 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
881 keymap = conf_hvp[constants.HV_KEYMAP]
883 keymap_path = self._InstanceKeymapFile(name)
884 # If a keymap file is specified, KVM won't use its internal defaults. By
885 # first including the "en-us" layout, an error on loading the actual
886 # layout (e.g. because it can't be found) won't lead to a non-functional
887 # keyboard. A keyboard with incorrect keys is still better than none.
888 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
889 kvm_cmd.extend(["-k", keymap_path])
891 # We have reasons to believe changing something like the nic driver/type
892 # upon migration won't exactly fly with the instance kernel, so for nic
893 # related parameters we'll use up_hvp
897 kvm_cmd.extend(["-net", "none"])
901 nic_type = up_hvp[constants.HV_NIC_TYPE]
902 if nic_type == constants.HT_NIC_PARAVIRTUAL:
903 # From version 0.12.0, kvm uses a new sintax for network configuration.
904 if (v_major, v_min) >= (0, 12):
905 nic_model = "virtio-net-pci"
910 if up_hvp[constants.HV_VHOST_NET]:
911 # vhost_net is only available from version 0.13.0 or newer
912 if (v_major, v_min) >= (0, 13):
913 tap_extra = ",vhost=on"
915 raise errors.HypervisorError("vhost_net is configured"
916 " but it is not available")
920 for nic_seq, nic in enumerate(kvm_nics):
921 tapname, tapfd = _OpenTap(vnet_hdr)
924 if (v_major, v_min) >= (0, 12):
925 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
926 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
927 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
929 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
931 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
932 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
935 target, port = incoming
936 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
938 # Changing the vnc password doesn't bother the guest that much. At most it
939 # will surprise people who connect to it. Whether positively or negatively
941 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
945 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
946 except EnvironmentError, err:
947 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
948 % (vnc_pwd_file, err))
950 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
951 utils.EnsureDirs([(self._InstanceChrootDir(name),
952 constants.SECURE_DIR_MODE)])
954 # Configure the network now for starting instances and bridged interfaces,
955 # during FinalizeMigration for incoming instances' routed interfaces
956 for nic_seq, nic in enumerate(kvm_nics):
958 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
960 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
962 if security_model == constants.HT_SM_POOL:
963 ss = ssconf.SimpleStore()
964 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
965 all_uids = set(uidpool.ExpandUidPool(uid_pool))
966 uid = uidpool.RequestUnusedUid(all_uids)
968 username = pwd.getpwuid(uid.GetUid()).pw_name
969 kvm_cmd.extend(["-runas", username])
970 self._RunKVMCmd(name, kvm_cmd, tapfds)
972 uidpool.ReleaseUid(uid)
976 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
978 self._RunKVMCmd(name, kvm_cmd, tapfds)
980 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
981 constants.RUN_DIRS_MODE)])
982 for nic_seq, tap in enumerate(taps):
983 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
987 change_cmd = "change vnc password %s" % vnc_pwd
988 self._CallMonitorCommand(instance.name, change_cmd)
990 for filename in temp_files:
991 utils.RemoveFile(filename)
993 def StartInstance(self, instance, block_devices, startup_paused):
994 """Start an instance.
997 self._CheckDown(instance.name)
998 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1000 self._SaveKVMRuntime(instance, kvm_runtime)
1001 self._ExecuteKVMRuntime(instance, kvm_runtime)
1003 def _CallMonitorCommand(self, instance_name, command):
1004 """Invoke a command on the instance monitor.
1007 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1008 (utils.ShellQuote(command),
1009 constants.SOCAT_PATH,
1010 utils.ShellQuote(self._InstanceMonitor(instance_name))))
1011 result = utils.RunCmd(socat)
1013 msg = ("Failed to send command '%s' to instance %s."
1014 " output: %s, error: %s, fail_reason: %s" %
1015 (command, instance_name,
1016 result.stdout, result.stderr, result.fail_reason))
1017 raise errors.HypervisorError(msg)
1022 def _ParseKVMVersion(cls, text):
1023 """Parse the KVM version from the --help output.
1026 @param text: output of kvm --help
1027 @return: (version, v_maj, v_min, v_rev)
1028 @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1031 match = cls._VERSION_RE.search(text.splitlines()[0])
1033 raise errors.HypervisorError("Unable to get KVM version")
1035 v_all = match.group(0)
1036 v_maj = int(match.group(1))
1037 v_min = int(match.group(2))
1039 v_rev = int(match.group(4))
1042 return (v_all, v_maj, v_min, v_rev)
1045 def _GetKVMVersion(cls):
1046 """Return the installed KVM version.
1048 @return: (version, v_maj, v_min, v_rev)
1049 @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1052 result = utils.RunCmd([constants.KVM_PATH, "--help"])
1054 raise errors.HypervisorError("Unable to get KVM version")
1055 return cls._ParseKVMVersion(result.output)
1057 def StopInstance(self, instance, force=False, retry=False, name=None):
1058 """Stop an instance.
1061 if name is not None and not force:
1062 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1064 name = instance.name
1065 acpi = instance.hvparams[constants.HV_ACPI]
1068 _, pid, alive = self._InstancePidAlive(name)
1069 if pid > 0 and alive:
1070 if force or not acpi:
1071 utils.KillProcess(pid)
1073 self._CallMonitorCommand(name, "system_powerdown")
1075 def CleanupInstance(self, instance_name):
1076 """Cleanup after a stopped instance
1079 pidfile, pid, alive = self._InstancePidAlive(instance_name)
1080 if pid > 0 and alive:
1081 raise errors.HypervisorError("Cannot cleanup a live instance")
1082 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1084 def RebootInstance(self, instance):
1085 """Reboot an instance.
1088 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1089 # socket the instance will stop, but now power up again. So we'll resort
1090 # to shutdown and restart.
1091 _, _, alive = self._InstancePidAlive(instance.name)
1093 raise errors.HypervisorError("Failed to reboot instance %s:"
1094 " not running" % instance.name)
1095 # StopInstance will delete the saved KVM runtime so:
1096 # ...first load it...
1097 kvm_runtime = self._LoadKVMRuntime(instance)
1098 # ...now we can safely call StopInstance...
1099 if not self.StopInstance(instance):
1100 self.StopInstance(instance, force=True)
1101 # ...and finally we can save it again, and execute it...
1102 self._SaveKVMRuntime(instance, kvm_runtime)
1103 self._ExecuteKVMRuntime(instance, kvm_runtime)
1105 def MigrationInfo(self, instance):
1106 """Get instance information to perform a migration.
1108 @type instance: L{objects.Instance}
1109 @param instance: instance to be migrated
1111 @return: content of the KVM runtime file
1114 return self._ReadKVMRuntime(instance.name)
1116 def AcceptInstance(self, instance, info, target):
1117 """Prepare to accept an instance.
1119 @type instance: L{objects.Instance}
1120 @param instance: instance to be accepted
1122 @param info: content of the KVM runtime file on the source node
1123 @type target: string
1124 @param target: target host (usually ip), on this node
1127 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1128 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1129 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1131 def FinalizeMigration(self, instance, info, success):
1132 """Finalize an instance migration.
1134 Stop the incoming mode KVM.
1136 @type instance: L{objects.Instance}
1137 @param instance: instance whose migration is being finalized
1141 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1142 kvm_nics = kvm_runtime[1]
1144 for nic_seq, nic in enumerate(kvm_nics):
1145 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1146 # Bridged interfaces have already been configured
1149 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1150 except EnvironmentError, err:
1151 logging.warning("Failed to find host interface for %s NIC #%d: %s",
1152 instance.name, nic_seq, str(err))
1155 self._ConfigureNIC(instance, nic_seq, nic, tap)
1156 except errors.HypervisorError, err:
1157 logging.warning(str(err))
1159 self._WriteKVMRuntime(instance.name, info)
1161 self.StopInstance(instance, force=True)
1163 def MigrateInstance(self, instance, target, live):
1164 """Migrate an instance to a target node.
1166 The migration will not be attempted if the instance is not
1169 @type instance: L{objects.Instance}
1170 @param instance: the instance to be migrated
1171 @type target: string
1172 @param target: ip address of the target node
1174 @param live: perform a live migration
1177 instance_name = instance.name
1178 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1179 pidfile, pid, alive = self._InstancePidAlive(instance_name)
1181 raise errors.HypervisorError("Instance not running, cannot migrate")
1184 self._CallMonitorCommand(instance_name, "stop")
1186 migrate_command = ("migrate_set_speed %dm" %
1187 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1188 self._CallMonitorCommand(instance_name, migrate_command)
1190 migrate_command = ("migrate_set_downtime %dms" %
1191 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1192 self._CallMonitorCommand(instance_name, migrate_command)
1194 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1195 self._CallMonitorCommand(instance_name, migrate_command)
1197 info_command = "info migrate"
1201 result = self._CallMonitorCommand(instance_name, info_command)
1202 match = self._MIGRATION_STATUS_RE.search(result.stdout)
1205 if not result.stdout:
1206 logging.info("KVM: empty 'info migrate' result")
1208 logging.warning("KVM: unknown 'info migrate' result: %s",
1210 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1212 status = match.group(1)
1213 if status == "completed":
1215 elif status == "active":
1216 # reset the broken answers count
1218 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1219 elif status == "failed" or status == "cancelled":
1221 self._CallMonitorCommand(instance_name, 'cont')
1222 raise errors.HypervisorError("Migration %s at the kvm level" %
1225 logging.warning("KVM: unknown migration status '%s'", status)
1227 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1228 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1229 raise errors.HypervisorError("Too many 'info migrate' broken answers")
1231 utils.KillProcess(pid)
1232 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1234 def GetNodeInfo(self):
1235 """Return information about the node.
1237 This is just a wrapper over the base GetLinuxNodeInfo method.
1239 @return: a dict with the following keys (values in MiB):
1240 - memory_total: the total memory size on the node
1241 - memory_free: the available memory on the node for instances
1242 - memory_dom0: the memory used by the node itself, if available
1245 return self.GetLinuxNodeInfo()
1248 def GetInstanceConsole(cls, instance, hvparams, beparams):
1249 """Return a command for connecting to the console of an instance.
1252 if hvparams[constants.HV_SERIAL_CONSOLE]:
1253 cmd = [constants.KVM_CONSOLE_WRAPPER,
1254 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1255 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1256 "STDIO,%s" % cls._SocatUnixConsoleParams(),
1257 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1258 return objects.InstanceConsole(instance=instance.name,
1259 kind=constants.CONS_SSH,
1260 host=instance.primary_node,
1261 user=constants.GANETI_RUNAS,
1264 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1265 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1266 display = instance.network_port - constants.VNC_BASE_PORT
1267 return objects.InstanceConsole(instance=instance.name,
1268 kind=constants.CONS_VNC,
1269 host=vnc_bind_address,
1270 port=instance.network_port,
1273 return objects.InstanceConsole(instance=instance.name,
1274 kind=constants.CONS_MESSAGE,
1275 message=("No serial shell for instance %s" %
1279 """Verify the hypervisor.
1281 Check that the binary exists.
1284 if not os.path.exists(constants.KVM_PATH):
1285 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1286 if not os.path.exists(constants.SOCAT_PATH):
1287 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1290 def CheckParameterSyntax(cls, hvparams):
1291 """Check the given parameters for validity.
1293 @type hvparams: dict
1294 @param hvparams: dictionary with parameter names/value
1295 @raise errors.HypervisorError: when a parameter is not valid
1298 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1300 kernel_path = hvparams[constants.HV_KERNEL_PATH]
1302 if not hvparams[constants.HV_ROOT_PATH]:
1303 raise errors.HypervisorError("Need a root partition for the instance,"
1304 " if a kernel is defined")
1306 if (hvparams[constants.HV_VNC_X509_VERIFY] and
1307 not hvparams[constants.HV_VNC_X509]):
1308 raise errors.HypervisorError("%s must be defined, if %s is" %
1309 (constants.HV_VNC_X509,
1310 constants.HV_VNC_X509_VERIFY))
1312 boot_order = hvparams[constants.HV_BOOT_ORDER]
1313 if (boot_order == constants.HT_BO_CDROM and
1314 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1315 raise errors.HypervisorError("Cannot boot from cdrom without an"
1318 security_model = hvparams[constants.HV_SECURITY_MODEL]
1319 if security_model == constants.HT_SM_USER:
1320 if not hvparams[constants.HV_SECURITY_DOMAIN]:
1321 raise errors.HypervisorError("A security domain (user to run kvm as)"
1322 " must be specified")
1323 elif (security_model == constants.HT_SM_NONE or
1324 security_model == constants.HT_SM_POOL):
1325 if hvparams[constants.HV_SECURITY_DOMAIN]:
1326 raise errors.HypervisorError("Cannot have a security domain when the"
1327 " security model is 'none' or 'pool'")
1329 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1331 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
1332 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1333 # if an IP version is specified, the spice_bind parameter must be an
1335 if (netutils.IP4Address.IsValid(spice_bind) and
1336 spice_ip_version != constants.IP4_VERSION):
1337 raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
1338 " the specified IP version is %s" %
1339 (spice_bind, spice_ip_version))
1341 if (netutils.IP6Address.IsValid(spice_bind) and
1342 spice_ip_version != constants.IP6_VERSION):
1343 raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
1344 " the specified IP version is %s" %
1345 (spice_bind, spice_ip_version))
1348 def ValidateParameters(cls, hvparams):
1349 """Check the given parameters for validity.
1351 @type hvparams: dict
1352 @param hvparams: dictionary with parameter names/value
1353 @raise errors.HypervisorError: when a parameter is not valid
1356 super(KVMHypervisor, cls).ValidateParameters(hvparams)
1358 security_model = hvparams[constants.HV_SECURITY_MODEL]
1359 if security_model == constants.HT_SM_USER:
1360 username = hvparams[constants.HV_SECURITY_DOMAIN]
1362 pwd.getpwnam(username)
1364 raise errors.HypervisorError("Unknown security domain user %s"
1367 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1369 # only one of VNC and SPICE can be used currently.
1370 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
1371 raise errors.HypervisorError("both SPICE and VNC are configured, but"
1372 " only one of them can be used at a"
1375 # KVM version should be >= 0.14.0
1376 _, v_major, v_min, _ = cls._GetKVMVersion()
1377 if (v_major, v_min) < (0, 14):
1378 raise errors.HypervisorError("spice is configured, but it is not"
1379 " available in versions of KVM < 0.14")
1381 # if spice_bind is not an IP address, it must be a valid interface
1382 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
1383 or netutils.IP6Address.IsValid(spice_bind))
1384 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
1385 raise errors.HypervisorError("spice: the %s parameter must be either"
1386 " a valid IP address or interface name" %
1387 constants.HV_KVM_SPICE_BIND)
1390 def PowercycleNode(cls):
1391 """KVM powercycle, just a wrapper over Linux powercycle.
1394 cls.LinuxPowercycle()