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