Use new console information in hypervisor abstraction
[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     kvm_version = self._GetKVMVersion()
726     if kvm_version:
727       _, v_major, v_min, _ = kvm_version
728     else:
729       raise errors.HypervisorError("Unable to get KVM version")
730
731     # We know it's safe to run as a different user upon migration, so we'll use
732     # the latest conf, from conf_hvp.
733     security_model = conf_hvp[constants.HV_SECURITY_MODEL]
734     if security_model == constants.HT_SM_USER:
735       kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
736
737     # We have reasons to believe changing something like the nic driver/type
738     # upon migration won't exactly fly with the instance kernel, so for nic
739     # related parameters we'll use up_hvp
740     if not kvm_nics:
741       kvm_cmd.extend(["-net", "none"])
742     else:
743       tap_extra = ""
744       nic_type = up_hvp[constants.HV_NIC_TYPE]
745       if nic_type == constants.HT_NIC_PARAVIRTUAL:
746         # From version 0.12.0, kvm uses a new sintax for network configuration.
747         if (v_major, v_min) >= (0, 12):
748           nic_model = "virtio-net-pci"
749         else:
750           nic_model = "virtio"
751
752         if up_hvp[constants.HV_VHOST_NET]:
753           # vhost_net is only available from version 0.13.0 or newer
754           if (v_major, v_min) >= (0, 13):
755             tap_extra = ",vhost=on"
756           else:
757             raise errors.HypervisorError("vhost_net is configured"
758                                         " but it is not available")
759       else:
760         nic_model = nic_type
761
762       for nic_seq, nic in enumerate(kvm_nics):
763         script = self._WriteNetScriptFile(instance, nic_seq, nic)
764         temp_files.append(script)
765         if (v_major, v_min) >= (0, 12):
766           nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
767           tap_val = "type=tap,id=netdev%s,script=%s%s" % (nic_seq,
768                                                           script, tap_extra)
769           kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
770         else:
771           nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
772                                                          nic.mac, nic_model)
773           tap_val = "tap,vlan=%s,script=%s" % (nic_seq, script)
774           kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
775
776     if incoming:
777       target, port = incoming
778       kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
779
780     # Changing the vnc password doesn't bother the guest that much. At most it
781     # will surprise people who connect to it. Whether positively or negatively
782     # it's debatable.
783     vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
784     vnc_pwd = None
785     if vnc_pwd_file:
786       try:
787         vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
788       except EnvironmentError, err:
789         raise errors.HypervisorError("Failed to open VNC password file %s: %s"
790                                      % (vnc_pwd_file, err))
791
792     if conf_hvp[constants.HV_KVM_USE_CHROOT]:
793       utils.EnsureDirs([(self._InstanceChrootDir(name),
794                          constants.SECURE_DIR_MODE)])
795
796     if security_model == constants.HT_SM_POOL:
797       ss = ssconf.SimpleStore()
798       uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
799       all_uids = set(uidpool.ExpandUidPool(uid_pool))
800       uid = uidpool.RequestUnusedUid(all_uids)
801       try:
802         username = pwd.getpwuid(uid.GetUid()).pw_name
803         kvm_cmd.extend(["-runas", username])
804         self._RunKVMCmd(name, kvm_cmd)
805       except:
806         uidpool.ReleaseUid(uid)
807         raise
808       else:
809         uid.Unlock()
810         utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
811     else:
812       self._RunKVMCmd(name, kvm_cmd)
813
814     if vnc_pwd:
815       change_cmd = 'change vnc password %s' % vnc_pwd
816       self._CallMonitorCommand(instance.name, change_cmd)
817
818     for filename in temp_files:
819       utils.RemoveFile(filename)
820
821   def StartInstance(self, instance, block_devices):
822     """Start an instance.
823
824     """
825     self._CheckDown(instance.name)
826     kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
827     self._SaveKVMRuntime(instance, kvm_runtime)
828     self._ExecuteKVMRuntime(instance, kvm_runtime)
829
830   def _CallMonitorCommand(self, instance_name, command):
831     """Invoke a command on the instance monitor.
832
833     """
834     socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
835              (utils.ShellQuote(command),
836               constants.SOCAT_PATH,
837               utils.ShellQuote(self._InstanceMonitor(instance_name))))
838     result = utils.RunCmd(socat)
839     if result.failed:
840       msg = ("Failed to send command '%s' to instance %s."
841              " output: %s, error: %s, fail_reason: %s" %
842              (command, instance_name,
843               result.stdout, result.stderr, result.fail_reason))
844       raise errors.HypervisorError(msg)
845
846     return result
847
848   @classmethod
849   def _GetKVMVersion(cls):
850     """Return the installed KVM version
851
852     @return: (version, v_maj, v_min, v_rev), or None
853
854     """
855     result = utils.RunCmd([constants.KVM_PATH, "--help"])
856     if result.failed:
857       return None
858     match = cls._VERSION_RE.search(result.output.splitlines()[0])
859     if not match:
860       return None
861
862     return (match.group(0), int(match.group(1)), int(match.group(2)),
863             int(match.group(3)))
864
865   def StopInstance(self, instance, force=False, retry=False, name=None):
866     """Stop an instance.
867
868     """
869     if name is not None and not force:
870       raise errors.HypervisorError("Cannot shutdown cleanly by name only")
871     if name is None:
872       name = instance.name
873       acpi = instance.hvparams[constants.HV_ACPI]
874     else:
875       acpi = False
876     _, pid, alive = self._InstancePidAlive(name)
877     if pid > 0 and alive:
878       if force or not acpi:
879         utils.KillProcess(pid)
880       else:
881         self._CallMonitorCommand(name, 'system_powerdown')
882
883   def CleanupInstance(self, instance_name):
884     """Cleanup after a stopped instance
885
886     """
887     pidfile, pid, alive = self._InstancePidAlive(instance_name)
888     if pid > 0 and alive:
889       raise errors.HypervisorError("Cannot cleanup a live instance")
890     self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
891
892   def RebootInstance(self, instance):
893     """Reboot an instance.
894
895     """
896     # For some reason if we do a 'send-key ctrl-alt-delete' to the control
897     # socket the instance will stop, but now power up again. So we'll resort
898     # to shutdown and restart.
899     _, _, alive = self._InstancePidAlive(instance.name)
900     if not alive:
901       raise errors.HypervisorError("Failed to reboot instance %s:"
902                                    " not running" % instance.name)
903     # StopInstance will delete the saved KVM runtime so:
904     # ...first load it...
905     kvm_runtime = self._LoadKVMRuntime(instance)
906     # ...now we can safely call StopInstance...
907     if not self.StopInstance(instance):
908       self.StopInstance(instance, force=True)
909     # ...and finally we can save it again, and execute it...
910     self._SaveKVMRuntime(instance, kvm_runtime)
911     self._ExecuteKVMRuntime(instance, kvm_runtime)
912
913   def MigrationInfo(self, instance):
914     """Get instance information to perform a migration.
915
916     @type instance: L{objects.Instance}
917     @param instance: instance to be migrated
918     @rtype: string
919     @return: content of the KVM runtime file
920
921     """
922     return self._ReadKVMRuntime(instance.name)
923
924   def AcceptInstance(self, instance, info, target):
925     """Prepare to accept an instance.
926
927     @type instance: L{objects.Instance}
928     @param instance: instance to be accepted
929     @type info: string
930     @param info: content of the KVM runtime file on the source node
931     @type target: string
932     @param target: target host (usually ip), on this node
933
934     """
935     kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
936     incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
937     self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
938
939   def FinalizeMigration(self, instance, info, success):
940     """Finalize an instance migration.
941
942     Stop the incoming mode KVM.
943
944     @type instance: L{objects.Instance}
945     @param instance: instance whose migration is being finalized
946
947     """
948     if success:
949       self._WriteKVMRuntime(instance.name, info)
950     else:
951       self.StopInstance(instance, force=True)
952
953   def MigrateInstance(self, instance, target, live):
954     """Migrate an instance to a target node.
955
956     The migration will not be attempted if the instance is not
957     currently running.
958
959     @type instance: L{objects.Instance}
960     @param instance: the instance to be migrated
961     @type target: string
962     @param target: ip address of the target node
963     @type live: boolean
964     @param live: perform a live migration
965
966     """
967     instance_name = instance.name
968     port = instance.hvparams[constants.HV_MIGRATION_PORT]
969     pidfile, pid, alive = self._InstancePidAlive(instance_name)
970     if not alive:
971       raise errors.HypervisorError("Instance not running, cannot migrate")
972
973     if not live:
974       self._CallMonitorCommand(instance_name, 'stop')
975
976     migrate_command = ('migrate_set_speed %dm' %
977         instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
978     self._CallMonitorCommand(instance_name, migrate_command)
979
980     migrate_command = ('migrate_set_downtime %dms' %
981         instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
982     self._CallMonitorCommand(instance_name, migrate_command)
983
984     migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
985     self._CallMonitorCommand(instance_name, migrate_command)
986
987     info_command = 'info migrate'
988     done = False
989     broken_answers = 0
990     while not done:
991       result = self._CallMonitorCommand(instance_name, info_command)
992       match = self._MIGRATION_STATUS_RE.search(result.stdout)
993       if not match:
994         broken_answers += 1
995         if not result.stdout:
996           logging.info("KVM: empty 'info migrate' result")
997         else:
998           logging.warning("KVM: unknown 'info migrate' result: %s",
999                           result.stdout)
1000         time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1001       else:
1002         status = match.group(1)
1003         if status == 'completed':
1004           done = True
1005         elif status == 'active':
1006           # reset the broken answers count
1007           broken_answers = 0
1008           time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1009         elif status == 'failed' or status == 'cancelled':
1010           if not live:
1011             self._CallMonitorCommand(instance_name, 'cont')
1012           raise errors.HypervisorError("Migration %s at the kvm level" %
1013                                        status)
1014         else:
1015           logging.warning("KVM: unknown migration status '%s'", status)
1016           broken_answers += 1
1017           time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1018       if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1019         raise errors.HypervisorError("Too many 'info migrate' broken answers")
1020
1021     utils.KillProcess(pid)
1022     self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1023
1024   def GetNodeInfo(self):
1025     """Return information about the node.
1026
1027     This is just a wrapper over the base GetLinuxNodeInfo method.
1028
1029     @return: a dict with the following keys (values in MiB):
1030           - memory_total: the total memory size on the node
1031           - memory_free: the available memory on the node for instances
1032           - memory_dom0: the memory used by the node itself, if available
1033
1034     """
1035     return self.GetLinuxNodeInfo()
1036
1037   @classmethod
1038   def GetInstanceConsole(cls, instance, hvparams, beparams):
1039     """Return a command for connecting to the console of an instance.
1040
1041     """
1042     if hvparams[constants.HV_SERIAL_CONSOLE]:
1043       cmd = [constants.SOCAT_PATH,
1044              "STDIO,%s" % cls._SocatUnixConsoleParams(),
1045              "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1046       return objects.InstanceConsole(instance=instance.name,
1047                                      kind=constants.CONS_SSH,
1048                                      host=instance.primary_node,
1049                                      user=constants.GANETI_RUNAS,
1050                                      command=cmd)
1051
1052     vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1053     if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1054       display = instance.network_port - constants.VNC_BASE_PORT
1055       return objects.InstanceConsole(instance=instance.name,
1056                                      kind=constants.CONS_VNC,
1057                                      host=vnc_bind_address,
1058                                      port=instance.network_port,
1059                                      display=display)
1060
1061     return objects.InstanceConsole(instance=instance.name,
1062                                    kind=constants.CONS_MESSAGE,
1063                                    message=("No serial shell for instance %s" %
1064                                             instance.name))
1065
1066   def Verify(self):
1067     """Verify the hypervisor.
1068
1069     Check that the binary exists.
1070
1071     """
1072     if not os.path.exists(constants.KVM_PATH):
1073       return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1074     if not os.path.exists(constants.SOCAT_PATH):
1075       return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1076
1077   @classmethod
1078   def CheckParameterSyntax(cls, hvparams):
1079     """Check the given parameters for validity.
1080
1081     @type hvparams:  dict
1082     @param hvparams: dictionary with parameter names/value
1083     @raise errors.HypervisorError: when a parameter is not valid
1084
1085     """
1086     super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1087
1088     kernel_path = hvparams[constants.HV_KERNEL_PATH]
1089     if kernel_path:
1090       if not hvparams[constants.HV_ROOT_PATH]:
1091         raise errors.HypervisorError("Need a root partition for the instance,"
1092                                      " if a kernel is defined")
1093
1094     if (hvparams[constants.HV_VNC_X509_VERIFY] and
1095         not hvparams[constants.HV_VNC_X509]):
1096       raise errors.HypervisorError("%s must be defined, if %s is" %
1097                                    (constants.HV_VNC_X509,
1098                                     constants.HV_VNC_X509_VERIFY))
1099
1100     boot_order = hvparams[constants.HV_BOOT_ORDER]
1101     if (boot_order == constants.HT_BO_CDROM and
1102         not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1103       raise errors.HypervisorError("Cannot boot from cdrom without an"
1104                                    " ISO path")
1105
1106     security_model = hvparams[constants.HV_SECURITY_MODEL]
1107     if security_model == constants.HT_SM_USER:
1108       if not hvparams[constants.HV_SECURITY_DOMAIN]:
1109         raise errors.HypervisorError("A security domain (user to run kvm as)"
1110                                      " must be specified")
1111     elif (security_model == constants.HT_SM_NONE or
1112           security_model == constants.HT_SM_POOL):
1113       if hvparams[constants.HV_SECURITY_DOMAIN]:
1114         raise errors.HypervisorError("Cannot have a security domain when the"
1115                                      " security model is 'none' or 'pool'")
1116
1117   @classmethod
1118   def ValidateParameters(cls, hvparams):
1119     """Check the given parameters for validity.
1120
1121     @type hvparams:  dict
1122     @param hvparams: dictionary with parameter names/value
1123     @raise errors.HypervisorError: when a parameter is not valid
1124
1125     """
1126     super(KVMHypervisor, cls).ValidateParameters(hvparams)
1127
1128     security_model = hvparams[constants.HV_SECURITY_MODEL]
1129     if security_model == constants.HT_SM_USER:
1130       username = hvparams[constants.HV_SECURITY_DOMAIN]
1131       try:
1132         pwd.getpwnam(username)
1133       except KeyError:
1134         raise errors.HypervisorError("Unknown security domain user %s"
1135                                      % username)
1136
1137   @classmethod
1138   def PowercycleNode(cls):
1139     """KVM powercycle, just a wrapper over Linux powercycle.
1140
1141     """
1142     cls.LinuxPowercycle()