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