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+)',
84 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
91 hv_base.BaseHypervisor.__init__(self)
92 # Let's make sure the directories we need exist, even if the RUN_DIR lives
93 # in a tmpfs filesystem or has been otherwise wiped out.
94 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
95 utils.EnsureDirs(dirs)
97 def _InstancePidFile(self, instance_name):
98 """Returns the instance pidfile.
101 pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
104 def _InstancePidAlive(self, instance_name):
105 """Returns the instance pid and pidfile
108 pidfile = self._InstancePidFile(instance_name)
109 pid = utils.ReadPidFile(pidfile)
110 alive = utils.IsProcessAlive(pid)
112 return (pidfile, pid, alive)
114 def _CheckDown(self, instance_name):
115 """Raises an error unless the given instance is down.
118 alive = self._InstancePidAlive(instance_name)[2]
120 raise errors.HypervisorError("Failed to start instance %s: %s" %
121 (instance_name, "already running"))
124 def _InstanceMonitor(cls, instance_name):
125 """Returns the instance monitor socket name
128 return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
131 def _InstanceSerial(cls, instance_name):
132 """Returns the instance serial socket name
135 return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
138 def _SocatUnixConsoleParams():
139 """Returns the correct parameters for socat
141 If we have a new-enough socat we can use raw mode with an escape character.
144 if constants.SOCAT_USE_ESCAPE:
145 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
147 return "echo=0,icanon=0"
150 def _InstanceKVMRuntime(cls, instance_name):
151 """Returns the instance KVM runtime filename
154 return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
157 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
158 """Removes an instance's rutime sockets/files.
161 utils.RemoveFile(pidfile)
162 utils.RemoveFile(cls._InstanceMonitor(instance_name))
163 utils.RemoveFile(cls._InstanceSerial(instance_name))
164 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
166 def _WriteNetScript(self, instance, seq, nic):
167 """Write a script to connect a net interface to the proper bridge.
169 This can be used by any qemu-type hypervisor.
171 @param instance: instance we're acting on
172 @type instance: instance object
173 @param seq: nic sequence number
175 @param nic: nic we're acting on
176 @type nic: nic object
177 @return: netscript file name
182 script.write("#!/bin/sh\n")
183 script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
184 script.write("export INSTANCE=%s\n" % instance.name)
185 script.write("export MAC=%s\n" % nic.mac)
187 script.write("export IP=%s\n" % nic.ip)
188 script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
189 if nic.nicparams[constants.NIC_LINK]:
190 script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
191 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
192 script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
193 script.write("export INTERFACE=$1\n")
194 # TODO: make this configurable at ./configure time
195 script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
196 script.write(" # Execute the user-specific vif file\n")
197 script.write(" %s\n" % self._KVM_NETWORK_SCRIPT)
198 script.write("else\n")
199 script.write(" /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
200 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
201 script.write(" # Connect the interface to the bridge\n")
202 script.write(" /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
203 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
205 raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
206 script.write(" # Route traffic targeted at the IP to the interface\n")
207 if nic.nicparams[constants.NIC_LINK]:
208 script.write(" while /sbin/ip rule del dev $INTERFACE; do :; done\n")
209 script.write(" /sbin/ip rule add dev $INTERFACE table $LINK\n")
210 script.write(" /sbin/ip route replace $IP table $LINK proto static"
213 script.write(" /sbin/ip route replace $IP proto static"
215 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
216 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
217 script.write(" if [ -d %s ]; then\n" % interface_v4_conf)
218 script.write(" echo 1 > %s/proxy_arp\n" % interface_v4_conf)
219 script.write(" echo 1 > %s/forwarding\n" % interface_v4_conf)
220 script.write(" fi\n")
221 script.write(" if [ -d %s ]; then\n" % interface_v6_conf)
222 script.write(" echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
223 script.write(" echo 1 > %s/forwarding\n" % interface_v6_conf)
224 script.write(" fi\n")
225 script.write("fi\n\n")
226 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
227 # mounted noexec sometimes, so we'll have to find another place.
228 (tmpfd, tmpfile_name) = tempfile.mkstemp()
229 tmpfile = os.fdopen(tmpfd, 'w')
231 tmpfile.write(script.getvalue())
234 os.chmod(tmpfile_name, 0755)
237 def ListInstances(self):
238 """Get the list of running instances.
240 We can do this by listing our live instances directory and
241 checking whether the associated kvm process is still alive.
245 for name in os.listdir(self._PIDS_DIR):
246 filename = "%s/%s" % (self._PIDS_DIR, name)
247 if utils.IsProcessAlive(utils.ReadPidFile(filename)):
251 def GetInstanceInfo(self, instance_name):
252 """Get instance properties.
254 @param instance_name: the instance name
256 @return: tuple (name, id, memory, vcpus, stat, times)
259 _, pid, alive = self._InstancePidAlive(instance_name)
263 cmdline_file = "/proc/%s/cmdline" % pid
265 cmdline = utils.ReadFile(cmdline_file)
266 except EnvironmentError, err:
267 raise errors.HypervisorError("Failed to list instance %s: %s" %
268 (instance_name, err))
275 arg_list = cmdline.split('\x00')
277 arg = arg_list.pop(0)
279 memory = int(arg_list.pop(0))
281 vcpus = int(arg_list.pop(0))
283 return (instance_name, pid, memory, vcpus, stat, times)
285 def GetAllInstancesInfo(self):
286 """Get properties of all instances.
288 @return: list of tuples (name, id, memory, vcpus, stat, times)
292 for name in os.listdir(self._PIDS_DIR):
293 filename = "%s/%s" % (self._PIDS_DIR, name)
294 if utils.IsProcessAlive(utils.ReadPidFile(filename)):
296 info = self.GetInstanceInfo(name)
297 except errors.HypervisorError:
304 def _GenerateKVMRuntime(self, instance, block_devices):
305 """Generate KVM information to start an instance.
308 pidfile = self._InstancePidFile(instance.name)
309 kvm = constants.KVM_PATH
311 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
312 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
313 kvm_cmd.extend(['-pidfile', pidfile])
314 # used just by the vnc server, if enabled
315 kvm_cmd.extend(['-name', instance.name])
316 kvm_cmd.extend(['-daemonize'])
317 if not instance.hvparams[constants.HV_ACPI]:
318 kvm_cmd.extend(['-no-acpi'])
320 hvp = instance.hvparams
321 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
322 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
323 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
326 kvm_cmd.extend(['-boot', 'n'])
328 disk_type = hvp[constants.HV_DISK_TYPE]
329 if disk_type == constants.HT_DISK_PARAVIRTUAL:
330 if_val = ',if=virtio'
332 if_val = ',if=%s' % disk_type
334 disk_cache = hvp[constants.HV_DISK_CACHE]
335 if disk_cache != constants.HT_CACHE_DEFAULT:
336 cache_val = ",cache=%s" % disk_cache
339 for cfdev, dev_path in block_devices:
340 if cfdev.mode != constants.DISK_RDWR:
341 raise errors.HypervisorError("Instance has read-only disks which"
342 " are not supported by KVM")
343 # TODO: handle FD_LOOP and FD_BLKTAP (?)
345 kvm_cmd.extend(['-boot', 'c'])
346 boot_val = ',boot=on'
347 # We only boot from the first disk
352 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
354 kvm_cmd.extend(['-drive', drive_val])
356 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
358 options = ',format=raw,media=cdrom'
360 kvm_cmd.extend(['-boot', 'd'])
361 options = '%s,boot=on' % options
363 options = '%s,if=virtio' % options
364 drive_val = 'file=%s%s' % (iso_image, options)
365 kvm_cmd.extend(['-drive', drive_val])
367 kernel_path = hvp[constants.HV_KERNEL_PATH]
369 kvm_cmd.extend(['-kernel', kernel_path])
370 initrd_path = hvp[constants.HV_INITRD_PATH]
372 kvm_cmd.extend(['-initrd', initrd_path])
373 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
374 hvp[constants.HV_KERNEL_ARGS]]
375 if hvp[constants.HV_SERIAL_CONSOLE]:
376 root_append.append('console=ttyS0,38400')
377 kvm_cmd.extend(['-append', ' '.join(root_append)])
379 mouse_type = hvp[constants.HV_USB_MOUSE]
381 kvm_cmd.extend(['-usb'])
382 kvm_cmd.extend(['-usbdevice', mouse_type])
384 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
386 if utils.IsValidIP(vnc_bind_address):
387 if instance.network_port > constants.VNC_BASE_PORT:
388 display = instance.network_port - constants.VNC_BASE_PORT
389 if vnc_bind_address == '0.0.0.0':
390 vnc_arg = ':%d' % (display)
392 vnc_arg = '%s:%d' % (vnc_bind_address, display)
394 logging.error("Network port is not a valid VNC display (%d < %d)."
395 " Not starting VNC", instance.network_port,
396 constants.VNC_BASE_PORT)
399 # Only allow tls and other option when not binding to a file, for now.
400 # kvm/qemu gets confused otherwise about the filename to use.
402 if hvp[constants.HV_VNC_TLS]:
403 vnc_append = '%s,tls' % vnc_append
404 if hvp[constants.HV_VNC_X509_VERIFY]:
405 vnc_append = '%s,x509verify=%s' % (vnc_append,
406 hvp[constants.HV_VNC_X509])
407 elif hvp[constants.HV_VNC_X509]:
408 vnc_append = '%s,x509=%s' % (vnc_append,
409 hvp[constants.HV_VNC_X509])
410 if hvp[constants.HV_VNC_PASSWORD_FILE]:
411 vnc_append = '%s,password' % vnc_append
413 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
416 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
418 kvm_cmd.extend(['-vnc', vnc_arg])
420 kvm_cmd.extend(['-nographic'])
422 monitor_dev = ("unix:%s,server,nowait" %
423 self._InstanceMonitor(instance.name))
424 kvm_cmd.extend(['-monitor', monitor_dev])
425 if hvp[constants.HV_SERIAL_CONSOLE]:
426 serial_dev = ('unix:%s,server,nowait' %
427 self._InstanceSerial(instance.name))
428 kvm_cmd.extend(['-serial', serial_dev])
430 kvm_cmd.extend(['-serial', 'none'])
432 if hvp[constants.HV_USE_LOCALTIME]:
433 kvm_cmd.extend(['-localtime'])
435 # Save the current instance nics, but defer their expansion as parameters,
436 # as we'll need to generate executable temp files for them.
437 kvm_nics = instance.nics
440 return (kvm_cmd, kvm_nics, hvparams)
442 def _WriteKVMRuntime(self, instance_name, data):
443 """Write an instance's KVM runtime
447 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
449 except EnvironmentError, err:
450 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
452 def _ReadKVMRuntime(self, instance_name):
453 """Read an instance's KVM runtime
457 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
458 except EnvironmentError, err:
459 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
462 def _SaveKVMRuntime(self, instance, kvm_runtime):
463 """Save an instance's KVM runtime
466 kvm_cmd, kvm_nics, hvparams = kvm_runtime
467 serialized_nics = [nic.ToDict() for nic in kvm_nics]
468 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
469 self._WriteKVMRuntime(instance.name, serialized_form)
471 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
472 """Load an instance's KVM runtime
475 if not serialized_runtime:
476 serialized_runtime = self._ReadKVMRuntime(instance.name)
477 loaded_runtime = serializer.Load(serialized_runtime)
478 kvm_cmd, serialized_nics, hvparams = loaded_runtime
479 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
480 return (kvm_cmd, kvm_nics, hvparams)
482 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
483 """Execute a KVM cmd, after completing it with some last minute data
485 @type incoming: tuple of strings
486 @param incoming: (target_host_ip, port)
489 hvp = instance.hvparams
491 self._CheckDown(name)
495 kvm_cmd, kvm_nics, hvparams = kvm_runtime
498 kvm_cmd.extend(['-net', 'none'])
500 nic_type = hvparams[constants.HV_NIC_TYPE]
501 if nic_type == constants.HT_NIC_PARAVIRTUAL:
502 nic_model = "model=virtio"
504 nic_model = "model=%s" % nic_type
506 for nic_seq, nic in enumerate(kvm_nics):
507 nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
508 script = self._WriteNetScript(instance, nic_seq, nic)
509 kvm_cmd.extend(['-net', nic_val])
510 kvm_cmd.extend(['-net', 'tap,script=%s' % script])
511 temp_files.append(script)
514 target, port = incoming
515 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
517 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
521 vnc_pwd = utils.ReadFile(vnc_pwd_file)
522 except EnvironmentError, err:
523 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
524 % (vnc_pwd_file, err))
526 result = utils.RunCmd(kvm_cmd)
528 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
529 (name, result.fail_reason, result.output))
531 if not self._InstancePidAlive(name)[2]:
532 raise errors.HypervisorError("Failed to start instance %s" % name)
535 change_cmd = 'change vnc password %s' % vnc_pwd
536 self._CallMonitorCommand(instance.name, change_cmd)
538 for filename in temp_files:
539 utils.RemoveFile(filename)
541 def StartInstance(self, instance, block_devices):
542 """Start an instance.
545 self._CheckDown(instance.name)
546 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
547 self._SaveKVMRuntime(instance, kvm_runtime)
548 self._ExecuteKVMRuntime(instance, kvm_runtime)
550 def _CallMonitorCommand(self, instance_name, command):
551 """Invoke a command on the instance monitor.
554 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
555 (utils.ShellQuote(command),
556 constants.SOCAT_PATH,
557 utils.ShellQuote(self._InstanceMonitor(instance_name))))
558 result = utils.RunCmd(socat)
560 msg = ("Failed to send command '%s' to instance %s."
561 " output: %s, error: %s, fail_reason: %s" %
562 (command, instance_name,
563 result.stdout, result.stderr, result.fail_reason))
564 raise errors.HypervisorError(msg)
568 def StopInstance(self, instance, force=False, retry=False):
572 pidfile, pid, alive = self._InstancePidAlive(instance.name)
573 if pid > 0 and alive:
574 if force or not instance.hvparams[constants.HV_ACPI]:
575 utils.KillProcess(pid)
577 self._CallMonitorCommand(instance.name, 'system_powerdown')
579 if not utils.IsProcessAlive(pid):
580 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
585 def RebootInstance(self, instance):
586 """Reboot an instance.
589 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
590 # socket the instance will stop, but now power up again. So we'll resort
591 # to shutdown and restart.
592 _, _, alive = self._InstancePidAlive(instance.name)
594 raise errors.HypervisorError("Failed to reboot instance %s:"
595 " not running" % instance.name)
596 # StopInstance will delete the saved KVM runtime so:
597 # ...first load it...
598 kvm_runtime = self._LoadKVMRuntime(instance)
599 # ...now we can safely call StopInstance...
600 if not self.StopInstance(instance):
601 self.StopInstance(instance, force=True)
602 # ...and finally we can save it again, and execute it...
603 self._SaveKVMRuntime(instance, kvm_runtime)
604 self._ExecuteKVMRuntime(instance, kvm_runtime)
606 def MigrationInfo(self, instance):
607 """Get instance information to perform a migration.
609 @type instance: L{objects.Instance}
610 @param instance: instance to be migrated
612 @return: content of the KVM runtime file
615 return self._ReadKVMRuntime(instance.name)
617 def AcceptInstance(self, instance, info, target):
618 """Prepare to accept an instance.
620 @type instance: L{objects.Instance}
621 @param instance: instance to be accepted
623 @param info: content of the KVM runtime file on the source node
625 @param target: target host (usually ip), on this node
628 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
629 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
630 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
632 def FinalizeMigration(self, instance, info, success):
633 """Finalize an instance migration.
635 Stop the incoming mode KVM.
637 @type instance: L{objects.Instance}
638 @param instance: instance whose migration is being aborted
642 self._WriteKVMRuntime(instance.name, info)
644 self.StopInstance(instance, force=True)
646 def MigrateInstance(self, instance, target, live):
647 """Migrate an instance to a target node.
649 The migration will not be attempted if the instance is not
652 @type instance: L{objects.Instance}
653 @param instance: the instance to be migrated
655 @param target: ip address of the target node
657 @param live: perform a live migration
660 instance_name = instance.name
661 port = instance.hvparams[constants.HV_MIGRATION_PORT]
662 pidfile, pid, alive = self._InstancePidAlive(instance_name)
664 raise errors.HypervisorError("Instance not running, cannot migrate")
666 if not utils.TcpPing(target, port, live_port_needed=True):
667 raise errors.HypervisorError("Remote host %s not listening on port"
668 " %s, cannot migrate" % (target, port))
671 self._CallMonitorCommand(instance_name, 'stop')
673 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
674 self._CallMonitorCommand(instance_name, migrate_command)
676 info_command = 'info migrate'
679 result = self._CallMonitorCommand(instance_name, info_command)
680 match = self._MIGRATION_STATUS_RE.search(result.stdout)
682 raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
685 status = match.group(1)
686 if status == 'completed':
688 elif status == 'active':
690 elif status == 'failed' or status == 'cancelled':
692 self._CallMonitorCommand(instance_name, 'cont')
693 raise errors.HypervisorError("Migration %s at the kvm level" %
696 logging.info("KVM: unknown migration status '%s'", status)
699 utils.KillProcess(pid)
700 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
702 def GetNodeInfo(self):
703 """Return information about the node.
705 This is just a wrapper over the base GetLinuxNodeInfo method.
707 @return: a dict with the following keys (values in MiB):
708 - memory_total: the total memory size on the node
709 - memory_free: the available memory on the node for instances
710 - memory_dom0: the memory used by the node itself, if available
713 return self.GetLinuxNodeInfo()
716 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
717 """Return a command for connecting to the console of an instance.
720 if hvparams[constants.HV_SERIAL_CONSOLE]:
721 shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
722 (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
723 utils.ShellQuote(cls._InstanceSerial(instance.name))))
725 shell_command = "echo 'No serial shell for instance %s'" % instance.name
727 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
729 if instance.network_port > constants.VNC_BASE_PORT:
730 display = instance.network_port - constants.VNC_BASE_PORT
731 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
732 " (display: %d)'" % (vnc_bind_address,
733 instance.network_port,
735 shell_command = "%s; %s" % (vnc_command, shell_command)
740 """Verify the hypervisor.
742 Check that the binary exists.
745 if not os.path.exists(constants.KVM_PATH):
746 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
747 if not os.path.exists(constants.SOCAT_PATH):
748 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
752 def CheckParameterSyntax(cls, hvparams):
753 """Check the given parameters for validity.
756 @param hvparams: dictionary with parameter names/value
757 @raise errors.HypervisorError: when a parameter is not valid
760 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
762 kernel_path = hvparams[constants.HV_KERNEL_PATH]
764 if not hvparams[constants.HV_ROOT_PATH]:
765 raise errors.HypervisorError("Need a root partition for the instance,"
766 " if a kernel is defined")
768 if (hvparams[constants.HV_VNC_X509_VERIFY] and
769 not hvparams[constants.HV_VNC_X509]):
770 raise errors.HypervisorError("%s must be defined, if %s is" %
771 (constants.HV_VNC_X509,
772 constants.HV_VNC_X509_VERIFY))
774 boot_order = hvparams[constants.HV_BOOT_ORDER]
776 if (boot_order == constants.HT_BO_CDROM and
777 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
778 raise errors.HypervisorError("Cannot boot from cdrom without an"
780 if (boot_order == constants.HT_BO_NETWORK and
781 hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
782 raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
783 " change the NIC type.")
786 def PowercycleNode(cls):
787 """KVM powercycle, just a wrapper over Linux powercycle.
790 cls.LinuxPowercycle()