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
32 from cStringIO import StringIO
34 from ganeti import utils
35 from ganeti import constants
36 from ganeti import errors
37 from ganeti import serializer
38 from ganeti import objects
39 from ganeti.hypervisor import hv_base
42 class KVMHypervisor(hv_base.BaseHypervisor):
43 """KVM hypervisor interface"""
45 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
46 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
47 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
48 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
49 _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
52 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
53 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
54 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
55 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
56 constants.HV_ACPI: hv_base.NO_CHECK,
57 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
58 constants.HV_VNC_BIND_ADDRESS:
59 (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
60 "the VNC bind address must be either a valid IP address or an absolute"
61 " pathname", None, None),
62 constants.HV_VNC_TLS: hv_base.NO_CHECK,
63 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
64 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
65 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
66 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
67 constants.HV_BOOT_ORDER:
68 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
69 constants.HV_NIC_TYPE:
70 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
71 constants.HV_DISK_TYPE:
72 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
73 constants.HV_USB_MOUSE:
74 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
75 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
76 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
77 constants.HV_DISK_CACHE:
78 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
81 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
83 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
84 _MIGRATION_INFO_RETRY_DELAY = 2
86 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
93 hv_base.BaseHypervisor.__init__(self)
94 # Let's make sure the directories we need exist, even if the RUN_DIR lives
95 # in a tmpfs filesystem or has been otherwise wiped out.
96 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
97 utils.EnsureDirs(dirs)
100 def _InstancePidFile(cls, instance_name):
101 """Returns the instance pidfile.
104 return utils.PathJoin(cls._PIDS_DIR, instance_name)
106 def _InstancePidAlive(self, instance_name):
107 """Returns the instance pid and pidfile
110 pidfile = self._InstancePidFile(instance_name)
111 pid = utils.ReadPidFile(pidfile)
112 alive = utils.IsProcessAlive(pid)
114 return (pidfile, pid, alive)
116 def _CheckDown(self, instance_name):
117 """Raises an error unless the given instance is down.
120 alive = self._InstancePidAlive(instance_name)[2]
122 raise errors.HypervisorError("Failed to start instance %s: %s" %
123 (instance_name, "already running"))
126 def _InstanceMonitor(cls, instance_name):
127 """Returns the instance monitor socket name
130 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
133 def _InstanceSerial(cls, instance_name):
134 """Returns the instance serial socket name
137 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
140 def _SocatUnixConsoleParams():
141 """Returns the correct parameters for socat
143 If we have a new-enough socat we can use raw mode with an escape character.
146 if constants.SOCAT_USE_ESCAPE:
147 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
149 return "echo=0,icanon=0"
152 def _InstanceKVMRuntime(cls, instance_name):
153 """Returns the instance KVM runtime filename
156 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
159 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
160 """Removes an instance's rutime sockets/files.
163 utils.RemoveFile(pidfile)
164 utils.RemoveFile(cls._InstanceMonitor(instance_name))
165 utils.RemoveFile(cls._InstanceSerial(instance_name))
166 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
168 def _WriteNetScript(self, instance, seq, nic):
169 """Write a script to connect a net interface to the proper bridge.
171 This can be used by any qemu-type hypervisor.
173 @param instance: instance we're acting on
174 @type instance: instance object
175 @param seq: nic sequence number
177 @param nic: nic we're acting on
178 @type nic: nic object
179 @return: netscript file name
184 script.write("#!/bin/sh\n")
185 script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
186 script.write("PATH=$PATH:/sbin:/usr/sbin\n")
187 script.write("export INSTANCE=%s\n" % instance.name)
188 script.write("export MAC=%s\n" % nic.mac)
190 script.write("export IP=%s\n" % nic.ip)
191 script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
192 if nic.nicparams[constants.NIC_LINK]:
193 script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
194 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
195 script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
196 script.write("export INTERFACE=$1\n")
197 # TODO: make this configurable at ./configure time
198 script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
199 script.write(" # Execute the user-specific vif file\n")
200 script.write(" %s\n" % self._KVM_NETWORK_SCRIPT)
201 script.write("else\n")
202 script.write(" ifconfig $INTERFACE 0.0.0.0 up\n")
203 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
204 script.write(" # Connect the interface to the bridge\n")
205 script.write(" brctl addif $BRIDGE $INTERFACE\n")
206 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
208 raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
209 script.write(" # Route traffic targeted at the IP to the interface\n")
210 if nic.nicparams[constants.NIC_LINK]:
211 script.write(" while ip rule del dev $INTERFACE; do :; done\n")
212 script.write(" ip rule add dev $INTERFACE table $LINK\n")
213 script.write(" ip route replace $IP table $LINK proto static"
216 script.write(" ip route replace $IP proto static"
218 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
219 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
220 script.write(" if [ -d %s ]; then\n" % interface_v4_conf)
221 script.write(" echo 1 > %s/proxy_arp\n" % interface_v4_conf)
222 script.write(" echo 1 > %s/forwarding\n" % interface_v4_conf)
223 script.write(" fi\n")
224 script.write(" if [ -d %s ]; then\n" % interface_v6_conf)
225 script.write(" echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
226 script.write(" echo 1 > %s/forwarding\n" % interface_v6_conf)
227 script.write(" fi\n")
228 script.write("fi\n\n")
229 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
230 # mounted noexec sometimes, so we'll have to find another place.
231 (tmpfd, tmpfile_name) = tempfile.mkstemp()
232 tmpfile = os.fdopen(tmpfd, 'w')
234 tmpfile.write(script.getvalue())
237 os.chmod(tmpfile_name, 0755)
240 def ListInstances(self):
241 """Get the list of running instances.
243 We can do this by listing our live instances directory and
244 checking whether the associated kvm process is still alive.
248 for name in os.listdir(self._PIDS_DIR):
249 filename = utils.PathJoin(self._PIDS_DIR, name)
250 if utils.IsProcessAlive(utils.ReadPidFile(filename)):
254 def GetInstanceInfo(self, instance_name):
255 """Get instance properties.
257 @param instance_name: the instance name
259 @return: tuple (name, id, memory, vcpus, stat, times)
262 _, pid, alive = self._InstancePidAlive(instance_name)
266 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
268 cmdline = utils.ReadFile(cmdline_file)
269 except EnvironmentError, err:
270 raise errors.HypervisorError("Failed to list instance %s: %s" %
271 (instance_name, err))
278 arg_list = cmdline.split('\x00')
280 arg = arg_list.pop(0)
282 memory = int(arg_list.pop(0))
284 vcpus = int(arg_list.pop(0))
286 return (instance_name, pid, memory, vcpus, stat, times)
288 def GetAllInstancesInfo(self):
289 """Get properties of all instances.
291 @return: list of tuples (name, id, memory, vcpus, stat, times)
295 for name in os.listdir(self._PIDS_DIR):
296 filename = utils.PathJoin(self._PIDS_DIR, name)
297 if utils.IsProcessAlive(utils.ReadPidFile(filename)):
299 info = self.GetInstanceInfo(name)
300 except errors.HypervisorError:
307 def _GenerateKVMRuntime(self, instance, block_devices):
308 """Generate KVM information to start an instance.
311 pidfile = self._InstancePidFile(instance.name)
312 kvm = constants.KVM_PATH
314 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
315 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
316 kvm_cmd.extend(['-pidfile', pidfile])
317 # used just by the vnc server, if enabled
318 kvm_cmd.extend(['-name', instance.name])
319 kvm_cmd.extend(['-daemonize'])
320 if not instance.hvparams[constants.HV_ACPI]:
321 kvm_cmd.extend(['-no-acpi'])
323 hvp = instance.hvparams
324 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
325 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
326 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
329 kvm_cmd.extend(['-boot', 'n'])
331 disk_type = hvp[constants.HV_DISK_TYPE]
332 if disk_type == constants.HT_DISK_PARAVIRTUAL:
333 if_val = ',if=virtio'
335 if_val = ',if=%s' % disk_type
337 disk_cache = hvp[constants.HV_DISK_CACHE]
338 if disk_cache != constants.HT_CACHE_DEFAULT:
339 cache_val = ",cache=%s" % disk_cache
342 for cfdev, dev_path in block_devices:
343 if cfdev.mode != constants.DISK_RDWR:
344 raise errors.HypervisorError("Instance has read-only disks which"
345 " are not supported by KVM")
346 # TODO: handle FD_LOOP and FD_BLKTAP (?)
348 kvm_cmd.extend(['-boot', 'c'])
349 boot_val = ',boot=on'
350 # We only boot from the first disk
355 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
357 kvm_cmd.extend(['-drive', drive_val])
359 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
361 options = ',format=raw,media=cdrom'
363 kvm_cmd.extend(['-boot', 'd'])
364 options = '%s,boot=on' % options
366 options = '%s,if=virtio' % options
367 drive_val = 'file=%s%s' % (iso_image, options)
368 kvm_cmd.extend(['-drive', drive_val])
370 kernel_path = hvp[constants.HV_KERNEL_PATH]
372 kvm_cmd.extend(['-kernel', kernel_path])
373 initrd_path = hvp[constants.HV_INITRD_PATH]
375 kvm_cmd.extend(['-initrd', initrd_path])
376 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
377 hvp[constants.HV_KERNEL_ARGS]]
378 if hvp[constants.HV_SERIAL_CONSOLE]:
379 root_append.append('console=ttyS0,38400')
380 kvm_cmd.extend(['-append', ' '.join(root_append)])
382 mouse_type = hvp[constants.HV_USB_MOUSE]
384 kvm_cmd.extend(['-usb'])
385 kvm_cmd.extend(['-usbdevice', mouse_type])
387 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
389 if utils.IsValidIP(vnc_bind_address):
390 if instance.network_port > constants.VNC_BASE_PORT:
391 display = instance.network_port - constants.VNC_BASE_PORT
392 if vnc_bind_address == '0.0.0.0':
393 vnc_arg = ':%d' % (display)
395 vnc_arg = '%s:%d' % (vnc_bind_address, display)
397 logging.error("Network port is not a valid VNC display (%d < %d)."
398 " Not starting VNC", instance.network_port,
399 constants.VNC_BASE_PORT)
402 # Only allow tls and other option when not binding to a file, for now.
403 # kvm/qemu gets confused otherwise about the filename to use.
405 if hvp[constants.HV_VNC_TLS]:
406 vnc_append = '%s,tls' % vnc_append
407 if hvp[constants.HV_VNC_X509_VERIFY]:
408 vnc_append = '%s,x509verify=%s' % (vnc_append,
409 hvp[constants.HV_VNC_X509])
410 elif hvp[constants.HV_VNC_X509]:
411 vnc_append = '%s,x509=%s' % (vnc_append,
412 hvp[constants.HV_VNC_X509])
413 if hvp[constants.HV_VNC_PASSWORD_FILE]:
414 vnc_append = '%s,password' % vnc_append
416 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
419 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
421 kvm_cmd.extend(['-vnc', vnc_arg])
423 kvm_cmd.extend(['-nographic'])
425 monitor_dev = ("unix:%s,server,nowait" %
426 self._InstanceMonitor(instance.name))
427 kvm_cmd.extend(['-monitor', monitor_dev])
428 if hvp[constants.HV_SERIAL_CONSOLE]:
429 serial_dev = ('unix:%s,server,nowait' %
430 self._InstanceSerial(instance.name))
431 kvm_cmd.extend(['-serial', serial_dev])
433 kvm_cmd.extend(['-serial', 'none'])
435 if hvp[constants.HV_USE_LOCALTIME]:
436 kvm_cmd.extend(['-localtime'])
438 # Save the current instance nics, but defer their expansion as parameters,
439 # as we'll need to generate executable temp files for them.
440 kvm_nics = instance.nics
443 return (kvm_cmd, kvm_nics, hvparams)
445 def _WriteKVMRuntime(self, instance_name, data):
446 """Write an instance's KVM runtime
450 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
452 except EnvironmentError, err:
453 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
455 def _ReadKVMRuntime(self, instance_name):
456 """Read an instance's KVM runtime
460 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
461 except EnvironmentError, err:
462 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
465 def _SaveKVMRuntime(self, instance, kvm_runtime):
466 """Save an instance's KVM runtime
469 kvm_cmd, kvm_nics, hvparams = kvm_runtime
470 serialized_nics = [nic.ToDict() for nic in kvm_nics]
471 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
472 self._WriteKVMRuntime(instance.name, serialized_form)
474 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
475 """Load an instance's KVM runtime
478 if not serialized_runtime:
479 serialized_runtime = self._ReadKVMRuntime(instance.name)
480 loaded_runtime = serializer.Load(serialized_runtime)
481 kvm_cmd, serialized_nics, hvparams = loaded_runtime
482 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
483 return (kvm_cmd, kvm_nics, hvparams)
485 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
486 """Execute a KVM cmd, after completing it with some last minute data
488 @type incoming: tuple of strings
489 @param incoming: (target_host_ip, port)
492 hvp = instance.hvparams
494 self._CheckDown(name)
498 kvm_cmd, kvm_nics, hvparams = kvm_runtime
501 kvm_cmd.extend(['-net', 'none'])
503 nic_type = hvparams[constants.HV_NIC_TYPE]
504 if nic_type == constants.HT_NIC_PARAVIRTUAL:
505 nic_model = "model=virtio"
507 nic_model = "model=%s" % nic_type
509 for nic_seq, nic in enumerate(kvm_nics):
510 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
511 script = self._WriteNetScript(instance, nic_seq, nic)
512 kvm_cmd.extend(['-net', nic_val])
513 kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
514 temp_files.append(script)
517 target, port = incoming
518 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
520 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
524 vnc_pwd = utils.ReadFile(vnc_pwd_file)
525 except EnvironmentError, err:
526 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
527 % (vnc_pwd_file, err))
529 result = utils.RunCmd(kvm_cmd)
531 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
532 (name, result.fail_reason, result.output))
534 if not self._InstancePidAlive(name)[2]:
535 raise errors.HypervisorError("Failed to start instance %s" % name)
538 change_cmd = 'change vnc password %s' % vnc_pwd
539 self._CallMonitorCommand(instance.name, change_cmd)
541 for filename in temp_files:
542 utils.RemoveFile(filename)
544 def StartInstance(self, instance, block_devices):
545 """Start an instance.
548 self._CheckDown(instance.name)
549 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
550 self._SaveKVMRuntime(instance, kvm_runtime)
551 self._ExecuteKVMRuntime(instance, kvm_runtime)
553 def _CallMonitorCommand(self, instance_name, command):
554 """Invoke a command on the instance monitor.
557 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
558 (utils.ShellQuote(command),
559 constants.SOCAT_PATH,
560 utils.ShellQuote(self._InstanceMonitor(instance_name))))
561 result = utils.RunCmd(socat)
563 msg = ("Failed to send command '%s' to instance %s."
564 " output: %s, error: %s, fail_reason: %s" %
565 (command, instance_name,
566 result.stdout, result.stderr, result.fail_reason))
567 raise errors.HypervisorError(msg)
571 def StopInstance(self, instance, force=False, retry=False):
575 pidfile, pid, alive = self._InstancePidAlive(instance.name)
576 if pid > 0 and alive:
577 if force or not instance.hvparams[constants.HV_ACPI]:
578 utils.KillProcess(pid)
580 self._CallMonitorCommand(instance.name, 'system_powerdown')
582 if not utils.IsProcessAlive(pid):
583 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
588 def RebootInstance(self, instance):
589 """Reboot an instance.
592 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
593 # socket the instance will stop, but now power up again. So we'll resort
594 # to shutdown and restart.
595 _, _, alive = self._InstancePidAlive(instance.name)
597 raise errors.HypervisorError("Failed to reboot instance %s:"
598 " not running" % instance.name)
599 # StopInstance will delete the saved KVM runtime so:
600 # ...first load it...
601 kvm_runtime = self._LoadKVMRuntime(instance)
602 # ...now we can safely call StopInstance...
603 if not self.StopInstance(instance):
604 self.StopInstance(instance, force=True)
605 # ...and finally we can save it again, and execute it...
606 self._SaveKVMRuntime(instance, kvm_runtime)
607 self._ExecuteKVMRuntime(instance, kvm_runtime)
609 def MigrationInfo(self, instance):
610 """Get instance information to perform a migration.
612 @type instance: L{objects.Instance}
613 @param instance: instance to be migrated
615 @return: content of the KVM runtime file
618 return self._ReadKVMRuntime(instance.name)
620 def AcceptInstance(self, instance, info, target):
621 """Prepare to accept an instance.
623 @type instance: L{objects.Instance}
624 @param instance: instance to be accepted
626 @param info: content of the KVM runtime file on the source node
628 @param target: target host (usually ip), on this node
631 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
632 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
633 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
635 def FinalizeMigration(self, instance, info, success):
636 """Finalize an instance migration.
638 Stop the incoming mode KVM.
640 @type instance: L{objects.Instance}
641 @param instance: instance whose migration is being aborted
645 self._WriteKVMRuntime(instance.name, info)
647 self.StopInstance(instance, force=True)
649 def MigrateInstance(self, instance, target, live):
650 """Migrate an instance to a target node.
652 The migration will not be attempted if the instance is not
655 @type instance: L{objects.Instance}
656 @param instance: the instance to be migrated
658 @param target: ip address of the target node
660 @param live: perform a live migration
663 instance_name = instance.name
664 port = instance.hvparams[constants.HV_MIGRATION_PORT]
665 pidfile, pid, alive = self._InstancePidAlive(instance_name)
667 raise errors.HypervisorError("Instance not running, cannot migrate")
669 if not utils.TcpPing(target, port, live_port_needed=True):
670 raise errors.HypervisorError("Remote host %s not listening on port"
671 " %s, cannot migrate" % (target, port))
674 self._CallMonitorCommand(instance_name, 'stop')
676 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
677 self._CallMonitorCommand(instance_name, migrate_command)
679 info_command = 'info migrate'
683 result = self._CallMonitorCommand(instance_name, info_command)
684 match = self._MIGRATION_STATUS_RE.search(result.stdout)
687 if not result.stdout:
688 logging.info("KVM: empty 'info migrate' result")
690 logging.warning("KVM: unknown 'info migrate' result: %s",
692 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
694 status = match.group(1)
695 if status == 'completed':
697 elif status == 'active':
698 # reset the broken answers count
700 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
701 elif status == 'failed' or status == 'cancelled':
703 self._CallMonitorCommand(instance_name, 'cont')
704 raise errors.HypervisorError("Migration %s at the kvm level" %
707 logging.warning("KVM: unknown migration status '%s'", status)
709 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
710 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
711 raise errors.HypervisorError("Too many 'info migrate' broken answers")
713 utils.KillProcess(pid)
714 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
716 def GetNodeInfo(self):
717 """Return information about the node.
719 This is just a wrapper over the base GetLinuxNodeInfo method.
721 @return: a dict with the following keys (values in MiB):
722 - memory_total: the total memory size on the node
723 - memory_free: the available memory on the node for instances
724 - memory_dom0: the memory used by the node itself, if available
727 return self.GetLinuxNodeInfo()
730 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
731 """Return a command for connecting to the console of an instance.
734 if hvparams[constants.HV_SERIAL_CONSOLE]:
735 shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
736 (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
737 utils.ShellQuote(cls._InstanceSerial(instance.name))))
739 shell_command = "echo 'No serial shell for instance %s'" % instance.name
741 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
743 if instance.network_port > constants.VNC_BASE_PORT:
744 display = instance.network_port - constants.VNC_BASE_PORT
745 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
746 " (display: %d)'" % (vnc_bind_address,
747 instance.network_port,
749 shell_command = "%s; %s" % (vnc_command, shell_command)
754 """Verify the hypervisor.
756 Check that the binary exists.
759 if not os.path.exists(constants.KVM_PATH):
760 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
761 if not os.path.exists(constants.SOCAT_PATH):
762 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
766 def CheckParameterSyntax(cls, hvparams):
767 """Check the given parameters for validity.
770 @param hvparams: dictionary with parameter names/value
771 @raise errors.HypervisorError: when a parameter is not valid
774 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
776 kernel_path = hvparams[constants.HV_KERNEL_PATH]
778 if not hvparams[constants.HV_ROOT_PATH]:
779 raise errors.HypervisorError("Need a root partition for the instance,"
780 " if a kernel is defined")
782 if (hvparams[constants.HV_VNC_X509_VERIFY] and
783 not hvparams[constants.HV_VNC_X509]):
784 raise errors.HypervisorError("%s must be defined, if %s is" %
785 (constants.HV_VNC_X509,
786 constants.HV_VNC_X509_VERIFY))
788 boot_order = hvparams[constants.HV_BOOT_ORDER]
789 if (boot_order == constants.HT_BO_CDROM and
790 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
791 raise errors.HypervisorError("Cannot boot from cdrom without an"
795 def PowercycleNode(cls):
796 """KVM powercycle, just a wrapper over Linux powercycle.
799 cls.LinuxPowercycle()