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