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