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