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