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