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