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