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.hypervisor import hv_base
43 class KVMHypervisor(hv_base.BaseHypervisor):
44 """KVM hypervisor interface"""
46 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
47 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
48 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
49 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
50 _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
53 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
54 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
55 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
56 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
57 constants.HV_ACPI: hv_base.NO_CHECK,
58 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
59 constants.HV_VNC_BIND_ADDRESS:
60 (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
61 "the VNC bind address must be either a valid IP address or an absolute"
62 " pathname", None, None),
63 constants.HV_VNC_TLS: hv_base.NO_CHECK,
64 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
65 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
66 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
67 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
68 constants.HV_BOOT_ORDER:
69 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
70 constants.HV_NIC_TYPE:
71 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
72 constants.HV_DISK_TYPE:
73 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
74 constants.HV_USB_MOUSE:
75 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
76 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
77 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
78 constants.HV_DISK_CACHE:
79 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
80 constants.HV_SECURITY_MODEL:
81 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
82 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
85 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
87 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
88 _MIGRATION_INFO_RETRY_DELAY = 2
90 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
97 hv_base.BaseHypervisor.__init__(self)
98 # Let's make sure the directories we need exist, even if the RUN_DIR lives
99 # in a tmpfs filesystem or has been otherwise wiped out.
100 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
101 utils.EnsureDirs(dirs)
104 def _InstancePidFile(cls, instance_name):
105 """Returns the instance pidfile.
108 return utils.PathJoin(cls._PIDS_DIR, instance_name)
111 def _InstancePidInfo(cls, pid):
112 """Check pid file for instance information.
114 Check that a pid file is associated with an instance, and retrieve
115 information from its command line.
117 @type pid: string or int
118 @param pid: process id of the instance to check
120 @return: (instance_name, memory, vcpus)
121 @raise errors.HypervisorError: when an instance cannot be found
124 alive = utils.IsProcessAlive(pid)
126 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
128 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
130 cmdline = utils.ReadFile(cmdline_file)
131 except EnvironmentError, err:
132 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
139 arg_list = cmdline.split('\x00')
141 arg = arg_list.pop(0)
143 instance = arg_list.pop(0)
145 memory = int(arg_list.pop(0))
147 vcpus = int(arg_list.pop(0))
150 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
153 return (instance, memory, vcpus)
155 def _InstancePidAlive(self, instance_name):
156 """Returns the instance pidfile, pid, and liveness.
158 @type instance_name: string
159 @param instance_name: instance name
161 @return: (pid file name, pid, liveness)
164 pidfile = self._InstancePidFile(instance_name)
165 pid = utils.ReadPidFile(pidfile)
169 cmd_instance = self._InstancePidInfo(pid)[0]
170 alive = (cmd_instance == instance_name)
171 except errors.HypervisorError:
174 return (pidfile, pid, alive)
176 def _CheckDown(self, instance_name):
177 """Raises an error unless the given instance is down.
180 alive = self._InstancePidAlive(instance_name)[2]
182 raise errors.HypervisorError("Failed to start instance %s: %s" %
183 (instance_name, "already running"))
186 def _InstanceMonitor(cls, instance_name):
187 """Returns the instance monitor socket name
190 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
193 def _InstanceSerial(cls, instance_name):
194 """Returns the instance serial socket name
197 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
200 def _SocatUnixConsoleParams():
201 """Returns the correct parameters for socat
203 If we have a new-enough socat we can use raw mode with an escape character.
206 if constants.SOCAT_USE_ESCAPE:
207 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
209 return "echo=0,icanon=0"
212 def _InstanceKVMRuntime(cls, instance_name):
213 """Returns the instance KVM runtime filename
216 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
219 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
220 """Removes an instance's rutime sockets/files.
223 utils.RemoveFile(pidfile)
224 utils.RemoveFile(cls._InstanceMonitor(instance_name))
225 utils.RemoveFile(cls._InstanceSerial(instance_name))
226 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
228 def _WriteNetScript(self, instance, seq, nic):
229 """Write a script to connect a net interface to the proper bridge.
231 This can be used by any qemu-type hypervisor.
233 @param instance: instance we're acting on
234 @type instance: instance object
235 @param seq: nic sequence number
237 @param nic: nic we're acting on
238 @type nic: nic object
239 @return: netscript file name
244 script.write("#!/bin/sh\n")
245 script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
246 script.write("PATH=$PATH:/sbin:/usr/sbin\n")
247 script.write("export INSTANCE=%s\n" % instance.name)
248 script.write("export MAC=%s\n" % nic.mac)
250 script.write("export IP=%s\n" % nic.ip)
251 script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
252 if nic.nicparams[constants.NIC_LINK]:
253 script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
254 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
255 script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
256 script.write("export INTERFACE=$1\n")
257 # TODO: make this configurable at ./configure time
258 script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
259 script.write(" # Execute the user-specific vif file\n")
260 script.write(" %s\n" % self._KVM_NETWORK_SCRIPT)
261 script.write("else\n")
262 script.write(" ifconfig $INTERFACE 0.0.0.0 up\n")
263 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
264 script.write(" # Connect the interface to the bridge\n")
265 script.write(" brctl addif $BRIDGE $INTERFACE\n")
266 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
268 raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
269 script.write(" # Route traffic targeted at the IP to the interface\n")
270 if nic.nicparams[constants.NIC_LINK]:
271 script.write(" while ip rule del dev $INTERFACE; do :; done\n")
272 script.write(" ip rule add dev $INTERFACE table $LINK\n")
273 script.write(" ip route replace $IP table $LINK proto static"
276 script.write(" ip route replace $IP proto static"
278 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
279 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
280 script.write(" if [ -d %s ]; then\n" % interface_v4_conf)
281 script.write(" echo 1 > %s/proxy_arp\n" % interface_v4_conf)
282 script.write(" echo 1 > %s/forwarding\n" % interface_v4_conf)
283 script.write(" fi\n")
284 script.write(" if [ -d %s ]; then\n" % interface_v6_conf)
285 script.write(" echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
286 script.write(" echo 1 > %s/forwarding\n" % interface_v6_conf)
287 script.write(" fi\n")
288 script.write("fi\n\n")
289 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
290 # mounted noexec sometimes, so we'll have to find another place.
291 (tmpfd, tmpfile_name) = tempfile.mkstemp()
292 tmpfile = os.fdopen(tmpfd, 'w')
294 tmpfile.write(script.getvalue())
297 os.chmod(tmpfile_name, 0755)
300 def ListInstances(self):
301 """Get the list of running instances.
303 We can do this by listing our live instances directory and
304 checking whether the associated kvm process is still alive.
308 for name in os.listdir(self._PIDS_DIR):
309 if self._InstancePidAlive(name)[2]:
313 def GetInstanceInfo(self, instance_name):
314 """Get instance properties.
316 @type instance_name: string
317 @param instance_name: the instance name
318 @rtype: tuple of strings
319 @return: (name, id, memory, vcpus, stat, times)
322 _, pid, alive = self._InstancePidAlive(instance_name)
326 _, memory, vcpus = self._InstancePidInfo(pid)
330 return (instance_name, pid, memory, vcpus, stat, times)
332 def GetAllInstancesInfo(self):
333 """Get properties of all instances.
335 @return: list of tuples (name, id, memory, vcpus, stat, times)
339 for name in os.listdir(self._PIDS_DIR):
341 info = self.GetInstanceInfo(name)
342 except errors.HypervisorError:
348 def _GenerateKVMRuntime(self, instance, block_devices):
349 """Generate KVM information to start an instance.
352 pidfile = self._InstancePidFile(instance.name)
353 kvm = constants.KVM_PATH
355 # used just by the vnc server, if enabled
356 kvm_cmd.extend(['-name', instance.name])
357 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
358 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
359 kvm_cmd.extend(['-pidfile', pidfile])
360 kvm_cmd.extend(['-daemonize'])
361 if not instance.hvparams[constants.HV_ACPI]:
362 kvm_cmd.extend(['-no-acpi'])
364 hvp = instance.hvparams
365 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
366 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
367 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
370 kvm_cmd.extend(['-boot', 'n'])
372 disk_type = hvp[constants.HV_DISK_TYPE]
373 if disk_type == constants.HT_DISK_PARAVIRTUAL:
374 if_val = ',if=virtio'
376 if_val = ',if=%s' % disk_type
378 disk_cache = hvp[constants.HV_DISK_CACHE]
379 if disk_cache != constants.HT_CACHE_DEFAULT:
380 cache_val = ",cache=%s" % disk_cache
383 for cfdev, dev_path in block_devices:
384 if cfdev.mode != constants.DISK_RDWR:
385 raise errors.HypervisorError("Instance has read-only disks which"
386 " are not supported by KVM")
387 # TODO: handle FD_LOOP and FD_BLKTAP (?)
389 kvm_cmd.extend(['-boot', 'c'])
390 boot_val = ',boot=on'
391 # We only boot from the first disk
396 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
398 kvm_cmd.extend(['-drive', drive_val])
400 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
402 options = ',format=raw,media=cdrom'
404 kvm_cmd.extend(['-boot', 'd'])
405 options = '%s,boot=on' % options
407 options = '%s,if=virtio' % options
408 drive_val = 'file=%s%s' % (iso_image, options)
409 kvm_cmd.extend(['-drive', drive_val])
411 kernel_path = hvp[constants.HV_KERNEL_PATH]
413 kvm_cmd.extend(['-kernel', kernel_path])
414 initrd_path = hvp[constants.HV_INITRD_PATH]
416 kvm_cmd.extend(['-initrd', initrd_path])
417 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
418 hvp[constants.HV_KERNEL_ARGS]]
419 if hvp[constants.HV_SERIAL_CONSOLE]:
420 root_append.append('console=ttyS0,38400')
421 kvm_cmd.extend(['-append', ' '.join(root_append)])
423 mouse_type = hvp[constants.HV_USB_MOUSE]
425 kvm_cmd.extend(['-usb'])
426 kvm_cmd.extend(['-usbdevice', mouse_type])
428 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
430 if utils.IsValidIP(vnc_bind_address):
431 if instance.network_port > constants.VNC_BASE_PORT:
432 display = instance.network_port - constants.VNC_BASE_PORT
433 if vnc_bind_address == '0.0.0.0':
434 vnc_arg = ':%d' % (display)
436 vnc_arg = '%s:%d' % (vnc_bind_address, display)
438 logging.error("Network port is not a valid VNC display (%d < %d)."
439 " Not starting VNC", instance.network_port,
440 constants.VNC_BASE_PORT)
443 # Only allow tls and other option when not binding to a file, for now.
444 # kvm/qemu gets confused otherwise about the filename to use.
446 if hvp[constants.HV_VNC_TLS]:
447 vnc_append = '%s,tls' % vnc_append
448 if hvp[constants.HV_VNC_X509_VERIFY]:
449 vnc_append = '%s,x509verify=%s' % (vnc_append,
450 hvp[constants.HV_VNC_X509])
451 elif hvp[constants.HV_VNC_X509]:
452 vnc_append = '%s,x509=%s' % (vnc_append,
453 hvp[constants.HV_VNC_X509])
454 if hvp[constants.HV_VNC_PASSWORD_FILE]:
455 vnc_append = '%s,password' % vnc_append
457 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
460 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
462 kvm_cmd.extend(['-vnc', vnc_arg])
464 kvm_cmd.extend(['-nographic'])
466 monitor_dev = ("unix:%s,server,nowait" %
467 self._InstanceMonitor(instance.name))
468 kvm_cmd.extend(['-monitor', monitor_dev])
469 if hvp[constants.HV_SERIAL_CONSOLE]:
470 serial_dev = ('unix:%s,server,nowait' %
471 self._InstanceSerial(instance.name))
472 kvm_cmd.extend(['-serial', serial_dev])
474 kvm_cmd.extend(['-serial', 'none'])
476 if hvp[constants.HV_USE_LOCALTIME]:
477 kvm_cmd.extend(['-localtime'])
479 # Save the current instance nics, but defer their expansion as parameters,
480 # as we'll need to generate executable temp files for them.
481 kvm_nics = instance.nics
484 return (kvm_cmd, kvm_nics, hvparams)
486 def _WriteKVMRuntime(self, instance_name, data):
487 """Write an instance's KVM runtime
491 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
493 except EnvironmentError, err:
494 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
496 def _ReadKVMRuntime(self, instance_name):
497 """Read an instance's KVM runtime
501 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
502 except EnvironmentError, err:
503 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
506 def _SaveKVMRuntime(self, instance, kvm_runtime):
507 """Save an instance's KVM runtime
510 kvm_cmd, kvm_nics, hvparams = kvm_runtime
511 serialized_nics = [nic.ToDict() for nic in kvm_nics]
512 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
513 self._WriteKVMRuntime(instance.name, serialized_form)
515 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
516 """Load an instance's KVM runtime
519 if not serialized_runtime:
520 serialized_runtime = self._ReadKVMRuntime(instance.name)
521 loaded_runtime = serializer.Load(serialized_runtime)
522 kvm_cmd, serialized_nics, hvparams = loaded_runtime
523 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
524 return (kvm_cmd, kvm_nics, hvparams)
526 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
527 """Execute a KVM cmd, after completing it with some last minute data
529 @type incoming: tuple of strings
530 @param incoming: (target_host_ip, port)
533 hvp = instance.hvparams
535 self._CheckDown(name)
539 kvm_cmd, kvm_nics, hvparams = kvm_runtime
541 security_model = hvp[constants.HV_SECURITY_MODEL]
542 if security_model == constants.HT_SM_USER:
543 kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
546 kvm_cmd.extend(['-net', 'none'])
548 nic_type = hvparams[constants.HV_NIC_TYPE]
549 if nic_type == constants.HT_NIC_PARAVIRTUAL:
550 nic_model = "model=virtio"
552 nic_model = "model=%s" % nic_type
554 for nic_seq, nic in enumerate(kvm_nics):
555 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
556 script = self._WriteNetScript(instance, nic_seq, nic)
557 kvm_cmd.extend(['-net', nic_val])
558 kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
559 temp_files.append(script)
562 target, port = incoming
563 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
565 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
569 vnc_pwd = utils.ReadFile(vnc_pwd_file)
570 except EnvironmentError, err:
571 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
572 % (vnc_pwd_file, err))
574 result = utils.RunCmd(kvm_cmd)
576 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
577 (name, result.fail_reason, result.output))
579 if not self._InstancePidAlive(name)[2]:
580 raise errors.HypervisorError("Failed to start instance %s" % name)
583 change_cmd = 'change vnc password %s' % vnc_pwd
584 self._CallMonitorCommand(instance.name, change_cmd)
586 for filename in temp_files:
587 utils.RemoveFile(filename)
589 def StartInstance(self, instance, block_devices):
590 """Start an instance.
593 self._CheckDown(instance.name)
594 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
595 self._SaveKVMRuntime(instance, kvm_runtime)
596 self._ExecuteKVMRuntime(instance, kvm_runtime)
598 def _CallMonitorCommand(self, instance_name, command):
599 """Invoke a command on the instance monitor.
602 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
603 (utils.ShellQuote(command),
604 constants.SOCAT_PATH,
605 utils.ShellQuote(self._InstanceMonitor(instance_name))))
606 result = utils.RunCmd(socat)
608 msg = ("Failed to send command '%s' to instance %s."
609 " output: %s, error: %s, fail_reason: %s" %
610 (command, instance_name,
611 result.stdout, result.stderr, result.fail_reason))
612 raise errors.HypervisorError(msg)
616 def StopInstance(self, instance, force=False, retry=False, name=None):
620 if name is not None and not force:
621 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
624 acpi = instance.hvparams[constants.HV_ACPI]
627 pidfile, pid, alive = self._InstancePidAlive(name)
628 if pid > 0 and alive:
629 if force or not acpi:
630 utils.KillProcess(pid)
632 self._CallMonitorCommand(name, 'system_powerdown')
634 if not self._InstancePidAlive(name)[2]:
635 self._RemoveInstanceRuntimeFiles(pidfile, name)
640 def RebootInstance(self, instance):
641 """Reboot an instance.
644 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
645 # socket the instance will stop, but now power up again. So we'll resort
646 # to shutdown and restart.
647 _, _, alive = self._InstancePidAlive(instance.name)
649 raise errors.HypervisorError("Failed to reboot instance %s:"
650 " not running" % instance.name)
651 # StopInstance will delete the saved KVM runtime so:
652 # ...first load it...
653 kvm_runtime = self._LoadKVMRuntime(instance)
654 # ...now we can safely call StopInstance...
655 if not self.StopInstance(instance):
656 self.StopInstance(instance, force=True)
657 # ...and finally we can save it again, and execute it...
658 self._SaveKVMRuntime(instance, kvm_runtime)
659 self._ExecuteKVMRuntime(instance, kvm_runtime)
661 def MigrationInfo(self, instance):
662 """Get instance information to perform a migration.
664 @type instance: L{objects.Instance}
665 @param instance: instance to be migrated
667 @return: content of the KVM runtime file
670 return self._ReadKVMRuntime(instance.name)
672 def AcceptInstance(self, instance, info, target):
673 """Prepare to accept an instance.
675 @type instance: L{objects.Instance}
676 @param instance: instance to be accepted
678 @param info: content of the KVM runtime file on the source node
680 @param target: target host (usually ip), on this node
683 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
684 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
685 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
687 def FinalizeMigration(self, instance, info, success):
688 """Finalize an instance migration.
690 Stop the incoming mode KVM.
692 @type instance: L{objects.Instance}
693 @param instance: instance whose migration is being aborted
697 self._WriteKVMRuntime(instance.name, info)
699 self.StopInstance(instance, force=True)
701 def MigrateInstance(self, instance, target, live):
702 """Migrate an instance to a target node.
704 The migration will not be attempted if the instance is not
707 @type instance: L{objects.Instance}
708 @param instance: the instance to be migrated
710 @param target: ip address of the target node
712 @param live: perform a live migration
715 instance_name = instance.name
716 port = instance.hvparams[constants.HV_MIGRATION_PORT]
717 pidfile, pid, alive = self._InstancePidAlive(instance_name)
719 raise errors.HypervisorError("Instance not running, cannot migrate")
721 if not utils.TcpPing(target, port, live_port_needed=True):
722 raise errors.HypervisorError("Remote host %s not listening on port"
723 " %s, cannot migrate" % (target, port))
726 self._CallMonitorCommand(instance_name, 'stop')
728 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
729 self._CallMonitorCommand(instance_name, migrate_command)
731 info_command = 'info migrate'
735 result = self._CallMonitorCommand(instance_name, info_command)
736 match = self._MIGRATION_STATUS_RE.search(result.stdout)
739 if not result.stdout:
740 logging.info("KVM: empty 'info migrate' result")
742 logging.warning("KVM: unknown 'info migrate' result: %s",
744 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
746 status = match.group(1)
747 if status == 'completed':
749 elif status == 'active':
750 # reset the broken answers count
752 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
753 elif status == 'failed' or status == 'cancelled':
755 self._CallMonitorCommand(instance_name, 'cont')
756 raise errors.HypervisorError("Migration %s at the kvm level" %
759 logging.warning("KVM: unknown migration status '%s'", status)
761 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
762 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
763 raise errors.HypervisorError("Too many 'info migrate' broken answers")
765 utils.KillProcess(pid)
766 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
768 def GetNodeInfo(self):
769 """Return information about the node.
771 This is just a wrapper over the base GetLinuxNodeInfo method.
773 @return: a dict with the following keys (values in MiB):
774 - memory_total: the total memory size on the node
775 - memory_free: the available memory on the node for instances
776 - memory_dom0: the memory used by the node itself, if available
779 return self.GetLinuxNodeInfo()
782 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
783 """Return a command for connecting to the console of an instance.
786 if hvparams[constants.HV_SERIAL_CONSOLE]:
787 shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
788 (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
789 utils.ShellQuote(cls._InstanceSerial(instance.name))))
791 shell_command = "echo 'No serial shell for instance %s'" % instance.name
793 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
795 if instance.network_port > constants.VNC_BASE_PORT:
796 display = instance.network_port - constants.VNC_BASE_PORT
797 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
798 " (display: %d)'" % (vnc_bind_address,
799 instance.network_port,
801 shell_command = "%s; %s" % (vnc_command, shell_command)
806 """Verify the hypervisor.
808 Check that the binary exists.
811 if not os.path.exists(constants.KVM_PATH):
812 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
813 if not os.path.exists(constants.SOCAT_PATH):
814 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
818 def CheckParameterSyntax(cls, hvparams):
819 """Check the given parameters for validity.
822 @param hvparams: dictionary with parameter names/value
823 @raise errors.HypervisorError: when a parameter is not valid
826 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
828 kernel_path = hvparams[constants.HV_KERNEL_PATH]
830 if not hvparams[constants.HV_ROOT_PATH]:
831 raise errors.HypervisorError("Need a root partition for the instance,"
832 " if a kernel is defined")
834 if (hvparams[constants.HV_VNC_X509_VERIFY] and
835 not hvparams[constants.HV_VNC_X509]):
836 raise errors.HypervisorError("%s must be defined, if %s is" %
837 (constants.HV_VNC_X509,
838 constants.HV_VNC_X509_VERIFY))
840 boot_order = hvparams[constants.HV_BOOT_ORDER]
841 if (boot_order == constants.HT_BO_CDROM and
842 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
843 raise errors.HypervisorError("Cannot boot from cdrom without an"
846 security_model = hvparams[constants.HV_SECURITY_MODEL]
847 if security_model == constants.HT_SM_USER:
848 if not hvparams[constants.HV_SECURITY_DOMAIN]:
849 raise errors.HypervisorError("A security domain (user to run kvm as)"
850 " must be specified")
851 elif (security_model == constants.HT_SM_NONE or
852 security_model == constants.HT_SM_POOL):
853 if hvparams[constants.HV_SECURITY_DOMAIN]:
854 raise errors.HypervisorError("Cannot have a security domain when the"
855 " security model is 'none' or 'pool'")
856 if security_model == constants.HT_SM_POOL:
857 raise errors.HypervisorError("Security model pool is not supported yet")
860 def ValidateParameters(cls, hvparams):
861 """Check the given parameters for validity.
864 @param hvparams: dictionary with parameter names/value
865 @raise errors.HypervisorError: when a parameter is not valid
868 super(KVMHypervisor, cls).ValidateParameters(hvparams)
870 security_model = hvparams[constants.HV_SECURITY_MODEL]
871 if security_model == constants.HT_SM_USER:
872 username = hvparams[constants.HV_SECURITY_DOMAIN]
874 pwd.getpwnam(username)
876 raise errors.HypervisorError("Unknown security domain user %s"
880 def PowercycleNode(cls):
881 """KVM powercycle, just a wrapper over Linux powercycle.
884 cls.LinuxPowercycle()