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