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