4 # Copyright (C) 2008, 2009, 2010 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 # KVM instances with chroot enabled are started in empty chroot directories.
141 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
142 # After an instance is stopped, its chroot directory is removed.
143 # If the chroot directory is not empty, it can't be removed.
144 # A non-empty chroot directory indicates a possible security incident.
145 # To support forensics, the non-empty chroot directory is quarantined in
146 # a separate directory, called 'chroot-quarantine'.
147 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
148 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
149 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
152 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
153 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
154 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
155 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
156 constants.HV_ACPI: hv_base.NO_CHECK,
157 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
158 constants.HV_VNC_BIND_ADDRESS:
159 (False, lambda x: (netutils.IP4Address.IsValid(x) or
160 utils.IsNormAbsPath(x)),
161 "the VNC bind address must be either a valid IP address or an absolute"
162 " pathname", None, None),
163 constants.HV_VNC_TLS: hv_base.NO_CHECK,
164 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
165 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
166 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
167 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
168 constants.HV_BOOT_ORDER:
169 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
170 constants.HV_NIC_TYPE:
171 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
172 constants.HV_DISK_TYPE:
173 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
174 constants.HV_USB_MOUSE:
175 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
176 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
177 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
178 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
179 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
180 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
181 constants.HV_DISK_CACHE:
182 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
183 constants.HV_SECURITY_MODEL:
184 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
185 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
186 constants.HV_KVM_FLAG:
187 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
188 constants.HV_VHOST_NET: hv_base.NO_CHECK,
189 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
190 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
193 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
195 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
196 _MIGRATION_INFO_RETRY_DELAY = 2
198 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
205 hv_base.BaseHypervisor.__init__(self)
206 # Let's make sure the directories we need exist, even if the RUN_DIR lives
207 # in a tmpfs filesystem or has been otherwise wiped out.
208 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
209 utils.EnsureDirs(dirs)
212 def _InstancePidFile(cls, instance_name):
213 """Returns the instance pidfile.
216 return utils.PathJoin(cls._PIDS_DIR, instance_name)
219 def _InstanceUidFile(cls, instance_name):
220 """Returns the instance uidfile.
223 return utils.PathJoin(cls._UIDS_DIR, instance_name)
226 def _InstancePidInfo(cls, pid):
227 """Check pid file for instance information.
229 Check that a pid file is associated with an instance, and retrieve
230 information from its command line.
232 @type pid: string or int
233 @param pid: process id of the instance to check
235 @return: (instance_name, memory, vcpus)
236 @raise errors.HypervisorError: when an instance cannot be found
239 alive = utils.IsProcessAlive(pid)
241 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
243 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
245 cmdline = utils.ReadFile(cmdline_file)
246 except EnvironmentError, err:
247 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
254 arg_list = cmdline.split('\x00')
256 arg = arg_list.pop(0)
258 instance = arg_list.pop(0)
260 memory = int(arg_list.pop(0))
262 vcpus = int(arg_list.pop(0))
265 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
268 return (instance, memory, vcpus)
270 def _InstancePidAlive(self, instance_name):
271 """Returns the instance pidfile, pid, and liveness.
273 @type instance_name: string
274 @param instance_name: instance name
276 @return: (pid file name, pid, liveness)
279 pidfile = self._InstancePidFile(instance_name)
280 pid = utils.ReadPidFile(pidfile)
284 cmd_instance = self._InstancePidInfo(pid)[0]
285 alive = (cmd_instance == instance_name)
286 except errors.HypervisorError:
289 return (pidfile, pid, alive)
291 def _CheckDown(self, instance_name):
292 """Raises an error unless the given instance is down.
295 alive = self._InstancePidAlive(instance_name)[2]
297 raise errors.HypervisorError("Failed to start instance %s: %s" %
298 (instance_name, "already running"))
301 def _InstanceMonitor(cls, instance_name):
302 """Returns the instance monitor socket name
305 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
308 def _InstanceSerial(cls, instance_name):
309 """Returns the instance serial socket name
312 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
315 def _SocatUnixConsoleParams():
316 """Returns the correct parameters for socat
318 If we have a new-enough socat we can use raw mode with an escape character.
321 if constants.SOCAT_USE_ESCAPE:
322 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
324 return "echo=0,icanon=0"
327 def _InstanceKVMRuntime(cls, instance_name):
328 """Returns the instance KVM runtime filename
331 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
334 def _InstanceChrootDir(cls, instance_name):
335 """Returns the name of the KVM chroot dir of the instance
338 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
341 def _InstanceNICDir(cls, instance_name):
342 """Returns the name of the directory holding the tap device files for a
346 return utils.PathJoin(cls._NICS_DIR, instance_name)
349 def _InstanceNICFile(cls, instance_name, seq):
350 """Returns the name of the file containing the tap device for a given NIC
353 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
356 def _TryReadUidFile(cls, uid_file):
357 """Try to read a uid file
360 if os.path.exists(uid_file):
362 uid = int(utils.ReadOneLineFile(uid_file))
364 except EnvironmentError:
365 logging.warning("Can't read uid file", exc_info=True)
366 except (TypeError, ValueError):
367 logging.warning("Can't parse uid file contents", exc_info=True)
371 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
372 """Removes an instance's rutime sockets/files/dirs.
375 utils.RemoveFile(pidfile)
376 utils.RemoveFile(cls._InstanceMonitor(instance_name))
377 utils.RemoveFile(cls._InstanceSerial(instance_name))
378 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
379 uid_file = cls._InstanceUidFile(instance_name)
380 uid = cls._TryReadUidFile(uid_file)
381 utils.RemoveFile(uid_file)
383 uidpool.ReleaseUid(uid)
384 shutil.rmtree(cls._InstanceNICDir(instance_name))
386 chroot_dir = cls._InstanceChrootDir(instance_name)
387 utils.RemoveDir(chroot_dir)
389 if err.errno == errno.ENOTEMPTY:
390 # The chroot directory is expected to be empty, but it isn't.
391 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
394 utils.TimestampForFilename()))
395 logging.warning("The chroot directory of instance %s can not be"
396 " removed as it is not empty. Moving it to the"
397 " quarantine instead. Please investigate the"
398 " contents (%s) and clean up manually",
399 instance_name, new_chroot_dir)
400 utils.RenameFile(chroot_dir, new_chroot_dir)
405 def _ConfigureNIC(instance, seq, nic, tap):
406 """Run the network configuration script for a specified NIC
408 @param instance: instance we're acting on
409 @type instance: instance object
410 @param seq: nic sequence number
412 @param nic: nic we're acting on
413 @type nic: nic object
414 @param tap: the host's tap interface this NIC corresponds to
420 tags = " ".join(instance.tags)
425 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
426 "INSTANCE": instance.name,
428 "MODE": nic.nicparams[constants.NIC_MODE],
430 "INTERFACE_INDEX": str(seq),
437 if nic.nicparams[constants.NIC_LINK]:
438 env["LINK"] = nic.nicparams[constants.NIC_LINK]
440 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
441 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
443 result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
445 raise errors.HypervisorError("Failed to configure interface %s: %s."
446 " Network configuration script output: %s" %
447 (tap, result.fail_reason, result.output))
449 def ListInstances(self):
450 """Get the list of running instances.
452 We can do this by listing our live instances directory and
453 checking whether the associated kvm process is still alive.
457 for name in os.listdir(self._PIDS_DIR):
458 if self._InstancePidAlive(name)[2]:
462 def GetInstanceInfo(self, instance_name):
463 """Get instance properties.
465 @type instance_name: string
466 @param instance_name: the instance name
467 @rtype: tuple of strings
468 @return: (name, id, memory, vcpus, stat, times)
471 _, pid, alive = self._InstancePidAlive(instance_name)
475 _, memory, vcpus = self._InstancePidInfo(pid)
479 return (instance_name, pid, memory, vcpus, stat, times)
481 def GetAllInstancesInfo(self):
482 """Get properties of all instances.
484 @return: list of tuples (name, id, memory, vcpus, stat, times)
488 for name in os.listdir(self._PIDS_DIR):
490 info = self.GetInstanceInfo(name)
491 except errors.HypervisorError:
497 def _GenerateKVMRuntime(self, instance, block_devices):
498 """Generate KVM information to start an instance.
501 pidfile = self._InstancePidFile(instance.name)
502 kvm = constants.KVM_PATH
504 # used just by the vnc server, if enabled
505 kvm_cmd.extend(['-name', instance.name])
506 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
507 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
508 kvm_cmd.extend(['-pidfile', pidfile])
509 kvm_cmd.extend(['-daemonize'])
510 if not instance.hvparams[constants.HV_ACPI]:
511 kvm_cmd.extend(['-no-acpi'])
513 hvp = instance.hvparams
514 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
515 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
516 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
518 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
519 kvm_cmd.extend(["-enable-kvm"])
520 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
521 kvm_cmd.extend(["-disable-kvm"])
524 kvm_cmd.extend(['-boot', 'n'])
526 disk_type = hvp[constants.HV_DISK_TYPE]
527 if disk_type == constants.HT_DISK_PARAVIRTUAL:
528 if_val = ',if=virtio'
530 if_val = ',if=%s' % disk_type
532 disk_cache = hvp[constants.HV_DISK_CACHE]
533 if disk_cache != constants.HT_CACHE_DEFAULT:
534 cache_val = ",cache=%s" % disk_cache
537 for cfdev, dev_path in block_devices:
538 if cfdev.mode != constants.DISK_RDWR:
539 raise errors.HypervisorError("Instance has read-only disks which"
540 " are not supported by KVM")
541 # TODO: handle FD_LOOP and FD_BLKTAP (?)
543 kvm_cmd.extend(['-boot', 'c'])
544 if disk_type != constants.HT_DISK_IDE:
545 boot_val = ',boot=on'
548 # We only boot from the first disk
553 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
555 kvm_cmd.extend(['-drive', drive_val])
557 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
559 options = ',format=raw,media=cdrom'
561 kvm_cmd.extend(['-boot', 'd'])
562 if disk_type != constants.HT_DISK_IDE:
563 options = '%s,boot=on' % options
565 if disk_type == constants.HT_DISK_PARAVIRTUAL:
566 if_val = ',if=virtio'
568 if_val = ',if=%s' % disk_type
569 options = '%s%s' % (options, if_val)
570 drive_val = 'file=%s%s' % (iso_image, options)
571 kvm_cmd.extend(['-drive', drive_val])
573 kernel_path = hvp[constants.HV_KERNEL_PATH]
575 kvm_cmd.extend(['-kernel', kernel_path])
576 initrd_path = hvp[constants.HV_INITRD_PATH]
578 kvm_cmd.extend(['-initrd', initrd_path])
579 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
580 hvp[constants.HV_KERNEL_ARGS]]
581 if hvp[constants.HV_SERIAL_CONSOLE]:
582 root_append.append('console=ttyS0,38400')
583 kvm_cmd.extend(['-append', ' '.join(root_append)])
585 mem_path = hvp[constants.HV_MEM_PATH]
587 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
589 mouse_type = hvp[constants.HV_USB_MOUSE]
590 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
593 kvm_cmd.extend(['-usb'])
594 kvm_cmd.extend(['-usbdevice', mouse_type])
595 elif vnc_bind_address:
596 kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
599 if netutils.IP4Address.IsValid(vnc_bind_address):
600 if instance.network_port > constants.VNC_BASE_PORT:
601 display = instance.network_port - constants.VNC_BASE_PORT
602 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
603 vnc_arg = ':%d' % (display)
605 vnc_arg = '%s:%d' % (vnc_bind_address, display)
607 logging.error("Network port is not a valid VNC display (%d < %d)."
608 " Not starting VNC", instance.network_port,
609 constants.VNC_BASE_PORT)
612 # Only allow tls and other option when not binding to a file, for now.
613 # kvm/qemu gets confused otherwise about the filename to use.
615 if hvp[constants.HV_VNC_TLS]:
616 vnc_append = '%s,tls' % vnc_append
617 if hvp[constants.HV_VNC_X509_VERIFY]:
618 vnc_append = '%s,x509verify=%s' % (vnc_append,
619 hvp[constants.HV_VNC_X509])
620 elif hvp[constants.HV_VNC_X509]:
621 vnc_append = '%s,x509=%s' % (vnc_append,
622 hvp[constants.HV_VNC_X509])
623 if hvp[constants.HV_VNC_PASSWORD_FILE]:
624 vnc_append = '%s,password' % vnc_append
626 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
629 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
631 kvm_cmd.extend(['-vnc', vnc_arg])
633 kvm_cmd.extend(['-nographic'])
635 monitor_dev = ("unix:%s,server,nowait" %
636 self._InstanceMonitor(instance.name))
637 kvm_cmd.extend(['-monitor', monitor_dev])
638 if hvp[constants.HV_SERIAL_CONSOLE]:
639 serial_dev = ('unix:%s,server,nowait' %
640 self._InstanceSerial(instance.name))
641 kvm_cmd.extend(['-serial', serial_dev])
643 kvm_cmd.extend(['-serial', 'none'])
645 if hvp[constants.HV_USE_LOCALTIME]:
646 kvm_cmd.extend(['-localtime'])
648 if hvp[constants.HV_KVM_USE_CHROOT]:
649 kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
651 # Save the current instance nics, but defer their expansion as parameters,
652 # as we'll need to generate executable temp files for them.
653 kvm_nics = instance.nics
656 return (kvm_cmd, kvm_nics, hvparams)
658 def _WriteKVMRuntime(self, instance_name, data):
659 """Write an instance's KVM runtime
663 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
665 except EnvironmentError, err:
666 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
668 def _ReadKVMRuntime(self, instance_name):
669 """Read an instance's KVM runtime
673 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
674 except EnvironmentError, err:
675 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
678 def _SaveKVMRuntime(self, instance, kvm_runtime):
679 """Save an instance's KVM runtime
682 kvm_cmd, kvm_nics, hvparams = kvm_runtime
683 serialized_nics = [nic.ToDict() for nic in kvm_nics]
684 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
685 self._WriteKVMRuntime(instance.name, serialized_form)
687 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
688 """Load an instance's KVM runtime
691 if not serialized_runtime:
692 serialized_runtime = self._ReadKVMRuntime(instance.name)
693 loaded_runtime = serializer.Load(serialized_runtime)
694 kvm_cmd, serialized_nics, hvparams = loaded_runtime
695 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
696 return (kvm_cmd, kvm_nics, hvparams)
698 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
699 """Run the KVM cmd and check for errors
702 @param name: instance name
703 @type kvm_cmd: list of strings
704 @param kvm_cmd: runcmd input for kvm
705 @type tap_fds: list of int
706 @param tap_fds: fds of tap devices opened by Ganeti
710 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
713 utils_wrapper.CloseFdNoError(fd)
716 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
717 (name, result.fail_reason, result.output))
718 if not self._InstancePidAlive(name)[2]:
719 raise errors.HypervisorError("Failed to start instance %s" % name)
721 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
722 """Execute a KVM cmd, after completing it with some last minute data
724 @type incoming: tuple of strings
725 @param incoming: (target_host_ip, port)
728 # Small _ExecuteKVMRuntime hv parameters programming howto:
729 # - conf_hvp contains the parameters as configured on ganeti. they might
730 # have changed since the instance started; only use them if the change
731 # won't affect the inside of the instance (which hasn't been rebooted).
732 # - up_hvp contains the parameters as they were when the instance was
733 # started, plus any new parameter which has been added between ganeti
734 # versions: it is paramount that those default to a value which won't
735 # affect the inside of the instance as well.
736 conf_hvp = instance.hvparams
738 self._CheckDown(name)
742 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
743 up_hvp = objects.FillDict(conf_hvp, up_hvp)
745 kvm_version = self._GetKVMVersion()
747 _, v_major, v_min, _ = kvm_version
749 raise errors.HypervisorError("Unable to get KVM version")
751 # We know it's safe to run as a different user upon migration, so we'll use
752 # the latest conf, from conf_hvp.
753 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
754 if security_model == constants.HT_SM_USER:
755 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
757 # We have reasons to believe changing something like the nic driver/type
758 # upon migration won't exactly fly with the instance kernel, so for nic
759 # related parameters we'll use up_hvp
763 kvm_cmd.extend(["-net", "none"])
767 nic_type = up_hvp[constants.HV_NIC_TYPE]
768 if nic_type == constants.HT_NIC_PARAVIRTUAL:
769 # From version 0.12.0, kvm uses a new sintax for network configuration.
770 if (v_major, v_min) >= (0, 12):
771 nic_model = "virtio-net-pci"
776 if up_hvp[constants.HV_VHOST_NET]:
777 # vhost_net is only available from version 0.13.0 or newer
778 if (v_major, v_min) >= (0, 13):
779 tap_extra = ",vhost=on"
781 raise errors.HypervisorError("vhost_net is configured"
782 " but it is not available")
786 for nic_seq, nic in enumerate(kvm_nics):
787 tapname, tapfd = _OpenTap(vnet_hdr)
790 if (v_major, v_min) >= (0, 12):
791 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
792 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
793 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
795 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
797 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
798 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
801 target, port = incoming
802 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
804 # Changing the vnc password doesn't bother the guest that much. At most it
805 # will surprise people who connect to it. Whether positively or negatively
807 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
811 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
812 except EnvironmentError, err:
813 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
814 % (vnc_pwd_file, err))
816 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
817 utils.EnsureDirs([(self._InstanceChrootDir(name),
818 constants.SECURE_DIR_MODE)])
821 # Configure the network now for starting instances, during
822 # FinalizeMigration for incoming instances
823 for nic_seq, nic in enumerate(kvm_nics):
824 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
826 if security_model == constants.HT_SM_POOL:
827 ss = ssconf.SimpleStore()
828 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
829 all_uids = set(uidpool.ExpandUidPool(uid_pool))
830 uid = uidpool.RequestUnusedUid(all_uids)
832 username = pwd.getpwuid(uid.GetUid()).pw_name
833 kvm_cmd.extend(["-runas", username])
834 self._RunKVMCmd(name, kvm_cmd, tapfds)
836 uidpool.ReleaseUid(uid)
840 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
842 self._RunKVMCmd(name, kvm_cmd, tapfds)
844 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
845 constants.RUN_DIRS_MODE)])
846 for nic_seq, tap in enumerate(taps):
847 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
851 change_cmd = 'change vnc password %s' % vnc_pwd
852 self._CallMonitorCommand(instance.name, change_cmd)
854 for filename in temp_files:
855 utils.RemoveFile(filename)
857 def StartInstance(self, instance, block_devices):
858 """Start an instance.
861 self._CheckDown(instance.name)
862 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
863 self._SaveKVMRuntime(instance, kvm_runtime)
864 self._ExecuteKVMRuntime(instance, kvm_runtime)
866 def _CallMonitorCommand(self, instance_name, command):
867 """Invoke a command on the instance monitor.
870 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
871 (utils.ShellQuote(command),
872 constants.SOCAT_PATH,
873 utils.ShellQuote(self._InstanceMonitor(instance_name))))
874 result = utils.RunCmd(socat)
876 msg = ("Failed to send command '%s' to instance %s."
877 " output: %s, error: %s, fail_reason: %s" %
878 (command, instance_name,
879 result.stdout, result.stderr, result.fail_reason))
880 raise errors.HypervisorError(msg)
885 def _GetKVMVersion(cls):
886 """Return the installed KVM version
888 @return: (version, v_maj, v_min, v_rev), or None
891 result = utils.RunCmd([constants.KVM_PATH, "--help"])
894 match = cls._VERSION_RE.search(result.output.splitlines()[0])
898 return (match.group(0), int(match.group(1)), int(match.group(2)),
901 def StopInstance(self, instance, force=False, retry=False, name=None):
905 if name is not None and not force:
906 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
909 acpi = instance.hvparams[constants.HV_ACPI]
912 _, pid, alive = self._InstancePidAlive(name)
913 if pid > 0 and alive:
914 if force or not acpi:
915 utils.KillProcess(pid)
917 self._CallMonitorCommand(name, 'system_powerdown')
919 def CleanupInstance(self, instance_name):
920 """Cleanup after a stopped instance
923 pidfile, pid, alive = self._InstancePidAlive(instance_name)
924 if pid > 0 and alive:
925 raise errors.HypervisorError("Cannot cleanup a live instance")
926 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
928 def RebootInstance(self, instance):
929 """Reboot an instance.
932 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
933 # socket the instance will stop, but now power up again. So we'll resort
934 # to shutdown and restart.
935 _, _, alive = self._InstancePidAlive(instance.name)
937 raise errors.HypervisorError("Failed to reboot instance %s:"
938 " not running" % instance.name)
939 # StopInstance will delete the saved KVM runtime so:
940 # ...first load it...
941 kvm_runtime = self._LoadKVMRuntime(instance)
942 # ...now we can safely call StopInstance...
943 if not self.StopInstance(instance):
944 self.StopInstance(instance, force=True)
945 # ...and finally we can save it again, and execute it...
946 self._SaveKVMRuntime(instance, kvm_runtime)
947 self._ExecuteKVMRuntime(instance, kvm_runtime)
949 def MigrationInfo(self, instance):
950 """Get instance information to perform a migration.
952 @type instance: L{objects.Instance}
953 @param instance: instance to be migrated
955 @return: content of the KVM runtime file
958 return self._ReadKVMRuntime(instance.name)
960 def AcceptInstance(self, instance, info, target):
961 """Prepare to accept an instance.
963 @type instance: L{objects.Instance}
964 @param instance: instance to be accepted
966 @param info: content of the KVM runtime file on the source node
968 @param target: target host (usually ip), on this node
971 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
972 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
973 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
975 def FinalizeMigration(self, instance, info, success):
976 """Finalize an instance migration.
978 Stop the incoming mode KVM.
980 @type instance: L{objects.Instance}
981 @param instance: instance whose migration is being finalized
985 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
986 kvm_nics = kvm_runtime[1]
988 for nic_seq, nic in enumerate(kvm_nics):
990 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
991 except EnvironmentError, err:
992 logging.warning("Failed to find host interface for %s NIC #%d: %s",
993 instance.name, nic_seq, str(err))
996 self._ConfigureNIC(instance, nic_seq, nic, tap)
997 except errors.HypervisorError, err:
998 logging.warning(str(err))
1000 self._WriteKVMRuntime(instance.name, info)
1002 self.StopInstance(instance, force=True)
1004 def MigrateInstance(self, instance, target, live):
1005 """Migrate an instance to a target node.
1007 The migration will not be attempted if the instance is not
1010 @type instance: L{objects.Instance}
1011 @param instance: the instance to be migrated
1012 @type target: string
1013 @param target: ip address of the target node
1015 @param live: perform a live migration
1018 instance_name = instance.name
1019 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1020 pidfile, pid, alive = self._InstancePidAlive(instance_name)
1022 raise errors.HypervisorError("Instance not running, cannot migrate")
1025 self._CallMonitorCommand(instance_name, 'stop')
1027 migrate_command = ('migrate_set_speed %dm' %
1028 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1029 self._CallMonitorCommand(instance_name, migrate_command)
1031 migrate_command = ('migrate_set_downtime %dms' %
1032 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1033 self._CallMonitorCommand(instance_name, migrate_command)
1035 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
1036 self._CallMonitorCommand(instance_name, migrate_command)
1038 info_command = 'info migrate'
1042 result = self._CallMonitorCommand(instance_name, info_command)
1043 match = self._MIGRATION_STATUS_RE.search(result.stdout)
1046 if not result.stdout:
1047 logging.info("KVM: empty 'info migrate' result")
1049 logging.warning("KVM: unknown 'info migrate' result: %s",
1051 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1053 status = match.group(1)
1054 if status == 'completed':
1056 elif status == 'active':
1057 # reset the broken answers count
1059 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1060 elif status == 'failed' or status == 'cancelled':
1062 self._CallMonitorCommand(instance_name, 'cont')
1063 raise errors.HypervisorError("Migration %s at the kvm level" %
1066 logging.warning("KVM: unknown migration status '%s'", status)
1068 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1069 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1070 raise errors.HypervisorError("Too many 'info migrate' broken answers")
1072 utils.KillProcess(pid)
1073 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1075 def GetNodeInfo(self):
1076 """Return information about the node.
1078 This is just a wrapper over the base GetLinuxNodeInfo method.
1080 @return: a dict with the following keys (values in MiB):
1081 - memory_total: the total memory size on the node
1082 - memory_free: the available memory on the node for instances
1083 - memory_dom0: the memory used by the node itself, if available
1086 return self.GetLinuxNodeInfo()
1089 def GetInstanceConsole(cls, instance, hvparams, beparams):
1090 """Return a command for connecting to the console of an instance.
1093 if hvparams[constants.HV_SERIAL_CONSOLE]:
1094 cmd = [constants.SOCAT_PATH,
1095 "STDIO,%s" % cls._SocatUnixConsoleParams(),
1096 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1097 return objects.InstanceConsole(instance=instance.name,
1098 kind=constants.CONS_SSH,
1099 host=instance.primary_node,
1100 user=constants.GANETI_RUNAS,
1103 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1104 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1105 display = instance.network_port - constants.VNC_BASE_PORT
1106 return objects.InstanceConsole(instance=instance.name,
1107 kind=constants.CONS_VNC,
1108 host=vnc_bind_address,
1109 port=instance.network_port,
1112 return objects.InstanceConsole(instance=instance.name,
1113 kind=constants.CONS_MESSAGE,
1114 message=("No serial shell for instance %s" %
1118 """Verify the hypervisor.
1120 Check that the binary exists.
1123 if not os.path.exists(constants.KVM_PATH):
1124 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1125 if not os.path.exists(constants.SOCAT_PATH):
1126 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1129 def CheckParameterSyntax(cls, hvparams):
1130 """Check the given parameters for validity.
1132 @type hvparams: dict
1133 @param hvparams: dictionary with parameter names/value
1134 @raise errors.HypervisorError: when a parameter is not valid
1137 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1139 kernel_path = hvparams[constants.HV_KERNEL_PATH]
1141 if not hvparams[constants.HV_ROOT_PATH]:
1142 raise errors.HypervisorError("Need a root partition for the instance,"
1143 " if a kernel is defined")
1145 if (hvparams[constants.HV_VNC_X509_VERIFY] and
1146 not hvparams[constants.HV_VNC_X509]):
1147 raise errors.HypervisorError("%s must be defined, if %s is" %
1148 (constants.HV_VNC_X509,
1149 constants.HV_VNC_X509_VERIFY))
1151 boot_order = hvparams[constants.HV_BOOT_ORDER]
1152 if (boot_order == constants.HT_BO_CDROM and
1153 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1154 raise errors.HypervisorError("Cannot boot from cdrom without an"
1157 security_model = hvparams[constants.HV_SECURITY_MODEL]
1158 if security_model == constants.HT_SM_USER:
1159 if not hvparams[constants.HV_SECURITY_DOMAIN]:
1160 raise errors.HypervisorError("A security domain (user to run kvm as)"
1161 " must be specified")
1162 elif (security_model == constants.HT_SM_NONE or
1163 security_model == constants.HT_SM_POOL):
1164 if hvparams[constants.HV_SECURITY_DOMAIN]:
1165 raise errors.HypervisorError("Cannot have a security domain when the"
1166 " security model is 'none' or 'pool'")
1169 def ValidateParameters(cls, hvparams):
1170 """Check the given parameters for validity.
1172 @type hvparams: dict
1173 @param hvparams: dictionary with parameter names/value
1174 @raise errors.HypervisorError: when a parameter is not valid
1177 super(KVMHypervisor, cls).ValidateParameters(hvparams)
1179 security_model = hvparams[constants.HV_SECURITY_MODEL]
1180 if security_model == constants.HT_SM_USER:
1181 username = hvparams[constants.HV_SECURITY_DOMAIN]
1183 pwd.getpwnam(username)
1185 raise errors.HypervisorError("Unknown security domain user %s"
1189 def PowercycleNode(cls):
1190 """KVM powercycle, just a wrapper over Linux powercycle.
1193 cls.LinuxPowercycle()