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.ReadOneLineFile(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")
291 script.write("export TAGS=\"%s\"\n" % " ".join(instance.tags))
292 # TODO: make this configurable at ./configure time
293 script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
294 script.write(" # Execute the user-specific vif file\n")
295 script.write(" %s\n" % self._KVM_NETWORK_SCRIPT)
296 script.write("else\n")
297 script.write(" ifconfig $INTERFACE 0.0.0.0 up\n")
298 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
299 script.write(" # Connect the interface to the bridge\n")
300 script.write(" brctl addif $BRIDGE $INTERFACE\n")
301 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
303 raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
304 script.write(" # Route traffic targeted at the IP to the interface\n")
305 if nic.nicparams[constants.NIC_LINK]:
306 script.write(" while ip rule del dev $INTERFACE; do :; done\n")
307 script.write(" ip rule add dev $INTERFACE table $LINK\n")
308 script.write(" ip route replace $IP table $LINK proto static"
311 script.write(" ip route replace $IP proto static"
313 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
314 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
315 script.write(" if [ -d %s ]; then\n" % interface_v4_conf)
316 script.write(" echo 1 > %s/proxy_arp\n" % interface_v4_conf)
317 script.write(" echo 1 > %s/forwarding\n" % interface_v4_conf)
318 script.write(" fi\n")
319 script.write(" if [ -d %s ]; then\n" % interface_v6_conf)
320 script.write(" echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
321 script.write(" echo 1 > %s/forwarding\n" % interface_v6_conf)
322 script.write(" fi\n")
323 script.write("fi\n\n")
324 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
325 # mounted noexec sometimes, so we'll have to find another place.
326 (tmpfd, tmpfile_name) = tempfile.mkstemp()
327 tmpfile = os.fdopen(tmpfd, 'w')
329 tmpfile.write(script.getvalue())
332 os.chmod(tmpfile_name, 0755)
335 def ListInstances(self):
336 """Get the list of running instances.
338 We can do this by listing our live instances directory and
339 checking whether the associated kvm process is still alive.
343 for name in os.listdir(self._PIDS_DIR):
344 if self._InstancePidAlive(name)[2]:
348 def GetInstanceInfo(self, instance_name):
349 """Get instance properties.
351 @type instance_name: string
352 @param instance_name: the instance name
353 @rtype: tuple of strings
354 @return: (name, id, memory, vcpus, stat, times)
357 _, pid, alive = self._InstancePidAlive(instance_name)
361 _, memory, vcpus = self._InstancePidInfo(pid)
365 return (instance_name, pid, memory, vcpus, stat, times)
367 def GetAllInstancesInfo(self):
368 """Get properties of all instances.
370 @return: list of tuples (name, id, memory, vcpus, stat, times)
374 for name in os.listdir(self._PIDS_DIR):
376 info = self.GetInstanceInfo(name)
377 except errors.HypervisorError:
383 def _GenerateKVMRuntime(self, instance, block_devices):
384 """Generate KVM information to start an instance.
387 pidfile = self._InstancePidFile(instance.name)
388 kvm = constants.KVM_PATH
390 # used just by the vnc server, if enabled
391 kvm_cmd.extend(['-name', instance.name])
392 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
393 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
394 kvm_cmd.extend(['-pidfile', pidfile])
395 kvm_cmd.extend(['-daemonize'])
396 if not instance.hvparams[constants.HV_ACPI]:
397 kvm_cmd.extend(['-no-acpi'])
399 hvp = instance.hvparams
400 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
401 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
402 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
404 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
405 kvm_cmd.extend(["-enable-kvm"])
406 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
407 kvm_cmd.extend(["-disable-kvm"])
410 kvm_cmd.extend(['-boot', 'n'])
412 disk_type = hvp[constants.HV_DISK_TYPE]
413 if disk_type == constants.HT_DISK_PARAVIRTUAL:
414 if_val = ',if=virtio'
416 if_val = ',if=%s' % disk_type
418 disk_cache = hvp[constants.HV_DISK_CACHE]
419 if disk_cache != constants.HT_CACHE_DEFAULT:
420 cache_val = ",cache=%s" % disk_cache
423 for cfdev, dev_path in block_devices:
424 if cfdev.mode != constants.DISK_RDWR:
425 raise errors.HypervisorError("Instance has read-only disks which"
426 " are not supported by KVM")
427 # TODO: handle FD_LOOP and FD_BLKTAP (?)
429 kvm_cmd.extend(['-boot', 'c'])
430 if disk_type != constants.HT_DISK_IDE:
431 boot_val = ',boot=on'
434 # We only boot from the first disk
439 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
441 kvm_cmd.extend(['-drive', drive_val])
443 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
445 options = ',format=raw,media=cdrom'
447 kvm_cmd.extend(['-boot', 'd'])
448 if disk_type != constants.HT_DISK_IDE:
449 options = '%s,boot=on' % options
451 if disk_type == constants.HT_DISK_PARAVIRTUAL:
452 if_val = ',if=virtio'
454 if_val = ',if=%s' % disk_type
455 options = '%s%s' % (options, if_val)
456 drive_val = 'file=%s%s' % (iso_image, options)
457 kvm_cmd.extend(['-drive', drive_val])
459 kernel_path = hvp[constants.HV_KERNEL_PATH]
461 kvm_cmd.extend(['-kernel', kernel_path])
462 initrd_path = hvp[constants.HV_INITRD_PATH]
464 kvm_cmd.extend(['-initrd', initrd_path])
465 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
466 hvp[constants.HV_KERNEL_ARGS]]
467 if hvp[constants.HV_SERIAL_CONSOLE]:
468 root_append.append('console=ttyS0,38400')
469 kvm_cmd.extend(['-append', ' '.join(root_append)])
471 mouse_type = hvp[constants.HV_USB_MOUSE]
473 kvm_cmd.extend(['-usb'])
474 kvm_cmd.extend(['-usbdevice', mouse_type])
476 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
478 if utils.IsValidIP(vnc_bind_address):
479 if instance.network_port > constants.VNC_BASE_PORT:
480 display = instance.network_port - constants.VNC_BASE_PORT
481 if vnc_bind_address == '0.0.0.0':
482 vnc_arg = ':%d' % (display)
484 vnc_arg = '%s:%d' % (vnc_bind_address, display)
486 logging.error("Network port is not a valid VNC display (%d < %d)."
487 " Not starting VNC", instance.network_port,
488 constants.VNC_BASE_PORT)
491 # Only allow tls and other option when not binding to a file, for now.
492 # kvm/qemu gets confused otherwise about the filename to use.
494 if hvp[constants.HV_VNC_TLS]:
495 vnc_append = '%s,tls' % vnc_append
496 if hvp[constants.HV_VNC_X509_VERIFY]:
497 vnc_append = '%s,x509verify=%s' % (vnc_append,
498 hvp[constants.HV_VNC_X509])
499 elif hvp[constants.HV_VNC_X509]:
500 vnc_append = '%s,x509=%s' % (vnc_append,
501 hvp[constants.HV_VNC_X509])
502 if hvp[constants.HV_VNC_PASSWORD_FILE]:
503 vnc_append = '%s,password' % vnc_append
505 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
508 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
510 kvm_cmd.extend(['-vnc', vnc_arg])
512 # Also add a tablet USB device to act as a mouse
513 # This solves various mouse alignment issues
514 kvm_cmd.extend(['-usbdevice', 'tablet'])
516 kvm_cmd.extend(['-nographic'])
518 monitor_dev = ("unix:%s,server,nowait" %
519 self._InstanceMonitor(instance.name))
520 kvm_cmd.extend(['-monitor', monitor_dev])
521 if hvp[constants.HV_SERIAL_CONSOLE]:
522 serial_dev = ('unix:%s,server,nowait' %
523 self._InstanceSerial(instance.name))
524 kvm_cmd.extend(['-serial', serial_dev])
526 kvm_cmd.extend(['-serial', 'none'])
528 if hvp[constants.HV_USE_LOCALTIME]:
529 kvm_cmd.extend(['-localtime'])
531 # Save the current instance nics, but defer their expansion as parameters,
532 # as we'll need to generate executable temp files for them.
533 kvm_nics = instance.nics
536 return (kvm_cmd, kvm_nics, hvparams)
538 def _WriteKVMRuntime(self, instance_name, data):
539 """Write an instance's KVM runtime
543 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
545 except EnvironmentError, err:
546 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
548 def _ReadKVMRuntime(self, instance_name):
549 """Read an instance's KVM runtime
553 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
554 except EnvironmentError, err:
555 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
558 def _SaveKVMRuntime(self, instance, kvm_runtime):
559 """Save an instance's KVM runtime
562 kvm_cmd, kvm_nics, hvparams = kvm_runtime
563 serialized_nics = [nic.ToDict() for nic in kvm_nics]
564 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
565 self._WriteKVMRuntime(instance.name, serialized_form)
567 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
568 """Load an instance's KVM runtime
571 if not serialized_runtime:
572 serialized_runtime = self._ReadKVMRuntime(instance.name)
573 loaded_runtime = serializer.Load(serialized_runtime)
574 kvm_cmd, serialized_nics, hvparams = loaded_runtime
575 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
576 return (kvm_cmd, kvm_nics, hvparams)
578 def _RunKVMCmd(self, name, kvm_cmd):
579 """Run the KVM cmd and check for errors
582 @param name: instance name
583 @type kvm_cmd: list of strings
584 @param kvm_cmd: runcmd input for kvm
587 result = utils.RunCmd(kvm_cmd)
589 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
590 (name, result.fail_reason, result.output))
591 if not self._InstancePidAlive(name)[2]:
592 raise errors.HypervisorError("Failed to start instance %s" % name)
594 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
595 """Execute a KVM cmd, after completing it with some last minute data
597 @type incoming: tuple of strings
598 @param incoming: (target_host_ip, port)
601 hvp = instance.hvparams
603 self._CheckDown(name)
607 kvm_cmd, kvm_nics, hvparams = kvm_runtime
609 security_model = hvp[constants.HV_SECURITY_MODEL]
610 if security_model == constants.HT_SM_USER:
611 kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
614 kvm_cmd.extend(['-net', 'none'])
616 nic_type = hvparams[constants.HV_NIC_TYPE]
617 if nic_type == constants.HT_NIC_PARAVIRTUAL:
618 nic_model = "model=virtio"
620 nic_model = "model=%s" % nic_type
622 for nic_seq, nic in enumerate(kvm_nics):
623 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
624 script = self._WriteNetScript(instance, nic_seq, nic)
625 kvm_cmd.extend(['-net', nic_val])
626 kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
627 temp_files.append(script)
630 target, port = incoming
631 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
633 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
637 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
638 except EnvironmentError, err:
639 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
640 % (vnc_pwd_file, err))
642 if security_model == constants.HT_SM_POOL:
643 ss = ssconf.SimpleStore()
644 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
645 all_uids = set(uidpool.ExpandUidPool(uid_pool))
646 uid = uidpool.RequestUnusedUid(all_uids)
648 username = pwd.getpwuid(uid.GetUid()).pw_name
649 kvm_cmd.extend(["-runas", username])
650 self._RunKVMCmd(name, kvm_cmd)
652 uidpool.ReleaseUid(uid)
656 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
658 self._RunKVMCmd(name, kvm_cmd)
661 change_cmd = 'change vnc password %s' % vnc_pwd
662 self._CallMonitorCommand(instance.name, change_cmd)
664 for filename in temp_files:
665 utils.RemoveFile(filename)
667 def StartInstance(self, instance, block_devices):
668 """Start an instance.
671 self._CheckDown(instance.name)
672 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
673 self._SaveKVMRuntime(instance, kvm_runtime)
674 self._ExecuteKVMRuntime(instance, kvm_runtime)
676 def _CallMonitorCommand(self, instance_name, command):
677 """Invoke a command on the instance monitor.
680 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
681 (utils.ShellQuote(command),
682 constants.SOCAT_PATH,
683 utils.ShellQuote(self._InstanceMonitor(instance_name))))
684 result = utils.RunCmd(socat)
686 msg = ("Failed to send command '%s' to instance %s."
687 " output: %s, error: %s, fail_reason: %s" %
688 (command, instance_name,
689 result.stdout, result.stderr, result.fail_reason))
690 raise errors.HypervisorError(msg)
694 def StopInstance(self, instance, force=False, retry=False, name=None):
698 if name is not None and not force:
699 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
702 acpi = instance.hvparams[constants.HV_ACPI]
705 _, pid, alive = self._InstancePidAlive(name)
706 if pid > 0 and alive:
707 if force or not acpi:
708 utils.KillProcess(pid)
710 self._CallMonitorCommand(name, 'system_powerdown')
712 def CleanupInstance(self, instance_name):
713 """Cleanup after a stopped instance
716 pidfile, pid, alive = self._InstancePidAlive(instance_name)
717 if pid > 0 and alive:
718 raise errors.HypervisorError("Cannot cleanup a live instance")
719 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
721 def RebootInstance(self, instance):
722 """Reboot an instance.
725 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
726 # socket the instance will stop, but now power up again. So we'll resort
727 # to shutdown and restart.
728 _, _, alive = self._InstancePidAlive(instance.name)
730 raise errors.HypervisorError("Failed to reboot instance %s:"
731 " not running" % instance.name)
732 # StopInstance will delete the saved KVM runtime so:
733 # ...first load it...
734 kvm_runtime = self._LoadKVMRuntime(instance)
735 # ...now we can safely call StopInstance...
736 if not self.StopInstance(instance):
737 self.StopInstance(instance, force=True)
738 # ...and finally we can save it again, and execute it...
739 self._SaveKVMRuntime(instance, kvm_runtime)
740 self._ExecuteKVMRuntime(instance, kvm_runtime)
742 def MigrationInfo(self, instance):
743 """Get instance information to perform a migration.
745 @type instance: L{objects.Instance}
746 @param instance: instance to be migrated
748 @return: content of the KVM runtime file
751 return self._ReadKVMRuntime(instance.name)
753 def AcceptInstance(self, instance, info, target):
754 """Prepare to accept an instance.
756 @type instance: L{objects.Instance}
757 @param instance: instance to be accepted
759 @param info: content of the KVM runtime file on the source node
761 @param target: target host (usually ip), on this node
764 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
765 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
766 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
768 def FinalizeMigration(self, instance, info, success):
769 """Finalize an instance migration.
771 Stop the incoming mode KVM.
773 @type instance: L{objects.Instance}
774 @param instance: instance whose migration is being aborted
778 self._WriteKVMRuntime(instance.name, info)
780 self.StopInstance(instance, force=True)
782 def MigrateInstance(self, instance, target, live):
783 """Migrate an instance to a target node.
785 The migration will not be attempted if the instance is not
788 @type instance: L{objects.Instance}
789 @param instance: the instance to be migrated
791 @param target: ip address of the target node
793 @param live: perform a live migration
796 instance_name = instance.name
797 port = instance.hvparams[constants.HV_MIGRATION_PORT]
798 pidfile, pid, alive = self._InstancePidAlive(instance_name)
800 raise errors.HypervisorError("Instance not running, cannot migrate")
802 if not utils.TcpPing(target, port, live_port_needed=True):
803 raise errors.HypervisorError("Remote host %s not listening on port"
804 " %s, cannot migrate" % (target, port))
807 self._CallMonitorCommand(instance_name, 'stop')
809 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
810 self._CallMonitorCommand(instance_name, migrate_command)
812 info_command = 'info migrate'
816 result = self._CallMonitorCommand(instance_name, info_command)
817 match = self._MIGRATION_STATUS_RE.search(result.stdout)
820 if not result.stdout:
821 logging.info("KVM: empty 'info migrate' result")
823 logging.warning("KVM: unknown 'info migrate' result: %s",
825 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
827 status = match.group(1)
828 if status == 'completed':
830 elif status == 'active':
831 # reset the broken answers count
833 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
834 elif status == 'failed' or status == 'cancelled':
836 self._CallMonitorCommand(instance_name, 'cont')
837 raise errors.HypervisorError("Migration %s at the kvm level" %
840 logging.warning("KVM: unknown migration status '%s'", status)
842 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
843 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
844 raise errors.HypervisorError("Too many 'info migrate' broken answers")
846 utils.KillProcess(pid)
847 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
849 def GetNodeInfo(self):
850 """Return information about the node.
852 This is just a wrapper over the base GetLinuxNodeInfo method.
854 @return: a dict with the following keys (values in MiB):
855 - memory_total: the total memory size on the node
856 - memory_free: the available memory on the node for instances
857 - memory_dom0: the memory used by the node itself, if available
860 return self.GetLinuxNodeInfo()
863 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
864 """Return a command for connecting to the console of an instance.
867 if hvparams[constants.HV_SERIAL_CONSOLE]:
868 shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
869 (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
870 utils.ShellQuote(cls._InstanceSerial(instance.name))))
872 shell_command = "echo 'No serial shell for instance %s'" % instance.name
874 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
876 if instance.network_port > constants.VNC_BASE_PORT:
877 display = instance.network_port - constants.VNC_BASE_PORT
878 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
879 " (display: %d)'" % (vnc_bind_address,
880 instance.network_port,
882 shell_command = "%s; %s" % (vnc_command, shell_command)
887 """Verify the hypervisor.
889 Check that the binary exists.
892 if not os.path.exists(constants.KVM_PATH):
893 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
894 if not os.path.exists(constants.SOCAT_PATH):
895 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
899 def CheckParameterSyntax(cls, hvparams):
900 """Check the given parameters for validity.
903 @param hvparams: dictionary with parameter names/value
904 @raise errors.HypervisorError: when a parameter is not valid
907 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
909 kernel_path = hvparams[constants.HV_KERNEL_PATH]
911 if not hvparams[constants.HV_ROOT_PATH]:
912 raise errors.HypervisorError("Need a root partition for the instance,"
913 " if a kernel is defined")
915 if (hvparams[constants.HV_VNC_X509_VERIFY] and
916 not hvparams[constants.HV_VNC_X509]):
917 raise errors.HypervisorError("%s must be defined, if %s is" %
918 (constants.HV_VNC_X509,
919 constants.HV_VNC_X509_VERIFY))
921 boot_order = hvparams[constants.HV_BOOT_ORDER]
922 if (boot_order == constants.HT_BO_CDROM and
923 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
924 raise errors.HypervisorError("Cannot boot from cdrom without an"
927 security_model = hvparams[constants.HV_SECURITY_MODEL]
928 if security_model == constants.HT_SM_USER:
929 if not hvparams[constants.HV_SECURITY_DOMAIN]:
930 raise errors.HypervisorError("A security domain (user to run kvm as)"
931 " must be specified")
932 elif (security_model == constants.HT_SM_NONE or
933 security_model == constants.HT_SM_POOL):
934 if hvparams[constants.HV_SECURITY_DOMAIN]:
935 raise errors.HypervisorError("Cannot have a security domain when the"
936 " security model is 'none' or 'pool'")
939 def ValidateParameters(cls, hvparams):
940 """Check the given parameters for validity.
943 @param hvparams: dictionary with parameter names/value
944 @raise errors.HypervisorError: when a parameter is not valid
947 super(KVMHypervisor, cls).ValidateParameters(hvparams)
949 security_model = hvparams[constants.HV_SECURITY_MODEL]
950 if security_model == constants.HT_SM_USER:
951 username = hvparams[constants.HV_SECURITY_DOMAIN]
953 pwd.getpwnam(username)
955 raise errors.HypervisorError("Unknown security domain user %s"
959 def PowercycleNode(cls):
960 """KVM powercycle, just a wrapper over Linux powercycle.
963 cls.LinuxPowercycle()