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):
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
527     hvp = instance.hvparams
528     boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
529     boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
530     boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
531     boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
532
533     if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
534       kvm_cmd.extend(["-enable-kvm"])
535     elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
536       kvm_cmd.extend(["-disable-kvm"])
537
538     if boot_network:
539       kvm_cmd.extend(['-boot', 'n'])
540
541     disk_type = hvp[constants.HV_DISK_TYPE]
542     if disk_type == constants.HT_DISK_PARAVIRTUAL:
543       if_val = ',if=virtio'
544     else:
545       if_val = ',if=%s' % disk_type
546     # Cache mode
547     disk_cache = hvp[constants.HV_DISK_CACHE]
548     if instance.disk_template in constants.DTS_EXT_MIRROR:
549       if disk_cache != "none":
550         # TODO: make this a hard error, instead of a silent overwrite
551         logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
552                         " to prevent shared storage corruption on migration",
553                         disk_cache)
554       cache_val = ",cache=none"
555     elif disk_cache != constants.HT_CACHE_DEFAULT:
556       cache_val = ",cache=%s" % disk_cache
557     else:
558       cache_val = ""
559     for cfdev, dev_path in block_devices:
560       if cfdev.mode != constants.DISK_RDWR:
561         raise errors.HypervisorError("Instance has read-only disks which"
562                                      " are not supported by KVM")
563       # TODO: handle FD_LOOP and FD_BLKTAP (?)
564       boot_val = ""
565       if boot_disk:
566         kvm_cmd.extend(['-boot', 'c'])
567         boot_disk = False
568         if (v_major, v_min) < (0, 14) and disk_type != constants.HT_DISK_IDE:
569           boot_val = ",boot=on"
570
571       drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
572                                                 cache_val)
573       kvm_cmd.extend(['-drive', drive_val])
574
575     #Now we can specify a different device type for CDROM devices.
576     cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
577     if not cdrom_disk_type:
578       cdrom_disk_type = disk_type
579
580     iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
581     if iso_image:
582       options = ',format=raw,media=cdrom'
583       if boot_cdrom:
584         kvm_cmd.extend(['-boot', 'd'])
585         if cdrom_disk_type != constants.HT_DISK_IDE:
586           options = '%s,boot=on,if=%s' % (options, constants.HT_DISK_IDE)
587         else:
588           options = '%s,boot=on' % options
589       else:
590         if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
591           if_val = ',if=virtio'
592         else:
593           if_val = ',if=%s' % cdrom_disk_type
594         options = '%s%s' % (options, if_val)
595       drive_val = 'file=%s%s' % (iso_image, options)
596       kvm_cmd.extend(['-drive', drive_val])
597
598     iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
599     if iso_image2:
600       options = ',format=raw,media=cdrom'
601       if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
602         if_val = ',if=virtio'
603       else:
604         if_val = ',if=%s' % cdrom_disk_type
605       options = '%s%s' % (options, if_val)
606       drive_val = 'file=%s%s' % (iso_image2, options)
607       kvm_cmd.extend(['-drive', drive_val])
608
609     floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
610     if floppy_image:
611       options = ',format=raw,media=disk'
612       if boot_floppy:
613         kvm_cmd.extend(['-boot', 'a'])
614         options = '%s,boot=on' % options
615       if_val = ',if=floppy'
616       options = '%s%s' % (options, if_val)
617       drive_val = 'file=%s%s' % (floppy_image, options)
618       kvm_cmd.extend(['-drive', drive_val])
619
620     kernel_path = hvp[constants.HV_KERNEL_PATH]
621     if kernel_path:
622       kvm_cmd.extend(['-kernel', kernel_path])
623       initrd_path = hvp[constants.HV_INITRD_PATH]
624       if initrd_path:
625         kvm_cmd.extend(['-initrd', initrd_path])
626       root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
627                      hvp[constants.HV_KERNEL_ARGS]]
628       if hvp[constants.HV_SERIAL_CONSOLE]:
629         root_append.append('console=ttyS0,38400')
630       kvm_cmd.extend(['-append', ' '.join(root_append)])
631
632     mem_path = hvp[constants.HV_MEM_PATH]
633     if mem_path:
634       kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
635
636     mouse_type = hvp[constants.HV_USB_MOUSE]
637     vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
638
639     if mouse_type:
640       kvm_cmd.extend(['-usb'])
641       kvm_cmd.extend(['-usbdevice', mouse_type])
642     elif vnc_bind_address:
643       kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
644
645     if vnc_bind_address:
646       if netutils.IP4Address.IsValid(vnc_bind_address):
647         if instance.network_port > constants.VNC_BASE_PORT:
648           display = instance.network_port - constants.VNC_BASE_PORT
649           if vnc_bind_address == constants.IP4_ADDRESS_ANY:
650             vnc_arg = ':%d' % (display)
651           else:
652             vnc_arg = '%s:%d' % (vnc_bind_address, display)
653         else:
654           logging.error("Network port is not a valid VNC display (%d < %d)."
655                         " Not starting VNC", instance.network_port,
656                         constants.VNC_BASE_PORT)
657           vnc_arg = 'none'
658
659         # Only allow tls and other option when not binding to a file, for now.
660         # kvm/qemu gets confused otherwise about the filename to use.
661         vnc_append = ''
662         if hvp[constants.HV_VNC_TLS]:
663           vnc_append = '%s,tls' % vnc_append
664           if hvp[constants.HV_VNC_X509_VERIFY]:
665             vnc_append = '%s,x509verify=%s' % (vnc_append,
666                                                hvp[constants.HV_VNC_X509])
667           elif hvp[constants.HV_VNC_X509]:
668             vnc_append = '%s,x509=%s' % (vnc_append,
669                                          hvp[constants.HV_VNC_X509])
670         if hvp[constants.HV_VNC_PASSWORD_FILE]:
671           vnc_append = '%s,password' % vnc_append
672
673         vnc_arg = '%s%s' % (vnc_arg, vnc_append)
674
675       else:
676         vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
677
678       kvm_cmd.extend(['-vnc', vnc_arg])
679     else:
680       kvm_cmd.extend(['-nographic'])
681
682     monitor_dev = ("unix:%s,server,nowait" %
683                    self._InstanceMonitor(instance.name))
684     kvm_cmd.extend(['-monitor', monitor_dev])
685     if hvp[constants.HV_SERIAL_CONSOLE]:
686       serial_dev = ('unix:%s,server,nowait' %
687                     self._InstanceSerial(instance.name))
688       kvm_cmd.extend(['-serial', serial_dev])
689     else:
690       kvm_cmd.extend(['-serial', 'none'])
691
692     if hvp[constants.HV_USE_LOCALTIME]:
693       kvm_cmd.extend(['-localtime'])
694
695     if hvp[constants.HV_KVM_USE_CHROOT]:
696       kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
697
698     # Save the current instance nics, but defer their expansion as parameters,
699     # as we'll need to generate executable temp files for them.
700     kvm_nics = instance.nics
701     hvparams = hvp
702
703     return (kvm_cmd, kvm_nics, hvparams)
704
705   def _WriteKVMRuntime(self, instance_name, data):
706     """Write an instance's KVM runtime
707
708     """
709     try:
710       utils.WriteFile(self._InstanceKVMRuntime(instance_name),
711                       data=data)
712     except EnvironmentError, err:
713       raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
714
715   def _ReadKVMRuntime(self, instance_name):
716     """Read an instance's KVM runtime
717
718     """
719     try:
720       file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
721     except EnvironmentError, err:
722       raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
723     return file_content
724
725   def _SaveKVMRuntime(self, instance, kvm_runtime):
726     """Save an instance's KVM runtime
727
728     """
729     kvm_cmd, kvm_nics, hvparams = kvm_runtime
730     serialized_nics = [nic.ToDict() for nic in kvm_nics]
731     serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
732     self._WriteKVMRuntime(instance.name, serialized_form)
733
734   def _LoadKVMRuntime(self, instance, serialized_runtime=None):
735     """Load an instance's KVM runtime
736
737     """
738     if not serialized_runtime:
739       serialized_runtime = self._ReadKVMRuntime(instance.name)
740     loaded_runtime = serializer.Load(serialized_runtime)
741     kvm_cmd, serialized_nics, hvparams = loaded_runtime
742     kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
743     return (kvm_cmd, kvm_nics, hvparams)
744
745   def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
746     """Run the KVM cmd and check for errors
747
748     @type name: string
749     @param name: instance name
750     @type kvm_cmd: list of strings
751     @param kvm_cmd: runcmd input for kvm
752     @type tap_fds: list of int
753     @param tap_fds: fds of tap devices opened by Ganeti
754
755     """
756     try:
757       result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
758     finally:
759       for fd in tap_fds:
760         utils_wrapper.CloseFdNoError(fd)
761
762     if result.failed:
763       raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
764                                    (name, result.fail_reason, result.output))
765     if not self._InstancePidAlive(name)[2]:
766       raise errors.HypervisorError("Failed to start instance %s" % name)
767
768   def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
769     """Execute a KVM cmd, after completing it with some last minute data
770
771     @type incoming: tuple of strings
772     @param incoming: (target_host_ip, port)
773
774     """
775     # Small _ExecuteKVMRuntime hv parameters programming howto:
776     #  - conf_hvp contains the parameters as configured on ganeti. they might
777     #    have changed since the instance started; only use them if the change
778     #    won't affect the inside of the instance (which hasn't been rebooted).
779     #  - up_hvp contains the parameters as they were when the instance was
780     #    started, plus any new parameter which has been added between ganeti
781     #    versions: it is paramount that those default to a value which won't
782     #    affect the inside of the instance as well.
783     conf_hvp = instance.hvparams
784     name = instance.name
785     self._CheckDown(name)
786
787     temp_files = []
788
789     kvm_cmd, kvm_nics, up_hvp = kvm_runtime
790     up_hvp = objects.FillDict(conf_hvp, up_hvp)
791
792     kvm_version = self._GetKVMVersion()
793     if kvm_version:
794       _, v_major, v_min, _ = kvm_version
795     else:
796       raise errors.HypervisorError("Unable to get KVM version")
797
798     # We know it's safe to run as a different user upon migration, so we'll use
799     # the latest conf, from conf_hvp.
800     security_model = conf_hvp[constants.HV_SECURITY_MODEL]
801     if security_model == constants.HT_SM_USER:
802       kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
803
804     # We have reasons to believe changing something like the nic driver/type
805     # upon migration won't exactly fly with the instance kernel, so for nic
806     # related parameters we'll use up_hvp
807     tapfds = []
808     taps = []
809     if not kvm_nics:
810       kvm_cmd.extend(["-net", "none"])
811     else:
812       vnet_hdr = False
813       tap_extra = ""
814       nic_type = up_hvp[constants.HV_NIC_TYPE]
815       if nic_type == constants.HT_NIC_PARAVIRTUAL:
816         # From version 0.12.0, kvm uses a new sintax for network configuration.
817         if (v_major, v_min) >= (0, 12):
818           nic_model = "virtio-net-pci"
819           vnet_hdr = True
820         else:
821           nic_model = "virtio"
822
823         if up_hvp[constants.HV_VHOST_NET]:
824           # vhost_net is only available from version 0.13.0 or newer
825           if (v_major, v_min) >= (0, 13):
826             tap_extra = ",vhost=on"
827           else:
828             raise errors.HypervisorError("vhost_net is configured"
829                                         " but it is not available")
830       else:
831         nic_model = nic_type
832
833       for nic_seq, nic in enumerate(kvm_nics):
834         tapname, tapfd = _OpenTap(vnet_hdr)
835         tapfds.append(tapfd)
836         taps.append(tapname)
837         if (v_major, v_min) >= (0, 12):
838           nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
839           tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
840           kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
841         else:
842           nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
843                                                          nic.mac, nic_model)
844           tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
845           kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
846
847     if incoming:
848       target, port = incoming
849       kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
850
851     # Changing the vnc password doesn't bother the guest that much. At most it
852     # will surprise people who connect to it. Whether positively or negatively
853     # it's debatable.
854     vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
855     vnc_pwd = None
856     if vnc_pwd_file:
857       try:
858         vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
859       except EnvironmentError, err:
860         raise errors.HypervisorError("Failed to open VNC password file %s: %s"
861                                      % (vnc_pwd_file, err))
862
863     if conf_hvp[constants.HV_KVM_USE_CHROOT]:
864       utils.EnsureDirs([(self._InstanceChrootDir(name),
865                          constants.SECURE_DIR_MODE)])
866
867     if not incoming:
868       # Configure the network now for starting instances, during
869       # FinalizeMigration for incoming instances
870       for nic_seq, nic in enumerate(kvm_nics):
871         self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
872
873     if security_model == constants.HT_SM_POOL:
874       ss = ssconf.SimpleStore()
875       uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
876       all_uids = set(uidpool.ExpandUidPool(uid_pool))
877       uid = uidpool.RequestUnusedUid(all_uids)
878       try:
879         username = pwd.getpwuid(uid.GetUid()).pw_name
880         kvm_cmd.extend(["-runas", username])
881         self._RunKVMCmd(name, kvm_cmd, tapfds)
882       except:
883         uidpool.ReleaseUid(uid)
884         raise
885       else:
886         uid.Unlock()
887         utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
888     else:
889       self._RunKVMCmd(name, kvm_cmd, tapfds)
890
891     utils.EnsureDirs([(self._InstanceNICDir(instance.name),
892                      constants.RUN_DIRS_MODE)])
893     for nic_seq, tap in enumerate(taps):
894       utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
895                       data=tap)
896
897     if vnc_pwd:
898       change_cmd = 'change vnc password %s' % vnc_pwd
899       self._CallMonitorCommand(instance.name, change_cmd)
900
901     for filename in temp_files:
902       utils.RemoveFile(filename)
903
904   def StartInstance(self, instance, block_devices):
905     """Start an instance.
906
907     """
908     self._CheckDown(instance.name)
909     kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
910     self._SaveKVMRuntime(instance, kvm_runtime)
911     self._ExecuteKVMRuntime(instance, kvm_runtime)
912
913   def _CallMonitorCommand(self, instance_name, command):
914     """Invoke a command on the instance monitor.
915
916     """
917     socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
918              (utils.ShellQuote(command),
919               constants.SOCAT_PATH,
920               utils.ShellQuote(self._InstanceMonitor(instance_name))))
921     result = utils.RunCmd(socat)
922     if result.failed:
923       msg = ("Failed to send command '%s' to instance %s."
924              " output: %s, error: %s, fail_reason: %s" %
925              (command, instance_name,
926               result.stdout, result.stderr, result.fail_reason))
927       raise errors.HypervisorError(msg)
928
929     return result
930
931   @classmethod
932   def _GetKVMVersion(cls):
933     """Return the installed KVM version
934
935     @return: (version, v_maj, v_min, v_rev), or None
936
937     """
938     result = utils.RunCmd([constants.KVM_PATH, "--help"])
939     if result.failed:
940       return None
941     match = cls._VERSION_RE.search(result.output.splitlines()[0])
942     if not match:
943       return None
944
945     return (match.group(0), int(match.group(1)), int(match.group(2)),
946             int(match.group(3)))
947
948   def StopInstance(self, instance, force=False, retry=False, name=None):
949     """Stop an instance.
950
951     """
952     if name is not None and not force:
953       raise errors.HypervisorError("Cannot shutdown cleanly by name only")
954     if name is None:
955       name = instance.name
956       acpi = instance.hvparams[constants.HV_ACPI]
957     else:
958       acpi = False
959     _, pid, alive = self._InstancePidAlive(name)
960     if pid > 0 and alive:
961       if force or not acpi:
962         utils.KillProcess(pid)
963       else:
964         self._CallMonitorCommand(name, 'system_powerdown')
965
966   def CleanupInstance(self, instance_name):
967     """Cleanup after a stopped instance
968
969     """
970     pidfile, pid, alive = self._InstancePidAlive(instance_name)
971     if pid > 0 and alive:
972       raise errors.HypervisorError("Cannot cleanup a live instance")
973     self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
974
975   def RebootInstance(self, instance):
976     """Reboot an instance.
977
978     """
979     # For some reason if we do a 'send-key ctrl-alt-delete' to the control
980     # socket the instance will stop, but now power up again. So we'll resort
981     # to shutdown and restart.
982     _, _, alive = self._InstancePidAlive(instance.name)
983     if not alive:
984       raise errors.HypervisorError("Failed to reboot instance %s:"
985                                    " not running" % instance.name)
986     # StopInstance will delete the saved KVM runtime so:
987     # ...first load it...
988     kvm_runtime = self._LoadKVMRuntime(instance)
989     # ...now we can safely call StopInstance...
990     if not self.StopInstance(instance):
991       self.StopInstance(instance, force=True)
992     # ...and finally we can save it again, and execute it...
993     self._SaveKVMRuntime(instance, kvm_runtime)
994     self._ExecuteKVMRuntime(instance, kvm_runtime)
995
996   def MigrationInfo(self, instance):
997     """Get instance information to perform a migration.
998
999     @type instance: L{objects.Instance}
1000     @param instance: instance to be migrated
1001     @rtype: string
1002     @return: content of the KVM runtime file
1003
1004     """
1005     return self._ReadKVMRuntime(instance.name)
1006
1007   def AcceptInstance(self, instance, info, target):
1008     """Prepare to accept an instance.
1009
1010     @type instance: L{objects.Instance}
1011     @param instance: instance to be accepted
1012     @type info: string
1013     @param info: content of the KVM runtime file on the source node
1014     @type target: string
1015     @param target: target host (usually ip), on this node
1016
1017     """
1018     kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1019     incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1020     self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1021
1022   def FinalizeMigration(self, instance, info, success):
1023     """Finalize an instance migration.
1024
1025     Stop the incoming mode KVM.
1026
1027     @type instance: L{objects.Instance}
1028     @param instance: instance whose migration is being finalized
1029
1030     """
1031     if success:
1032       kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1033       kvm_nics = kvm_runtime[1]
1034
1035       for nic_seq, nic in enumerate(kvm_nics):
1036         try:
1037           tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1038         except EnvironmentError, err:
1039           logging.warning("Failed to find host interface for %s NIC #%d: %s",
1040                           instance.name, nic_seq, str(err))
1041           continue
1042         try:
1043           self._ConfigureNIC(instance, nic_seq, nic, tap)
1044         except errors.HypervisorError, err:
1045           logging.warning(str(err))
1046
1047       self._WriteKVMRuntime(instance.name, info)
1048     else:
1049       self.StopInstance(instance, force=True)
1050
1051   def MigrateInstance(self, instance, target, live):
1052     """Migrate an instance to a target node.
1053
1054     The migration will not be attempted if the instance is not
1055     currently running.
1056
1057     @type instance: L{objects.Instance}
1058     @param instance: the instance to be migrated
1059     @type target: string
1060     @param target: ip address of the target node
1061     @type live: boolean
1062     @param live: perform a live migration
1063
1064     """
1065     instance_name = instance.name
1066     port = instance.hvparams[constants.HV_MIGRATION_PORT]
1067     pidfile, pid, alive = self._InstancePidAlive(instance_name)
1068     if not alive:
1069       raise errors.HypervisorError("Instance not running, cannot migrate")
1070
1071     if not live:
1072       self._CallMonitorCommand(instance_name, 'stop')
1073
1074     migrate_command = ('migrate_set_speed %dm' %
1075         instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1076     self._CallMonitorCommand(instance_name, migrate_command)
1077
1078     migrate_command = ('migrate_set_downtime %dms' %
1079         instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1080     self._CallMonitorCommand(instance_name, migrate_command)
1081
1082     migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
1083     self._CallMonitorCommand(instance_name, migrate_command)
1084
1085     info_command = 'info migrate'
1086     done = False
1087     broken_answers = 0
1088     while not done:
1089       result = self._CallMonitorCommand(instance_name, info_command)
1090       match = self._MIGRATION_STATUS_RE.search(result.stdout)
1091       if not match:
1092         broken_answers += 1
1093         if not result.stdout:
1094           logging.info("KVM: empty 'info migrate' result")
1095         else:
1096           logging.warning("KVM: unknown 'info migrate' result: %s",
1097                           result.stdout)
1098         time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1099       else:
1100         status = match.group(1)
1101         if status == 'completed':
1102           done = True
1103         elif status == 'active':
1104           # reset the broken answers count
1105           broken_answers = 0
1106           time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1107         elif status == 'failed' or status == 'cancelled':
1108           if not live:
1109             self._CallMonitorCommand(instance_name, 'cont')
1110           raise errors.HypervisorError("Migration %s at the kvm level" %
1111                                        status)
1112         else:
1113           logging.warning("KVM: unknown migration status '%s'", status)
1114           broken_answers += 1
1115           time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1116       if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1117         raise errors.HypervisorError("Too many 'info migrate' broken answers")
1118
1119     utils.KillProcess(pid)
1120     self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1121
1122   def GetNodeInfo(self):
1123     """Return information about the node.
1124
1125     This is just a wrapper over the base GetLinuxNodeInfo method.
1126
1127     @return: a dict with the following keys (values in MiB):
1128           - memory_total: the total memory size on the node
1129           - memory_free: the available memory on the node for instances
1130           - memory_dom0: the memory used by the node itself, if available
1131
1132     """
1133     return self.GetLinuxNodeInfo()
1134
1135   @classmethod
1136   def GetInstanceConsole(cls, instance, hvparams, beparams):
1137     """Return a command for connecting to the console of an instance.
1138
1139     """
1140     if hvparams[constants.HV_SERIAL_CONSOLE]:
1141       cmd = [constants.KVM_CONSOLE_WRAPPER,
1142              utils.ShellQuote(instance.name), constants.SOCAT_PATH,
1143              utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1144              "STDIO,%s" % cls._SocatUnixConsoleParams(),
1145              "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1146       return objects.InstanceConsole(instance=instance.name,
1147                                      kind=constants.CONS_SSH,
1148                                      host=instance.primary_node,
1149                                      user=constants.GANETI_RUNAS,
1150                                      command=cmd)
1151
1152     vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1153     if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1154       display = instance.network_port - constants.VNC_BASE_PORT
1155       return objects.InstanceConsole(instance=instance.name,
1156                                      kind=constants.CONS_VNC,
1157                                      host=vnc_bind_address,
1158                                      port=instance.network_port,
1159                                      display=display)
1160
1161     return objects.InstanceConsole(instance=instance.name,
1162                                    kind=constants.CONS_MESSAGE,
1163                                    message=("No serial shell for instance %s" %
1164                                             instance.name))
1165
1166   def Verify(self):
1167     """Verify the hypervisor.
1168
1169     Check that the binary exists.
1170
1171     """
1172     if not os.path.exists(constants.KVM_PATH):
1173       return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1174     if not os.path.exists(constants.SOCAT_PATH):
1175       return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1176
1177   @classmethod
1178   def CheckParameterSyntax(cls, hvparams):
1179     """Check the given parameters for validity.
1180
1181     @type hvparams:  dict
1182     @param hvparams: dictionary with parameter names/value
1183     @raise errors.HypervisorError: when a parameter is not valid
1184
1185     """
1186     super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1187
1188     kernel_path = hvparams[constants.HV_KERNEL_PATH]
1189     if kernel_path:
1190       if not hvparams[constants.HV_ROOT_PATH]:
1191         raise errors.HypervisorError("Need a root partition for the instance,"
1192                                      " if a kernel is defined")
1193
1194     if (hvparams[constants.HV_VNC_X509_VERIFY] and
1195         not hvparams[constants.HV_VNC_X509]):
1196       raise errors.HypervisorError("%s must be defined, if %s is" %
1197                                    (constants.HV_VNC_X509,
1198                                     constants.HV_VNC_X509_VERIFY))
1199
1200     boot_order = hvparams[constants.HV_BOOT_ORDER]
1201     if (boot_order == constants.HT_BO_CDROM and
1202         not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1203       raise errors.HypervisorError("Cannot boot from cdrom without an"
1204                                    " ISO path")
1205
1206     security_model = hvparams[constants.HV_SECURITY_MODEL]
1207     if security_model == constants.HT_SM_USER:
1208       if not hvparams[constants.HV_SECURITY_DOMAIN]:
1209         raise errors.HypervisorError("A security domain (user to run kvm as)"
1210                                      " must be specified")
1211     elif (security_model == constants.HT_SM_NONE or
1212           security_model == constants.HT_SM_POOL):
1213       if hvparams[constants.HV_SECURITY_DOMAIN]:
1214         raise errors.HypervisorError("Cannot have a security domain when the"
1215                                      " security model is 'none' or 'pool'")
1216
1217   @classmethod
1218   def ValidateParameters(cls, hvparams):
1219     """Check the given parameters for validity.
1220
1221     @type hvparams:  dict
1222     @param hvparams: dictionary with parameter names/value
1223     @raise errors.HypervisorError: when a parameter is not valid
1224
1225     """
1226     super(KVMHypervisor, cls).ValidateParameters(hvparams)
1227
1228     security_model = hvparams[constants.HV_SECURITY_MODEL]
1229     if security_model == constants.HT_SM_USER:
1230       username = hvparams[constants.HV_SECURITY_DOMAIN]
1231       try:
1232         pwd.getpwnam(username)
1233       except KeyError:
1234         raise errors.HypervisorError("Unknown security domain user %s"
1235                                      % username)
1236
1237   @classmethod
1238   def PowercycleNode(cls):
1239     """KVM powercycle, just a wrapper over Linux powercycle.
1240
1241     """
1242     cls.LinuxPowercycle()