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