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