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