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),
89 constants.HV_VHOST_NET: hv_base.NO_CHECK,
92 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
94 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
95 _MIGRATION_INFO_RETRY_DELAY = 2
97 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
104 hv_base.BaseHypervisor.__init__(self)
105 # Let's make sure the directories we need exist, even if the RUN_DIR lives
106 # in a tmpfs filesystem or has been otherwise wiped out.
107 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
108 utils.EnsureDirs(dirs)
111 def _InstancePidFile(cls, instance_name):
112 """Returns the instance pidfile.
115 return utils.PathJoin(cls._PIDS_DIR, instance_name)
118 def _InstanceUidFile(cls, instance_name):
119 """Returns the instance uidfile.
122 return utils.PathJoin(cls._UIDS_DIR, instance_name)
125 def _InstancePidInfo(cls, pid):
126 """Check pid file for instance information.
128 Check that a pid file is associated with an instance, and retrieve
129 information from its command line.
131 @type pid: string or int
132 @param pid: process id of the instance to check
134 @return: (instance_name, memory, vcpus)
135 @raise errors.HypervisorError: when an instance cannot be found
138 alive = utils.IsProcessAlive(pid)
140 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
142 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
144 cmdline = utils.ReadFile(cmdline_file)
145 except EnvironmentError, err:
146 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
153 arg_list = cmdline.split('\x00')
155 arg = arg_list.pop(0)
157 instance = arg_list.pop(0)
159 memory = int(arg_list.pop(0))
161 vcpus = int(arg_list.pop(0))
164 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
167 return (instance, memory, vcpus)
169 def _InstancePidAlive(self, instance_name):
170 """Returns the instance pidfile, pid, and liveness.
172 @type instance_name: string
173 @param instance_name: instance name
175 @return: (pid file name, pid, liveness)
178 pidfile = self._InstancePidFile(instance_name)
179 pid = utils.ReadPidFile(pidfile)
183 cmd_instance = self._InstancePidInfo(pid)[0]
184 alive = (cmd_instance == instance_name)
185 except errors.HypervisorError:
188 return (pidfile, pid, alive)
190 def _CheckDown(self, instance_name):
191 """Raises an error unless the given instance is down.
194 alive = self._InstancePidAlive(instance_name)[2]
196 raise errors.HypervisorError("Failed to start instance %s: %s" %
197 (instance_name, "already running"))
200 def _InstanceMonitor(cls, instance_name):
201 """Returns the instance monitor socket name
204 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
207 def _InstanceSerial(cls, instance_name):
208 """Returns the instance serial socket name
211 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
214 def _SocatUnixConsoleParams():
215 """Returns the correct parameters for socat
217 If we have a new-enough socat we can use raw mode with an escape character.
220 if constants.SOCAT_USE_ESCAPE:
221 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
223 return "echo=0,icanon=0"
226 def _InstanceKVMRuntime(cls, instance_name):
227 """Returns the instance KVM runtime filename
230 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
233 def _TryReadUidFile(cls, uid_file):
234 """Try to read a uid file
237 if os.path.exists(uid_file):
239 uid = int(utils.ReadOneLineFile(uid_file))
241 except EnvironmentError:
242 logging.warning("Can't read uid file", exc_info=True)
243 except (TypeError, ValueError):
244 logging.warning("Can't parse uid file contents", exc_info=True)
248 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
249 """Removes an instance's rutime sockets/files.
252 utils.RemoveFile(pidfile)
253 utils.RemoveFile(cls._InstanceMonitor(instance_name))
254 utils.RemoveFile(cls._InstanceSerial(instance_name))
255 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
256 uid_file = cls._InstanceUidFile(instance_name)
257 uid = cls._TryReadUidFile(uid_file)
258 utils.RemoveFile(uid_file)
260 uidpool.ReleaseUid(uid)
262 def _WriteNetScript(self, instance, seq, nic):
263 """Write a script to connect a net interface to the proper bridge.
265 This can be used by any qemu-type hypervisor.
267 @param instance: instance we're acting on
268 @type instance: instance object
269 @param seq: nic sequence number
271 @param nic: nic we're acting on
272 @type nic: nic object
273 @return: netscript file name
278 script.write("#!/bin/sh\n")
279 script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
280 script.write("PATH=$PATH:/sbin:/usr/sbin\n")
281 script.write("export INSTANCE=%s\n" % instance.name)
282 script.write("export MAC=%s\n" % nic.mac)
284 script.write("export IP=%s\n" % nic.ip)
285 script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
286 if nic.nicparams[constants.NIC_LINK]:
287 script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
288 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
289 script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
290 script.write("export INTERFACE=$1\n")
292 script.write("export TAGS=\"%s\"\n" % " ".join(instance.tags))
293 # TODO: make this configurable at ./configure time
294 script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
295 script.write(" # Execute the user-specific vif file\n")
296 script.write(" %s\n" % self._KVM_NETWORK_SCRIPT)
297 script.write("else\n")
298 script.write(" ifconfig $INTERFACE 0.0.0.0 up\n")
299 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
300 script.write(" # Connect the interface to the bridge\n")
301 script.write(" brctl addif $BRIDGE $INTERFACE\n")
302 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
304 raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
305 script.write(" # Route traffic targeted at the IP to the interface\n")
306 if nic.nicparams[constants.NIC_LINK]:
307 script.write(" while ip rule del dev $INTERFACE; do :; done\n")
308 script.write(" ip rule add dev $INTERFACE table $LINK\n")
309 script.write(" ip route replace $IP table $LINK proto static"
312 script.write(" ip route replace $IP proto static"
314 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
315 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
316 script.write(" if [ -d %s ]; then\n" % interface_v4_conf)
317 script.write(" echo 1 > %s/proxy_arp\n" % interface_v4_conf)
318 script.write(" echo 1 > %s/forwarding\n" % interface_v4_conf)
319 script.write(" fi\n")
320 script.write(" if [ -d %s ]; then\n" % interface_v6_conf)
321 script.write(" echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
322 script.write(" echo 1 > %s/forwarding\n" % interface_v6_conf)
323 script.write(" fi\n")
324 script.write("fi\n\n")
325 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
326 # mounted noexec sometimes, so we'll have to find another place.
327 (tmpfd, tmpfile_name) = tempfile.mkstemp()
328 tmpfile = os.fdopen(tmpfd, 'w')
330 tmpfile.write(script.getvalue())
333 os.chmod(tmpfile_name, 0755)
336 def ListInstances(self):
337 """Get the list of running instances.
339 We can do this by listing our live instances directory and
340 checking whether the associated kvm process is still alive.
344 for name in os.listdir(self._PIDS_DIR):
345 if self._InstancePidAlive(name)[2]:
349 def GetInstanceInfo(self, instance_name):
350 """Get instance properties.
352 @type instance_name: string
353 @param instance_name: the instance name
354 @rtype: tuple of strings
355 @return: (name, id, memory, vcpus, stat, times)
358 _, pid, alive = self._InstancePidAlive(instance_name)
362 _, memory, vcpus = self._InstancePidInfo(pid)
366 return (instance_name, pid, memory, vcpus, stat, times)
368 def GetAllInstancesInfo(self):
369 """Get properties of all instances.
371 @return: list of tuples (name, id, memory, vcpus, stat, times)
375 for name in os.listdir(self._PIDS_DIR):
377 info = self.GetInstanceInfo(name)
378 except errors.HypervisorError:
384 def _GenerateKVMRuntime(self, instance, block_devices):
385 """Generate KVM information to start an instance.
388 pidfile = self._InstancePidFile(instance.name)
389 kvm = constants.KVM_PATH
391 # used just by the vnc server, if enabled
392 kvm_cmd.extend(['-name', instance.name])
393 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
394 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
395 kvm_cmd.extend(['-pidfile', pidfile])
396 kvm_cmd.extend(['-daemonize'])
397 if not instance.hvparams[constants.HV_ACPI]:
398 kvm_cmd.extend(['-no-acpi'])
400 hvp = instance.hvparams
401 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
402 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
403 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
405 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
406 kvm_cmd.extend(["-enable-kvm"])
407 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
408 kvm_cmd.extend(["-disable-kvm"])
411 kvm_cmd.extend(['-boot', 'n'])
413 disk_type = hvp[constants.HV_DISK_TYPE]
414 if disk_type == constants.HT_DISK_PARAVIRTUAL:
415 if_val = ',if=virtio'
417 if_val = ',if=%s' % disk_type
419 disk_cache = hvp[constants.HV_DISK_CACHE]
420 if disk_cache != constants.HT_CACHE_DEFAULT:
421 cache_val = ",cache=%s" % disk_cache
424 for cfdev, dev_path in block_devices:
425 if cfdev.mode != constants.DISK_RDWR:
426 raise errors.HypervisorError("Instance has read-only disks which"
427 " are not supported by KVM")
428 # TODO: handle FD_LOOP and FD_BLKTAP (?)
430 kvm_cmd.extend(['-boot', 'c'])
431 if disk_type != constants.HT_DISK_IDE:
432 boot_val = ',boot=on'
435 # We only boot from the first disk
440 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
442 kvm_cmd.extend(['-drive', drive_val])
444 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
446 options = ',format=raw,media=cdrom'
448 kvm_cmd.extend(['-boot', 'd'])
449 if disk_type != constants.HT_DISK_IDE:
450 options = '%s,boot=on' % options
452 if disk_type == constants.HT_DISK_PARAVIRTUAL:
453 if_val = ',if=virtio'
455 if_val = ',if=%s' % disk_type
456 options = '%s%s' % (options, if_val)
457 drive_val = 'file=%s%s' % (iso_image, options)
458 kvm_cmd.extend(['-drive', drive_val])
460 kernel_path = hvp[constants.HV_KERNEL_PATH]
462 kvm_cmd.extend(['-kernel', kernel_path])
463 initrd_path = hvp[constants.HV_INITRD_PATH]
465 kvm_cmd.extend(['-initrd', initrd_path])
466 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
467 hvp[constants.HV_KERNEL_ARGS]]
468 if hvp[constants.HV_SERIAL_CONSOLE]:
469 root_append.append('console=ttyS0,38400')
470 kvm_cmd.extend(['-append', ' '.join(root_append)])
472 mouse_type = hvp[constants.HV_USB_MOUSE]
474 kvm_cmd.extend(['-usb'])
475 kvm_cmd.extend(['-usbdevice', mouse_type])
477 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
479 if utils.IsValidIP(vnc_bind_address):
480 if instance.network_port > constants.VNC_BASE_PORT:
481 display = instance.network_port - constants.VNC_BASE_PORT
482 if vnc_bind_address == '0.0.0.0':
483 vnc_arg = ':%d' % (display)
485 vnc_arg = '%s:%d' % (vnc_bind_address, display)
487 logging.error("Network port is not a valid VNC display (%d < %d)."
488 " Not starting VNC", instance.network_port,
489 constants.VNC_BASE_PORT)
492 # Only allow tls and other option when not binding to a file, for now.
493 # kvm/qemu gets confused otherwise about the filename to use.
495 if hvp[constants.HV_VNC_TLS]:
496 vnc_append = '%s,tls' % vnc_append
497 if hvp[constants.HV_VNC_X509_VERIFY]:
498 vnc_append = '%s,x509verify=%s' % (vnc_append,
499 hvp[constants.HV_VNC_X509])
500 elif hvp[constants.HV_VNC_X509]:
501 vnc_append = '%s,x509=%s' % (vnc_append,
502 hvp[constants.HV_VNC_X509])
503 if hvp[constants.HV_VNC_PASSWORD_FILE]:
504 vnc_append = '%s,password' % vnc_append
506 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
509 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
511 kvm_cmd.extend(['-vnc', vnc_arg])
513 # Also add a tablet USB device to act as a mouse
514 # This solves various mouse alignment issues
515 kvm_cmd.extend(['-usbdevice', 'tablet'])
517 kvm_cmd.extend(['-nographic'])
519 monitor_dev = ("unix:%s,server,nowait" %
520 self._InstanceMonitor(instance.name))
521 kvm_cmd.extend(['-monitor', monitor_dev])
522 if hvp[constants.HV_SERIAL_CONSOLE]:
523 serial_dev = ('unix:%s,server,nowait' %
524 self._InstanceSerial(instance.name))
525 kvm_cmd.extend(['-serial', serial_dev])
527 kvm_cmd.extend(['-serial', 'none'])
529 if hvp[constants.HV_USE_LOCALTIME]:
530 kvm_cmd.extend(['-localtime'])
532 # Save the current instance nics, but defer their expansion as parameters,
533 # as we'll need to generate executable temp files for them.
534 kvm_nics = instance.nics
537 return (kvm_cmd, kvm_nics, hvparams)
539 def _WriteKVMRuntime(self, instance_name, data):
540 """Write an instance's KVM runtime
544 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
546 except EnvironmentError, err:
547 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
549 def _ReadKVMRuntime(self, instance_name):
550 """Read an instance's KVM runtime
554 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
555 except EnvironmentError, err:
556 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
559 def _SaveKVMRuntime(self, instance, kvm_runtime):
560 """Save an instance's KVM runtime
563 kvm_cmd, kvm_nics, hvparams = kvm_runtime
564 serialized_nics = [nic.ToDict() for nic in kvm_nics]
565 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
566 self._WriteKVMRuntime(instance.name, serialized_form)
568 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
569 """Load an instance's KVM runtime
572 if not serialized_runtime:
573 serialized_runtime = self._ReadKVMRuntime(instance.name)
574 loaded_runtime = serializer.Load(serialized_runtime)
575 kvm_cmd, serialized_nics, hvparams = loaded_runtime
576 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
577 return (kvm_cmd, kvm_nics, hvparams)
579 def _RunKVMCmd(self, name, kvm_cmd):
580 """Run the KVM cmd and check for errors
583 @param name: instance name
584 @type kvm_cmd: list of strings
585 @param kvm_cmd: runcmd input for kvm
588 result = utils.RunCmd(kvm_cmd)
590 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
591 (name, result.fail_reason, result.output))
592 if not self._InstancePidAlive(name)[2]:
593 raise errors.HypervisorError("Failed to start instance %s" % name)
595 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
596 """Execute a KVM cmd, after completing it with some last minute data
598 @type incoming: tuple of strings
599 @param incoming: (target_host_ip, port)
602 hvp = instance.hvparams
604 self._CheckDown(name)
608 kvm_cmd, kvm_nics, hvparams = kvm_runtime
610 security_model = hvp[constants.HV_SECURITY_MODEL]
611 if security_model == constants.HT_SM_USER:
612 kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
615 kvm_cmd.extend(["-net", "none"])
618 nic_type = hvparams[constants.HV_NIC_TYPE]
619 if nic_type == constants.HT_NIC_PARAVIRTUAL:
620 nic_model = "model=virtio"
621 if hvparams[constants.HV_VHOST_NET]:
622 tap_extra = ",vhost=on"
624 nic_model = "model=%s" % nic_type
626 for nic_seq, nic in enumerate(kvm_nics):
627 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
628 script = self._WriteNetScript(instance, nic_seq, nic)
629 tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
630 kvm_cmd.extend(["-net", nic_val])
631 kvm_cmd.extend(["-net", tap_val])
632 temp_files.append(script)
635 target, port = incoming
636 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
638 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
642 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
643 except EnvironmentError, err:
644 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
645 % (vnc_pwd_file, err))
647 if security_model == constants.HT_SM_POOL:
648 ss = ssconf.SimpleStore()
649 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
650 all_uids = set(uidpool.ExpandUidPool(uid_pool))
651 uid = uidpool.RequestUnusedUid(all_uids)
653 username = pwd.getpwuid(uid.GetUid()).pw_name
654 kvm_cmd.extend(["-runas", username])
655 self._RunKVMCmd(name, kvm_cmd)
657 uidpool.ReleaseUid(uid)
661 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
663 self._RunKVMCmd(name, kvm_cmd)
666 change_cmd = 'change vnc password %s' % vnc_pwd
667 self._CallMonitorCommand(instance.name, change_cmd)
669 for filename in temp_files:
670 utils.RemoveFile(filename)
672 def StartInstance(self, instance, block_devices):
673 """Start an instance.
676 self._CheckDown(instance.name)
677 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
678 self._SaveKVMRuntime(instance, kvm_runtime)
679 self._ExecuteKVMRuntime(instance, kvm_runtime)
681 def _CallMonitorCommand(self, instance_name, command):
682 """Invoke a command on the instance monitor.
685 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
686 (utils.ShellQuote(command),
687 constants.SOCAT_PATH,
688 utils.ShellQuote(self._InstanceMonitor(instance_name))))
689 result = utils.RunCmd(socat)
691 msg = ("Failed to send command '%s' to instance %s."
692 " output: %s, error: %s, fail_reason: %s" %
693 (command, instance_name,
694 result.stdout, result.stderr, result.fail_reason))
695 raise errors.HypervisorError(msg)
699 def StopInstance(self, instance, force=False, retry=False, name=None):
703 if name is not None and not force:
704 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
707 acpi = instance.hvparams[constants.HV_ACPI]
710 _, pid, alive = self._InstancePidAlive(name)
711 if pid > 0 and alive:
712 if force or not acpi:
713 utils.KillProcess(pid)
715 self._CallMonitorCommand(name, 'system_powerdown')
717 def CleanupInstance(self, instance_name):
718 """Cleanup after a stopped instance
721 pidfile, pid, alive = self._InstancePidAlive(instance_name)
722 if pid > 0 and alive:
723 raise errors.HypervisorError("Cannot cleanup a live instance")
724 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
726 def RebootInstance(self, instance):
727 """Reboot an instance.
730 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
731 # socket the instance will stop, but now power up again. So we'll resort
732 # to shutdown and restart.
733 _, _, alive = self._InstancePidAlive(instance.name)
735 raise errors.HypervisorError("Failed to reboot instance %s:"
736 " not running" % instance.name)
737 # StopInstance will delete the saved KVM runtime so:
738 # ...first load it...
739 kvm_runtime = self._LoadKVMRuntime(instance)
740 # ...now we can safely call StopInstance...
741 if not self.StopInstance(instance):
742 self.StopInstance(instance, force=True)
743 # ...and finally we can save it again, and execute it...
744 self._SaveKVMRuntime(instance, kvm_runtime)
745 self._ExecuteKVMRuntime(instance, kvm_runtime)
747 def MigrationInfo(self, instance):
748 """Get instance information to perform a migration.
750 @type instance: L{objects.Instance}
751 @param instance: instance to be migrated
753 @return: content of the KVM runtime file
756 return self._ReadKVMRuntime(instance.name)
758 def AcceptInstance(self, instance, info, target):
759 """Prepare to accept an instance.
761 @type instance: L{objects.Instance}
762 @param instance: instance to be accepted
764 @param info: content of the KVM runtime file on the source node
766 @param target: target host (usually ip), on this node
769 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
770 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
771 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
773 def FinalizeMigration(self, instance, info, success):
774 """Finalize an instance migration.
776 Stop the incoming mode KVM.
778 @type instance: L{objects.Instance}
779 @param instance: instance whose migration is being aborted
783 self._WriteKVMRuntime(instance.name, info)
785 self.StopInstance(instance, force=True)
787 def MigrateInstance(self, instance, target, live):
788 """Migrate an instance to a target node.
790 The migration will not be attempted if the instance is not
793 @type instance: L{objects.Instance}
794 @param instance: the instance to be migrated
796 @param target: ip address of the target node
798 @param live: perform a live migration
801 instance_name = instance.name
802 port = instance.hvparams[constants.HV_MIGRATION_PORT]
803 pidfile, pid, alive = self._InstancePidAlive(instance_name)
805 raise errors.HypervisorError("Instance not running, cannot migrate")
807 if not utils.TcpPing(target, port, live_port_needed=True):
808 raise errors.HypervisorError("Remote host %s not listening on port"
809 " %s, cannot migrate" % (target, port))
812 self._CallMonitorCommand(instance_name, 'stop')
814 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
815 self._CallMonitorCommand(instance_name, migrate_command)
817 info_command = 'info migrate'
821 result = self._CallMonitorCommand(instance_name, info_command)
822 match = self._MIGRATION_STATUS_RE.search(result.stdout)
825 if not result.stdout:
826 logging.info("KVM: empty 'info migrate' result")
828 logging.warning("KVM: unknown 'info migrate' result: %s",
830 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
832 status = match.group(1)
833 if status == 'completed':
835 elif status == 'active':
836 # reset the broken answers count
838 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
839 elif status == 'failed' or status == 'cancelled':
841 self._CallMonitorCommand(instance_name, 'cont')
842 raise errors.HypervisorError("Migration %s at the kvm level" %
845 logging.warning("KVM: unknown migration status '%s'", status)
847 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
848 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
849 raise errors.HypervisorError("Too many 'info migrate' broken answers")
851 utils.KillProcess(pid)
852 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
854 def GetNodeInfo(self):
855 """Return information about the node.
857 This is just a wrapper over the base GetLinuxNodeInfo method.
859 @return: a dict with the following keys (values in MiB):
860 - memory_total: the total memory size on the node
861 - memory_free: the available memory on the node for instances
862 - memory_dom0: the memory used by the node itself, if available
865 return self.GetLinuxNodeInfo()
868 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
869 """Return a command for connecting to the console of an instance.
872 if hvparams[constants.HV_SERIAL_CONSOLE]:
873 shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
874 (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
875 utils.ShellQuote(cls._InstanceSerial(instance.name))))
877 shell_command = "echo 'No serial shell for instance %s'" % instance.name
879 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
881 if instance.network_port > constants.VNC_BASE_PORT:
882 display = instance.network_port - constants.VNC_BASE_PORT
883 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
884 " (display: %d)'" % (vnc_bind_address,
885 instance.network_port,
887 shell_command = "%s; %s" % (vnc_command, shell_command)
892 """Verify the hypervisor.
894 Check that the binary exists.
897 if not os.path.exists(constants.KVM_PATH):
898 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
899 if not os.path.exists(constants.SOCAT_PATH):
900 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
904 def CheckParameterSyntax(cls, hvparams):
905 """Check the given parameters for validity.
908 @param hvparams: dictionary with parameter names/value
909 @raise errors.HypervisorError: when a parameter is not valid
912 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
914 kernel_path = hvparams[constants.HV_KERNEL_PATH]
916 if not hvparams[constants.HV_ROOT_PATH]:
917 raise errors.HypervisorError("Need a root partition for the instance,"
918 " if a kernel is defined")
920 if (hvparams[constants.HV_VNC_X509_VERIFY] and
921 not hvparams[constants.HV_VNC_X509]):
922 raise errors.HypervisorError("%s must be defined, if %s is" %
923 (constants.HV_VNC_X509,
924 constants.HV_VNC_X509_VERIFY))
926 boot_order = hvparams[constants.HV_BOOT_ORDER]
927 if (boot_order == constants.HT_BO_CDROM and
928 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
929 raise errors.HypervisorError("Cannot boot from cdrom without an"
932 security_model = hvparams[constants.HV_SECURITY_MODEL]
933 if security_model == constants.HT_SM_USER:
934 if not hvparams[constants.HV_SECURITY_DOMAIN]:
935 raise errors.HypervisorError("A security domain (user to run kvm as)"
936 " must be specified")
937 elif (security_model == constants.HT_SM_NONE or
938 security_model == constants.HT_SM_POOL):
939 if hvparams[constants.HV_SECURITY_DOMAIN]:
940 raise errors.HypervisorError("Cannot have a security domain when the"
941 " security model is 'none' or 'pool'")
944 def ValidateParameters(cls, hvparams):
945 """Check the given parameters for validity.
948 @param hvparams: dictionary with parameter names/value
949 @raise errors.HypervisorError: when a parameter is not valid
952 super(KVMHypervisor, cls).ValidateParameters(hvparams)
954 security_model = hvparams[constants.HV_SECURITY_MODEL]
955 if security_model == constants.HT_SM_USER:
956 username = hvparams[constants.HV_SECURITY_DOMAIN]
958 pwd.getpwnam(username)
960 raise errors.HypervisorError("Unknown security domain user %s"
964 def PowercycleNode(cls):
965 """KVM powercycle, just a wrapper over Linux powercycle.
968 cls.LinuxPowercycle()