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