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
34 from cStringIO import StringIO
36 from ganeti import utils
37 from ganeti import constants
38 from ganeti import errors
39 from ganeti import serializer
40 from ganeti import objects
41 from ganeti import uidpool
42 from ganeti import ssconf
43 from ganeti.hypervisor import hv_base
44 from ganeti import netutils
47 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
50 def _WriteNetScript(instance, nic, index):
51 """Write a script to connect a net interface to the proper bridge.
53 This can be used by any qemu-type hypervisor.
55 @type instance: L{objects.Instance}
56 @param instance: Instance object
57 @type nic: L{objects.NIC}
58 @param nic: NIC object
60 @param index: NIC index
66 tags = " ".join(instance.tags)
71 sw = utils.ShellWriter(buf)
73 sw.Write("# this is autogenerated by Ganeti, please do not edit")
74 sw.Write("export PATH=$PATH:/sbin:/usr/sbin")
75 sw.Write("export INSTANCE=%s", utils.ShellQuote(instance.name))
76 sw.Write("export MAC=%s", utils.ShellQuote(nic.mac))
77 sw.Write("export MODE=%s",
78 utils.ShellQuote(nic.nicparams[constants.NIC_MODE]))
79 sw.Write("export INTERFACE=\"$1\"")
80 sw.Write("export TAGS=%s", utils.ShellQuote(tags))
83 sw.Write("export IP=%s", utils.ShellQuote(nic.ip))
85 if nic.nicparams[constants.NIC_LINK]:
86 sw.Write("export LINK=%s",
87 utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
89 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
90 sw.Write("export BRIDGE=%s",
91 utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
93 # TODO: make this configurable at ./configure time
94 sw.Write("if [ -x %s ]; then", utils.ShellQuote(_KVM_NETWORK_SCRIPT))
97 sw.Write("# Execute the user-specific vif file")
98 sw.Write(_KVM_NETWORK_SCRIPT)
104 sw.Write("ifconfig $INTERFACE 0.0.0.0 up")
106 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
107 sw.Write("# Connect the interface to the bridge")
108 sw.Write("brctl addif $BRIDGE $INTERFACE")
110 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
112 raise errors.HypervisorError("nic/%d is routed, but has no IP"
115 sw.Write("# Route traffic targeted at the IP to the interface")
116 if nic.nicparams[constants.NIC_LINK]:
117 sw.Write("while ip rule del dev $INTERFACE; do :; done")
118 sw.Write("ip rule add dev $INTERFACE table $LINK")
119 sw.Write("ip route replace $IP table $LINK proto static"
122 sw.Write("ip route replace $IP proto static dev $INTERFACE")
124 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
125 sw.Write(" if [ -d %s ]; then", interface_v4_conf)
128 sw.Write("echo 1 > %s/proxy_arp", interface_v4_conf)
129 sw.Write("echo 1 > %s/forwarding", interface_v4_conf)
134 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
135 sw.Write("if [ -d %s ]; then", interface_v6_conf)
138 sw.Write("echo 1 > %s/proxy_ndp", interface_v6_conf)
139 sw.Write("echo 1 > %s/forwarding", interface_v6_conf)
147 return buf.getvalue()
150 class KVMHypervisor(hv_base.BaseHypervisor):
151 """KVM hypervisor interface"""
154 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
155 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
156 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
157 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
158 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
159 # KVM instances with chroot enabled are started in empty chroot directories.
160 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
161 # After an instance is stopped, its chroot directory is removed.
162 # If the chroot directory is not empty, it can't be removed.
163 # A non-empty chroot directory indicates a possible security incident.
164 # To support forensics, the non-empty chroot directory is quarantined in
165 # a separate directory, called 'chroot-quarantine'.
166 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
167 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR,
168 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
171 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
172 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
173 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
174 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
175 constants.HV_ACPI: hv_base.NO_CHECK,
176 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
177 constants.HV_VNC_BIND_ADDRESS:
178 (False, lambda x: (netutils.IP4Address.IsValid(x) or
179 utils.IsNormAbsPath(x)),
180 "the VNC bind address must be either a valid IP address or an absolute"
181 " pathname", None, None),
182 constants.HV_VNC_TLS: hv_base.NO_CHECK,
183 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
184 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
185 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
186 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
187 constants.HV_BOOT_ORDER:
188 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
189 constants.HV_NIC_TYPE:
190 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
191 constants.HV_DISK_TYPE:
192 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
193 constants.HV_USB_MOUSE:
194 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
195 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
196 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
197 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
198 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
199 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
200 constants.HV_DISK_CACHE:
201 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
202 constants.HV_SECURITY_MODEL:
203 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
204 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
205 constants.HV_KVM_FLAG:
206 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
207 constants.HV_VHOST_NET: hv_base.NO_CHECK,
208 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
209 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
212 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
214 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
215 _MIGRATION_INFO_RETRY_DELAY = 2
217 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
224 hv_base.BaseHypervisor.__init__(self)
225 # Let's make sure the directories we need exist, even if the RUN_DIR lives
226 # in a tmpfs filesystem or has been otherwise wiped out.
227 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
228 utils.EnsureDirs(dirs)
231 def _InstancePidFile(cls, instance_name):
232 """Returns the instance pidfile.
235 return utils.PathJoin(cls._PIDS_DIR, instance_name)
238 def _InstanceUidFile(cls, instance_name):
239 """Returns the instance uidfile.
242 return utils.PathJoin(cls._UIDS_DIR, instance_name)
245 def _InstancePidInfo(cls, pid):
246 """Check pid file for instance information.
248 Check that a pid file is associated with an instance, and retrieve
249 information from its command line.
251 @type pid: string or int
252 @param pid: process id of the instance to check
254 @return: (instance_name, memory, vcpus)
255 @raise errors.HypervisorError: when an instance cannot be found
258 alive = utils.IsProcessAlive(pid)
260 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
262 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
264 cmdline = utils.ReadFile(cmdline_file)
265 except EnvironmentError, err:
266 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
273 arg_list = cmdline.split('\x00')
275 arg = arg_list.pop(0)
277 instance = arg_list.pop(0)
279 memory = int(arg_list.pop(0))
281 vcpus = int(arg_list.pop(0))
284 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
287 return (instance, memory, vcpus)
289 def _InstancePidAlive(self, instance_name):
290 """Returns the instance pidfile, pid, and liveness.
292 @type instance_name: string
293 @param instance_name: instance name
295 @return: (pid file name, pid, liveness)
298 pidfile = self._InstancePidFile(instance_name)
299 pid = utils.ReadPidFile(pidfile)
303 cmd_instance = self._InstancePidInfo(pid)[0]
304 alive = (cmd_instance == instance_name)
305 except errors.HypervisorError:
308 return (pidfile, pid, alive)
310 def _CheckDown(self, instance_name):
311 """Raises an error unless the given instance is down.
314 alive = self._InstancePidAlive(instance_name)[2]
316 raise errors.HypervisorError("Failed to start instance %s: %s" %
317 (instance_name, "already running"))
320 def _InstanceMonitor(cls, instance_name):
321 """Returns the instance monitor socket name
324 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
327 def _InstanceSerial(cls, instance_name):
328 """Returns the instance serial socket name
331 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
334 def _SocatUnixConsoleParams():
335 """Returns the correct parameters for socat
337 If we have a new-enough socat we can use raw mode with an escape character.
340 if constants.SOCAT_USE_ESCAPE:
341 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
343 return "echo=0,icanon=0"
346 def _InstanceKVMRuntime(cls, instance_name):
347 """Returns the instance KVM runtime filename
350 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
353 def _InstanceChrootDir(cls, instance_name):
354 """Returns the name of the KVM chroot dir of the instance
357 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
360 def _TryReadUidFile(cls, uid_file):
361 """Try to read a uid file
364 if os.path.exists(uid_file):
366 uid = int(utils.ReadOneLineFile(uid_file))
368 except EnvironmentError:
369 logging.warning("Can't read uid file", exc_info=True)
370 except (TypeError, ValueError):
371 logging.warning("Can't parse uid file contents", exc_info=True)
375 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
376 """Removes an instance's rutime sockets/files/dirs.
379 utils.RemoveFile(pidfile)
380 utils.RemoveFile(cls._InstanceMonitor(instance_name))
381 utils.RemoveFile(cls._InstanceSerial(instance_name))
382 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
383 uid_file = cls._InstanceUidFile(instance_name)
384 uid = cls._TryReadUidFile(uid_file)
385 utils.RemoveFile(uid_file)
387 uidpool.ReleaseUid(uid)
389 chroot_dir = cls._InstanceChrootDir(instance_name)
390 utils.RemoveDir(chroot_dir)
392 if err.errno == errno.ENOTEMPTY:
393 # The chroot directory is expected to be empty, but it isn't.
394 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
397 utils.TimestampForFilename()))
398 logging.warning("The chroot directory of instance %s can not be"
399 " removed as it is not empty. Moving it to the"
400 " quarantine instead. Please investigate the"
401 " contents (%s) and clean up manually",
402 instance_name, new_chroot_dir)
403 utils.RenameFile(chroot_dir, new_chroot_dir)
408 def _WriteNetScriptFile(instance, seq, nic):
409 """Write a script to connect a net interface to the proper bridge.
411 This can be used by any qemu-type hypervisor.
413 @param instance: instance we're acting on
414 @type instance: instance object
415 @param seq: nic sequence number
417 @param nic: nic we're acting on
418 @type nic: nic object
419 @return: netscript file name
423 script = _WriteNetScript(instance, nic, seq)
425 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
426 # mounted noexec sometimes, so we'll have to find another place.
427 (tmpfd, tmpfile_name) = tempfile.mkstemp()
428 tmpfile = os.fdopen(tmpfd, 'w')
430 tmpfile.write(script)
433 os.chmod(tmpfile_name, 0755)
436 def ListInstances(self):
437 """Get the list of running instances.
439 We can do this by listing our live instances directory and
440 checking whether the associated kvm process is still alive.
444 for name in os.listdir(self._PIDS_DIR):
445 if self._InstancePidAlive(name)[2]:
449 def GetInstanceInfo(self, instance_name):
450 """Get instance properties.
452 @type instance_name: string
453 @param instance_name: the instance name
454 @rtype: tuple of strings
455 @return: (name, id, memory, vcpus, stat, times)
458 _, pid, alive = self._InstancePidAlive(instance_name)
462 _, memory, vcpus = self._InstancePidInfo(pid)
466 return (instance_name, pid, memory, vcpus, stat, times)
468 def GetAllInstancesInfo(self):
469 """Get properties of all instances.
471 @return: list of tuples (name, id, memory, vcpus, stat, times)
475 for name in os.listdir(self._PIDS_DIR):
477 info = self.GetInstanceInfo(name)
478 except errors.HypervisorError:
484 def _GenerateKVMRuntime(self, instance, block_devices):
485 """Generate KVM information to start an instance.
488 pidfile = self._InstancePidFile(instance.name)
489 kvm = constants.KVM_PATH
491 # used just by the vnc server, if enabled
492 kvm_cmd.extend(['-name', instance.name])
493 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
494 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
495 kvm_cmd.extend(['-pidfile', pidfile])
496 kvm_cmd.extend(['-daemonize'])
497 if not instance.hvparams[constants.HV_ACPI]:
498 kvm_cmd.extend(['-no-acpi'])
500 hvp = instance.hvparams
501 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
502 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
503 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
505 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
506 kvm_cmd.extend(["-enable-kvm"])
507 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
508 kvm_cmd.extend(["-disable-kvm"])
511 kvm_cmd.extend(['-boot', 'n'])
513 disk_type = hvp[constants.HV_DISK_TYPE]
514 if disk_type == constants.HT_DISK_PARAVIRTUAL:
515 if_val = ',if=virtio'
517 if_val = ',if=%s' % disk_type
519 disk_cache = hvp[constants.HV_DISK_CACHE]
520 if disk_cache != constants.HT_CACHE_DEFAULT:
521 cache_val = ",cache=%s" % disk_cache
524 for cfdev, dev_path in block_devices:
525 if cfdev.mode != constants.DISK_RDWR:
526 raise errors.HypervisorError("Instance has read-only disks which"
527 " are not supported by KVM")
528 # TODO: handle FD_LOOP and FD_BLKTAP (?)
530 kvm_cmd.extend(['-boot', 'c'])
531 if disk_type != constants.HT_DISK_IDE:
532 boot_val = ',boot=on'
535 # We only boot from the first disk
540 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
542 kvm_cmd.extend(['-drive', drive_val])
544 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
546 options = ',format=raw,media=cdrom'
548 kvm_cmd.extend(['-boot', 'd'])
549 if disk_type != constants.HT_DISK_IDE:
550 options = '%s,boot=on' % options
552 if disk_type == constants.HT_DISK_PARAVIRTUAL:
553 if_val = ',if=virtio'
555 if_val = ',if=%s' % disk_type
556 options = '%s%s' % (options, if_val)
557 drive_val = 'file=%s%s' % (iso_image, options)
558 kvm_cmd.extend(['-drive', drive_val])
560 kernel_path = hvp[constants.HV_KERNEL_PATH]
562 kvm_cmd.extend(['-kernel', kernel_path])
563 initrd_path = hvp[constants.HV_INITRD_PATH]
565 kvm_cmd.extend(['-initrd', initrd_path])
566 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
567 hvp[constants.HV_KERNEL_ARGS]]
568 if hvp[constants.HV_SERIAL_CONSOLE]:
569 root_append.append('console=ttyS0,38400')
570 kvm_cmd.extend(['-append', ' '.join(root_append)])
572 mem_path = hvp[constants.HV_MEM_PATH]
574 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
576 mouse_type = hvp[constants.HV_USB_MOUSE]
577 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
580 kvm_cmd.extend(['-usb'])
581 kvm_cmd.extend(['-usbdevice', mouse_type])
582 elif vnc_bind_address:
583 kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
586 if netutils.IP4Address.IsValid(vnc_bind_address):
587 if instance.network_port > constants.VNC_BASE_PORT:
588 display = instance.network_port - constants.VNC_BASE_PORT
589 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
590 vnc_arg = ':%d' % (display)
592 vnc_arg = '%s:%d' % (vnc_bind_address, display)
594 logging.error("Network port is not a valid VNC display (%d < %d)."
595 " Not starting VNC", instance.network_port,
596 constants.VNC_BASE_PORT)
599 # Only allow tls and other option when not binding to a file, for now.
600 # kvm/qemu gets confused otherwise about the filename to use.
602 if hvp[constants.HV_VNC_TLS]:
603 vnc_append = '%s,tls' % vnc_append
604 if hvp[constants.HV_VNC_X509_VERIFY]:
605 vnc_append = '%s,x509verify=%s' % (vnc_append,
606 hvp[constants.HV_VNC_X509])
607 elif hvp[constants.HV_VNC_X509]:
608 vnc_append = '%s,x509=%s' % (vnc_append,
609 hvp[constants.HV_VNC_X509])
610 if hvp[constants.HV_VNC_PASSWORD_FILE]:
611 vnc_append = '%s,password' % vnc_append
613 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
616 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
618 kvm_cmd.extend(['-vnc', vnc_arg])
620 kvm_cmd.extend(['-nographic'])
622 monitor_dev = ("unix:%s,server,nowait" %
623 self._InstanceMonitor(instance.name))
624 kvm_cmd.extend(['-monitor', monitor_dev])
625 if hvp[constants.HV_SERIAL_CONSOLE]:
626 serial_dev = ('unix:%s,server,nowait' %
627 self._InstanceSerial(instance.name))
628 kvm_cmd.extend(['-serial', serial_dev])
630 kvm_cmd.extend(['-serial', 'none'])
632 if hvp[constants.HV_USE_LOCALTIME]:
633 kvm_cmd.extend(['-localtime'])
635 if hvp[constants.HV_KVM_USE_CHROOT]:
636 kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
638 # Save the current instance nics, but defer their expansion as parameters,
639 # as we'll need to generate executable temp files for them.
640 kvm_nics = instance.nics
643 return (kvm_cmd, kvm_nics, hvparams)
645 def _WriteKVMRuntime(self, instance_name, data):
646 """Write an instance's KVM runtime
650 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
652 except EnvironmentError, err:
653 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
655 def _ReadKVMRuntime(self, instance_name):
656 """Read an instance's KVM runtime
660 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
661 except EnvironmentError, err:
662 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
665 def _SaveKVMRuntime(self, instance, kvm_runtime):
666 """Save an instance's KVM runtime
669 kvm_cmd, kvm_nics, hvparams = kvm_runtime
670 serialized_nics = [nic.ToDict() for nic in kvm_nics]
671 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
672 self._WriteKVMRuntime(instance.name, serialized_form)
674 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
675 """Load an instance's KVM runtime
678 if not serialized_runtime:
679 serialized_runtime = self._ReadKVMRuntime(instance.name)
680 loaded_runtime = serializer.Load(serialized_runtime)
681 kvm_cmd, serialized_nics, hvparams = loaded_runtime
682 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
683 return (kvm_cmd, kvm_nics, hvparams)
685 def _RunKVMCmd(self, name, kvm_cmd):
686 """Run the KVM cmd and check for errors
689 @param name: instance name
690 @type kvm_cmd: list of strings
691 @param kvm_cmd: runcmd input for kvm
694 result = utils.RunCmd(kvm_cmd)
696 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
697 (name, result.fail_reason, result.output))
698 if not self._InstancePidAlive(name)[2]:
699 raise errors.HypervisorError("Failed to start instance %s" % name)
701 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
702 """Execute a KVM cmd, after completing it with some last minute data
704 @type incoming: tuple of strings
705 @param incoming: (target_host_ip, port)
708 # Small _ExecuteKVMRuntime hv parameters programming howto:
709 # - conf_hvp contains the parameters as configured on ganeti. they might
710 # have changed since the instance started; only use them if the change
711 # won't affect the inside of the instance (which hasn't been rebooted).
712 # - up_hvp contains the parameters as they were when the instance was
713 # started, plus any new parameter which has been added between ganeti
714 # versions: it is paramount that those default to a value which won't
715 # affect the inside of the instance as well.
716 conf_hvp = instance.hvparams
718 self._CheckDown(name)
722 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
723 up_hvp = objects.FillDict(conf_hvp, up_hvp)
725 kvm_version = self._GetKVMVersion()
727 _, v_major, v_min, _ = kvm_version
729 raise errors.HypervisorError("Unable to get KVM version")
731 # We know it's safe to run as a different user upon migration, so we'll use
732 # the latest conf, from conf_hvp.
733 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
734 if security_model == constants.HT_SM_USER:
735 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
737 # We have reasons to believe changing something like the nic driver/type
738 # upon migration won't exactly fly with the instance kernel, so for nic
739 # related parameters we'll use up_hvp
741 kvm_cmd.extend(["-net", "none"])
744 nic_type = up_hvp[constants.HV_NIC_TYPE]
745 if nic_type == constants.HT_NIC_PARAVIRTUAL:
746 # From version 0.12.0, kvm uses a new sintax for network configuration.
747 if (v_major, v_min) >= (0, 12):
748 nic_model = "virtio-net-pci"
752 if up_hvp[constants.HV_VHOST_NET]:
753 # vhost_net is only available from version 0.13.0 or newer
754 if (v_major, v_min) >= (0, 13):
755 tap_extra = ",vhost=on"
757 raise errors.HypervisorError("vhost_net is configured"
758 " but it is not available")
762 for nic_seq, nic in enumerate(kvm_nics):
763 script = self._WriteNetScriptFile(instance, nic_seq, nic)
764 temp_files.append(script)
765 if (v_major, v_min) >= (0, 12):
766 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
767 tap_val = "type=tap,id=netdev%s,script=%s%s" % (nic_seq,
769 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
771 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
773 tap_val = "tap,vlan=%s,script=%s" % (nic_seq, script)
774 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
777 target, port = incoming
778 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
780 # Changing the vnc password doesn't bother the guest that much. At most it
781 # will surprise people who connect to it. Whether positively or negatively
783 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
787 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
788 except EnvironmentError, err:
789 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
790 % (vnc_pwd_file, err))
792 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
793 utils.EnsureDirs([(self._InstanceChrootDir(name),
794 constants.SECURE_DIR_MODE)])
796 if security_model == constants.HT_SM_POOL:
797 ss = ssconf.SimpleStore()
798 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
799 all_uids = set(uidpool.ExpandUidPool(uid_pool))
800 uid = uidpool.RequestUnusedUid(all_uids)
802 username = pwd.getpwuid(uid.GetUid()).pw_name
803 kvm_cmd.extend(["-runas", username])
804 self._RunKVMCmd(name, kvm_cmd)
806 uidpool.ReleaseUid(uid)
810 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
812 self._RunKVMCmd(name, kvm_cmd)
815 change_cmd = 'change vnc password %s' % vnc_pwd
816 self._CallMonitorCommand(instance.name, change_cmd)
818 for filename in temp_files:
819 utils.RemoveFile(filename)
821 def StartInstance(self, instance, block_devices):
822 """Start an instance.
825 self._CheckDown(instance.name)
826 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
827 self._SaveKVMRuntime(instance, kvm_runtime)
828 self._ExecuteKVMRuntime(instance, kvm_runtime)
830 def _CallMonitorCommand(self, instance_name, command):
831 """Invoke a command on the instance monitor.
834 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
835 (utils.ShellQuote(command),
836 constants.SOCAT_PATH,
837 utils.ShellQuote(self._InstanceMonitor(instance_name))))
838 result = utils.RunCmd(socat)
840 msg = ("Failed to send command '%s' to instance %s."
841 " output: %s, error: %s, fail_reason: %s" %
842 (command, instance_name,
843 result.stdout, result.stderr, result.fail_reason))
844 raise errors.HypervisorError(msg)
849 def _GetKVMVersion(cls):
850 """Return the installed KVM version
852 @return: (version, v_maj, v_min, v_rev), or None
855 result = utils.RunCmd([constants.KVM_PATH, "--help"])
858 match = cls._VERSION_RE.search(result.output.splitlines()[0])
862 return (match.group(0), int(match.group(1)), int(match.group(2)),
865 def StopInstance(self, instance, force=False, retry=False, name=None):
869 if name is not None and not force:
870 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
873 acpi = instance.hvparams[constants.HV_ACPI]
876 _, pid, alive = self._InstancePidAlive(name)
877 if pid > 0 and alive:
878 if force or not acpi:
879 utils.KillProcess(pid)
881 self._CallMonitorCommand(name, 'system_powerdown')
883 def CleanupInstance(self, instance_name):
884 """Cleanup after a stopped instance
887 pidfile, pid, alive = self._InstancePidAlive(instance_name)
888 if pid > 0 and alive:
889 raise errors.HypervisorError("Cannot cleanup a live instance")
890 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
892 def RebootInstance(self, instance):
893 """Reboot an instance.
896 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
897 # socket the instance will stop, but now power up again. So we'll resort
898 # to shutdown and restart.
899 _, _, alive = self._InstancePidAlive(instance.name)
901 raise errors.HypervisorError("Failed to reboot instance %s:"
902 " not running" % instance.name)
903 # StopInstance will delete the saved KVM runtime so:
904 # ...first load it...
905 kvm_runtime = self._LoadKVMRuntime(instance)
906 # ...now we can safely call StopInstance...
907 if not self.StopInstance(instance):
908 self.StopInstance(instance, force=True)
909 # ...and finally we can save it again, and execute it...
910 self._SaveKVMRuntime(instance, kvm_runtime)
911 self._ExecuteKVMRuntime(instance, kvm_runtime)
913 def MigrationInfo(self, instance):
914 """Get instance information to perform a migration.
916 @type instance: L{objects.Instance}
917 @param instance: instance to be migrated
919 @return: content of the KVM runtime file
922 return self._ReadKVMRuntime(instance.name)
924 def AcceptInstance(self, instance, info, target):
925 """Prepare to accept an instance.
927 @type instance: L{objects.Instance}
928 @param instance: instance to be accepted
930 @param info: content of the KVM runtime file on the source node
932 @param target: target host (usually ip), on this node
935 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
936 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
937 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
939 def FinalizeMigration(self, instance, info, success):
940 """Finalize an instance migration.
942 Stop the incoming mode KVM.
944 @type instance: L{objects.Instance}
945 @param instance: instance whose migration is being finalized
949 self._WriteKVMRuntime(instance.name, info)
951 self.StopInstance(instance, force=True)
953 def MigrateInstance(self, instance, target, live):
954 """Migrate an instance to a target node.
956 The migration will not be attempted if the instance is not
959 @type instance: L{objects.Instance}
960 @param instance: the instance to be migrated
962 @param target: ip address of the target node
964 @param live: perform a live migration
967 instance_name = instance.name
968 port = instance.hvparams[constants.HV_MIGRATION_PORT]
969 pidfile, pid, alive = self._InstancePidAlive(instance_name)
971 raise errors.HypervisorError("Instance not running, cannot migrate")
974 self._CallMonitorCommand(instance_name, 'stop')
976 migrate_command = ('migrate_set_speed %dm' %
977 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
978 self._CallMonitorCommand(instance_name, migrate_command)
980 migrate_command = ('migrate_set_downtime %dms' %
981 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
982 self._CallMonitorCommand(instance_name, migrate_command)
984 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
985 self._CallMonitorCommand(instance_name, migrate_command)
987 info_command = 'info migrate'
991 result = self._CallMonitorCommand(instance_name, info_command)
992 match = self._MIGRATION_STATUS_RE.search(result.stdout)
995 if not result.stdout:
996 logging.info("KVM: empty 'info migrate' result")
998 logging.warning("KVM: unknown 'info migrate' result: %s",
1000 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1002 status = match.group(1)
1003 if status == 'completed':
1005 elif status == 'active':
1006 # reset the broken answers count
1008 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1009 elif status == 'failed' or status == 'cancelled':
1011 self._CallMonitorCommand(instance_name, 'cont')
1012 raise errors.HypervisorError("Migration %s at the kvm level" %
1015 logging.warning("KVM: unknown migration status '%s'", status)
1017 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1018 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1019 raise errors.HypervisorError("Too many 'info migrate' broken answers")
1021 utils.KillProcess(pid)
1022 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1024 def GetNodeInfo(self):
1025 """Return information about the node.
1027 This is just a wrapper over the base GetLinuxNodeInfo method.
1029 @return: a dict with the following keys (values in MiB):
1030 - memory_total: the total memory size on the node
1031 - memory_free: the available memory on the node for instances
1032 - memory_dom0: the memory used by the node itself, if available
1035 return self.GetLinuxNodeInfo()
1038 def GetInstanceConsole(cls, instance, hvparams, beparams):
1039 """Return a command for connecting to the console of an instance.
1042 if hvparams[constants.HV_SERIAL_CONSOLE]:
1043 cmd = [constants.SOCAT_PATH,
1044 "STDIO,%s" % cls._SocatUnixConsoleParams(),
1045 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1046 return objects.InstanceConsole(instance=instance.name,
1047 kind=constants.CONS_SSH,
1048 host=instance.primary_node,
1049 user=constants.GANETI_RUNAS,
1052 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1053 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1054 display = instance.network_port - constants.VNC_BASE_PORT
1055 return objects.InstanceConsole(instance=instance.name,
1056 kind=constants.CONS_VNC,
1057 host=vnc_bind_address,
1058 port=instance.network_port,
1061 return objects.InstanceConsole(instance=instance.name,
1062 kind=constants.CONS_MESSAGE,
1063 message=("No serial shell for instance %s" %
1067 """Verify the hypervisor.
1069 Check that the binary exists.
1072 if not os.path.exists(constants.KVM_PATH):
1073 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1074 if not os.path.exists(constants.SOCAT_PATH):
1075 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1078 def CheckParameterSyntax(cls, hvparams):
1079 """Check the given parameters for validity.
1081 @type hvparams: dict
1082 @param hvparams: dictionary with parameter names/value
1083 @raise errors.HypervisorError: when a parameter is not valid
1086 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1088 kernel_path = hvparams[constants.HV_KERNEL_PATH]
1090 if not hvparams[constants.HV_ROOT_PATH]:
1091 raise errors.HypervisorError("Need a root partition for the instance,"
1092 " if a kernel is defined")
1094 if (hvparams[constants.HV_VNC_X509_VERIFY] and
1095 not hvparams[constants.HV_VNC_X509]):
1096 raise errors.HypervisorError("%s must be defined, if %s is" %
1097 (constants.HV_VNC_X509,
1098 constants.HV_VNC_X509_VERIFY))
1100 boot_order = hvparams[constants.HV_BOOT_ORDER]
1101 if (boot_order == constants.HT_BO_CDROM and
1102 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1103 raise errors.HypervisorError("Cannot boot from cdrom without an"
1106 security_model = hvparams[constants.HV_SECURITY_MODEL]
1107 if security_model == constants.HT_SM_USER:
1108 if not hvparams[constants.HV_SECURITY_DOMAIN]:
1109 raise errors.HypervisorError("A security domain (user to run kvm as)"
1110 " must be specified")
1111 elif (security_model == constants.HT_SM_NONE or
1112 security_model == constants.HT_SM_POOL):
1113 if hvparams[constants.HV_SECURITY_DOMAIN]:
1114 raise errors.HypervisorError("Cannot have a security domain when the"
1115 " security model is 'none' or 'pool'")
1118 def ValidateParameters(cls, hvparams):
1119 """Check the given parameters for validity.
1121 @type hvparams: dict
1122 @param hvparams: dictionary with parameter names/value
1123 @raise errors.HypervisorError: when a parameter is not valid
1126 super(KVMHypervisor, cls).ValidateParameters(hvparams)
1128 security_model = hvparams[constants.HV_SECURITY_MODEL]
1129 if security_model == constants.HT_SM_USER:
1130 username = hvparams[constants.HV_SECURITY_DOMAIN]
1132 pwd.getpwnam(username)
1134 raise errors.HypervisorError("Unknown security domain user %s"
1138 def PowercycleNode(cls):
1139 """KVM powercycle, just a wrapper over Linux powercycle.
1142 cls.LinuxPowercycle()