4 # Copyright (C) 2008 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
33 from cStringIO import StringIO
35 from ganeti import utils
36 from ganeti import constants
37 from ganeti import errors
38 from ganeti import serializer
39 from ganeti import objects
40 from ganeti import uidpool
41 from ganeti import ssconf
42 from ganeti.hypervisor import hv_base
45 class KVMHypervisor(hv_base.BaseHypervisor):
46 """KVM hypervisor interface"""
49 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
50 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
51 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
52 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
53 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
54 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR]
57 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
58 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
59 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
60 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
61 constants.HV_ACPI: hv_base.NO_CHECK,
62 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
63 constants.HV_VNC_BIND_ADDRESS:
64 (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
65 "the VNC bind address must be either a valid IP address or an absolute"
66 " pathname", None, None),
67 constants.HV_VNC_TLS: hv_base.NO_CHECK,
68 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
69 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
70 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
71 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
72 constants.HV_BOOT_ORDER:
73 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
74 constants.HV_NIC_TYPE:
75 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
76 constants.HV_DISK_TYPE:
77 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
78 constants.HV_USB_MOUSE:
79 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
80 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
81 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
82 constants.HV_DISK_CACHE:
83 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
84 constants.HV_SECURITY_MODEL:
85 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
86 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
87 constants.HV_KVM_FLAG:
88 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
91 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
93 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
94 _MIGRATION_INFO_RETRY_DELAY = 2
96 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
103 hv_base.BaseHypervisor.__init__(self)
104 # Let's make sure the directories we need exist, even if the RUN_DIR lives
105 # in a tmpfs filesystem or has been otherwise wiped out.
106 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
107 utils.EnsureDirs(dirs)
110 def _InstancePidFile(cls, instance_name):
111 """Returns the instance pidfile.
114 return utils.PathJoin(cls._PIDS_DIR, instance_name)
117 def _InstanceUidFile(cls, instance_name):
118 """Returns the instance uidfile.
121 return utils.PathJoin(cls._UIDS_DIR, instance_name)
124 def _InstancePidInfo(cls, pid):
125 """Check pid file for instance information.
127 Check that a pid file is associated with an instance, and retrieve
128 information from its command line.
130 @type pid: string or int
131 @param pid: process id of the instance to check
133 @return: (instance_name, memory, vcpus)
134 @raise errors.HypervisorError: when an instance cannot be found
137 alive = utils.IsProcessAlive(pid)
139 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
141 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
143 cmdline = utils.ReadFile(cmdline_file)
144 except EnvironmentError, err:
145 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
152 arg_list = cmdline.split('\x00')
154 arg = arg_list.pop(0)
156 instance = arg_list.pop(0)
158 memory = int(arg_list.pop(0))
160 vcpus = int(arg_list.pop(0))
163 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
166 return (instance, memory, vcpus)
168 def _InstancePidAlive(self, instance_name):
169 """Returns the instance pidfile, pid, and liveness.
171 @type instance_name: string
172 @param instance_name: instance name
174 @return: (pid file name, pid, liveness)
177 pidfile = self._InstancePidFile(instance_name)
178 pid = utils.ReadPidFile(pidfile)
182 cmd_instance = self._InstancePidInfo(pid)[0]
183 alive = (cmd_instance == instance_name)
184 except errors.HypervisorError:
187 return (pidfile, pid, alive)
189 def _CheckDown(self, instance_name):
190 """Raises an error unless the given instance is down.
193 alive = self._InstancePidAlive(instance_name)[2]
195 raise errors.HypervisorError("Failed to start instance %s: %s" %
196 (instance_name, "already running"))
199 def _InstanceMonitor(cls, instance_name):
200 """Returns the instance monitor socket name
203 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
206 def _InstanceSerial(cls, instance_name):
207 """Returns the instance serial socket name
210 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
213 def _SocatUnixConsoleParams():
214 """Returns the correct parameters for socat
216 If we have a new-enough socat we can use raw mode with an escape character.
219 if constants.SOCAT_USE_ESCAPE:
220 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
222 return "echo=0,icanon=0"
225 def _InstanceKVMRuntime(cls, instance_name):
226 """Returns the instance KVM runtime filename
229 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
232 def _TryReadUidFile(cls, uid_file):
233 """Try to read a uid file
236 if os.path.exists(uid_file):
238 uid = int(utils.ReadFile(uid_file))
240 except EnvironmentError:
241 logging.warning("Can't read uid file", exc_info=True)
242 except (TypeError, ValueError):
243 logging.warning("Can't parse uid file contents", exc_info=True)
247 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
248 """Removes an instance's rutime sockets/files.
251 utils.RemoveFile(pidfile)
252 utils.RemoveFile(cls._InstanceMonitor(instance_name))
253 utils.RemoveFile(cls._InstanceSerial(instance_name))
254 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
255 uid_file = cls._InstanceUidFile(instance_name)
256 uid = cls._TryReadUidFile(uid_file)
257 utils.RemoveFile(uid_file)
259 uidpool.ReleaseUid(uid)
261 def _WriteNetScript(self, instance, seq, nic):
262 """Write a script to connect a net interface to the proper bridge.
264 This can be used by any qemu-type hypervisor.
266 @param instance: instance we're acting on
267 @type instance: instance object
268 @param seq: nic sequence number
270 @param nic: nic we're acting on
271 @type nic: nic object
272 @return: netscript file name
277 script.write("#!/bin/sh\n")
278 script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
279 script.write("PATH=$PATH:/sbin:/usr/sbin\n")
280 script.write("export INSTANCE=%s\n" % instance.name)
281 script.write("export MAC=%s\n" % nic.mac)
283 script.write("export IP=%s\n" % nic.ip)
284 script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
285 if nic.nicparams[constants.NIC_LINK]:
286 script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
287 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
288 script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
289 script.write("export INTERFACE=$1\n")
290 # TODO: make this configurable at ./configure time
291 script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
292 script.write(" # Execute the user-specific vif file\n")
293 script.write(" %s\n" % self._KVM_NETWORK_SCRIPT)
294 script.write("else\n")
295 script.write(" ifconfig $INTERFACE 0.0.0.0 up\n")
296 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
297 script.write(" # Connect the interface to the bridge\n")
298 script.write(" brctl addif $BRIDGE $INTERFACE\n")
299 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
301 raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
302 script.write(" # Route traffic targeted at the IP to the interface\n")
303 if nic.nicparams[constants.NIC_LINK]:
304 script.write(" while ip rule del dev $INTERFACE; do :; done\n")
305 script.write(" ip rule add dev $INTERFACE table $LINK\n")
306 script.write(" ip route replace $IP table $LINK proto static"
309 script.write(" ip route replace $IP proto static"
311 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
312 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
313 script.write(" if [ -d %s ]; then\n" % interface_v4_conf)
314 script.write(" echo 1 > %s/proxy_arp\n" % interface_v4_conf)
315 script.write(" echo 1 > %s/forwarding\n" % interface_v4_conf)
316 script.write(" fi\n")
317 script.write(" if [ -d %s ]; then\n" % interface_v6_conf)
318 script.write(" echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
319 script.write(" echo 1 > %s/forwarding\n" % interface_v6_conf)
320 script.write(" fi\n")
321 script.write("fi\n\n")
322 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
323 # mounted noexec sometimes, so we'll have to find another place.
324 (tmpfd, tmpfile_name) = tempfile.mkstemp()
325 tmpfile = os.fdopen(tmpfd, 'w')
327 tmpfile.write(script.getvalue())
330 os.chmod(tmpfile_name, 0755)
333 def ListInstances(self):
334 """Get the list of running instances.
336 We can do this by listing our live instances directory and
337 checking whether the associated kvm process is still alive.
341 for name in os.listdir(self._PIDS_DIR):
342 if self._InstancePidAlive(name)[2]:
346 def GetInstanceInfo(self, instance_name):
347 """Get instance properties.
349 @type instance_name: string
350 @param instance_name: the instance name
351 @rtype: tuple of strings
352 @return: (name, id, memory, vcpus, stat, times)
355 _, pid, alive = self._InstancePidAlive(instance_name)
359 _, memory, vcpus = self._InstancePidInfo(pid)
363 return (instance_name, pid, memory, vcpus, stat, times)
365 def GetAllInstancesInfo(self):
366 """Get properties of all instances.
368 @return: list of tuples (name, id, memory, vcpus, stat, times)
372 for name in os.listdir(self._PIDS_DIR):
374 info = self.GetInstanceInfo(name)
375 except errors.HypervisorError:
381 def _GenerateKVMRuntime(self, instance, block_devices):
382 """Generate KVM information to start an instance.
385 pidfile = self._InstancePidFile(instance.name)
386 kvm = constants.KVM_PATH
388 # used just by the vnc server, if enabled
389 kvm_cmd.extend(['-name', instance.name])
390 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
391 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
392 kvm_cmd.extend(['-pidfile', pidfile])
393 kvm_cmd.extend(['-daemonize'])
394 if not instance.hvparams[constants.HV_ACPI]:
395 kvm_cmd.extend(['-no-acpi'])
397 hvp = instance.hvparams
398 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
399 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
400 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
402 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
403 kvm_cmd.extend(["-enable-kvm"])
404 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
405 kvm_cmd.extend(["-disable-kvm"])
408 kvm_cmd.extend(['-boot', 'n'])
410 disk_type = hvp[constants.HV_DISK_TYPE]
411 if disk_type == constants.HT_DISK_PARAVIRTUAL:
412 if_val = ',if=virtio'
414 if_val = ',if=%s' % disk_type
416 disk_cache = hvp[constants.HV_DISK_CACHE]
417 if disk_cache != constants.HT_CACHE_DEFAULT:
418 cache_val = ",cache=%s" % disk_cache
421 for cfdev, dev_path in block_devices:
422 if cfdev.mode != constants.DISK_RDWR:
423 raise errors.HypervisorError("Instance has read-only disks which"
424 " are not supported by KVM")
425 # TODO: handle FD_LOOP and FD_BLKTAP (?)
427 kvm_cmd.extend(['-boot', 'c'])
428 if disk_type != constants.HT_DISK_IDE:
429 boot_val = ',boot=on'
432 # We only boot from the first disk
437 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
439 kvm_cmd.extend(['-drive', drive_val])
441 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
443 options = ',format=raw,media=cdrom'
445 kvm_cmd.extend(['-boot', 'd'])
446 if disk_type != constants.HT_DISK_IDE:
447 options = '%s,boot=on' % options
449 if disk_type == constants.HT_DISK_PARAVIRTUAL:
450 if_val = ',if=virtio'
452 if_val = ',if=%s' % disk_type
453 options = '%s%s' % (options, if_val)
454 drive_val = 'file=%s%s' % (iso_image, options)
455 kvm_cmd.extend(['-drive', drive_val])
457 kernel_path = hvp[constants.HV_KERNEL_PATH]
459 kvm_cmd.extend(['-kernel', kernel_path])
460 initrd_path = hvp[constants.HV_INITRD_PATH]
462 kvm_cmd.extend(['-initrd', initrd_path])
463 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
464 hvp[constants.HV_KERNEL_ARGS]]
465 if hvp[constants.HV_SERIAL_CONSOLE]:
466 root_append.append('console=ttyS0,38400')
467 kvm_cmd.extend(['-append', ' '.join(root_append)])
469 mouse_type = hvp[constants.HV_USB_MOUSE]
471 kvm_cmd.extend(['-usb'])
472 kvm_cmd.extend(['-usbdevice', mouse_type])
474 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
476 if utils.IsValidIP(vnc_bind_address):
477 if instance.network_port > constants.VNC_BASE_PORT:
478 display = instance.network_port - constants.VNC_BASE_PORT
479 if vnc_bind_address == '0.0.0.0':
480 vnc_arg = ':%d' % (display)
482 vnc_arg = '%s:%d' % (vnc_bind_address, display)
484 logging.error("Network port is not a valid VNC display (%d < %d)."
485 " Not starting VNC", instance.network_port,
486 constants.VNC_BASE_PORT)
489 # Only allow tls and other option when not binding to a file, for now.
490 # kvm/qemu gets confused otherwise about the filename to use.
492 if hvp[constants.HV_VNC_TLS]:
493 vnc_append = '%s,tls' % vnc_append
494 if hvp[constants.HV_VNC_X509_VERIFY]:
495 vnc_append = '%s,x509verify=%s' % (vnc_append,
496 hvp[constants.HV_VNC_X509])
497 elif hvp[constants.HV_VNC_X509]:
498 vnc_append = '%s,x509=%s' % (vnc_append,
499 hvp[constants.HV_VNC_X509])
500 if hvp[constants.HV_VNC_PASSWORD_FILE]:
501 vnc_append = '%s,password' % vnc_append
503 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
506 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
508 kvm_cmd.extend(['-vnc', vnc_arg])
510 # Also add a tablet USB device to act as a mouse
511 # This solves various mouse alignment issues
512 kvm_cmd.extend(['-usbdevice', 'tablet'])
514 kvm_cmd.extend(['-nographic'])
516 monitor_dev = ("unix:%s,server,nowait" %
517 self._InstanceMonitor(instance.name))
518 kvm_cmd.extend(['-monitor', monitor_dev])
519 if hvp[constants.HV_SERIAL_CONSOLE]:
520 serial_dev = ('unix:%s,server,nowait' %
521 self._InstanceSerial(instance.name))
522 kvm_cmd.extend(['-serial', serial_dev])
524 kvm_cmd.extend(['-serial', 'none'])
526 if hvp[constants.HV_USE_LOCALTIME]:
527 kvm_cmd.extend(['-localtime'])
529 # Save the current instance nics, but defer their expansion as parameters,
530 # as we'll need to generate executable temp files for them.
531 kvm_nics = instance.nics
534 return (kvm_cmd, kvm_nics, hvparams)
536 def _WriteKVMRuntime(self, instance_name, data):
537 """Write an instance's KVM runtime
541 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
543 except EnvironmentError, err:
544 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
546 def _ReadKVMRuntime(self, instance_name):
547 """Read an instance's KVM runtime
551 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
552 except EnvironmentError, err:
553 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
556 def _SaveKVMRuntime(self, instance, kvm_runtime):
557 """Save an instance's KVM runtime
560 kvm_cmd, kvm_nics, hvparams = kvm_runtime
561 serialized_nics = [nic.ToDict() for nic in kvm_nics]
562 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
563 self._WriteKVMRuntime(instance.name, serialized_form)
565 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
566 """Load an instance's KVM runtime
569 if not serialized_runtime:
570 serialized_runtime = self._ReadKVMRuntime(instance.name)
571 loaded_runtime = serializer.Load(serialized_runtime)
572 kvm_cmd, serialized_nics, hvparams = loaded_runtime
573 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
574 return (kvm_cmd, kvm_nics, hvparams)
576 def _RunKVMCmd(self, name, kvm_cmd):
577 """Run the KVM cmd and check for errors
580 @param name: instance name
581 @type kvm_cmd: list of strings
582 @param kvm_cmd: runcmd input for kvm
585 result = utils.RunCmd(kvm_cmd)
587 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
588 (name, result.fail_reason, result.output))
589 if not self._InstancePidAlive(name)[2]:
590 raise errors.HypervisorError("Failed to start instance %s" % name)
592 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
593 """Execute a KVM cmd, after completing it with some last minute data
595 @type incoming: tuple of strings
596 @param incoming: (target_host_ip, port)
599 hvp = instance.hvparams
601 self._CheckDown(name)
605 kvm_cmd, kvm_nics, hvparams = kvm_runtime
607 security_model = hvp[constants.HV_SECURITY_MODEL]
608 if security_model == constants.HT_SM_USER:
609 kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
612 kvm_cmd.extend(['-net', 'none'])
614 nic_type = hvparams[constants.HV_NIC_TYPE]
615 if nic_type == constants.HT_NIC_PARAVIRTUAL:
616 nic_model = "model=virtio"
618 nic_model = "model=%s" % nic_type
620 for nic_seq, nic in enumerate(kvm_nics):
621 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
622 script = self._WriteNetScript(instance, nic_seq, nic)
623 kvm_cmd.extend(['-net', nic_val])
624 kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
625 temp_files.append(script)
628 target, port = incoming
629 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
631 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
635 vnc_pwd = utils.ReadFile(vnc_pwd_file)
636 except EnvironmentError, err:
637 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
638 % (vnc_pwd_file, err))
640 if security_model == constants.HT_SM_POOL:
641 ss = ssconf.SimpleStore()
642 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
643 all_uids = set(uidpool.ExpandUidPool(uid_pool))
644 uid = uidpool.RequestUnusedUid(all_uids)
646 username = pwd.getpwuid(uid.GetUid()).pw_name
647 kvm_cmd.extend(["-runas", username])
648 self._RunKVMCmd(name, kvm_cmd)
650 uidpool.ReleaseUid(uid)
654 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
656 self._RunKVMCmd(name, kvm_cmd)
659 change_cmd = 'change vnc password %s' % vnc_pwd
660 self._CallMonitorCommand(instance.name, change_cmd)
662 for filename in temp_files:
663 utils.RemoveFile(filename)
665 def StartInstance(self, instance, block_devices):
666 """Start an instance.
669 self._CheckDown(instance.name)
670 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
671 self._SaveKVMRuntime(instance, kvm_runtime)
672 self._ExecuteKVMRuntime(instance, kvm_runtime)
674 def _CallMonitorCommand(self, instance_name, command):
675 """Invoke a command on the instance monitor.
678 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
679 (utils.ShellQuote(command),
680 constants.SOCAT_PATH,
681 utils.ShellQuote(self._InstanceMonitor(instance_name))))
682 result = utils.RunCmd(socat)
684 msg = ("Failed to send command '%s' to instance %s."
685 " output: %s, error: %s, fail_reason: %s" %
686 (command, instance_name,
687 result.stdout, result.stderr, result.fail_reason))
688 raise errors.HypervisorError(msg)
692 def StopInstance(self, instance, force=False, retry=False, name=None):
696 if name is not None and not force:
697 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
700 acpi = instance.hvparams[constants.HV_ACPI]
703 _, pid, alive = self._InstancePidAlive(name)
704 if pid > 0 and alive:
705 if force or not acpi:
706 utils.KillProcess(pid)
708 self._CallMonitorCommand(name, 'system_powerdown')
710 def CleanupInstance(self, instance_name):
711 """Cleanup after a stopped instance
714 pidfile, pid, alive = self._InstancePidAlive(instance_name)
715 if pid > 0 and alive:
716 raise errors.HypervisorError("Cannot cleanup a live instance")
717 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
719 def RebootInstance(self, instance):
720 """Reboot an instance.
723 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
724 # socket the instance will stop, but now power up again. So we'll resort
725 # to shutdown and restart.
726 _, _, alive = self._InstancePidAlive(instance.name)
728 raise errors.HypervisorError("Failed to reboot instance %s:"
729 " not running" % instance.name)
730 # StopInstance will delete the saved KVM runtime so:
731 # ...first load it...
732 kvm_runtime = self._LoadKVMRuntime(instance)
733 # ...now we can safely call StopInstance...
734 if not self.StopInstance(instance):
735 self.StopInstance(instance, force=True)
736 # ...and finally we can save it again, and execute it...
737 self._SaveKVMRuntime(instance, kvm_runtime)
738 self._ExecuteKVMRuntime(instance, kvm_runtime)
740 def MigrationInfo(self, instance):
741 """Get instance information to perform a migration.
743 @type instance: L{objects.Instance}
744 @param instance: instance to be migrated
746 @return: content of the KVM runtime file
749 return self._ReadKVMRuntime(instance.name)
751 def AcceptInstance(self, instance, info, target):
752 """Prepare to accept an instance.
754 @type instance: L{objects.Instance}
755 @param instance: instance to be accepted
757 @param info: content of the KVM runtime file on the source node
759 @param target: target host (usually ip), on this node
762 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
763 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
764 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
766 def FinalizeMigration(self, instance, info, success):
767 """Finalize an instance migration.
769 Stop the incoming mode KVM.
771 @type instance: L{objects.Instance}
772 @param instance: instance whose migration is being aborted
776 self._WriteKVMRuntime(instance.name, info)
778 self.StopInstance(instance, force=True)
780 def MigrateInstance(self, instance, target, live):
781 """Migrate an instance to a target node.
783 The migration will not be attempted if the instance is not
786 @type instance: L{objects.Instance}
787 @param instance: the instance to be migrated
789 @param target: ip address of the target node
791 @param live: perform a live migration
794 instance_name = instance.name
795 port = instance.hvparams[constants.HV_MIGRATION_PORT]
796 pidfile, pid, alive = self._InstancePidAlive(instance_name)
798 raise errors.HypervisorError("Instance not running, cannot migrate")
800 if not utils.TcpPing(target, port, live_port_needed=True):
801 raise errors.HypervisorError("Remote host %s not listening on port"
802 " %s, cannot migrate" % (target, port))
805 self._CallMonitorCommand(instance_name, 'stop')
807 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
808 self._CallMonitorCommand(instance_name, migrate_command)
810 info_command = 'info migrate'
814 result = self._CallMonitorCommand(instance_name, info_command)
815 match = self._MIGRATION_STATUS_RE.search(result.stdout)
818 if not result.stdout:
819 logging.info("KVM: empty 'info migrate' result")
821 logging.warning("KVM: unknown 'info migrate' result: %s",
823 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
825 status = match.group(1)
826 if status == 'completed':
828 elif status == 'active':
829 # reset the broken answers count
831 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
832 elif status == 'failed' or status == 'cancelled':
834 self._CallMonitorCommand(instance_name, 'cont')
835 raise errors.HypervisorError("Migration %s at the kvm level" %
838 logging.warning("KVM: unknown migration status '%s'", status)
840 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
841 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
842 raise errors.HypervisorError("Too many 'info migrate' broken answers")
844 utils.KillProcess(pid)
845 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
847 def GetNodeInfo(self):
848 """Return information about the node.
850 This is just a wrapper over the base GetLinuxNodeInfo method.
852 @return: a dict with the following keys (values in MiB):
853 - memory_total: the total memory size on the node
854 - memory_free: the available memory on the node for instances
855 - memory_dom0: the memory used by the node itself, if available
858 return self.GetLinuxNodeInfo()
861 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
862 """Return a command for connecting to the console of an instance.
865 if hvparams[constants.HV_SERIAL_CONSOLE]:
866 shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
867 (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
868 utils.ShellQuote(cls._InstanceSerial(instance.name))))
870 shell_command = "echo 'No serial shell for instance %s'" % instance.name
872 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
874 if instance.network_port > constants.VNC_BASE_PORT:
875 display = instance.network_port - constants.VNC_BASE_PORT
876 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
877 " (display: %d)'" % (vnc_bind_address,
878 instance.network_port,
880 shell_command = "%s; %s" % (vnc_command, shell_command)
885 """Verify the hypervisor.
887 Check that the binary exists.
890 if not os.path.exists(constants.KVM_PATH):
891 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
892 if not os.path.exists(constants.SOCAT_PATH):
893 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
897 def CheckParameterSyntax(cls, hvparams):
898 """Check the given parameters for validity.
901 @param hvparams: dictionary with parameter names/value
902 @raise errors.HypervisorError: when a parameter is not valid
905 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
907 kernel_path = hvparams[constants.HV_KERNEL_PATH]
909 if not hvparams[constants.HV_ROOT_PATH]:
910 raise errors.HypervisorError("Need a root partition for the instance,"
911 " if a kernel is defined")
913 if (hvparams[constants.HV_VNC_X509_VERIFY] and
914 not hvparams[constants.HV_VNC_X509]):
915 raise errors.HypervisorError("%s must be defined, if %s is" %
916 (constants.HV_VNC_X509,
917 constants.HV_VNC_X509_VERIFY))
919 boot_order = hvparams[constants.HV_BOOT_ORDER]
920 if (boot_order == constants.HT_BO_CDROM and
921 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
922 raise errors.HypervisorError("Cannot boot from cdrom without an"
925 security_model = hvparams[constants.HV_SECURITY_MODEL]
926 if security_model == constants.HT_SM_USER:
927 if not hvparams[constants.HV_SECURITY_DOMAIN]:
928 raise errors.HypervisorError("A security domain (user to run kvm as)"
929 " must be specified")
930 elif (security_model == constants.HT_SM_NONE or
931 security_model == constants.HT_SM_POOL):
932 if hvparams[constants.HV_SECURITY_DOMAIN]:
933 raise errors.HypervisorError("Cannot have a security domain when the"
934 " security model is 'none' or 'pool'")
937 def ValidateParameters(cls, hvparams):
938 """Check the given parameters for validity.
941 @param hvparams: dictionary with parameter names/value
942 @raise errors.HypervisorError: when a parameter is not valid
945 super(KVMHypervisor, cls).ValidateParameters(hvparams)
947 security_model = hvparams[constants.HV_SECURITY_MODEL]
948 if security_model == constants.HT_SM_USER:
949 username = hvparams[constants.HV_SECURITY_DOMAIN]
951 pwd.getpwnam(username)
953 raise errors.HypervisorError("Unknown security domain user %s"
957 def PowercycleNode(cls):
958 """KVM powercycle, just a wrapper over Linux powercycle.
961 cls.LinuxPowercycle()