4 # Copyright (C) 2008, 2009, 2010, 2011 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]
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.
527 _, v_major, v_min, _ = self._GetKVMVersion()
529 pidfile = self._InstancePidFile(instance.name)
530 kvm = constants.KVM_PATH
532 # used just by the vnc server, if enabled
533 kvm_cmd.extend(["-name", instance.name])
534 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MEMORY]])
535 kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
536 kvm_cmd.extend(["-pidfile", pidfile])
537 kvm_cmd.extend(["-daemonize"])
538 if not instance.hvparams[constants.HV_ACPI]:
539 kvm_cmd.extend(["-no-acpi"])
541 kvm_cmd.extend(["-S"])
542 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
543 constants.INSTANCE_REBOOT_EXIT:
544 kvm_cmd.extend(["-no-reboot"])
546 hvp = instance.hvparams
547 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
548 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
549 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
550 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
552 self.ValidateParameters(hvp)
554 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
555 kvm_cmd.extend(["-enable-kvm"])
556 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
557 kvm_cmd.extend(["-disable-kvm"])
560 kvm_cmd.extend(["-boot", "n"])
562 disk_type = hvp[constants.HV_DISK_TYPE]
563 if disk_type == constants.HT_DISK_PARAVIRTUAL:
564 if_val = ",if=virtio"
566 if_val = ",if=%s" % disk_type
568 disk_cache = hvp[constants.HV_DISK_CACHE]
569 if instance.disk_template in constants.DTS_EXT_MIRROR:
570 if disk_cache != "none":
571 # TODO: make this a hard error, instead of a silent overwrite
572 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
573 " to prevent shared storage corruption on migration",
575 cache_val = ",cache=none"
576 elif disk_cache != constants.HT_CACHE_DEFAULT:
577 cache_val = ",cache=%s" % disk_cache
580 for cfdev, dev_path in block_devices:
581 if cfdev.mode != constants.DISK_RDWR:
582 raise errors.HypervisorError("Instance has read-only disks which"
583 " are not supported by KVM")
584 # TODO: handle FD_LOOP and FD_BLKTAP (?)
587 kvm_cmd.extend(["-boot", "c"])
589 if (v_major, v_min) < (0, 14) and disk_type != constants.HT_DISK_IDE:
590 boot_val = ",boot=on"
592 drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
594 kvm_cmd.extend(["-drive", drive_val])
596 #Now we can specify a different device type for CDROM devices.
597 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
598 if not cdrom_disk_type:
599 cdrom_disk_type = disk_type
601 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
603 options = ",format=raw,media=cdrom"
605 kvm_cmd.extend(["-boot", "d"])
606 if cdrom_disk_type != constants.HT_DISK_IDE:
607 options = "%s,boot=on,if=%s" % (options, constants.HT_DISK_IDE)
609 options = "%s,boot=on" % options
611 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
612 if_val = ",if=virtio"
614 if_val = ",if=%s" % cdrom_disk_type
615 options = "%s%s" % (options, if_val)
616 drive_val = "file=%s%s" % (iso_image, options)
617 kvm_cmd.extend(["-drive", drive_val])
619 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
621 options = ",format=raw,media=cdrom"
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_image2, options)
628 kvm_cmd.extend(["-drive", drive_val])
630 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
632 options = ",format=raw,media=disk"
634 kvm_cmd.extend(["-boot", "a"])
635 options = "%s,boot=on" % options
636 if_val = ",if=floppy"
637 options = "%s%s" % (options, if_val)
638 drive_val = "file=%s%s" % (floppy_image, options)
639 kvm_cmd.extend(["-drive", drive_val])
641 kernel_path = hvp[constants.HV_KERNEL_PATH]
643 kvm_cmd.extend(["-kernel", kernel_path])
644 initrd_path = hvp[constants.HV_INITRD_PATH]
646 kvm_cmd.extend(["-initrd", initrd_path])
647 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
648 hvp[constants.HV_KERNEL_ARGS]]
649 if hvp[constants.HV_SERIAL_CONSOLE]:
650 root_append.append("console=ttyS0,38400")
651 kvm_cmd.extend(["-append", " ".join(root_append)])
653 mem_path = hvp[constants.HV_MEM_PATH]
655 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
657 monitor_dev = ("unix:%s,server,nowait" %
658 self._InstanceMonitor(instance.name))
659 kvm_cmd.extend(["-monitor", monitor_dev])
660 if hvp[constants.HV_SERIAL_CONSOLE]:
661 serial_dev = ("unix:%s,server,nowait" %
662 self._InstanceSerial(instance.name))
663 kvm_cmd.extend(["-serial", serial_dev])
665 kvm_cmd.extend(["-serial", "none"])
667 mouse_type = hvp[constants.HV_USB_MOUSE]
668 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
669 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
670 spice_ip_version = None
673 kvm_cmd.extend(["-usb"])
674 kvm_cmd.extend(["-usbdevice", mouse_type])
675 elif vnc_bind_address:
676 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
678 keymap = hvp[constants.HV_KEYMAP]
680 keymap_path = self._InstanceKeymapFile(instance.name)
681 # If a keymap file is specified, KVM won't use its internal defaults. By
682 # first including the "en-us" layout, an error on loading the actual
683 # layout (e.g. because it can't be found) won't lead to a non-functional
684 # keyboard. A keyboard with incorrect keys is still better than none.
685 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
686 kvm_cmd.extend(["-k", keymap_path])
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 if netutils.IsValidInterface(spice_bind):
724 # The user specified a network interface, we have to figure out the IP
726 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
727 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
729 # if the user specified an IP version and the interface does not
730 # have that kind of IP addresses, throw an exception
731 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
732 if not addresses[spice_ip_version]:
733 raise errors.HypervisorError("spice: unable to get an IPv%s address"
734 " for %s" % (spice_ip_version,
737 # the user did not specify an IP version, we have to figure it out
738 elif (addresses[constants.IP4_VERSION] and
739 addresses[constants.IP6_VERSION]):
740 # we have both ipv4 and ipv6, let's use the cluster default IP
742 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
743 spice_ip_version = netutils.IPAddress.GetVersionFromAddressFamily(
745 elif addresses[constants.IP4_VERSION]:
746 spice_ip_version = constants.IP4_VERSION
747 elif addresses[constants.IP6_VERSION]:
748 spice_ip_version = constants.IP6_VERSION
750 raise errors.HypervisorError("spice: unable to get an IP address"
751 " for %s" % (spice_bind))
753 spice_address = addresses[spice_ip_version][0]
756 # spice_bind is known to be a valid IP address, because
757 # ValidateParameters checked it.
758 spice_address = spice_bind
760 spice_arg = "addr=%s,port=%s,disable-ticketing" % (spice_address,
761 instance.network_port)
763 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
765 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
766 kvm_cmd.extend(["-spice", spice_arg])
769 kvm_cmd.extend(["-nographic"])
771 if hvp[constants.HV_USE_LOCALTIME]:
772 kvm_cmd.extend(["-localtime"])
774 if hvp[constants.HV_KVM_USE_CHROOT]:
775 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
777 # Save the current instance nics, but defer their expansion as parameters,
778 # as we'll need to generate executable temp files for them.
779 kvm_nics = instance.nics
782 return (kvm_cmd, kvm_nics, hvparams)
784 def _WriteKVMRuntime(self, instance_name, data):
785 """Write an instance's KVM runtime
789 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
791 except EnvironmentError, err:
792 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
794 def _ReadKVMRuntime(self, instance_name):
795 """Read an instance's KVM runtime
799 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
800 except EnvironmentError, err:
801 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
804 def _SaveKVMRuntime(self, instance, kvm_runtime):
805 """Save an instance's KVM runtime
808 kvm_cmd, kvm_nics, hvparams = kvm_runtime
809 serialized_nics = [nic.ToDict() for nic in kvm_nics]
810 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
811 self._WriteKVMRuntime(instance.name, serialized_form)
813 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
814 """Load an instance's KVM runtime
817 if not serialized_runtime:
818 serialized_runtime = self._ReadKVMRuntime(instance.name)
819 loaded_runtime = serializer.Load(serialized_runtime)
820 kvm_cmd, serialized_nics, hvparams = loaded_runtime
821 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
822 return (kvm_cmd, kvm_nics, hvparams)
824 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
825 """Run the KVM cmd and check for errors
828 @param name: instance name
829 @type kvm_cmd: list of strings
830 @param kvm_cmd: runcmd input for kvm
831 @type tap_fds: list of int
832 @param tap_fds: fds of tap devices opened by Ganeti
836 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
839 utils_wrapper.CloseFdNoError(fd)
842 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
843 (name, result.fail_reason, result.output))
844 if not self._InstancePidAlive(name)[2]:
845 raise errors.HypervisorError("Failed to start instance %s" % name)
847 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
848 """Execute a KVM cmd, after completing it with some last minute data
850 @type incoming: tuple of strings
851 @param incoming: (target_host_ip, port)
854 # Small _ExecuteKVMRuntime hv parameters programming howto:
855 # - conf_hvp contains the parameters as configured on ganeti. they might
856 # have changed since the instance started; only use them if the change
857 # won't affect the inside of the instance (which hasn't been rebooted).
858 # - up_hvp contains the parameters as they were when the instance was
859 # started, plus any new parameter which has been added between ganeti
860 # versions: it is paramount that those default to a value which won't
861 # affect the inside of the instance as well.
862 conf_hvp = instance.hvparams
864 self._CheckDown(name)
868 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
869 up_hvp = objects.FillDict(conf_hvp, up_hvp)
871 _, v_major, v_min, _ = self._GetKVMVersion()
873 # We know it's safe to run as a different user upon migration, so we'll use
874 # the latest conf, from conf_hvp.
875 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
876 if security_model == constants.HT_SM_USER:
877 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
879 # We have reasons to believe changing something like the nic driver/type
880 # upon migration won't exactly fly with the instance kernel, so for nic
881 # related parameters we'll use up_hvp
885 kvm_cmd.extend(["-net", "none"])
889 nic_type = up_hvp[constants.HV_NIC_TYPE]
890 if nic_type == constants.HT_NIC_PARAVIRTUAL:
891 # From version 0.12.0, kvm uses a new sintax for network configuration.
892 if (v_major, v_min) >= (0, 12):
893 nic_model = "virtio-net-pci"
898 if up_hvp[constants.HV_VHOST_NET]:
899 # vhost_net is only available from version 0.13.0 or newer
900 if (v_major, v_min) >= (0, 13):
901 tap_extra = ",vhost=on"
903 raise errors.HypervisorError("vhost_net is configured"
904 " but it is not available")
908 for nic_seq, nic in enumerate(kvm_nics):
909 tapname, tapfd = _OpenTap(vnet_hdr)
912 if (v_major, v_min) >= (0, 12):
913 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
914 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
915 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
917 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
919 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
920 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
923 target, port = incoming
924 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
926 # Changing the vnc password doesn't bother the guest that much. At most it
927 # will surprise people who connect to it. Whether positively or negatively
929 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
933 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
934 except EnvironmentError, err:
935 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
936 % (vnc_pwd_file, err))
938 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
939 utils.EnsureDirs([(self._InstanceChrootDir(name),
940 constants.SECURE_DIR_MODE)])
942 # Configure the network now for starting instances and bridged interfaces,
943 # during FinalizeMigration for incoming instances' routed interfaces
944 for nic_seq, nic in enumerate(kvm_nics):
946 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
948 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
950 if security_model == constants.HT_SM_POOL:
951 ss = ssconf.SimpleStore()
952 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
953 all_uids = set(uidpool.ExpandUidPool(uid_pool))
954 uid = uidpool.RequestUnusedUid(all_uids)
956 username = pwd.getpwuid(uid.GetUid()).pw_name
957 kvm_cmd.extend(["-runas", username])
958 self._RunKVMCmd(name, kvm_cmd, tapfds)
960 uidpool.ReleaseUid(uid)
964 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
966 self._RunKVMCmd(name, kvm_cmd, tapfds)
968 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
969 constants.RUN_DIRS_MODE)])
970 for nic_seq, tap in enumerate(taps):
971 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
975 change_cmd = "change vnc password %s" % vnc_pwd
976 self._CallMonitorCommand(instance.name, change_cmd)
978 for filename in temp_files:
979 utils.RemoveFile(filename)
981 def StartInstance(self, instance, block_devices, startup_paused):
982 """Start an instance.
985 self._CheckDown(instance.name)
986 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
988 self._SaveKVMRuntime(instance, kvm_runtime)
989 self._ExecuteKVMRuntime(instance, kvm_runtime)
991 def _CallMonitorCommand(self, instance_name, command):
992 """Invoke a command on the instance monitor.
995 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
996 (utils.ShellQuote(command),
997 constants.SOCAT_PATH,
998 utils.ShellQuote(self._InstanceMonitor(instance_name))))
999 result = utils.RunCmd(socat)
1001 msg = ("Failed to send command '%s' to instance %s."
1002 " output: %s, error: %s, fail_reason: %s" %
1003 (command, instance_name,
1004 result.stdout, result.stderr, result.fail_reason))
1005 raise errors.HypervisorError(msg)
1010 def _ParseKVMVersion(cls, text):
1011 """Parse the KVM version from the --help output.
1014 @param text: output of kvm --help
1015 @return: (version, v_maj, v_min, v_rev)
1016 @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1019 match = cls._VERSION_RE.search(text.splitlines()[0])
1021 raise errors.HypervisorError("Unable to get KVM version")
1023 v_all = match.group(0)
1024 v_maj = int(match.group(1))
1025 v_min = int(match.group(2))
1027 v_rev = int(match.group(4))
1030 return (v_all, v_maj, v_min, v_rev)
1033 def _GetKVMVersion(cls):
1034 """Return the installed KVM version.
1036 @return: (version, v_maj, v_min, v_rev)
1037 @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1040 result = utils.RunCmd([constants.KVM_PATH, "--help"])
1042 raise errors.HypervisorError("Unable to get KVM version")
1043 return cls._ParseKVMVersion(result.output)
1045 def StopInstance(self, instance, force=False, retry=False, name=None):
1046 """Stop an instance.
1049 if name is not None and not force:
1050 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1052 name = instance.name
1053 acpi = instance.hvparams[constants.HV_ACPI]
1056 _, pid, alive = self._InstancePidAlive(name)
1057 if pid > 0 and alive:
1058 if force or not acpi:
1059 utils.KillProcess(pid)
1061 self._CallMonitorCommand(name, "system_powerdown")
1063 def CleanupInstance(self, instance_name):
1064 """Cleanup after a stopped instance
1067 pidfile, pid, alive = self._InstancePidAlive(instance_name)
1068 if pid > 0 and alive:
1069 raise errors.HypervisorError("Cannot cleanup a live instance")
1070 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1072 def RebootInstance(self, instance):
1073 """Reboot an instance.
1076 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1077 # socket the instance will stop, but now power up again. So we'll resort
1078 # to shutdown and restart.
1079 _, _, alive = self._InstancePidAlive(instance.name)
1081 raise errors.HypervisorError("Failed to reboot instance %s:"
1082 " not running" % instance.name)
1083 # StopInstance will delete the saved KVM runtime so:
1084 # ...first load it...
1085 kvm_runtime = self._LoadKVMRuntime(instance)
1086 # ...now we can safely call StopInstance...
1087 if not self.StopInstance(instance):
1088 self.StopInstance(instance, force=True)
1089 # ...and finally we can save it again, and execute it...
1090 self._SaveKVMRuntime(instance, kvm_runtime)
1091 self._ExecuteKVMRuntime(instance, kvm_runtime)
1093 def MigrationInfo(self, instance):
1094 """Get instance information to perform a migration.
1096 @type instance: L{objects.Instance}
1097 @param instance: instance to be migrated
1099 @return: content of the KVM runtime file
1102 return self._ReadKVMRuntime(instance.name)
1104 def AcceptInstance(self, instance, info, target):
1105 """Prepare to accept an instance.
1107 @type instance: L{objects.Instance}
1108 @param instance: instance to be accepted
1110 @param info: content of the KVM runtime file on the source node
1111 @type target: string
1112 @param target: target host (usually ip), on this node
1115 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1116 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1117 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1119 def FinalizeMigration(self, instance, info, success):
1120 """Finalize an instance migration.
1122 Stop the incoming mode KVM.
1124 @type instance: L{objects.Instance}
1125 @param instance: instance whose migration is being finalized
1129 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1130 kvm_nics = kvm_runtime[1]
1132 for nic_seq, nic in enumerate(kvm_nics):
1133 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1134 # Bridged interfaces have already been configured
1137 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1138 except EnvironmentError, err:
1139 logging.warning("Failed to find host interface for %s NIC #%d: %s",
1140 instance.name, nic_seq, str(err))
1143 self._ConfigureNIC(instance, nic_seq, nic, tap)
1144 except errors.HypervisorError, err:
1145 logging.warning(str(err))
1147 self._WriteKVMRuntime(instance.name, info)
1149 self.StopInstance(instance, force=True)
1151 def MigrateInstance(self, instance, target, live):
1152 """Migrate an instance to a target node.
1154 The migration will not be attempted if the instance is not
1157 @type instance: L{objects.Instance}
1158 @param instance: the instance to be migrated
1159 @type target: string
1160 @param target: ip address of the target node
1162 @param live: perform a live migration
1165 instance_name = instance.name
1166 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1167 pidfile, pid, alive = self._InstancePidAlive(instance_name)
1169 raise errors.HypervisorError("Instance not running, cannot migrate")
1172 self._CallMonitorCommand(instance_name, "stop")
1174 migrate_command = ("migrate_set_speed %dm" %
1175 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1176 self._CallMonitorCommand(instance_name, migrate_command)
1178 migrate_command = ("migrate_set_downtime %dms" %
1179 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1180 self._CallMonitorCommand(instance_name, migrate_command)
1182 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1183 self._CallMonitorCommand(instance_name, migrate_command)
1185 info_command = "info migrate"
1189 result = self._CallMonitorCommand(instance_name, info_command)
1190 match = self._MIGRATION_STATUS_RE.search(result.stdout)
1193 if not result.stdout:
1194 logging.info("KVM: empty 'info migrate' result")
1196 logging.warning("KVM: unknown 'info migrate' result: %s",
1198 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1200 status = match.group(1)
1201 if status == "completed":
1203 elif status == "active":
1204 # reset the broken answers count
1206 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1207 elif status == "failed" or status == "cancelled":
1209 self._CallMonitorCommand(instance_name, 'cont')
1210 raise errors.HypervisorError("Migration %s at the kvm level" %
1213 logging.warning("KVM: unknown migration status '%s'", status)
1215 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1216 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1217 raise errors.HypervisorError("Too many 'info migrate' broken answers")
1219 utils.KillProcess(pid)
1220 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1222 def GetNodeInfo(self):
1223 """Return information about the node.
1225 This is just a wrapper over the base GetLinuxNodeInfo method.
1227 @return: a dict with the following keys (values in MiB):
1228 - memory_total: the total memory size on the node
1229 - memory_free: the available memory on the node for instances
1230 - memory_dom0: the memory used by the node itself, if available
1233 return self.GetLinuxNodeInfo()
1236 def GetInstanceConsole(cls, instance, hvparams, beparams):
1237 """Return a command for connecting to the console of an instance.
1240 if hvparams[constants.HV_SERIAL_CONSOLE]:
1241 cmd = [constants.KVM_CONSOLE_WRAPPER,
1242 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1243 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1244 "STDIO,%s" % cls._SocatUnixConsoleParams(),
1245 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1246 return objects.InstanceConsole(instance=instance.name,
1247 kind=constants.CONS_SSH,
1248 host=instance.primary_node,
1249 user=constants.GANETI_RUNAS,
1252 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1253 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1254 display = instance.network_port - constants.VNC_BASE_PORT
1255 return objects.InstanceConsole(instance=instance.name,
1256 kind=constants.CONS_VNC,
1257 host=vnc_bind_address,
1258 port=instance.network_port,
1261 return objects.InstanceConsole(instance=instance.name,
1262 kind=constants.CONS_MESSAGE,
1263 message=("No serial shell for instance %s" %
1267 """Verify the hypervisor.
1269 Check that the binary exists.
1272 if not os.path.exists(constants.KVM_PATH):
1273 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1274 if not os.path.exists(constants.SOCAT_PATH):
1275 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1278 def CheckParameterSyntax(cls, hvparams):
1279 """Check the given parameters for validity.
1281 @type hvparams: dict
1282 @param hvparams: dictionary with parameter names/value
1283 @raise errors.HypervisorError: when a parameter is not valid
1286 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1288 kernel_path = hvparams[constants.HV_KERNEL_PATH]
1290 if not hvparams[constants.HV_ROOT_PATH]:
1291 raise errors.HypervisorError("Need a root partition for the instance,"
1292 " if a kernel is defined")
1294 if (hvparams[constants.HV_VNC_X509_VERIFY] and
1295 not hvparams[constants.HV_VNC_X509]):
1296 raise errors.HypervisorError("%s must be defined, if %s is" %
1297 (constants.HV_VNC_X509,
1298 constants.HV_VNC_X509_VERIFY))
1300 boot_order = hvparams[constants.HV_BOOT_ORDER]
1301 if (boot_order == constants.HT_BO_CDROM and
1302 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1303 raise errors.HypervisorError("Cannot boot from cdrom without an"
1306 security_model = hvparams[constants.HV_SECURITY_MODEL]
1307 if security_model == constants.HT_SM_USER:
1308 if not hvparams[constants.HV_SECURITY_DOMAIN]:
1309 raise errors.HypervisorError("A security domain (user to run kvm as)"
1310 " must be specified")
1311 elif (security_model == constants.HT_SM_NONE or
1312 security_model == constants.HT_SM_POOL):
1313 if hvparams[constants.HV_SECURITY_DOMAIN]:
1314 raise errors.HypervisorError("Cannot have a security domain when the"
1315 " security model is 'none' or 'pool'")
1317 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1319 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
1320 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1321 # if an IP version is specified, the spice_bind parameter must be an
1323 if (netutils.IP4Address.IsValid(spice_bind) and
1324 spice_ip_version != constants.IP4_VERSION):
1325 raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
1326 " the specified IP version is %s" %
1327 (spice_bind, spice_ip_version))
1329 if (netutils.IP6Address.IsValid(spice_bind) and
1330 spice_ip_version != constants.IP6_VERSION):
1331 raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
1332 " the specified IP version is %s" %
1333 (spice_bind, spice_ip_version))
1336 def ValidateParameters(cls, hvparams):
1337 """Check the given parameters for validity.
1339 @type hvparams: dict
1340 @param hvparams: dictionary with parameter names/value
1341 @raise errors.HypervisorError: when a parameter is not valid
1344 super(KVMHypervisor, cls).ValidateParameters(hvparams)
1346 security_model = hvparams[constants.HV_SECURITY_MODEL]
1347 if security_model == constants.HT_SM_USER:
1348 username = hvparams[constants.HV_SECURITY_DOMAIN]
1350 pwd.getpwnam(username)
1352 raise errors.HypervisorError("Unknown security domain user %s"
1355 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1357 # only one of VNC and SPICE can be used currently.
1358 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
1359 raise errors.HypervisorError("both SPICE and VNC are configured, but"
1360 " only one of them can be used at a"
1363 # KVM version should be >= 0.14.0
1364 _, v_major, v_min, _ = cls._GetKVMVersion()
1365 if (v_major, v_min) < (0, 14):
1366 raise errors.HypervisorError("spice is configured, but it is not"
1367 " available in versions of KVM < 0.14")
1369 # if spice_bind is not an IP address, it must be a valid interface
1370 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
1371 or netutils.IP6Address.IsValid(spice_bind))
1372 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
1373 raise errors.HypervisorError("spice: the %s parameter must be either"
1374 " a valid IP address or interface name" %
1375 constants.HV_KVM_SPICE_BIND)
1378 def PowercycleNode(cls):
1379 """KVM powercycle, just a wrapper over Linux powercycle.
1382 cls.LinuxPowercycle()