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