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