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,
53 constants.HV_INITRD_PATH,
54 constants.HV_ROOT_PATH,
56 constants.HV_SERIAL_CONSOLE,
57 constants.HV_VNC_BIND_ADDRESS,
59 constants.HV_VNC_X509,
60 constants.HV_VNC_X509_VERIFY,
61 constants.HV_CDROM_IMAGE_PATH,
62 constants.HV_BOOT_ORDER,
63 constants.HV_NIC_TYPE,
64 constants.HV_DISK_TYPE,
65 constants.HV_USB_MOUSE,
68 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
72 hv_base.BaseHypervisor.__init__(self)
73 # Let's make sure the directories we need exist, even if the RUN_DIR lives
74 # in a tmpfs filesystem or has been otherwise wiped out.
75 for mydir in self._DIRS:
76 if not os.path.exists(mydir):
79 def _InstancePidAlive(self, instance_name):
80 """Returns the instance pid and pidfile
83 pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
84 pid = utils.ReadPidFile(pidfile)
85 alive = utils.IsProcessAlive(pid)
87 return (pidfile, pid, alive)
90 def _InstanceMonitor(cls, instance_name):
91 """Returns the instance monitor socket name
94 return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
97 def _InstanceSerial(cls, instance_name):
98 """Returns the instance serial socket name
101 return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
104 def _InstanceKVMRuntime(cls, instance_name):
105 """Returns the instance KVM runtime filename
108 return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
110 def _WriteNetScript(self, instance, seq, nic):
111 """Write a script to connect a net interface to the proper bridge.
113 This can be used by any qemu-type hypervisor.
115 @param instance: instance we're acting on
116 @type instance: instance object
117 @param seq: nic sequence number
119 @param nic: nic we're acting on
120 @type nic: nic object
121 @return: netscript file name
126 script.write("#!/bin/sh\n")
127 script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
128 script.write("export INSTANCE=%s\n" % instance.name)
129 script.write("export MAC=%s\n" % nic.mac)
130 script.write("export IP=%s\n" % nic.ip)
131 script.write("export BRIDGE=%s\n" % nic.bridge)
132 script.write("export INTERFACE=$1\n")
133 # TODO: make this configurable at ./configure time
134 script.write("if [ -x /etc/ganeti/kvm-vif-bridge ]; then\n")
135 script.write(" # Execute the user-specific vif file\n")
136 script.write(" /etc/ganeti/kvm-vif-bridge\n")
137 script.write("else\n")
138 script.write(" # Connect the interface to the bridge\n")
139 script.write(" /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
140 script.write(" /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
141 script.write("fi\n\n")
142 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
143 # mounted noexec sometimes, so we'll have to find another place.
144 (tmpfd, tmpfile_name) = tempfile.mkstemp()
145 tmpfile = os.fdopen(tmpfd, 'w')
146 tmpfile.write(script.getvalue())
148 os.chmod(tmpfile_name, 0755)
151 def ListInstances(self):
152 """Get the list of running instances.
154 We can do this by listing our live instances directory and
155 checking whether the associated kvm process is still alive.
159 for name in os.listdir(self._PIDS_DIR):
160 filename = "%s/%s" % (self._PIDS_DIR, name)
161 if utils.IsProcessAlive(utils.ReadPidFile(filename)):
165 def GetInstanceInfo(self, instance_name):
166 """Get instance properties.
168 @param instance_name: the instance name
170 @return: tuple (name, id, memory, vcpus, stat, times)
173 pidfile, pid, alive = self._InstancePidAlive(instance_name)
177 cmdline_file = "/proc/%s/cmdline" % pid
179 fh = open(cmdline_file, 'r')
184 except EnvironmentError, err:
185 raise errors.HypervisorError("Failed to list instance %s: %s" %
186 (instance_name, err))
193 arg_list = cmdline.split('\x00')
195 arg = arg_list.pop(0)
197 memory = arg_list.pop(0)
199 vcpus = arg_list.pop(0)
201 return (instance_name, pid, memory, vcpus, stat, times)
203 def GetAllInstancesInfo(self):
204 """Get properties of all instances.
206 @return: list of tuples (name, id, memory, vcpus, stat, times)
210 for name in os.listdir(self._PIDS_DIR):
211 filename = "%s/%s" % (self._PIDS_DIR, name)
212 if utils.IsProcessAlive(utils.ReadPidFile(filename)):
214 info = self.GetInstanceInfo(name)
215 except errors.HypervisorError, err:
222 def _GenerateKVMRuntime(self, instance, block_devices, extra_args):
223 """Generate KVM information to start an instance.
226 pidfile, pid, alive = self._InstancePidAlive(instance.name)
227 kvm = constants.KVM_PATH
229 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
230 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
231 kvm_cmd.extend(['-pidfile', pidfile])
232 # used just by the vnc server, if enabled
233 kvm_cmd.extend(['-name', instance.name])
234 kvm_cmd.extend(['-daemonize'])
235 if not instance.hvparams[constants.HV_ACPI]:
236 kvm_cmd.extend(['-no-acpi'])
238 boot_disk = (instance.hvparams[constants.HV_BOOT_ORDER] == "disk")
239 boot_cdrom = (instance.hvparams[constants.HV_BOOT_ORDER] == "cdrom")
240 boot_network = (instance.hvparams[constants.HV_BOOT_ORDER] == "network")
243 kvm_cmd.extend(['-boot', 'n'])
245 disk_type = instance.hvparams[constants.HV_DISK_TYPE]
246 if disk_type == constants.HT_DISK_PARAVIRTUAL:
247 if_val = ',if=virtio'
249 if_val = ',if=%s' % disk_type
250 for cfdev, dev_path in block_devices:
251 if cfdev.mode != constants.DISK_RDWR:
252 raise errors.HypervisorError("Instance has read-only disks which"
253 " are not supported by KVM")
254 # TODO: handle FD_LOOP and FD_BLKTAP (?)
256 kvm_cmd.extend(['-boot', 'c'])
257 boot_val = ',boot=on'
258 # We only boot from the first disk
263 drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
264 kvm_cmd.extend(['-drive', drive_val])
266 iso_image = instance.hvparams[constants.HV_CDROM_IMAGE_PATH]
268 options = ',format=raw,media=cdrom'
270 kvm_cmd.extend(['-boot', 'd'])
271 options = '%s,boot=on' % options
273 options = '%s,if=virtio' % options
274 drive_val = 'file=%s%s' % (iso_image, options)
275 kvm_cmd.extend(['-drive', drive_val])
277 kernel_path = instance.hvparams[constants.HV_KERNEL_PATH]
279 kvm_cmd.extend(['-kernel', kernel_path])
280 initrd_path = instance.hvparams[constants.HV_INITRD_PATH]
282 kvm_cmd.extend(['-initrd', initrd_path])
283 root_append = 'root=%s ro' % instance.hvparams[constants.HV_ROOT_PATH]
284 if instance.hvparams[constants.HV_SERIAL_CONSOLE]:
285 kvm_cmd.extend(['-append', 'console=ttyS0,38400 %s' % root_append])
287 kvm_cmd.extend(['-append', root_append])
289 mouse_type = instance.hvparams[constants.HV_USB_MOUSE]
291 kvm_cmd.extend(['-usb'])
292 kvm_cmd.extend(['-usbdevice', mouse_type])
294 # FIXME: handle vnc password
295 vnc_bind_address = instance.hvparams[constants.HV_VNC_BIND_ADDRESS]
297 if utils.IsValidIP(vnc_bind_address):
298 if instance.network_port > constants.VNC_BASE_PORT:
299 display = instance.network_port - constants.VNC_BASE_PORT
300 if vnc_bind_address == '0.0.0.0':
301 vnc_arg = ':%d' % (display)
303 vnc_arg = '%s:%d' % (constants.HV_VNC_BIND_ADDRESS, display)
305 logging.error("Network port is not a valid VNC display (%d < %d)."
306 " Not starting VNC" %
307 (instance.network_port,
308 constants.VNC_BASE_PORT))
311 # Only allow tls and other option when not binding to a file, for now.
312 # kvm/qemu gets confused otherwise about the filename to use.
314 if instance.hvparams[constants.HV_VNC_TLS]:
315 vnc_append = '%s,tls' % vnc_append
316 if instance.hvparams[constants.HV_VNC_X509_VERIFY]:
317 vnc_append = '%s,x509verify=%s' % (vnc_append,
318 instance.hvparams[constants.HV_VNC_X509])
319 elif instance.hvparams[constants.HV_VNC_X509]:
320 vnc_append = '%s,x509=%s' % (vnc_append,
321 instance.hvparams[constants.HV_VNC_X509])
322 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
325 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
327 kvm_cmd.extend(['-vnc', vnc_arg])
329 kvm_cmd.extend(['-nographic'])
331 monitor_dev = 'unix:%s,server,nowait' % \
332 self._InstanceMonitor(instance.name)
333 kvm_cmd.extend(['-monitor', monitor_dev])
334 if instance.hvparams[constants.HV_SERIAL_CONSOLE]:
335 serial_dev = 'unix:%s,server,nowait' % self._InstanceSerial(instance.name)
336 kvm_cmd.extend(['-serial', serial_dev])
338 kvm_cmd.extend(['-serial', 'none'])
340 # Save the current instance nics, but defer their expansion as parameters,
341 # as we'll need to generate executable temp files for them.
342 kvm_nics = instance.nics
343 hvparams = instance.hvparams
345 return (kvm_cmd, kvm_nics, hvparams)
347 def _WriteKVMRuntime(self, instance_name, data):
348 """Write an instance's KVM runtime
352 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
354 except EnvironmentError, err:
355 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
357 def _ReadKVMRuntime(self, instance_name):
358 """Read an instance's KVM runtime
362 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
363 except EnvironmentError, err:
364 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
367 def _SaveKVMRuntime(self, instance, kvm_runtime):
368 """Save an instance's KVM runtime
371 kvm_cmd, kvm_nics, hvparams = kvm_runtime
372 serialized_nics = [nic.ToDict() for nic in kvm_nics]
373 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
374 self._WriteKVMRuntime(instance.name, serialized_form)
376 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
377 """Load an instance's KVM runtime
380 if not serialized_runtime:
381 serialized_runtime = self._ReadKVMRuntime(instance.name)
382 loaded_runtime = serializer.Load(serialized_runtime)
383 kvm_cmd, serialized_nics, hvparams = loaded_runtime
384 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
385 return (kvm_cmd, kvm_nics, hvparams)
387 def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
388 """Execute a KVM cmd, after completing it with some last minute data
390 @type incoming: tuple of strings
391 @param incoming: (target_host_ip, port)
394 pidfile, pid, alive = self._InstancePidAlive(instance.name)
396 raise errors.HypervisorError("Failed to start instance %s: %s" %
397 (instance.name, "already running"))
401 kvm_cmd, kvm_nics, hvparams = kvm_runtime
404 kvm_cmd.extend(['-net', 'none'])
406 nic_type = hvparams[constants.HV_NIC_TYPE]
407 if nic_type == constants.HT_NIC_PARAVIRTUAL:
408 nic_model = "model=virtio"
410 nic_model = "model=%s" % nic_type
412 for nic_seq, nic in enumerate(kvm_nics):
413 nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
414 script = self._WriteNetScript(instance, nic_seq, nic)
415 kvm_cmd.extend(['-net', nic_val])
416 kvm_cmd.extend(['-net', 'tap,script=%s' % script])
417 temp_files.append(script)
420 target, port = incoming
421 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
423 result = utils.RunCmd(kvm_cmd)
425 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
426 (instance.name, result.fail_reason,
429 if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
430 raise errors.HypervisorError("Failed to start instance %s: %s" %
433 for filename in temp_files:
434 utils.RemoveFile(filename)
436 def StartInstance(self, instance, block_devices, extra_args):
437 """Start an instance.
440 pidfile, pid, alive = self._InstancePidAlive(instance.name)
442 raise errors.HypervisorError("Failed to start instance %s: %s" %
443 (instance.name, "already running"))
445 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, extra_args)
446 self._SaveKVMRuntime(instance, kvm_runtime)
447 self._ExecuteKVMRuntime(instance, kvm_runtime)
449 def _CallMonitorCommand(self, instance_name, command):
450 """Invoke a command on the instance monitor.
453 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
454 (utils.ShellQuote(command),
455 constants.SOCAT_PATH,
456 utils.ShellQuote(self._InstanceMonitor(instance_name))))
457 result = utils.RunCmd(socat)
459 msg = ("Failed to send command '%s' to instance %s."
460 " output: %s, error: %s, fail_reason: %s" %
461 (command, instance_name,
462 result.stdout, result.stderr, result.fail_reason))
463 raise errors.HypervisorError(msg)
467 def _RetryInstancePowerdown(self, instance, pid, timeout=30):
468 """Wait for an instance to power down.
471 # Wait up to $timeout seconds
472 end = time.time() + timeout
474 while time.time() < end and utils.IsProcessAlive(pid):
475 self._CallMonitorCommand(instance.name, 'system_powerdown')
477 # Make wait time longer for next try
481 def StopInstance(self, instance, force=False):
485 pidfile, pid, alive = self._InstancePidAlive(instance.name)
486 if pid > 0 and alive:
487 if force or not instance.hvparams[constants.HV_ACPI]:
488 utils.KillProcess(pid)
490 self._RetryInstancePowerdown(instance, pid)
492 if not utils.IsProcessAlive(pid):
493 utils.RemoveFile(pidfile)
494 utils.RemoveFile(self._InstanceMonitor(instance.name))
495 utils.RemoveFile(self._InstanceSerial(instance.name))
496 utils.RemoveFile(self._InstanceKVMRuntime(instance.name))
501 def RebootInstance(self, instance):
502 """Reboot an instance.
505 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
506 # socket the instance will stop, but now power up again. So we'll resort
507 # to shutdown and restart.
508 pidfile, pid, alive = self._InstancePidAlive(instance.name)
510 raise errors.HypervisorError("Failed to reboot instance %s: not running" %
512 # StopInstance will delete the saved KVM runtime so:
513 # ...first load it...
514 kvm_runtime = self._LoadKVMRuntime(instance)
515 # ...now we can safely call StopInstance...
516 if not self.StopInstance(instance):
517 self.StopInstance(instance, force=True)
518 # ...and finally we can save it again, and execute it...
519 self._SaveKVMRuntime(instance, kvm_runtime)
520 self._ExecuteKVMRuntime(instance, kvm_runtime)
522 def MigrationInfo(self, instance):
523 """Get instance information to perform a migration.
525 @type instance: L{objects.Instance}
526 @param instance: instance to be migrated
528 @return: content of the KVM runtime file
531 return self._ReadKVMRuntime(instance.name)
533 def AcceptInstance(self, instance, info, target):
534 """Prepare to accept an instance.
536 @type instance: L{objects.Instance}
537 @param instance: instance to be accepted
539 @param info: content of the KVM runtime file on the source node
541 @param target: target host (usually ip), on this node
544 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
545 incoming_address = (target, constants.KVM_MIGRATION_PORT)
546 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
548 def FinalizeMigration(self, instance, info, success):
549 """Finalize an instance migration.
551 Stop the incoming mode KVM.
553 @type instance: L{objects.Instance}
554 @param instance: instance whose migration is being aborted
558 self._WriteKVMRuntime(instance.name, info)
560 self.StopInstance(instance, force=True)
562 def MigrateInstance(self, instance_name, target, live):
563 """Migrate an instance to a target node.
565 The migration will not be attempted if the instance is not
568 @type instance_name: string
569 @param instance_name: name of the instance to be migrated
571 @param target: ip address of the target node
573 @param live: perform a live migration
576 pidfile, pid, alive = self._InstancePidAlive(instance_name)
578 raise errors.HypervisorError("Instance not running, cannot migrate")
581 self._CallMonitorCommand(instance_name, 'stop')
583 migrate_command = ('migrate -d tcp:%s:%s' %
584 (target, constants.KVM_MIGRATION_PORT))
585 self._CallMonitorCommand(instance_name, migrate_command)
587 info_command = 'info migrate'
590 result = self._CallMonitorCommand(instance_name, info_command)
591 match = self._MIGRATION_STATUS_RE.search(result.stdout)
593 raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
596 status = match.group(1)
597 if status == 'completed':
599 elif status == 'active':
601 elif status == 'failed' or status == 'cancelled':
603 self._CallMonitorCommand(instance_name, 'cont')
604 raise errors.HypervisorError("Migration %s at the kvm level" %
607 logging.info("KVM: unknown migration status '%s'" % status)
610 utils.KillProcess(pid)
611 utils.RemoveFile(pidfile)
612 utils.RemoveFile(self._InstanceMonitor(instance_name))
613 utils.RemoveFile(self._InstanceSerial(instance_name))
614 utils.RemoveFile(self._InstanceKVMRuntime(instance_name))
616 def GetNodeInfo(self):
617 """Return information about the node.
619 @return: a dict with the following keys (values in MiB):
620 - memory_total: the total memory size on the node
621 - memory_free: the available memory on the node for instances
622 - memory_dom0: the memory used by the node itself, if available
625 # global ram usage from the xm info command
628 # note: in xen 3, memory has changed to total_memory
630 fh = file("/proc/meminfo")
632 data = fh.readlines()
635 except EnvironmentError, err:
636 raise errors.HypervisorError("Failed to list node info: %s" % err)
641 splitfields = line.split(":", 1)
643 if len(splitfields) > 1:
644 key = splitfields[0].strip()
645 val = splitfields[1].strip()
646 if key == 'MemTotal':
647 result['memory_total'] = int(val.split()[0])/1024
648 elif key in ('MemFree', 'Buffers', 'Cached'):
649 sum_free += int(val.split()[0])/1024
650 elif key == 'Active':
651 result['memory_dom0'] = int(val.split()[0])/1024
652 result['memory_free'] = sum_free
656 fh = open("/proc/cpuinfo")
658 cpu_total = len(re.findall("(?m)^processor\s*:\s*[0-9]+\s*$",
662 except EnvironmentError, err:
663 raise errors.HypervisorError("Failed to list node info: %s" % err)
664 result['cpu_total'] = cpu_total
665 # FIXME: export correct data here
666 result['cpu_nodes'] = 1
667 result['cpu_sockets'] = 1
672 def GetShellCommandForConsole(cls, instance, hvparams, beparams):
673 """Return a command for connecting to the console of an instance.
676 if hvparams[constants.HV_SERIAL_CONSOLE]:
677 # FIXME: The socat shell is not perfect. In particular the way we start
678 # it ctrl+c will close it, rather than being passed to the other end.
679 # On the other hand if we pass the option 'raw' (or ignbrk=1) there
680 # will be no way of exiting socat (except killing it from another shell)
681 # and ctrl+c doesn't work anyway, printing ^C rather than being
682 # interpreted by kvm. For now we'll leave it this way, which at least
683 # allows a minimal interaction and changes on the machine.
684 shell_command = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
685 (constants.SOCAT_PATH,
686 utils.ShellQuote(cls._InstanceSerial(instance.name))))
688 shell_command = "echo 'No serial shell for instance %s'" % instance.name
690 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
692 if instance.network_port > constants.VNC_BASE_PORT:
693 display = instance.network_port - constants.VNC_BASE_PORT
694 vnc_command = ("echo 'Instance has VNC listening on %s:%d"
695 " (display: %d)'" % (vnc_bind_address,
696 instance.network_port,
698 shell_command = "%s; %s" % (vnc_command, shell_command)
703 """Verify the hypervisor.
705 Check that the binary exists.
708 if not os.path.exists(constants.KVM_PATH):
709 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
710 if not os.path.exists(constants.SOCAT_PATH):
711 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
715 def CheckParameterSyntax(cls, hvparams):
716 """Check the given parameters for validity.
719 @param hvparams: dictionary with parameter names/value
720 @raise errors.HypervisorError: when a parameter is not valid
723 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
725 kernel_path = hvparams[constants.HV_KERNEL_PATH]
727 if not os.path.isabs(hvparams[constants.HV_KERNEL_PATH]):
728 raise errors.HypervisorError("The kernel path must be an absolute path"
731 if not hvparams[constants.HV_ROOT_PATH]:
732 raise errors.HypervisorError("Need a root partition for the instance"
733 ", if a kernel is defined")
735 if hvparams[constants.HV_INITRD_PATH]:
736 if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
737 raise errors.HypervisorError("The initrd path must an absolute path"
740 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
742 if not utils.IsValidIP(vnc_bind_address):
743 if not os.path.isabs(vnc_bind_address):
744 raise errors.HypervisorError("The VNC bind address must be either"
745 " a valid IP address or an absolute"
746 " pathname. '%s' given" %
749 if hvparams[constants.HV_VNC_X509_VERIFY] and \
750 not hvparams[constants.HV_VNC_X509]:
751 raise errors.HypervisorError("%s must be defined, if %s is" %
752 (constants.HV_VNC_X509,
753 constants.HV_VNC_X509_VERIFY))
755 if hvparams[constants.HV_VNC_X509]:
756 if not os.path.isabs(hvparams[constants.HV_VNC_X509]):
757 raise errors.HypervisorError("The vnc x509 path must an absolute path"
760 iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
761 if iso_path and not os.path.isabs(iso_path):
762 raise errors.HypervisorError("The path to the CDROM image must be"
763 " an absolute path, if defined")
765 boot_order = hvparams[constants.HV_BOOT_ORDER]
766 if boot_order not in ('cdrom', 'disk', 'network'):
767 raise errors.HypervisorError("The boot order must be 'cdrom', 'disk' or"
770 if boot_order == 'cdrom' and not iso_path:
771 raise errors.HypervisorError("Cannot boot from cdrom without an ISO path")
773 nic_type = hvparams[constants.HV_NIC_TYPE]
774 if nic_type not in constants.HT_KVM_VALID_NIC_TYPES:
775 raise errors.HypervisorError("Invalid NIC type %s specified for the KVM"
776 " hypervisor. Please choose one of: %s" %
778 constants.HT_KVM_VALID_NIC_TYPES))
779 elif boot_order == 'network' and nic_type == constants.HT_NIC_PARAVIRTUAL:
780 raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
781 " change the nic type.")
783 disk_type = hvparams[constants.HV_DISK_TYPE]
784 if disk_type not in constants.HT_KVM_VALID_DISK_TYPES:
785 raise errors.HypervisorError("Invalid disk type %s specified for the KVM"
786 " hypervisor. Please choose one of: %s" %
788 constants.HT_KVM_VALID_DISK_TYPES))
790 mouse_type = hvparams[constants.HV_USB_MOUSE]
791 if mouse_type and mouse_type not in ('mouse', 'tablet'):
792 raise errors.HypervisorError("Invalid usb mouse type %s specified for"
793 " the KVM hyervisor. Please choose"
794 " 'mouse' or 'tablet'" % mouse_type)
796 def ValidateParameters(self, hvparams):
797 """Check the given parameters for validity.
799 For the KVM hypervisor, this checks the existence of the
803 super(KVMHypervisor, self).ValidateParameters(hvparams)
805 kernel_path = hvparams[constants.HV_KERNEL_PATH]
806 if kernel_path and not os.path.isfile(kernel_path):
807 raise errors.HypervisorError("Instance kernel '%s' not found or"
808 " not a file" % kernel_path)
809 initrd_path = hvparams[constants.HV_INITRD_PATH]
810 if initrd_path and not os.path.isfile(initrd_path):
811 raise errors.HypervisorError("Instance initrd '%s' not found or"
812 " not a file" % initrd_path)
814 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
815 if vnc_bind_address and not utils.IsValidIP(vnc_bind_address) and \
816 not os.path.isdir(vnc_bind_address):
817 raise errors.HypervisorError("Instance vnc bind address must be either"
818 " an ip address or an existing directory")
820 vnc_x509 = hvparams[constants.HV_VNC_X509]
821 if vnc_x509 and not os.path.isdir(vnc_x509):
822 raise errors.HypervisorError("Instance vnc x509 path '%s' not found"
823 " or not a directory" % vnc_x509)
825 iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
826 if iso_path and not os.path.isfile(iso_path):
827 raise errors.HypervisorError("Instance cdrom image '%s' not found or"
828 " not a file" % iso_path)