Adding a wrapper around connecting to kvm console
[ganeti-local] / lib / hypervisor / hv_kvm.py
1 #
2 #
3
4 # Copyright (C) 2008, 2009, 2010, 2011 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21
22 """KVM hypervisor
23
24 """
25
26 import errno
27 import os
28 import os.path
29 import re
30 import tempfile
31 import time
32 import logging
33 import pwd
34 import struct
35 import fcntl
36 import shutil
37
38 from ganeti import utils
39 from ganeti import constants
40 from ganeti import errors
41 from ganeti import serializer
42 from ganeti import objects
43 from ganeti import uidpool
44 from ganeti import ssconf
45 from ganeti.hypervisor import hv_base
46 from ganeti import netutils
47 from ganeti.utils import wrapper as utils_wrapper
48
49
50 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
51
52 # TUN/TAP driver constants, taken from <linux/if_tun.h>
53 # They are architecture-independent and already hardcoded in qemu-kvm source,
54 # so we can safely include them here.
55 TUNSETIFF = 0x400454ca
56 TUNGETIFF = 0x800454d2
57 TUNGETFEATURES = 0x800454cf
58 IFF_TAP = 0x0002
59 IFF_NO_PI = 0x1000
60 IFF_VNET_HDR = 0x4000
61
62
63 def _ProbeTapVnetHdr(fd):
64   """Check whether to enable the IFF_VNET_HDR flag.
65
66   To do this, _all_ of the following conditions must be met:
67    1. TUNGETFEATURES ioctl() *must* be implemented
68    2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
69    3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
70       drivers/net/tun.c there is no way to test this until after the tap device
71       has been created using TUNSETIFF, and there is no way to change the
72       IFF_VNET_HDR flag after creating the interface, catch-22! However both
73       TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
74       thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
75
76    @type fd: int
77    @param fd: the file descriptor of /dev/net/tun
78
79   """
80   req = struct.pack("I", 0)
81   try:
82     res = fcntl.ioctl(fd, TUNGETFEATURES, req)
83   except EnvironmentError:
84     logging.warning("TUNGETFEATURES ioctl() not implemented")
85     return False
86
87   tunflags = struct.unpack("I", res)[0]
88   if tunflags & IFF_VNET_HDR:
89     return True
90   else:
91     logging.warning("Host does not support IFF_VNET_HDR, not enabling")
92     return False
93
94
95 def _OpenTap(vnet_hdr=True):
96   """Open a new tap device and return its file descriptor.
97
98   This is intended to be used by a qemu-type hypervisor together with the -net
99   tap,fd=<fd> command line parameter.
100
101   @type vnet_hdr: boolean
102   @param vnet_hdr: Enable the VNET Header
103   @return: (ifname, tapfd)
104   @rtype: tuple
105
106   """
107   try:
108     tapfd = os.open("/dev/net/tun", os.O_RDWR)
109   except EnvironmentError:
110     raise errors.HypervisorError("Failed to open /dev/net/tun")
111
112   flags = IFF_TAP | IFF_NO_PI
113
114   if vnet_hdr and _ProbeTapVnetHdr(tapfd):
115     flags |= IFF_VNET_HDR
116
117   # The struct ifreq ioctl request (see netdevice(7))
118   ifr = struct.pack("16sh", "", flags)
119
120   try:
121     res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
122   except EnvironmentError:
123     raise errors.HypervisorError("Failed to allocate a new TAP device")
124
125   # Get the interface name from the ioctl
126   ifname = struct.unpack("16sh", res)[0].strip("\x00")
127   return (ifname, tapfd)
128
129
130 class KVMHypervisor(hv_base.BaseHypervisor):
131   """KVM hypervisor interface"""
132   CAN_MIGRATE = True
133
134   _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
135   _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
136   _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
137   _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
138   _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
139   _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
140   # KVM instances with chroot enabled are started in empty chroot directories.
141   _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
142   # After an instance is stopped, its chroot directory is removed.
143   # If the chroot directory is not empty, it can't be removed.
144   # A non-empty chroot directory indicates a possible security incident.
145   # To support forensics, the non-empty chroot directory is quarantined in
146   # a separate directory, called 'chroot-quarantine'.
147   _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
148   _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
149            _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
150
151   PARAMETERS = {
152     constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
153     constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
154     constants.HV_ROOT_PATH: hv_base.NO_CHECK,
155     constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
156     constants.HV_ACPI: hv_base.NO_CHECK,
157     constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
158     constants.HV_VNC_BIND_ADDRESS:
159       (False, lambda x: (netutils.IP4Address.IsValid(x) or
160                          utils.IsNormAbsPath(x)),
161        "the VNC bind address must be either a valid IP address or an absolute"
162        " pathname", None, None),
163     constants.HV_VNC_TLS: hv_base.NO_CHECK,
164     constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
165     constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
166     constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
167     constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
168     constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
169     constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
170     constants.HV_BOOT_ORDER:
171       hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
172     constants.HV_NIC_TYPE:
173       hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
174     constants.HV_DISK_TYPE:
175       hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
176     constants.HV_KVM_CDROM_DISK_TYPE:
177       hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
178     constants.HV_USB_MOUSE:
179       hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
180     constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
181     constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
182     constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
183     constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
184     constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
185     constants.HV_DISK_CACHE:
186       hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
187     constants.HV_SECURITY_MODEL:
188       hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
189     constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
190     constants.HV_KVM_FLAG:
191       hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
192     constants.HV_VHOST_NET: hv_base.NO_CHECK,
193     constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
194     constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
195     }
196
197   _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
198                                     re.M | re.I)
199   _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
200   _MIGRATION_INFO_RETRY_DELAY = 2
201
202   _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
203
204   ANCILLARY_FILES = [
205     _KVM_NETWORK_SCRIPT,
206     ]
207
208   def __init__(self):
209     hv_base.BaseHypervisor.__init__(self)
210     # Let's make sure the directories we need exist, even if the RUN_DIR lives
211     # in a tmpfs filesystem or has been otherwise wiped out.
212     dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
213     utils.EnsureDirs(dirs)
214
215   @classmethod
216   def _InstancePidFile(cls, instance_name):
217     """Returns the instance pidfile.
218
219     """
220     return utils.PathJoin(cls._PIDS_DIR, instance_name)
221
222   @classmethod
223   def _InstanceUidFile(cls, instance_name):
224     """Returns the instance uidfile.
225
226     """
227     return utils.PathJoin(cls._UIDS_DIR, instance_name)
228
229   @classmethod
230   def _InstancePidInfo(cls, pid):
231     """Check pid file for instance information.
232
233     Check that a pid file is associated with an instance, and retrieve
234     information from its command line.
235
236     @type pid: string or int
237     @param pid: process id of the instance to check
238     @rtype: tuple
239     @return: (instance_name, memory, vcpus)
240     @raise errors.HypervisorError: when an instance cannot be found
241
242     """
243     alive = utils.IsProcessAlive(pid)
244     if not alive:
245       raise errors.HypervisorError("Cannot get info for pid %s" % pid)
246
247     cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
248     try:
249       cmdline = utils.ReadFile(cmdline_file)
250     except EnvironmentError, err:
251       raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
252                                    (pid, err))
253
254     instance = None
255     memory = 0
256     vcpus = 0
257
258     arg_list = cmdline.split('\x00')
259     while arg_list:
260       arg =  arg_list.pop(0)
261       if arg == "-name":
262         instance = arg_list.pop(0)
263       elif arg == "-m":
264         memory = int(arg_list.pop(0))
265       elif arg == "-smp":
266         vcpus = int(arg_list.pop(0))
267
268     if instance is None:
269       raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
270                                    " instance" % pid)
271
272     return (instance, memory, vcpus)
273
274   def _InstancePidAlive(self, instance_name):
275     """Returns the instance pidfile, pid, and liveness.
276
277     @type instance_name: string
278     @param instance_name: instance name
279     @rtype: tuple
280     @return: (pid file name, pid, liveness)
281
282     """
283     pidfile = self._InstancePidFile(instance_name)
284     pid = utils.ReadPidFile(pidfile)
285
286     alive = False
287     try:
288       cmd_instance = self._InstancePidInfo(pid)[0]
289       alive = (cmd_instance == instance_name)
290     except errors.HypervisorError:
291       pass
292
293     return (pidfile, pid, alive)
294
295   def _CheckDown(self, instance_name):
296     """Raises an error unless the given instance is down.
297
298     """
299     alive = self._InstancePidAlive(instance_name)[2]
300     if alive:
301       raise errors.HypervisorError("Failed to start instance %s: %s" %
302                                    (instance_name, "already running"))
303
304   @classmethod
305   def _InstanceMonitor(cls, instance_name):
306     """Returns the instance monitor socket name
307
308     """
309     return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
310
311   @classmethod
312   def _InstanceSerial(cls, instance_name):
313     """Returns the instance serial socket name
314
315     """
316     return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
317
318   @staticmethod
319   def _SocatUnixConsoleParams():
320     """Returns the correct parameters for socat
321
322     If we have a new-enough socat we can use raw mode with an escape character.
323
324     """
325     if constants.SOCAT_USE_ESCAPE:
326       return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
327     else:
328       return "echo=0,icanon=0"
329
330   @classmethod
331   def _InstanceKVMRuntime(cls, instance_name):
332     """Returns the instance KVM runtime filename
333
334     """
335     return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
336
337   @classmethod
338   def _InstanceChrootDir(cls, instance_name):
339     """Returns the name of the KVM chroot dir of the instance
340
341     """
342     return utils.PathJoin(cls._CHROOT_DIR, instance_name)
343
344   @classmethod
345   def _InstanceNICDir(cls, instance_name):
346     """Returns the name of the directory holding the tap device files for a
347     given instance.
348
349     """
350     return utils.PathJoin(cls._NICS_DIR, instance_name)
351
352   @classmethod
353   def _InstanceNICFile(cls, instance_name, seq):
354     """Returns the name of the file containing the tap device for a given NIC
355
356     """
357     return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
358
359   @classmethod
360   def _TryReadUidFile(cls, uid_file):
361     """Try to read a uid file
362
363     """
364     if os.path.exists(uid_file):
365       try:
366         uid = int(utils.ReadOneLineFile(uid_file))
367         return uid
368       except EnvironmentError:
369         logging.warning("Can't read uid file", exc_info=True)
370       except (TypeError, ValueError):
371         logging.warning("Can't parse uid file contents", exc_info=True)
372     return None
373
374   @classmethod
375   def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
376     """Removes an instance's rutime sockets/files/dirs.
377
378     """
379     utils.RemoveFile(pidfile)
380     utils.RemoveFile(cls._InstanceMonitor(instance_name))
381     utils.RemoveFile(cls._InstanceSerial(instance_name))
382     utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
383     uid_file = cls._InstanceUidFile(instance_name)
384     uid = cls._TryReadUidFile(uid_file)
385     utils.RemoveFile(uid_file)
386     if uid is not None:
387       uidpool.ReleaseUid(uid)
388     try:
389       shutil.rmtree(cls._InstanceNICDir(instance_name))
390     except OSError, err:
391       if err.errno != errno.ENOENT:
392         raise
393     try:
394       chroot_dir = cls._InstanceChrootDir(instance_name)
395       utils.RemoveDir(chroot_dir)
396     except OSError, err:
397       if err.errno == errno.ENOTEMPTY:
398         # The chroot directory is expected to be empty, but it isn't.
399         new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
400                                           prefix="%s-%s-" %
401                                           (instance_name,
402                                            utils.TimestampForFilename()))
403         logging.warning("The chroot directory of instance %s can not be"
404                         " removed as it is not empty. Moving it to the"
405                         " quarantine instead. Please investigate the"
406                         " contents (%s) and clean up manually",
407                         instance_name, new_chroot_dir)
408         utils.RenameFile(chroot_dir, new_chroot_dir)
409       else:
410         raise
411
412   @staticmethod
413   def _ConfigureNIC(instance, seq, nic, tap):
414     """Run the network configuration script for a specified NIC
415
416     @param instance: instance we're acting on
417     @type instance: instance object
418     @param seq: nic sequence number
419     @type seq: int
420     @param nic: nic we're acting on
421     @type nic: nic object
422     @param tap: the host's tap interface this NIC corresponds to
423     @type tap: str
424
425     """
426
427     if instance.tags:
428       tags = " ".join(instance.tags)
429     else:
430       tags = ""
431
432     env = {
433       "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
434       "INSTANCE": instance.name,
435       "MAC": nic.mac,
436       "MODE": nic.nicparams[constants.NIC_MODE],
437       "INTERFACE": tap,
438       "INTERFACE_INDEX": str(seq),
439       "TAGS": tags,
440     }
441
442     if nic.ip:
443       env["IP"] = nic.ip
444
445     if nic.nicparams[constants.NIC_LINK]:
446       env["LINK"] = nic.nicparams[constants.NIC_LINK]
447
448     if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
449       env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
450
451     result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
452     if result.failed:
453       raise errors.HypervisorError("Failed to configure interface %s: %s."
454                                    " Network configuration script output: %s" %
455                                    (tap, result.fail_reason, result.output))
456
457   def ListInstances(self):
458     """Get the list of running instances.
459
460     We can do this by listing our live instances directory and
461     checking whether the associated kvm process is still alive.
462
463     """
464     result = []
465     for name in os.listdir(self._PIDS_DIR):
466       if self._InstancePidAlive(name)[2]:
467         result.append(name)
468     return result
469
470   def GetInstanceInfo(self, instance_name):
471     """Get instance properties.
472
473     @type instance_name: string
474     @param instance_name: the instance name
475     @rtype: tuple of strings
476     @return: (name, id, memory, vcpus, stat, times)
477
478     """
479     _, pid, alive = self._InstancePidAlive(instance_name)
480     if not alive:
481       return None
482
483     _, memory, vcpus = self._InstancePidInfo(pid)
484     stat = "---b-"
485     times = "0"
486
487     return (instance_name, pid, memory, vcpus, stat, times)
488
489   def GetAllInstancesInfo(self):
490     """Get properties of all instances.
491
492     @return: list of tuples (name, id, memory, vcpus, stat, times)
493
494     """
495     data = []
496     for name in os.listdir(self._PIDS_DIR):
497       try:
498         info = self.GetInstanceInfo(name)
499       except errors.HypervisorError:
500         continue
501       if info:
502         data.append(info)
503     return data
504
505   def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
506     """Generate KVM information to start an instance.
507
508     """
509     kvm_version = self._GetKVMVersion()
510     if kvm_version:
511       _, v_major, v_min, _ = kvm_version
512     else:
513       raise errors.HypervisorError("Unable to get KVM version")
514
515     pidfile  = self._InstancePidFile(instance.name)
516     kvm = constants.KVM_PATH
517     kvm_cmd = [kvm]
518     # used just by the vnc server, if enabled
519     kvm_cmd.extend(['-name', instance.name])
520     kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
521     kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
522     kvm_cmd.extend(['-pidfile', pidfile])
523     kvm_cmd.extend(['-daemonize'])
524     if not instance.hvparams[constants.HV_ACPI]:
525       kvm_cmd.extend(['-no-acpi'])
526     if startup_paused:
527       kvm_cmd.extend(['-S'])
528
529     hvp = instance.hvparams
530     boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
531     boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
532     boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
533     boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
534
535     if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
536       kvm_cmd.extend(["-enable-kvm"])
537     elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
538       kvm_cmd.extend(["-disable-kvm"])
539
540     if boot_network:
541       kvm_cmd.extend(['-boot', 'n'])
542
543     disk_type = hvp[constants.HV_DISK_TYPE]
544     if disk_type == constants.HT_DISK_PARAVIRTUAL:
545       if_val = ',if=virtio'
546     else:
547       if_val = ',if=%s' % disk_type
548     # Cache mode
549     disk_cache = hvp[constants.HV_DISK_CACHE]
550     if instance.disk_template in constants.DTS_EXT_MIRROR:
551       if disk_cache != "none":
552         # TODO: make this a hard error, instead of a silent overwrite
553         logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
554                         " to prevent shared storage corruption on migration",
555                         disk_cache)
556       cache_val = ",cache=none"
557     elif disk_cache != constants.HT_CACHE_DEFAULT:
558       cache_val = ",cache=%s" % disk_cache
559     else:
560       cache_val = ""
561     for cfdev, dev_path in block_devices:
562       if cfdev.mode != constants.DISK_RDWR:
563         raise errors.HypervisorError("Instance has read-only disks which"
564                                      " are not supported by KVM")
565       # TODO: handle FD_LOOP and FD_BLKTAP (?)
566       boot_val = ""
567       if boot_disk:
568         kvm_cmd.extend(['-boot', 'c'])
569         boot_disk = False
570         if (v_major, v_min) < (0, 14) and disk_type != constants.HT_DISK_IDE:
571           boot_val = ",boot=on"
572
573       drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
574                                                 cache_val)
575       kvm_cmd.extend(['-drive', drive_val])
576
577     #Now we can specify a different device type for CDROM devices.
578     cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
579     if not cdrom_disk_type:
580       cdrom_disk_type = disk_type
581
582     iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
583     if iso_image:
584       options = ',format=raw,media=cdrom'
585       if boot_cdrom:
586         kvm_cmd.extend(['-boot', 'd'])
587         if cdrom_disk_type != constants.HT_DISK_IDE:
588           options = '%s,boot=on,if=%s' % (options, constants.HT_DISK_IDE)
589         else:
590           options = '%s,boot=on' % options
591       else:
592         if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
593           if_val = ',if=virtio'
594         else:
595           if_val = ',if=%s' % cdrom_disk_type
596         options = '%s%s' % (options, if_val)
597       drive_val = 'file=%s%s' % (iso_image, options)
598       kvm_cmd.extend(['-drive', drive_val])
599
600     iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
601     if iso_image2:
602       options = ',format=raw,media=cdrom'
603       if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
604         if_val = ',if=virtio'
605       else:
606         if_val = ',if=%s' % cdrom_disk_type
607       options = '%s%s' % (options, if_val)
608       drive_val = 'file=%s%s' % (iso_image2, options)
609       kvm_cmd.extend(['-drive', drive_val])
610
611     floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
612     if floppy_image:
613       options = ',format=raw,media=disk'
614       if boot_floppy:
615         kvm_cmd.extend(['-boot', 'a'])
616         options = '%s,boot=on' % options
617       if_val = ',if=floppy'
618       options = '%s%s' % (options, if_val)
619       drive_val = 'file=%s%s' % (floppy_image, options)
620       kvm_cmd.extend(['-drive', drive_val])
621
622     kernel_path = hvp[constants.HV_KERNEL_PATH]
623     if kernel_path:
624       kvm_cmd.extend(['-kernel', kernel_path])
625       initrd_path = hvp[constants.HV_INITRD_PATH]
626       if initrd_path:
627         kvm_cmd.extend(['-initrd', initrd_path])
628       root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
629                      hvp[constants.HV_KERNEL_ARGS]]
630       if hvp[constants.HV_SERIAL_CONSOLE]:
631         root_append.append('console=ttyS0,38400')
632       kvm_cmd.extend(['-append', ' '.join(root_append)])
633
634     mem_path = hvp[constants.HV_MEM_PATH]
635     if mem_path:
636       kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
637
638     mouse_type = hvp[constants.HV_USB_MOUSE]
639     vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
640
641     if mouse_type:
642       kvm_cmd.extend(['-usb'])
643       kvm_cmd.extend(['-usbdevice', mouse_type])
644     elif vnc_bind_address:
645       kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
646
647     if vnc_bind_address:
648       if netutils.IP4Address.IsValid(vnc_bind_address):
649         if instance.network_port > constants.VNC_BASE_PORT:
650           display = instance.network_port - constants.VNC_BASE_PORT
651           if vnc_bind_address == constants.IP4_ADDRESS_ANY:
652             vnc_arg = ':%d' % (display)
653           else:
654             vnc_arg = '%s:%d' % (vnc_bind_address, display)
655         else:
656           logging.error("Network port is not a valid VNC display (%d < %d)."
657                         " Not starting VNC", instance.network_port,
658                         constants.VNC_BASE_PORT)
659           vnc_arg = 'none'
660
661         # Only allow tls and other option when not binding to a file, for now.
662         # kvm/qemu gets confused otherwise about the filename to use.
663         vnc_append = ''
664         if hvp[constants.HV_VNC_TLS]:
665           vnc_append = '%s,tls' % vnc_append
666           if hvp[constants.HV_VNC_X509_VERIFY]:
667             vnc_append = '%s,x509verify=%s' % (vnc_append,
668                                                hvp[constants.HV_VNC_X509])
669           elif hvp[constants.HV_VNC_X509]:
670             vnc_append = '%s,x509=%s' % (vnc_append,
671                                          hvp[constants.HV_VNC_X509])
672         if hvp[constants.HV_VNC_PASSWORD_FILE]:
673           vnc_append = '%s,password' % vnc_append
674
675         vnc_arg = '%s%s' % (vnc_arg, vnc_append)
676
677       else:
678         vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
679
680       kvm_cmd.extend(['-vnc', vnc_arg])
681     else:
682       kvm_cmd.extend(['-nographic'])
683
684     monitor_dev = ("unix:%s,server,nowait" %
685                    self._InstanceMonitor(instance.name))
686     kvm_cmd.extend(['-monitor', monitor_dev])
687     if hvp[constants.HV_SERIAL_CONSOLE]:
688       serial_dev = ('unix:%s,server,nowait' %
689                     self._InstanceSerial(instance.name))
690       kvm_cmd.extend(['-serial', serial_dev])
691     else:
692       kvm_cmd.extend(['-serial', 'none'])
693
694     if hvp[constants.HV_USE_LOCALTIME]:
695       kvm_cmd.extend(['-localtime'])
696
697     if hvp[constants.HV_KVM_USE_CHROOT]:
698       kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
699
700     # Save the current instance nics, but defer their expansion as parameters,
701     # as we'll need to generate executable temp files for them.
702     kvm_nics = instance.nics
703     hvparams = hvp
704
705     return (kvm_cmd, kvm_nics, hvparams)
706
707   def _WriteKVMRuntime(self, instance_name, data):
708     """Write an instance's KVM runtime
709
710     """
711     try:
712       utils.WriteFile(self._InstanceKVMRuntime(instance_name),
713                       data=data)
714     except EnvironmentError, err:
715       raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
716
717   def _ReadKVMRuntime(self, instance_name):
718     """Read an instance's KVM runtime
719
720     """
721     try:
722       file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
723     except EnvironmentError, err:
724       raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
725     return file_content
726
727   def _SaveKVMRuntime(self, instance, kvm_runtime):
728     """Save an instance's KVM runtime
729
730     """
731     kvm_cmd, kvm_nics, hvparams = kvm_runtime
732     serialized_nics = [nic.ToDict() for nic in kvm_nics]
733     serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
734     self._WriteKVMRuntime(instance.name, serialized_form)
735
736   def _LoadKVMRuntime(self, instance, serialized_runtime=None):
737     """Load an instance's KVM runtime
738
739     """
740     if not serialized_runtime:
741       serialized_runtime = self._ReadKVMRuntime(instance.name)
742     loaded_runtime = serializer.Load(serialized_runtime)
743     kvm_cmd, serialized_nics, hvparams = loaded_runtime
744     kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
745     return (kvm_cmd, kvm_nics, hvparams)
746
747   def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
748     """Run the KVM cmd and check for errors
749
750     @type name: string
751     @param name: instance name
752     @type kvm_cmd: list of strings
753     @param kvm_cmd: runcmd input for kvm
754     @type tap_fds: list of int
755     @param tap_fds: fds of tap devices opened by Ganeti
756
757     """
758     try:
759       result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
760     finally:
761       for fd in tap_fds:
762         utils_wrapper.CloseFdNoError(fd)
763
764     if result.failed:
765       raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
766                                    (name, result.fail_reason, result.output))
767     if not self._InstancePidAlive(name)[2]:
768       raise errors.HypervisorError("Failed to start instance %s" % name)
769
770   def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
771     """Execute a KVM cmd, after completing it with some last minute data
772
773     @type incoming: tuple of strings
774     @param incoming: (target_host_ip, port)
775
776     """
777     # Small _ExecuteKVMRuntime hv parameters programming howto:
778     #  - conf_hvp contains the parameters as configured on ganeti. they might
779     #    have changed since the instance started; only use them if the change
780     #    won't affect the inside of the instance (which hasn't been rebooted).
781     #  - up_hvp contains the parameters as they were when the instance was
782     #    started, plus any new parameter which has been added between ganeti
783     #    versions: it is paramount that those default to a value which won't
784     #    affect the inside of the instance as well.
785     conf_hvp = instance.hvparams
786     name = instance.name
787     self._CheckDown(name)
788
789     temp_files = []
790
791     kvm_cmd, kvm_nics, up_hvp = kvm_runtime
792     up_hvp = objects.FillDict(conf_hvp, up_hvp)
793
794     kvm_version = self._GetKVMVersion()
795     if kvm_version:
796       _, v_major, v_min, _ = kvm_version
797     else:
798       raise errors.HypervisorError("Unable to get KVM version")
799
800     # We know it's safe to run as a different user upon migration, so we'll use
801     # the latest conf, from conf_hvp.
802     security_model = conf_hvp[constants.HV_SECURITY_MODEL]
803     if security_model == constants.HT_SM_USER:
804       kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
805
806     # We have reasons to believe changing something like the nic driver/type
807     # upon migration won't exactly fly with the instance kernel, so for nic
808     # related parameters we'll use up_hvp
809     tapfds = []
810     taps = []
811     if not kvm_nics:
812       kvm_cmd.extend(["-net", "none"])
813     else:
814       vnet_hdr = False
815       tap_extra = ""
816       nic_type = up_hvp[constants.HV_NIC_TYPE]
817       if nic_type == constants.HT_NIC_PARAVIRTUAL:
818         # From version 0.12.0, kvm uses a new sintax for network configuration.
819         if (v_major, v_min) >= (0, 12):
820           nic_model = "virtio-net-pci"
821           vnet_hdr = True
822         else:
823           nic_model = "virtio"
824
825         if up_hvp[constants.HV_VHOST_NET]:
826           # vhost_net is only available from version 0.13.0 or newer
827           if (v_major, v_min) >= (0, 13):
828             tap_extra = ",vhost=on"
829           else:
830             raise errors.HypervisorError("vhost_net is configured"
831                                         " but it is not available")
832       else:
833         nic_model = nic_type
834
835       for nic_seq, nic in enumerate(kvm_nics):
836         tapname, tapfd = _OpenTap(vnet_hdr)
837         tapfds.append(tapfd)
838         taps.append(tapname)
839         if (v_major, v_min) >= (0, 12):
840           nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
841           tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
842           kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
843         else:
844           nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
845                                                          nic.mac, nic_model)
846           tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
847           kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
848
849     if incoming:
850       target, port = incoming
851       kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
852
853     # Changing the vnc password doesn't bother the guest that much. At most it
854     # will surprise people who connect to it. Whether positively or negatively
855     # it's debatable.
856     vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
857     vnc_pwd = None
858     if vnc_pwd_file:
859       try:
860         vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
861       except EnvironmentError, err:
862         raise errors.HypervisorError("Failed to open VNC password file %s: %s"
863                                      % (vnc_pwd_file, err))
864
865     if conf_hvp[constants.HV_KVM_USE_CHROOT]:
866       utils.EnsureDirs([(self._InstanceChrootDir(name),
867                          constants.SECURE_DIR_MODE)])
868
869     # Configure the network now for starting instances and bridged interfaces,
870     # during FinalizeMigration for incoming instances' routed interfaces
871     for nic_seq, nic in enumerate(kvm_nics):
872       if (incoming and
873           nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
874         continue
875       self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
876
877     if security_model == constants.HT_SM_POOL:
878       ss = ssconf.SimpleStore()
879       uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
880       all_uids = set(uidpool.ExpandUidPool(uid_pool))
881       uid = uidpool.RequestUnusedUid(all_uids)
882       try:
883         username = pwd.getpwuid(uid.GetUid()).pw_name
884         kvm_cmd.extend(["-runas", username])
885         self._RunKVMCmd(name, kvm_cmd, tapfds)
886       except:
887         uidpool.ReleaseUid(uid)
888         raise
889       else:
890         uid.Unlock()
891         utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
892     else:
893       self._RunKVMCmd(name, kvm_cmd, tapfds)
894
895     utils.EnsureDirs([(self._InstanceNICDir(instance.name),
896                      constants.RUN_DIRS_MODE)])
897     for nic_seq, tap in enumerate(taps):
898       utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
899                       data=tap)
900
901     if vnc_pwd:
902       change_cmd = 'change vnc password %s' % vnc_pwd
903       self._CallMonitorCommand(instance.name, change_cmd)
904
905     for filename in temp_files:
906       utils.RemoveFile(filename)
907
908   def StartInstance(self, instance, block_devices, startup_paused):
909     """Start an instance.
910
911     """
912     self._CheckDown(instance.name)
913     kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
914                                            startup_paused)
915     self._SaveKVMRuntime(instance, kvm_runtime)
916     self._ExecuteKVMRuntime(instance, kvm_runtime)
917
918   def _CallMonitorCommand(self, instance_name, command):
919     """Invoke a command on the instance monitor.
920
921     """
922     socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
923              (utils.ShellQuote(command),
924               constants.SOCAT_PATH,
925               utils.ShellQuote(self._InstanceMonitor(instance_name))))
926     result = utils.RunCmd(socat)
927     if result.failed:
928       msg = ("Failed to send command '%s' to instance %s."
929              " output: %s, error: %s, fail_reason: %s" %
930              (command, instance_name,
931               result.stdout, result.stderr, result.fail_reason))
932       raise errors.HypervisorError(msg)
933
934     return result
935
936   @classmethod
937   def _GetKVMVersion(cls):
938     """Return the installed KVM version
939
940     @return: (version, v_maj, v_min, v_rev), or None
941
942     """
943     result = utils.RunCmd([constants.KVM_PATH, "--help"])
944     if result.failed:
945       return None
946     match = cls._VERSION_RE.search(result.output.splitlines()[0])
947     if not match:
948       return None
949
950     return (match.group(0), int(match.group(1)), int(match.group(2)),
951             int(match.group(3)))
952
953   def StopInstance(self, instance, force=False, retry=False, name=None):
954     """Stop an instance.
955
956     """
957     if name is not None and not force:
958       raise errors.HypervisorError("Cannot shutdown cleanly by name only")
959     if name is None:
960       name = instance.name
961       acpi = instance.hvparams[constants.HV_ACPI]
962     else:
963       acpi = False
964     _, pid, alive = self._InstancePidAlive(name)
965     if pid > 0 and alive:
966       if force or not acpi:
967         utils.KillProcess(pid)
968       else:
969         self._CallMonitorCommand(name, 'system_powerdown')
970
971   def CleanupInstance(self, instance_name):
972     """Cleanup after a stopped instance
973
974     """
975     pidfile, pid, alive = self._InstancePidAlive(instance_name)
976     if pid > 0 and alive:
977       raise errors.HypervisorError("Cannot cleanup a live instance")
978     self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
979
980   def RebootInstance(self, instance):
981     """Reboot an instance.
982
983     """
984     # For some reason if we do a 'send-key ctrl-alt-delete' to the control
985     # socket the instance will stop, but now power up again. So we'll resort
986     # to shutdown and restart.
987     _, _, alive = self._InstancePidAlive(instance.name)
988     if not alive:
989       raise errors.HypervisorError("Failed to reboot instance %s:"
990                                    " not running" % instance.name)
991     # StopInstance will delete the saved KVM runtime so:
992     # ...first load it...
993     kvm_runtime = self._LoadKVMRuntime(instance)
994     # ...now we can safely call StopInstance...
995     if not self.StopInstance(instance):
996       self.StopInstance(instance, force=True)
997     # ...and finally we can save it again, and execute it...
998     self._SaveKVMRuntime(instance, kvm_runtime)
999     self._ExecuteKVMRuntime(instance, kvm_runtime)
1000
1001   def MigrationInfo(self, instance):
1002     """Get instance information to perform a migration.
1003
1004     @type instance: L{objects.Instance}
1005     @param instance: instance to be migrated
1006     @rtype: string
1007     @return: content of the KVM runtime file
1008
1009     """
1010     return self._ReadKVMRuntime(instance.name)
1011
1012   def AcceptInstance(self, instance, info, target):
1013     """Prepare to accept an instance.
1014
1015     @type instance: L{objects.Instance}
1016     @param instance: instance to be accepted
1017     @type info: string
1018     @param info: content of the KVM runtime file on the source node
1019     @type target: string
1020     @param target: target host (usually ip), on this node
1021
1022     """
1023     kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1024     incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1025     self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1026
1027   def FinalizeMigration(self, instance, info, success):
1028     """Finalize an instance migration.
1029
1030     Stop the incoming mode KVM.
1031
1032     @type instance: L{objects.Instance}
1033     @param instance: instance whose migration is being finalized
1034
1035     """
1036     if success:
1037       kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1038       kvm_nics = kvm_runtime[1]
1039
1040       for nic_seq, nic in enumerate(kvm_nics):
1041         if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1042           # Bridged interfaces have already been configured
1043           continue
1044         try:
1045           tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1046         except EnvironmentError, err:
1047           logging.warning("Failed to find host interface for %s NIC #%d: %s",
1048                           instance.name, nic_seq, str(err))
1049           continue
1050         try:
1051           self._ConfigureNIC(instance, nic_seq, nic, tap)
1052         except errors.HypervisorError, err:
1053           logging.warning(str(err))
1054
1055       self._WriteKVMRuntime(instance.name, info)
1056     else:
1057       self.StopInstance(instance, force=True)
1058
1059   def MigrateInstance(self, instance, target, live):
1060     """Migrate an instance to a target node.
1061
1062     The migration will not be attempted if the instance is not
1063     currently running.
1064
1065     @type instance: L{objects.Instance}
1066     @param instance: the instance to be migrated
1067     @type target: string
1068     @param target: ip address of the target node
1069     @type live: boolean
1070     @param live: perform a live migration
1071
1072     """
1073     instance_name = instance.name
1074     port = instance.hvparams[constants.HV_MIGRATION_PORT]
1075     pidfile, pid, alive = self._InstancePidAlive(instance_name)
1076     if not alive:
1077       raise errors.HypervisorError("Instance not running, cannot migrate")
1078
1079     if not live:
1080       self._CallMonitorCommand(instance_name, 'stop')
1081
1082     migrate_command = ('migrate_set_speed %dm' %
1083         instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1084     self._CallMonitorCommand(instance_name, migrate_command)
1085
1086     migrate_command = ('migrate_set_downtime %dms' %
1087         instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1088     self._CallMonitorCommand(instance_name, migrate_command)
1089
1090     migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
1091     self._CallMonitorCommand(instance_name, migrate_command)
1092
1093     info_command = 'info migrate'
1094     done = False
1095     broken_answers = 0
1096     while not done:
1097       result = self._CallMonitorCommand(instance_name, info_command)
1098       match = self._MIGRATION_STATUS_RE.search(result.stdout)
1099       if not match:
1100         broken_answers += 1
1101         if not result.stdout:
1102           logging.info("KVM: empty 'info migrate' result")
1103         else:
1104           logging.warning("KVM: unknown 'info migrate' result: %s",
1105                           result.stdout)
1106         time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1107       else:
1108         status = match.group(1)
1109         if status == 'completed':
1110           done = True
1111         elif status == 'active':
1112           # reset the broken answers count
1113           broken_answers = 0
1114           time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1115         elif status == 'failed' or status == 'cancelled':
1116           if not live:
1117             self._CallMonitorCommand(instance_name, 'cont')
1118           raise errors.HypervisorError("Migration %s at the kvm level" %
1119                                        status)
1120         else:
1121           logging.warning("KVM: unknown migration status '%s'", status)
1122           broken_answers += 1
1123           time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1124       if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1125         raise errors.HypervisorError("Too many 'info migrate' broken answers")
1126
1127     utils.KillProcess(pid)
1128     self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1129
1130   def GetNodeInfo(self):
1131     """Return information about the node.
1132
1133     This is just a wrapper over the base GetLinuxNodeInfo method.
1134
1135     @return: a dict with the following keys (values in MiB):
1136           - memory_total: the total memory size on the node
1137           - memory_free: the available memory on the node for instances
1138           - memory_dom0: the memory used by the node itself, if available
1139
1140     """
1141     return self.GetLinuxNodeInfo()
1142
1143   @classmethod
1144   def GetInstanceConsole(cls, instance, hvparams, beparams):
1145     """Return a command for connecting to the console of an instance.
1146
1147     """
1148     if hvparams[constants.HV_SERIAL_CONSOLE]:
1149       cmd = [constants.KVM_CONSOLE_WRAPPER,
1150              constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1151              utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1152              "STDIO,%s" % cls._SocatUnixConsoleParams(),
1153              "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1154       return objects.InstanceConsole(instance=instance.name,
1155                                      kind=constants.CONS_SSH,
1156                                      host=instance.primary_node,
1157                                      user=constants.GANETI_RUNAS,
1158                                      command=cmd)
1159
1160     vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1161     if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1162       display = instance.network_port - constants.VNC_BASE_PORT
1163       return objects.InstanceConsole(instance=instance.name,
1164                                      kind=constants.CONS_VNC,
1165                                      host=vnc_bind_address,
1166                                      port=instance.network_port,
1167                                      display=display)
1168
1169     return objects.InstanceConsole(instance=instance.name,
1170                                    kind=constants.CONS_MESSAGE,
1171                                    message=("No serial shell for instance %s" %
1172                                             instance.name))
1173
1174   def Verify(self):
1175     """Verify the hypervisor.
1176
1177     Check that the binary exists.
1178
1179     """
1180     if not os.path.exists(constants.KVM_PATH):
1181       return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1182     if not os.path.exists(constants.SOCAT_PATH):
1183       return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1184
1185   @classmethod
1186   def CheckParameterSyntax(cls, hvparams):
1187     """Check the given parameters for validity.
1188
1189     @type hvparams:  dict
1190     @param hvparams: dictionary with parameter names/value
1191     @raise errors.HypervisorError: when a parameter is not valid
1192
1193     """
1194     super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1195
1196     kernel_path = hvparams[constants.HV_KERNEL_PATH]
1197     if kernel_path:
1198       if not hvparams[constants.HV_ROOT_PATH]:
1199         raise errors.HypervisorError("Need a root partition for the instance,"
1200                                      " if a kernel is defined")
1201
1202     if (hvparams[constants.HV_VNC_X509_VERIFY] and
1203         not hvparams[constants.HV_VNC_X509]):
1204       raise errors.HypervisorError("%s must be defined, if %s is" %
1205                                    (constants.HV_VNC_X509,
1206                                     constants.HV_VNC_X509_VERIFY))
1207
1208     boot_order = hvparams[constants.HV_BOOT_ORDER]
1209     if (boot_order == constants.HT_BO_CDROM and
1210         not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1211       raise errors.HypervisorError("Cannot boot from cdrom without an"
1212                                    " ISO path")
1213
1214     security_model = hvparams[constants.HV_SECURITY_MODEL]
1215     if security_model == constants.HT_SM_USER:
1216       if not hvparams[constants.HV_SECURITY_DOMAIN]:
1217         raise errors.HypervisorError("A security domain (user to run kvm as)"
1218                                      " must be specified")
1219     elif (security_model == constants.HT_SM_NONE or
1220           security_model == constants.HT_SM_POOL):
1221       if hvparams[constants.HV_SECURITY_DOMAIN]:
1222         raise errors.HypervisorError("Cannot have a security domain when the"
1223                                      " security model is 'none' or 'pool'")
1224
1225   @classmethod
1226   def ValidateParameters(cls, hvparams):
1227     """Check the given parameters for validity.
1228
1229     @type hvparams:  dict
1230     @param hvparams: dictionary with parameter names/value
1231     @raise errors.HypervisorError: when a parameter is not valid
1232
1233     """
1234     super(KVMHypervisor, cls).ValidateParameters(hvparams)
1235
1236     security_model = hvparams[constants.HV_SECURITY_MODEL]
1237     if security_model == constants.HT_SM_USER:
1238       username = hvparams[constants.HV_SECURITY_DOMAIN]
1239       try:
1240         pwd.getpwnam(username)
1241       except KeyError:
1242         raise errors.HypervisorError("Unknown security domain user %s"
1243                                      % username)
1244
1245   @classmethod
1246   def PowercycleNode(cls):
1247     """KVM powercycle, just a wrapper over Linux powercycle.
1248
1249     """
1250     cls.LinuxPowercycle()