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