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