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,
211 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
213 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
214 _MIGRATION_INFO_RETRY_DELAY = 2
221 hv_base.BaseHypervisor.__init__(self)
222 # Let's make sure the directories we need exist, even if the RUN_DIR lives
223 # in a tmpfs filesystem or has been otherwise wiped out.
224 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
225 utils.EnsureDirs(dirs)
228 def _InstancePidFile(cls, instance_name):
229 """Returns the instance pidfile.
232 return utils.PathJoin(cls._PIDS_DIR, instance_name)
235 def _InstanceUidFile(cls, instance_name):
236 """Returns the instance uidfile.
239 return utils.PathJoin(cls._UIDS_DIR, instance_name)
242 def _InstancePidInfo(cls, pid):
243 """Check pid file for instance information.
245 Check that a pid file is associated with an instance, and retrieve
246 information from its command line.
248 @type pid: string or int
249 @param pid: process id of the instance to check
251 @return: (instance_name, memory, vcpus)
252 @raise errors.HypervisorError: when an instance cannot be found
255 alive = utils.IsProcessAlive(pid)
257 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
259 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
261 cmdline = utils.ReadFile(cmdline_file)
262 except EnvironmentError, err:
263 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
270 arg_list = cmdline.split('\x00')
272 arg = arg_list.pop(0)
274 instance = arg_list.pop(0)
276 memory = int(arg_list.pop(0))
278 vcpus = int(arg_list.pop(0))
281 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
284 return (instance, memory, vcpus)
286 def _InstancePidAlive(self, instance_name):
287 """Returns the instance pidfile, pid, and liveness.
289 @type instance_name: string
290 @param instance_name: instance name
292 @return: (pid file name, pid, liveness)
295 pidfile = self._InstancePidFile(instance_name)
296 pid = utils.ReadPidFile(pidfile)
300 cmd_instance = self._InstancePidInfo(pid)[0]
301 alive = (cmd_instance == instance_name)
302 except errors.HypervisorError:
305 return (pidfile, pid, alive)
307 def _CheckDown(self, instance_name):
308 """Raises an error unless the given instance is down.
311 alive = self._InstancePidAlive(instance_name)[2]
313 raise errors.HypervisorError("Failed to start instance %s: %s" %
314 (instance_name, "already running"))
317 def _InstanceMonitor(cls, instance_name):
318 """Returns the instance monitor socket name
321 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
324 def _InstanceSerial(cls, instance_name):
325 """Returns the instance serial socket name
328 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
331 def _SocatUnixConsoleParams():
332 """Returns the correct parameters for socat
334 If we have a new-enough socat we can use raw mode with an escape character.
337 if constants.SOCAT_USE_ESCAPE:
338 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
340 return "echo=0,icanon=0"
343 def _InstanceKVMRuntime(cls, instance_name):
344 """Returns the instance KVM runtime filename
347 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
350 def _InstanceChrootDir(cls, instance_name):
351 """Returns the name of the KVM chroot dir of the instance
354 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
357 def _TryReadUidFile(cls, uid_file):
358 """Try to read a uid file
361 if os.path.exists(uid_file):
363 uid = int(utils.ReadOneLineFile(uid_file))
365 except EnvironmentError:
366 logging.warning("Can't read uid file", exc_info=True)
367 except (TypeError, ValueError):
368 logging.warning("Can't parse uid file contents", exc_info=True)
372 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
373 """Removes an instance's rutime sockets/files/dirs.
376 utils.RemoveFile(pidfile)
377 utils.RemoveFile(cls._InstanceMonitor(instance_name))
378 utils.RemoveFile(cls._InstanceSerial(instance_name))
379 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
380 uid_file = cls._InstanceUidFile(instance_name)
381 uid = cls._TryReadUidFile(uid_file)
382 utils.RemoveFile(uid_file)
384 uidpool.ReleaseUid(uid)
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 _WriteNetScriptFile(instance, seq, nic):
406 """Write a script to connect a net interface to the proper bridge.
408 This can be used by any qemu-type hypervisor.
410 @param instance: instance we're acting on
411 @type instance: instance object
412 @param seq: nic sequence number
414 @param nic: nic we're acting on
415 @type nic: nic object
416 @return: netscript file name
420 script = _WriteNetScript(instance, nic, seq)
422 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
423 # mounted noexec sometimes, so we'll have to find another place.
424 (tmpfd, tmpfile_name) = tempfile.mkstemp()
425 tmpfile = os.fdopen(tmpfd, 'w')
427 tmpfile.write(script)
430 os.chmod(tmpfile_name, 0755)
433 def ListInstances(self):
434 """Get the list of running instances.
436 We can do this by listing our live instances directory and
437 checking whether the associated kvm process is still alive.
441 for name in os.listdir(self._PIDS_DIR):
442 if self._InstancePidAlive(name)[2]:
446 def GetInstanceInfo(self, instance_name):
447 """Get instance properties.
449 @type instance_name: string
450 @param instance_name: the instance name
451 @rtype: tuple of strings
452 @return: (name, id, memory, vcpus, stat, times)
455 _, pid, alive = self._InstancePidAlive(instance_name)
459 _, memory, vcpus = self._InstancePidInfo(pid)
463 return (instance_name, pid, memory, vcpus, stat, times)
465 def GetAllInstancesInfo(self):
466 """Get properties of all instances.
468 @return: list of tuples (name, id, memory, vcpus, stat, times)
472 for name in os.listdir(self._PIDS_DIR):
474 info = self.GetInstanceInfo(name)
475 except errors.HypervisorError:
481 def _GenerateKVMRuntime(self, instance, block_devices):
482 """Generate KVM information to start an instance.
485 pidfile = self._InstancePidFile(instance.name)
486 kvm = constants.KVM_PATH
488 # used just by the vnc server, if enabled
489 kvm_cmd.extend(['-name', instance.name])
490 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
491 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
492 kvm_cmd.extend(['-pidfile', pidfile])
493 kvm_cmd.extend(['-daemonize'])
494 if not instance.hvparams[constants.HV_ACPI]:
495 kvm_cmd.extend(['-no-acpi'])
497 hvp = instance.hvparams
498 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
499 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
500 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
502 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
503 kvm_cmd.extend(["-enable-kvm"])
504 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
505 kvm_cmd.extend(["-disable-kvm"])
508 kvm_cmd.extend(['-boot', 'n'])
510 disk_type = hvp[constants.HV_DISK_TYPE]
511 if disk_type == constants.HT_DISK_PARAVIRTUAL:
512 if_val = ',if=virtio'
514 if_val = ',if=%s' % disk_type
516 disk_cache = hvp[constants.HV_DISK_CACHE]
517 if disk_cache != constants.HT_CACHE_DEFAULT:
518 cache_val = ",cache=%s" % disk_cache
521 for cfdev, dev_path in block_devices:
522 if cfdev.mode != constants.DISK_RDWR:
523 raise errors.HypervisorError("Instance has read-only disks which"
524 " are not supported by KVM")
525 # TODO: handle FD_LOOP and FD_BLKTAP (?)
527 kvm_cmd.extend(['-boot', 'c'])
528 if disk_type != constants.HT_DISK_IDE:
529 boot_val = ',boot=on'
532 # We only boot from the first disk
537 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
539 kvm_cmd.extend(['-drive', drive_val])
541 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
543 options = ',format=raw,media=cdrom'
545 kvm_cmd.extend(['-boot', 'd'])
546 if disk_type != constants.HT_DISK_IDE:
547 options = '%s,boot=on' % options
549 if disk_type == constants.HT_DISK_PARAVIRTUAL:
550 if_val = ',if=virtio'
552 if_val = ',if=%s' % disk_type
553 options = '%s%s' % (options, if_val)
554 drive_val = 'file=%s%s' % (iso_image, options)
555 kvm_cmd.extend(['-drive', drive_val])
557 kernel_path = hvp[constants.HV_KERNEL_PATH]
559 kvm_cmd.extend(['-kernel', kernel_path])
560 initrd_path = hvp[constants.HV_INITRD_PATH]
562 kvm_cmd.extend(['-initrd', initrd_path])
563 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
564 hvp[constants.HV_KERNEL_ARGS]]
565 if hvp[constants.HV_SERIAL_CONSOLE]:
566 root_append.append('console=ttyS0,38400')
567 kvm_cmd.extend(['-append', ' '.join(root_append)])
569 mouse_type = hvp[constants.HV_USB_MOUSE]
571 kvm_cmd.extend(['-usb'])
572 kvm_cmd.extend(['-usbdevice', mouse_type])
574 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
576 if netutils.IP4Address.IsValid(vnc_bind_address):
577 if instance.network_port > constants.VNC_BASE_PORT:
578 display = instance.network_port - constants.VNC_BASE_PORT
579 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
580 vnc_arg = ':%d' % (display)
582 vnc_arg = '%s:%d' % (vnc_bind_address, display)
584 logging.error("Network port is not a valid VNC display (%d < %d)."
585 " Not starting VNC", instance.network_port,
586 constants.VNC_BASE_PORT)
589 # Only allow tls and other option when not binding to a file, for now.
590 # kvm/qemu gets confused otherwise about the filename to use.
592 if hvp[constants.HV_VNC_TLS]:
593 vnc_append = '%s,tls' % vnc_append
594 if hvp[constants.HV_VNC_X509_VERIFY]:
595 vnc_append = '%s,x509verify=%s' % (vnc_append,
596 hvp[constants.HV_VNC_X509])
597 elif hvp[constants.HV_VNC_X509]:
598 vnc_append = '%s,x509=%s' % (vnc_append,
599 hvp[constants.HV_VNC_X509])
600 if hvp[constants.HV_VNC_PASSWORD_FILE]:
601 vnc_append = '%s,password' % vnc_append
603 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
606 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
608 kvm_cmd.extend(['-vnc', vnc_arg])
610 # Also add a tablet USB device to act as a mouse
611 # This solves various mouse alignment issues
612 kvm_cmd.extend(['-usbdevice', 'tablet'])
614 kvm_cmd.extend(['-nographic'])
616 monitor_dev = ("unix:%s,server,nowait" %
617 self._InstanceMonitor(instance.name))
618 kvm_cmd.extend(['-monitor', monitor_dev])
619 if hvp[constants.HV_SERIAL_CONSOLE]:
620 serial_dev = ('unix:%s,server,nowait' %
621 self._InstanceSerial(instance.name))
622 kvm_cmd.extend(['-serial', serial_dev])
624 kvm_cmd.extend(['-serial', 'none'])
626 if hvp[constants.HV_USE_LOCALTIME]:
627 kvm_cmd.extend(['-localtime'])
629 if hvp[constants.HV_KVM_USE_CHROOT]:
630 kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
632 # Save the current instance nics, but defer their expansion as parameters,
633 # as we'll need to generate executable temp files for them.
634 kvm_nics = instance.nics
637 return (kvm_cmd, kvm_nics, hvparams)
639 def _WriteKVMRuntime(self, instance_name, data):
640 """Write an instance's KVM runtime
644 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
646 except EnvironmentError, err:
647 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
649 def _ReadKVMRuntime(self, instance_name):
650 """Read an instance's KVM runtime
654 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
655 except EnvironmentError, err:
656 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
659 def _SaveKVMRuntime(self, instance, kvm_runtime):
660 """Save an instance's KVM runtime
663 kvm_cmd, kvm_nics, hvparams = kvm_runtime
664 serialized_nics = [nic.ToDict() for nic in kvm_nics]
665 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
666 self._WriteKVMRuntime(instance.name, serialized_form)
668 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
669 """Load an instance's KVM runtime
672 if not serialized_runtime:
673 serialized_runtime = self._ReadKVMRuntime(instance.name)
674 loaded_runtime = serializer.Load(serialized_runtime)
675 kvm_cmd, serialized_nics, hvparams = loaded_runtime
676 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
677 return (kvm_cmd, kvm_nics, hvparams)
679 def _RunKVMCmd(self, name, kvm_cmd):
680 """Run the KVM cmd and check for errors
683 @param name: instance name
684 @type kvm_cmd: list of strings
685 @param kvm_cmd: runcmd input for kvm
688 result = utils.RunCmd(kvm_cmd)
690 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
691 (name, result.fail_reason, result.output))
692 if not self._InstancePidAlive(name)[2]:
693 raise errors.HypervisorError("Failed to start instance %s" % name)
695 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
696 """Execute a KVM cmd, after completing it with some last minute data
698 @type incoming: tuple of strings
699 @param incoming: (target_host_ip, port)
702 # Small _ExecuteKVMRuntime hv parameters programming howto:
703 # - conf_hvp contains the parameters as configured on ganeti. they might
704 # have changed since the instance started; only use them if the change
705 # won't affect the inside of the instance (which hasn't been rebooted).
706 # - up_hvp contains the parameters as they were when the instance was
707 # started, plus any new parameter which has been added between ganeti
708 # versions: it is paramount that those default to a value which won't
709 # affect the inside of the instance as well.
710 conf_hvp = instance.hvparams
712 self._CheckDown(name)
716 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
717 up_hvp = objects.FillDict(conf_hvp, up_hvp)
719 # We know it's safe to run as a different user upon migration, so we'll use
720 # the latest conf, from conf_hvp.
721 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
722 if security_model == constants.HT_SM_USER:
723 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
725 # We have reasons to believe changing something like the nic driver/type
726 # upon migration won't exactly fly with the instance kernel, so for nic
727 # related parameters we'll use up_hvp
729 kvm_cmd.extend(["-net", "none"])
732 nic_type = up_hvp[constants.HV_NIC_TYPE]
733 if nic_type == constants.HT_NIC_PARAVIRTUAL:
734 nic_model = "model=virtio"
735 if up_hvp[constants.HV_VHOST_NET]:
736 tap_extra = ",vhost=on"
738 nic_model = "model=%s" % nic_type
740 for nic_seq, nic in enumerate(kvm_nics):
741 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
742 script = self._WriteNetScriptFile(instance, nic_seq, nic)
743 tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
744 kvm_cmd.extend(["-net", nic_val])
745 kvm_cmd.extend(["-net", tap_val])
746 temp_files.append(script)
749 target, port = incoming
750 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
752 # Changing the vnc password doesn't bother the guest that much. At most it
753 # will surprise people who connect to it. Whether positively or negatively
755 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
759 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
760 except EnvironmentError, err:
761 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
762 % (vnc_pwd_file, err))
764 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
765 utils.EnsureDirs([(self._InstanceChrootDir(name),
766 constants.SECURE_DIR_MODE)])
768 if security_model == constants.HT_SM_POOL:
769 ss = ssconf.SimpleStore()
770 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
771 all_uids = set(uidpool.ExpandUidPool(uid_pool))
772 uid = uidpool.RequestUnusedUid(all_uids)
774 username = pwd.getpwuid(uid.GetUid()).pw_name
775 kvm_cmd.extend(["-runas", username])
776 self._RunKVMCmd(name, kvm_cmd)
778 uidpool.ReleaseUid(uid)
782 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
784 self._RunKVMCmd(name, kvm_cmd)
787 change_cmd = 'change vnc password %s' % vnc_pwd
788 self._CallMonitorCommand(instance.name, change_cmd)
790 for filename in temp_files:
791 utils.RemoveFile(filename)
793 def StartInstance(self, instance, block_devices):
794 """Start an instance.
797 self._CheckDown(instance.name)
798 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
799 self._SaveKVMRuntime(instance, kvm_runtime)
800 self._ExecuteKVMRuntime(instance, kvm_runtime)
802 def _CallMonitorCommand(self, instance_name, command):
803 """Invoke a command on the instance monitor.
806 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
807 (utils.ShellQuote(command),
808 constants.SOCAT_PATH,
809 utils.ShellQuote(self._InstanceMonitor(instance_name))))
810 result = utils.RunCmd(socat)
812 msg = ("Failed to send command '%s' to instance %s."
813 " output: %s, error: %s, fail_reason: %s" %
814 (command, instance_name,
815 result.stdout, result.stderr, result.fail_reason))
816 raise errors.HypervisorError(msg)
820 def StopInstance(self, instance, force=False, retry=False, name=None):
824 if name is not None and not force:
825 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
828 acpi = instance.hvparams[constants.HV_ACPI]
831 _, pid, alive = self._InstancePidAlive(name)
832 if pid > 0 and alive:
833 if force or not acpi:
834 utils.KillProcess(pid)
836 self._CallMonitorCommand(name, 'system_powerdown')
838 def CleanupInstance(self, instance_name):
839 """Cleanup after a stopped instance
842 pidfile, pid, alive = self._InstancePidAlive(instance_name)
843 if pid > 0 and alive:
844 raise errors.HypervisorError("Cannot cleanup a live instance")
845 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
847 def RebootInstance(self, instance):
848 """Reboot an instance.
851 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
852 # socket the instance will stop, but now power up again. So we'll resort
853 # to shutdown and restart.
854 _, _, alive = self._InstancePidAlive(instance.name)
856 raise errors.HypervisorError("Failed to reboot instance %s:"
857 " not running" % instance.name)
858 # StopInstance will delete the saved KVM runtime so:
859 # ...first load it...
860 kvm_runtime = self._LoadKVMRuntime(instance)
861 # ...now we can safely call StopInstance...
862 if not self.StopInstance(instance):
863 self.StopInstance(instance, force=True)
864 # ...and finally we can save it again, and execute it...
865 self._SaveKVMRuntime(instance, kvm_runtime)
866 self._ExecuteKVMRuntime(instance, kvm_runtime)
868 def MigrationInfo(self, instance):
869 """Get instance information to perform a migration.
871 @type instance: L{objects.Instance}
872 @param instance: instance to be migrated
874 @return: content of the KVM runtime file
877 return self._ReadKVMRuntime(instance.name)
879 def AcceptInstance(self, instance, info, target):
880 """Prepare to accept an instance.
882 @type instance: L{objects.Instance}
883 @param instance: instance to be accepted
885 @param info: content of the KVM runtime file on the source node
887 @param target: target host (usually ip), on this node
890 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
891 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
892 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
894 def FinalizeMigration(self, instance, info, success):
895 """Finalize an instance migration.
897 Stop the incoming mode KVM.
899 @type instance: L{objects.Instance}
900 @param instance: instance whose migration is being finalized
904 self._WriteKVMRuntime(instance.name, info)
906 self.StopInstance(instance, force=True)
908 def MigrateInstance(self, instance, target, live):
909 """Migrate an instance to a target node.
911 The migration will not be attempted if the instance is not
914 @type instance: L{objects.Instance}
915 @param instance: the instance to be migrated
917 @param target: ip address of the target node
919 @param live: perform a live migration
922 instance_name = instance.name
923 port = instance.hvparams[constants.HV_MIGRATION_PORT]
924 pidfile, pid, alive = self._InstancePidAlive(instance_name)
926 raise errors.HypervisorError("Instance not running, cannot migrate")
928 if not netutils.TcpPing(target, port, live_port_needed=True):
929 raise errors.HypervisorError("Remote host %s not listening on port"
930 " %s, cannot migrate" % (target, port))
933 self._CallMonitorCommand(instance_name, 'stop')
935 migrate_command = ('migrate_set_speed %dm' %
936 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
937 self._CallMonitorCommand(instance_name, migrate_command)
939 migrate_command = ('migrate_set_downtime %dms' %
940 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
941 self._CallMonitorCommand(instance_name, migrate_command)
943 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
944 self._CallMonitorCommand(instance_name, migrate_command)
946 info_command = 'info migrate'
950 result = self._CallMonitorCommand(instance_name, info_command)
951 match = self._MIGRATION_STATUS_RE.search(result.stdout)
954 if not result.stdout:
955 logging.info("KVM: empty 'info migrate' result")
957 logging.warning("KVM: unknown 'info migrate' result: %s",
959 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
961 status = match.group(1)
962 if status == 'completed':
964 elif status == 'active':
965 # reset the broken answers count
967 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
968 elif status == 'failed' or status == 'cancelled':
970 self._CallMonitorCommand(instance_name, 'cont')
971 raise errors.HypervisorError("Migration %s at the kvm level" %
974 logging.warning("KVM: unknown migration status '%s'", status)
976 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
977 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
978 raise errors.HypervisorError("Too many 'info migrate' broken answers")
980 utils.KillProcess(pid)
981 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
983 def GetNodeInfo(self):
984 """Return information about the node.
986 This is just a wrapper over the base GetLinuxNodeInfo method.
988 @return: a dict with the following keys (values in MiB):
989 - memory_total: the total memory size on the node
990 - memory_free: the available memory on the node for instances
991 - memory_dom0: the memory used by the node itself, if available
994 return self.GetLinuxNodeInfo()
997 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
998 """Return a command for connecting to the console of an instance.
1001 if hvparams[constants.HV_SERIAL_CONSOLE]:
1002 shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
1003 (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
1004 utils.ShellQuote(cls._InstanceSerial(instance.name))))
1006 shell_command = "echo 'No serial shell for instance %s'" % instance.name
1008 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1009 if vnc_bind_address:
1010 if instance.network_port > constants.VNC_BASE_PORT:
1011 display = instance.network_port - constants.VNC_BASE_PORT
1012 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
1013 " (display: %d)'" % (vnc_bind_address,
1014 instance.network_port,
1016 shell_command = "%s; %s" % (vnc_command, shell_command)
1018 return shell_command
1021 """Verify the hypervisor.
1023 Check that the binary exists.
1026 if not os.path.exists(constants.KVM_PATH):
1027 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1028 if not os.path.exists(constants.SOCAT_PATH):
1029 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1033 def CheckParameterSyntax(cls, hvparams):
1034 """Check the given parameters for validity.
1036 @type hvparams: dict
1037 @param hvparams: dictionary with parameter names/value
1038 @raise errors.HypervisorError: when a parameter is not valid
1041 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1043 kernel_path = hvparams[constants.HV_KERNEL_PATH]
1045 if not hvparams[constants.HV_ROOT_PATH]:
1046 raise errors.HypervisorError("Need a root partition for the instance,"
1047 " if a kernel is defined")
1049 if (hvparams[constants.HV_VNC_X509_VERIFY] and
1050 not hvparams[constants.HV_VNC_X509]):
1051 raise errors.HypervisorError("%s must be defined, if %s is" %
1052 (constants.HV_VNC_X509,
1053 constants.HV_VNC_X509_VERIFY))
1055 boot_order = hvparams[constants.HV_BOOT_ORDER]
1056 if (boot_order == constants.HT_BO_CDROM and
1057 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1058 raise errors.HypervisorError("Cannot boot from cdrom without an"
1061 security_model = hvparams[constants.HV_SECURITY_MODEL]
1062 if security_model == constants.HT_SM_USER:
1063 if not hvparams[constants.HV_SECURITY_DOMAIN]:
1064 raise errors.HypervisorError("A security domain (user to run kvm as)"
1065 " must be specified")
1066 elif (security_model == constants.HT_SM_NONE or
1067 security_model == constants.HT_SM_POOL):
1068 if hvparams[constants.HV_SECURITY_DOMAIN]:
1069 raise errors.HypervisorError("Cannot have a security domain when the"
1070 " security model is 'none' or 'pool'")
1073 def ValidateParameters(cls, hvparams):
1074 """Check the given parameters for validity.
1076 @type hvparams: dict
1077 @param hvparams: dictionary with parameter names/value
1078 @raise errors.HypervisorError: when a parameter is not valid
1081 super(KVMHypervisor, cls).ValidateParameters(hvparams)
1083 security_model = hvparams[constants.HV_SECURITY_MODEL]
1084 if security_model == constants.HT_SM_USER:
1085 username = hvparams[constants.HV_SECURITY_DOMAIN]
1087 pwd.getpwnam(username)
1089 raise errors.HypervisorError("Unknown security domain user %s"
1093 def PowercycleNode(cls):
1094 """KVM powercycle, just a wrapper over Linux powercycle.
1097 cls.LinuxPowercycle()