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