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