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