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,
78 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
81 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
88 hv_base.BaseHypervisor.__init__(self)
89 # Let's make sure the directories we need exist, even if the RUN_DIR lives
90 # in a tmpfs filesystem or has been otherwise wiped out.
91 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
92 utils.EnsureDirs(dirs)
94 def _InstancePidAlive(self, instance_name):
95 """Returns the instance pid and pidfile
98 pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
99 pid = utils.ReadPidFile(pidfile)
100 alive = utils.IsProcessAlive(pid)
102 return (pidfile, pid, alive)
105 def _InstanceMonitor(cls, instance_name):
106 """Returns the instance monitor socket name
109 return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
112 def _InstanceSerial(cls, instance_name):
113 """Returns the instance serial socket name
116 return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
119 def _InstanceKVMRuntime(cls, instance_name):
120 """Returns the instance KVM runtime filename
123 return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
126 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
127 """Removes an instance's rutime sockets/files.
130 utils.RemoveFile(pidfile)
131 utils.RemoveFile(cls._InstanceMonitor(instance_name))
132 utils.RemoveFile(cls._InstanceSerial(instance_name))
133 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
135 def _WriteNetScript(self, instance, seq, nic):
136 """Write a script to connect a net interface to the proper bridge.
138 This can be used by any qemu-type hypervisor.
140 @param instance: instance we're acting on
141 @type instance: instance object
142 @param seq: nic sequence number
144 @param nic: nic we're acting on
145 @type nic: nic object
146 @return: netscript file name
151 script.write("#!/bin/sh\n")
152 script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
153 script.write("export INSTANCE=%s\n" % instance.name)
154 script.write("export MAC=%s\n" % nic.mac)
156 script.write("export IP=%s\n" % nic.ip)
157 script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
158 if nic.nicparams[constants.NIC_LINK]:
159 script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
160 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
161 script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
162 script.write("export INTERFACE=$1\n")
163 # TODO: make this configurable at ./configure time
164 script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
165 script.write(" # Execute the user-specific vif file\n")
166 script.write(" %s\n" % self._KVM_NETWORK_SCRIPT)
167 script.write("else\n")
168 script.write(" /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
169 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
170 script.write(" # Connect the interface to the bridge\n")
171 script.write(" /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
172 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
173 script.write(" # Route traffic targeted at the IP to the interface\n")
174 if nic.nicparams[constants.NIC_LINK]:
175 script.write(" while /sbin/ip rule del dev $INTERFACE; do :; done\n")
176 script.write(" /sbin/ip rule add dev $INTERFACE table $LINK\n")
177 script.write(" /sbin/ip route replace $IP table $LINK proto static"
180 script.write(" /sbin/ip route replace $IP proto static"
182 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
183 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
184 script.write(" if [ -d %s ]; then\n" % interface_v4_conf)
185 script.write(" echo 1 > %s/proxy_arp\n" % interface_v4_conf)
186 script.write(" echo 1 > %s/forwarding\n" % interface_v4_conf)
187 script.write(" fi\n")
188 script.write(" if [ -d %s ]; then\n" % interface_v6_conf)
189 script.write(" echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
190 script.write(" echo 1 > %s/forwarding\n" % interface_v6_conf)
191 script.write(" fi\n")
192 script.write("fi\n\n")
193 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
194 # mounted noexec sometimes, so we'll have to find another place.
195 (tmpfd, tmpfile_name) = tempfile.mkstemp()
196 tmpfile = os.fdopen(tmpfd, 'w')
198 tmpfile.write(script.getvalue())
201 os.chmod(tmpfile_name, 0755)
204 def ListInstances(self):
205 """Get the list of running instances.
207 We can do this by listing our live instances directory and
208 checking whether the associated kvm process is still alive.
212 for name in os.listdir(self._PIDS_DIR):
213 filename = "%s/%s" % (self._PIDS_DIR, name)
214 if utils.IsProcessAlive(utils.ReadPidFile(filename)):
218 def GetInstanceInfo(self, instance_name):
219 """Get instance properties.
221 @param instance_name: the instance name
223 @return: tuple (name, id, memory, vcpus, stat, times)
226 pidfile, pid, alive = self._InstancePidAlive(instance_name)
230 cmdline_file = "/proc/%s/cmdline" % pid
232 cmdline = utils.ReadFile(cmdline_file)
233 except EnvironmentError, err:
234 raise errors.HypervisorError("Failed to list instance %s: %s" %
235 (instance_name, err))
242 arg_list = cmdline.split('\x00')
244 arg = arg_list.pop(0)
246 memory = int(arg_list.pop(0))
248 vcpus = int(arg_list.pop(0))
250 return (instance_name, pid, memory, vcpus, stat, times)
252 def GetAllInstancesInfo(self):
253 """Get properties of all instances.
255 @return: list of tuples (name, id, memory, vcpus, stat, times)
259 for name in os.listdir(self._PIDS_DIR):
260 filename = "%s/%s" % (self._PIDS_DIR, name)
261 if utils.IsProcessAlive(utils.ReadPidFile(filename)):
263 info = self.GetInstanceInfo(name)
264 except errors.HypervisorError, err:
271 def _GenerateKVMRuntime(self, instance, block_devices):
272 """Generate KVM information to start an instance.
275 pidfile, pid, alive = self._InstancePidAlive(instance.name)
276 kvm = constants.KVM_PATH
278 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
279 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
280 kvm_cmd.extend(['-pidfile', pidfile])
281 # used just by the vnc server, if enabled
282 kvm_cmd.extend(['-name', instance.name])
283 kvm_cmd.extend(['-daemonize'])
284 if not instance.hvparams[constants.HV_ACPI]:
285 kvm_cmd.extend(['-no-acpi'])
287 hvp = instance.hvparams
288 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
289 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
290 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
293 kvm_cmd.extend(['-boot', 'n'])
295 disk_type = hvp[constants.HV_DISK_TYPE]
296 if disk_type == constants.HT_DISK_PARAVIRTUAL:
297 if_val = ',if=virtio'
299 if_val = ',if=%s' % disk_type
300 for cfdev, dev_path in block_devices:
301 if cfdev.mode != constants.DISK_RDWR:
302 raise errors.HypervisorError("Instance has read-only disks which"
303 " are not supported by KVM")
304 # TODO: handle FD_LOOP and FD_BLKTAP (?)
306 kvm_cmd.extend(['-boot', 'c'])
307 boot_val = ',boot=on'
308 # We only boot from the first disk
313 drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
314 kvm_cmd.extend(['-drive', drive_val])
316 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
318 options = ',format=raw,media=cdrom'
320 kvm_cmd.extend(['-boot', 'd'])
321 options = '%s,boot=on' % options
323 options = '%s,if=virtio' % options
324 drive_val = 'file=%s%s' % (iso_image, options)
325 kvm_cmd.extend(['-drive', drive_val])
327 kernel_path = hvp[constants.HV_KERNEL_PATH]
329 kvm_cmd.extend(['-kernel', kernel_path])
330 initrd_path = hvp[constants.HV_INITRD_PATH]
332 kvm_cmd.extend(['-initrd', initrd_path])
333 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
334 hvp[constants.HV_KERNEL_ARGS]]
335 if hvp[constants.HV_SERIAL_CONSOLE]:
336 root_append.append('console=ttyS0,38400')
337 kvm_cmd.extend(['-append', ' '.join(root_append)])
339 mouse_type = hvp[constants.HV_USB_MOUSE]
341 kvm_cmd.extend(['-usb'])
342 kvm_cmd.extend(['-usbdevice', mouse_type])
344 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
346 if utils.IsValidIP(vnc_bind_address):
347 if instance.network_port > constants.VNC_BASE_PORT:
348 display = instance.network_port - constants.VNC_BASE_PORT
349 if vnc_bind_address == '0.0.0.0':
350 vnc_arg = ':%d' % (display)
352 vnc_arg = '%s:%d' % (vnc_bind_address, display)
354 logging.error("Network port is not a valid VNC display (%d < %d)."
355 " Not starting VNC" %
356 (instance.network_port,
357 constants.VNC_BASE_PORT))
360 # Only allow tls and other option when not binding to a file, for now.
361 # kvm/qemu gets confused otherwise about the filename to use.
363 if hvp[constants.HV_VNC_TLS]:
364 vnc_append = '%s,tls' % vnc_append
365 if hvp[constants.HV_VNC_X509_VERIFY]:
366 vnc_append = '%s,x509verify=%s' % (vnc_append,
367 hvp[constants.HV_VNC_X509])
368 elif hvp[constants.HV_VNC_X509]:
369 vnc_append = '%s,x509=%s' % (vnc_append,
370 hvp[constants.HV_VNC_X509])
371 if hvp[constants.HV_VNC_PASSWORD_FILE]:
372 vnc_append = '%s,password' % vnc_append
374 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
377 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
379 kvm_cmd.extend(['-vnc', vnc_arg])
381 kvm_cmd.extend(['-nographic'])
383 monitor_dev = ("unix:%s,server,nowait" %
384 self._InstanceMonitor(instance.name))
385 kvm_cmd.extend(['-monitor', monitor_dev])
386 if hvp[constants.HV_SERIAL_CONSOLE]:
387 serial_dev = ('unix:%s,server,nowait' %
388 self._InstanceSerial(instance.name))
389 kvm_cmd.extend(['-serial', serial_dev])
391 kvm_cmd.extend(['-serial', 'none'])
393 # Save the current instance nics, but defer their expansion as parameters,
394 # as we'll need to generate executable temp files for them.
395 kvm_nics = instance.nics
398 return (kvm_cmd, kvm_nics, hvparams)
400 def _WriteKVMRuntime(self, instance_name, data):
401 """Write an instance's KVM runtime
405 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
407 except EnvironmentError, err:
408 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
410 def _ReadKVMRuntime(self, instance_name):
411 """Read an instance's KVM runtime
415 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
416 except EnvironmentError, err:
417 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
420 def _SaveKVMRuntime(self, instance, kvm_runtime):
421 """Save an instance's KVM runtime
424 kvm_cmd, kvm_nics, hvparams = kvm_runtime
425 serialized_nics = [nic.ToDict() for nic in kvm_nics]
426 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
427 self._WriteKVMRuntime(instance.name, serialized_form)
429 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
430 """Load an instance's KVM runtime
433 if not serialized_runtime:
434 serialized_runtime = self._ReadKVMRuntime(instance.name)
435 loaded_runtime = serializer.Load(serialized_runtime)
436 kvm_cmd, serialized_nics, hvparams = loaded_runtime
437 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
438 return (kvm_cmd, kvm_nics, hvparams)
440 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
441 """Execute a KVM cmd, after completing it with some last minute data
443 @type incoming: tuple of strings
444 @param incoming: (target_host_ip, port)
447 pidfile, pid, alive = self._InstancePidAlive(instance.name)
448 hvp = instance.hvparams
450 raise errors.HypervisorError("Failed to start instance %s: %s" %
451 (instance.name, "already running"))
455 kvm_cmd, kvm_nics, hvparams = kvm_runtime
458 kvm_cmd.extend(['-net', 'none'])
460 nic_type = hvparams[constants.HV_NIC_TYPE]
461 if nic_type == constants.HT_NIC_PARAVIRTUAL:
462 nic_model = "model=virtio"
464 nic_model = "model=%s" % nic_type
466 for nic_seq, nic in enumerate(kvm_nics):
467 nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
468 script = self._WriteNetScript(instance, nic_seq, nic)
469 kvm_cmd.extend(['-net', nic_val])
470 kvm_cmd.extend(['-net', 'tap,script=%s' % script])
471 temp_files.append(script)
474 target, port = incoming
475 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
477 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
481 vnc_pwd = utils.ReadFile(vnc_pwd_file)
482 except EnvironmentError, err:
483 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
484 % (vnc_pwd_file, err))
486 result = utils.RunCmd(kvm_cmd)
488 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
489 (instance.name, result.fail_reason,
492 if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
493 raise errors.HypervisorError("Failed to start instance %s: %s" %
497 change_cmd = 'change vnc password %s' % vnc_pwd
498 self._CallMonitorCommand(instance.name, change_cmd)
500 for filename in temp_files:
501 utils.RemoveFile(filename)
503 def StartInstance(self, instance, block_devices):
504 """Start an instance.
507 pidfile, pid, alive = self._InstancePidAlive(instance.name)
509 raise errors.HypervisorError("Failed to start instance %s: %s" %
510 (instance.name, "already running"))
512 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
513 self._SaveKVMRuntime(instance, kvm_runtime)
514 self._ExecuteKVMRuntime(instance, kvm_runtime)
516 def _CallMonitorCommand(self, instance_name, command):
517 """Invoke a command on the instance monitor.
520 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
521 (utils.ShellQuote(command),
522 constants.SOCAT_PATH,
523 utils.ShellQuote(self._InstanceMonitor(instance_name))))
524 result = utils.RunCmd(socat)
526 msg = ("Failed to send command '%s' to instance %s."
527 " output: %s, error: %s, fail_reason: %s" %
528 (command, instance_name,
529 result.stdout, result.stderr, result.fail_reason))
530 raise errors.HypervisorError(msg)
534 def StopInstance(self, instance, force=False, retry=False):
538 pidfile, pid, alive = self._InstancePidAlive(instance.name)
539 if pid > 0 and alive:
540 if force or not instance.hvparams[constants.HV_ACPI]:
541 utils.KillProcess(pid)
543 self._CallMonitorCommand(instance.name, 'system_powerdown')
545 if not utils.IsProcessAlive(pid):
546 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
551 def RebootInstance(self, instance):
552 """Reboot an instance.
555 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
556 # socket the instance will stop, but now power up again. So we'll resort
557 # to shutdown and restart.
558 pidfile, pid, alive = self._InstancePidAlive(instance.name)
560 raise errors.HypervisorError("Failed to reboot instance %s:"
561 " not running" % instance.name)
562 # StopInstance will delete the saved KVM runtime so:
563 # ...first load it...
564 kvm_runtime = self._LoadKVMRuntime(instance)
565 # ...now we can safely call StopInstance...
566 if not self.StopInstance(instance):
567 self.StopInstance(instance, force=True)
568 # ...and finally we can save it again, and execute it...
569 self._SaveKVMRuntime(instance, kvm_runtime)
570 self._ExecuteKVMRuntime(instance, kvm_runtime)
572 def MigrationInfo(self, instance):
573 """Get instance information to perform a migration.
575 @type instance: L{objects.Instance}
576 @param instance: instance to be migrated
578 @return: content of the KVM runtime file
581 return self._ReadKVMRuntime(instance.name)
583 def AcceptInstance(self, instance, info, target):
584 """Prepare to accept an instance.
586 @type instance: L{objects.Instance}
587 @param instance: instance to be accepted
589 @param info: content of the KVM runtime file on the source node
591 @param target: target host (usually ip), on this node
594 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
595 incoming_address = (target, constants.KVM_MIGRATION_PORT)
596 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
598 def FinalizeMigration(self, instance, info, success):
599 """Finalize an instance migration.
601 Stop the incoming mode KVM.
603 @type instance: L{objects.Instance}
604 @param instance: instance whose migration is being aborted
608 self._WriteKVMRuntime(instance.name, info)
610 self.StopInstance(instance, force=True)
612 def MigrateInstance(self, instance, target, live):
613 """Migrate an instance to a target node.
615 The migration will not be attempted if the instance is not
618 @type instance: L{objects.Instance}
619 @param instance: the instance to be migrated
621 @param target: ip address of the target node
623 @param live: perform a live migration
626 instance_name = instance.name
627 pidfile, pid, alive = self._InstancePidAlive(instance_name)
629 raise errors.HypervisorError("Instance not running, cannot migrate")
632 self._CallMonitorCommand(instance_name, 'stop')
634 migrate_command = ('migrate -d tcp:%s:%s' %
635 (target, constants.KVM_MIGRATION_PORT))
636 self._CallMonitorCommand(instance_name, migrate_command)
638 info_command = 'info migrate'
641 result = self._CallMonitorCommand(instance_name, info_command)
642 match = self._MIGRATION_STATUS_RE.search(result.stdout)
644 raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
647 status = match.group(1)
648 if status == 'completed':
650 elif status == 'active':
652 elif status == 'failed' or status == 'cancelled':
654 self._CallMonitorCommand(instance_name, 'cont')
655 raise errors.HypervisorError("Migration %s at the kvm level" %
658 logging.info("KVM: unknown migration status '%s'" % status)
661 utils.KillProcess(pid)
662 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
664 def GetNodeInfo(self):
665 """Return information about the node.
667 This is just a wrapper over the base GetLinuxNodeInfo method.
669 @return: a dict with the following keys (values in MiB):
670 - memory_total: the total memory size on the node
671 - memory_free: the available memory on the node for instances
672 - memory_dom0: the memory used by the node itself, if available
675 return self.GetLinuxNodeInfo()
678 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
679 """Return a command for connecting to the console of an instance.
682 if hvparams[constants.HV_SERIAL_CONSOLE]:
683 # FIXME: The socat shell is not perfect. In particular the way we start
684 # it ctrl+c will close it, rather than being passed to the other end.
685 # On the other hand if we pass the option 'raw' (or ignbrk=1) there
686 # will be no way of exiting socat (except killing it from another shell)
687 # and ctrl+c doesn't work anyway, printing ^C rather than being
688 # interpreted by kvm. For now we'll leave it this way, which at least
689 # allows a minimal interaction and changes on the machine.
690 shell_command = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
691 (constants.SOCAT_PATH,
692 utils.ShellQuote(cls._InstanceSerial(instance.name))))
694 shell_command = "echo 'No serial shell for instance %s'" % instance.name
696 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
698 if instance.network_port > constants.VNC_BASE_PORT:
699 display = instance.network_port - constants.VNC_BASE_PORT
700 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
701 " (display: %d)'" % (vnc_bind_address,
702 instance.network_port,
704 shell_command = "%s; %s" % (vnc_command, shell_command)
709 """Verify the hypervisor.
711 Check that the binary exists.
714 if not os.path.exists(constants.KVM_PATH):
715 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
716 if not os.path.exists(constants.SOCAT_PATH):
717 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
721 def CheckParameterSyntax(cls, hvparams):
722 """Check the given parameters for validity.
725 @param hvparams: dictionary with parameter names/value
726 @raise errors.HypervisorError: when a parameter is not valid
729 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
731 kernel_path = hvparams[constants.HV_KERNEL_PATH]
733 if not hvparams[constants.HV_ROOT_PATH]:
734 raise errors.HypervisorError("Need a root partition for the instance,"
735 " if a kernel is defined")
737 if (hvparams[constants.HV_VNC_X509_VERIFY] and
738 not hvparams[constants.HV_VNC_X509]):
739 raise errors.HypervisorError("%s must be defined, if %s is" %
740 (constants.HV_VNC_X509,
741 constants.HV_VNC_X509_VERIFY))
743 boot_order = hvparams[constants.HV_BOOT_ORDER]
745 if (boot_order == constants.HT_BO_CDROM and
746 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
747 raise errors.HypervisorError("Cannot boot from cdrom without an"
749 if (boot_order == constants.HT_BO_NETWORK and
750 hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
751 raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
752 " change the NIC type.")
755 def PowercycleNode(cls):
756 """KVM powercycle, just a wrapper over Linux powercycle.
759 cls.LinuxPowercycle()