Fix interaction between CPU pinning and KVM migration
[ganeti-local] / lib / hypervisor / hv_kvm.py
1 #
2 #
3
4 # Copyright (C) 2008, 2009, 2010, 2011 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 errno
27 import os
28 import os.path
29 import re
30 import tempfile
31 import time
32 import logging
33 import pwd
34 import struct
35 import fcntl
36 import shutil
37 import socket
38 import StringIO
39 try:
40   import affinity   # pylint: disable=F0401
41 except ImportError:
42   affinity = None
43
44 from ganeti import utils
45 from ganeti import constants
46 from ganeti import errors
47 from ganeti import serializer
48 from ganeti import objects
49 from ganeti import uidpool
50 from ganeti import ssconf
51 from ganeti.hypervisor import hv_base
52 from ganeti import netutils
53 from ganeti.utils import wrapper as utils_wrapper
54
55
56 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
57 _KVM_START_PAUSED_FLAG = "-S"
58
59 # TUN/TAP driver constants, taken from <linux/if_tun.h>
60 # They are architecture-independent and already hardcoded in qemu-kvm source,
61 # so we can safely include them here.
62 TUNSETIFF = 0x400454ca
63 TUNGETIFF = 0x800454d2
64 TUNGETFEATURES = 0x800454cf
65 IFF_TAP = 0x0002
66 IFF_NO_PI = 0x1000
67 IFF_VNET_HDR = 0x4000
68
69
70 def _ProbeTapVnetHdr(fd):
71   """Check whether to enable the IFF_VNET_HDR flag.
72
73   To do this, _all_ of the following conditions must be met:
74    1. TUNGETFEATURES ioctl() *must* be implemented
75    2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
76    3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
77       drivers/net/tun.c there is no way to test this until after the tap device
78       has been created using TUNSETIFF, and there is no way to change the
79       IFF_VNET_HDR flag after creating the interface, catch-22! However both
80       TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
81       thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
82
83    @type fd: int
84    @param fd: the file descriptor of /dev/net/tun
85
86   """
87   req = struct.pack("I", 0)
88   try:
89     res = fcntl.ioctl(fd, TUNGETFEATURES, req)
90   except EnvironmentError:
91     logging.warning("TUNGETFEATURES ioctl() not implemented")
92     return False
93
94   tunflags = struct.unpack("I", res)[0]
95   if tunflags & IFF_VNET_HDR:
96     return True
97   else:
98     logging.warning("Host does not support IFF_VNET_HDR, not enabling")
99     return False
100
101
102 def _OpenTap(vnet_hdr=True):
103   """Open a new tap device and return its file descriptor.
104
105   This is intended to be used by a qemu-type hypervisor together with the -net
106   tap,fd=<fd> command line parameter.
107
108   @type vnet_hdr: boolean
109   @param vnet_hdr: Enable the VNET Header
110   @return: (ifname, tapfd)
111   @rtype: tuple
112
113   """
114   try:
115     tapfd = os.open("/dev/net/tun", os.O_RDWR)
116   except EnvironmentError:
117     raise errors.HypervisorError("Failed to open /dev/net/tun")
118
119   flags = IFF_TAP | IFF_NO_PI
120
121   if vnet_hdr and _ProbeTapVnetHdr(tapfd):
122     flags |= IFF_VNET_HDR
123
124   # The struct ifreq ioctl request (see netdevice(7))
125   ifr = struct.pack("16sh", "", flags)
126
127   try:
128     res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
129   except EnvironmentError:
130     raise errors.HypervisorError("Failed to allocate a new TAP device")
131
132   # Get the interface name from the ioctl
133   ifname = struct.unpack("16sh", res)[0].strip("\x00")
134   return (ifname, tapfd)
135
136
137 class QmpMessage:
138   """QEMU Messaging Protocol (QMP) message.
139
140   """
141
142   def __init__(self, data):
143     """Creates a new QMP message based on the passed data.
144
145     """
146     if not isinstance(data, dict):
147       raise TypeError("QmpMessage must be initialized with a dict")
148
149     self.data = data
150
151   def __getitem__(self, field_name):
152     """Get the value of the required field if present, or None.
153
154     Overrides the [] operator to provide access to the message data,
155     returning None if the required item is not in the message
156     @return: the value of the field_name field, or None if field_name
157              is not contained in the message
158
159     """
160
161     if field_name in self.data:
162       return self.data[field_name]
163
164     return None
165
166   def __setitem__(self, field_name, field_value):
167     """Set the value of the required field_name to field_value.
168
169     """
170     self.data[field_name] = field_value
171
172   @staticmethod
173   def BuildFromJsonString(json_string):
174     """Build a QmpMessage from a JSON encoded string.
175
176     @type json_string: str
177     @param json_string: JSON string representing the message
178     @rtype: L{QmpMessage}
179     @return: a L{QmpMessage} built from json_string
180
181     """
182     # Parse the string
183     data = serializer.LoadJson(json_string)
184     return QmpMessage(data)
185
186   def __str__(self):
187     # The protocol expects the JSON object to be sent as a single
188     # line, hence the need for indent=False.
189     return serializer.DumpJson(self.data, indent=False)
190
191   def __eq__(self, other):
192     # When comparing two QmpMessages, we are interested in comparing
193     # their internal representation of the message data
194     return self.data == other.data
195
196
197 class QmpConnection:
198   """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
199
200   """
201   _FIRST_MESSAGE_KEY = "QMP"
202   _EVENT_KEY = "event"
203   _ERROR_KEY = "error"
204   _ERROR_CLASS_KEY = "class"
205   _ERROR_DATA_KEY = "data"
206   _ERROR_DESC_KEY = "desc"
207   _EXECUTE_KEY = "execute"
208   _ARGUMENTS_KEY = "arguments"
209   _CAPABILITIES_COMMAND = "qmp_capabilities"
210   _MESSAGE_END_TOKEN = "\r\n"
211   _SOCKET_TIMEOUT = 5
212
213   def __init__(self, monitor_filename):
214     """Instantiates the QmpConnection object.
215
216     @type monitor_filename: string
217     @param monitor_filename: the filename of the UNIX raw socket on which the
218                              QMP monitor is listening
219
220     """
221     self.monitor_filename = monitor_filename
222     self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
223     # We want to fail if the server doesn't send a complete message
224     # in a reasonable amount of time
225     self.sock.settimeout(self._SOCKET_TIMEOUT)
226     self._connected = False
227     self._buf = ""
228
229   def _check_connection(self):
230     """Make sure that the connection is established.
231
232     """
233     if not self._connected:
234       raise errors.ProgrammerError("To use a QmpConnection you need to first"
235                                    " invoke connect() on it")
236
237   def connect(self):
238     """Connects to the QMP monitor.
239
240     Connects to the UNIX socket and makes sure that we can actually send and
241     receive data to the kvm instance via QMP.
242
243     @raise errors.HypervisorError: when there are communication errors
244     @raise errors.ProgrammerError: when there are data serialization errors
245
246     """
247     self.sock.connect(self.monitor_filename)
248     self._connected = True
249
250     # Check if we receive a correct greeting message from the server
251     # (As per the QEMU Protocol Specification 0.1 - section 2.2)
252     greeting = self._Recv()
253     if not greeting[self._FIRST_MESSAGE_KEY]:
254       self._connected = False
255       raise errors.HypervisorError("kvm: qmp communication error (wrong"
256                                    " server greeting")
257
258     # Let's put the monitor in command mode using the qmp_capabilities
259     # command, or else no command will be executable.
260     # (As per the QEMU Protocol Specification 0.1 - section 4)
261     self.Execute(self._CAPABILITIES_COMMAND)
262
263   def _ParseMessage(self, buf):
264     """Extract and parse a QMP message from the given buffer.
265
266     Seeks for a QMP message in the given buf. If found, it parses it and
267     returns it together with the rest of the characters in the buf.
268     If no message is found, returns None and the whole buffer.
269
270     @raise errors.ProgrammerError: when there are data serialization errors
271
272     """
273     message = None
274     # Check if we got the message end token (CRLF, as per the QEMU Protocol
275     # Specification 0.1 - Section 2.1.1)
276     pos = buf.find(self._MESSAGE_END_TOKEN)
277     if pos >= 0:
278       try:
279         message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
280       except Exception, err:
281         raise errors.ProgrammerError("QMP data serialization error: %s" % err)
282       buf = buf[pos + 1:]
283
284     return (message, buf)
285
286   def _Recv(self):
287     """Receives a message from QMP and decodes the received JSON object.
288
289     @rtype: QmpMessage
290     @return: the received message
291     @raise errors.HypervisorError: when there are communication errors
292     @raise errors.ProgrammerError: when there are data serialization errors
293
294     """
295     self._check_connection()
296
297     # Check if there is already a message in the buffer
298     (message, self._buf) = self._ParseMessage(self._buf)
299     if message:
300       return message
301
302     recv_buffer = StringIO.StringIO(self._buf)
303     recv_buffer.seek(len(self._buf))
304     try:
305       while True:
306         data = self.sock.recv(4096)
307         if not data:
308           break
309         recv_buffer.write(data)
310
311         (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
312         if message:
313           return message
314
315     except socket.timeout, err:
316       raise errors.HypervisorError("Timeout while receiving a QMP message: "
317                                    "%s" % (err))
318     except socket.error, err:
319       raise errors.HypervisorError("Unable to receive data from KVM using the"
320                                    " QMP protocol: %s" % err)
321
322   def _Send(self, message):
323     """Encodes and sends a message to KVM using QMP.
324
325     @type message: QmpMessage
326     @param message: message to send to KVM
327     @raise errors.HypervisorError: when there are communication errors
328     @raise errors.ProgrammerError: when there are data serialization errors
329
330     """
331     self._check_connection()
332     try:
333       message_str = str(message)
334     except Exception, err:
335       raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
336
337     try:
338       self.sock.sendall(message_str)
339     except socket.timeout, err:
340       raise errors.HypervisorError("Timeout while sending a QMP message: "
341                                    "%s (%s)" % (err.string, err.errno))
342     except socket.error, err:
343       raise errors.HypervisorError("Unable to send data from KVM using the"
344                                    " QMP protocol: %s" % err)
345
346   def Execute(self, command, arguments=None):
347     """Executes a QMP command and returns the response of the server.
348
349     @type command: str
350     @param command: the command to execute
351     @type arguments: dict
352     @param arguments: dictionary of arguments to be passed to the command
353     @rtype: dict
354     @return: dictionary representing the received JSON object
355     @raise errors.HypervisorError: when there are communication errors
356     @raise errors.ProgrammerError: when there are data serialization errors
357
358     """
359     self._check_connection()
360     message = QmpMessage({self._EXECUTE_KEY: command})
361     if arguments:
362       message[self._ARGUMENTS_KEY] = arguments
363     self._Send(message)
364
365     # Events can occur between the sending of the command and the reception
366     # of the response, so we need to filter out messages with the event key.
367     while True:
368       response = self._Recv()
369       err = response[self._ERROR_KEY]
370       if err:
371         raise errors.HypervisorError("kvm: error executing the %s"
372                                      " command: %s (%s, %s):" %
373                                      (command,
374                                       err[self._ERROR_DESC_KEY],
375                                       err[self._ERROR_CLASS_KEY],
376                                       err[self._ERROR_DATA_KEY]))
377
378       elif not response[self._EVENT_KEY]:
379         return response
380
381
382 class KVMHypervisor(hv_base.BaseHypervisor):
383   """KVM hypervisor interface"""
384   CAN_MIGRATE = True
385
386   _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
387   _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
388   _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
389   _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
390   _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
391   _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
392   _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
393   # KVM instances with chroot enabled are started in empty chroot directories.
394   _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
395   # After an instance is stopped, its chroot directory is removed.
396   # If the chroot directory is not empty, it can't be removed.
397   # A non-empty chroot directory indicates a possible security incident.
398   # To support forensics, the non-empty chroot directory is quarantined in
399   # a separate directory, called 'chroot-quarantine'.
400   _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
401   _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
402            _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
403
404   PARAMETERS = {
405     constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
406     constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
407     constants.HV_ROOT_PATH: hv_base.NO_CHECK,
408     constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
409     constants.HV_ACPI: hv_base.NO_CHECK,
410     constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
411     constants.HV_VNC_BIND_ADDRESS:
412       (False, lambda x: (netutils.IP4Address.IsValid(x) or
413                          utils.IsNormAbsPath(x)),
414        "the VNC bind address must be either a valid IP address or an absolute"
415        " pathname", None, None),
416     constants.HV_VNC_TLS: hv_base.NO_CHECK,
417     constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
418     constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
419     constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
420     constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
421     constants.HV_KVM_SPICE_IP_VERSION:
422       (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
423                          x in constants.VALID_IP_VERSIONS),
424        "the SPICE IP version should be 4 or 6",
425        None, None),
426     constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
427     constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
428       hv_base.ParamInSet(False,
429         constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
430     constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
431       hv_base.ParamInSet(False,
432         constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
433     constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
434       hv_base.ParamInSet(False,
435         constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
436     constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
437       hv_base.ParamInSet(False,
438         constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
439     constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
440     constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
441     constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
442     constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
443     constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
444     constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
445     constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
446     constants.HV_BOOT_ORDER:
447       hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
448     constants.HV_NIC_TYPE:
449       hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
450     constants.HV_DISK_TYPE:
451       hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
452     constants.HV_KVM_CDROM_DISK_TYPE:
453       hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
454     constants.HV_USB_MOUSE:
455       hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
456     constants.HV_KEYMAP: hv_base.NO_CHECK,
457     constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
458     constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
459     constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
460     constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
461     constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
462     constants.HV_DISK_CACHE:
463       hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
464     constants.HV_SECURITY_MODEL:
465       hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
466     constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
467     constants.HV_KVM_FLAG:
468       hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
469     constants.HV_VHOST_NET: hv_base.NO_CHECK,
470     constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
471     constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
472     constants.HV_REBOOT_BEHAVIOR:
473       hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
474     constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
475     }
476
477   _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
478                                     re.M | re.I)
479   _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
480   _MIGRATION_INFO_RETRY_DELAY = 2
481
482   _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
483
484   _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
485   _CPU_INFO_CMD = "info cpus"
486   _CONT_CMD = "cont"
487
488   ANCILLARY_FILES = [
489     _KVM_NETWORK_SCRIPT,
490     ]
491
492   def __init__(self):
493     hv_base.BaseHypervisor.__init__(self)
494     # Let's make sure the directories we need exist, even if the RUN_DIR lives
495     # in a tmpfs filesystem or has been otherwise wiped out.
496     dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
497     utils.EnsureDirs(dirs)
498
499   @classmethod
500   def _InstancePidFile(cls, instance_name):
501     """Returns the instance pidfile.
502
503     """
504     return utils.PathJoin(cls._PIDS_DIR, instance_name)
505
506   @classmethod
507   def _InstanceUidFile(cls, instance_name):
508     """Returns the instance uidfile.
509
510     """
511     return utils.PathJoin(cls._UIDS_DIR, instance_name)
512
513   @classmethod
514   def _InstancePidInfo(cls, pid):
515     """Check pid file for instance information.
516
517     Check that a pid file is associated with an instance, and retrieve
518     information from its command line.
519
520     @type pid: string or int
521     @param pid: process id of the instance to check
522     @rtype: tuple
523     @return: (instance_name, memory, vcpus)
524     @raise errors.HypervisorError: when an instance cannot be found
525
526     """
527     alive = utils.IsProcessAlive(pid)
528     if not alive:
529       raise errors.HypervisorError("Cannot get info for pid %s" % pid)
530
531     cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
532     try:
533       cmdline = utils.ReadFile(cmdline_file)
534     except EnvironmentError, err:
535       raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
536                                    (pid, err))
537
538     instance = None
539     memory = 0
540     vcpus = 0
541
542     arg_list = cmdline.split("\x00")
543     while arg_list:
544       arg = arg_list.pop(0)
545       if arg == "-name":
546         instance = arg_list.pop(0)
547       elif arg == "-m":
548         memory = int(arg_list.pop(0))
549       elif arg == "-smp":
550         vcpus = int(arg_list.pop(0))
551
552     if instance is None:
553       raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
554                                    " instance" % pid)
555
556     return (instance, memory, vcpus)
557
558   def _InstancePidAlive(self, instance_name):
559     """Returns the instance pidfile, pid, and liveness.
560
561     @type instance_name: string
562     @param instance_name: instance name
563     @rtype: tuple
564     @return: (pid file name, pid, liveness)
565
566     """
567     pidfile = self._InstancePidFile(instance_name)
568     pid = utils.ReadPidFile(pidfile)
569
570     alive = False
571     try:
572       cmd_instance = self._InstancePidInfo(pid)[0]
573       alive = (cmd_instance == instance_name)
574     except errors.HypervisorError:
575       pass
576
577     return (pidfile, pid, alive)
578
579   def _CheckDown(self, instance_name):
580     """Raises an error unless the given instance is down.
581
582     """
583     alive = self._InstancePidAlive(instance_name)[2]
584     if alive:
585       raise errors.HypervisorError("Failed to start instance %s: %s" %
586                                    (instance_name, "already running"))
587
588   @classmethod
589   def _InstanceMonitor(cls, instance_name):
590     """Returns the instance monitor socket name
591
592     """
593     return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
594
595   @classmethod
596   def _InstanceSerial(cls, instance_name):
597     """Returns the instance serial socket name
598
599     """
600     return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
601
602   @classmethod
603   def _InstanceQmpMonitor(cls, instance_name):
604     """Returns the instance serial QMP socket name
605
606     """
607     return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
608
609   @staticmethod
610   def _SocatUnixConsoleParams():
611     """Returns the correct parameters for socat
612
613     If we have a new-enough socat we can use raw mode with an escape character.
614
615     """
616     if constants.SOCAT_USE_ESCAPE:
617       return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
618     else:
619       return "echo=0,icanon=0"
620
621   @classmethod
622   def _InstanceKVMRuntime(cls, instance_name):
623     """Returns the instance KVM runtime filename
624
625     """
626     return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
627
628   @classmethod
629   def _InstanceChrootDir(cls, instance_name):
630     """Returns the name of the KVM chroot dir of the instance
631
632     """
633     return utils.PathJoin(cls._CHROOT_DIR, instance_name)
634
635   @classmethod
636   def _InstanceNICDir(cls, instance_name):
637     """Returns the name of the directory holding the tap device files for a
638     given instance.
639
640     """
641     return utils.PathJoin(cls._NICS_DIR, instance_name)
642
643   @classmethod
644   def _InstanceNICFile(cls, instance_name, seq):
645     """Returns the name of the file containing the tap device for a given NIC
646
647     """
648     return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
649
650   @classmethod
651   def _InstanceKeymapFile(cls, instance_name):
652     """Returns the name of the file containing the keymap for a given instance
653
654     """
655     return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
656
657   @classmethod
658   def _TryReadUidFile(cls, uid_file):
659     """Try to read a uid file
660
661     """
662     if os.path.exists(uid_file):
663       try:
664         uid = int(utils.ReadOneLineFile(uid_file))
665         return uid
666       except EnvironmentError:
667         logging.warning("Can't read uid file", exc_info=True)
668       except (TypeError, ValueError):
669         logging.warning("Can't parse uid file contents", exc_info=True)
670     return None
671
672   @classmethod
673   def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
674     """Removes an instance's rutime sockets/files/dirs.
675
676     """
677     utils.RemoveFile(pidfile)
678     utils.RemoveFile(cls._InstanceMonitor(instance_name))
679     utils.RemoveFile(cls._InstanceSerial(instance_name))
680     utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
681     utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
682     utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
683     uid_file = cls._InstanceUidFile(instance_name)
684     uid = cls._TryReadUidFile(uid_file)
685     utils.RemoveFile(uid_file)
686     if uid is not None:
687       uidpool.ReleaseUid(uid)
688     try:
689       shutil.rmtree(cls._InstanceNICDir(instance_name))
690     except OSError, err:
691       if err.errno != errno.ENOENT:
692         raise
693     try:
694       chroot_dir = cls._InstanceChrootDir(instance_name)
695       utils.RemoveDir(chroot_dir)
696     except OSError, err:
697       if err.errno == errno.ENOTEMPTY:
698         # The chroot directory is expected to be empty, but it isn't.
699         new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
700                                           prefix="%s-%s-" %
701                                           (instance_name,
702                                            utils.TimestampForFilename()))
703         logging.warning("The chroot directory of instance %s can not be"
704                         " removed as it is not empty. Moving it to the"
705                         " quarantine instead. Please investigate the"
706                         " contents (%s) and clean up manually",
707                         instance_name, new_chroot_dir)
708         utils.RenameFile(chroot_dir, new_chroot_dir)
709       else:
710         raise
711
712   @staticmethod
713   def _ConfigureNIC(instance, seq, nic, tap):
714     """Run the network configuration script for a specified NIC
715
716     @param instance: instance we're acting on
717     @type instance: instance object
718     @param seq: nic sequence number
719     @type seq: int
720     @param nic: nic we're acting on
721     @type nic: nic object
722     @param tap: the host's tap interface this NIC corresponds to
723     @type tap: str
724
725     """
726
727     if instance.tags:
728       tags = " ".join(instance.tags)
729     else:
730       tags = ""
731
732     env = {
733       "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
734       "INSTANCE": instance.name,
735       "MAC": nic.mac,
736       "MODE": nic.nicparams[constants.NIC_MODE],
737       "INTERFACE": tap,
738       "INTERFACE_INDEX": str(seq),
739       "TAGS": tags,
740     }
741
742     if nic.ip:
743       env["IP"] = nic.ip
744
745     if nic.nicparams[constants.NIC_LINK]:
746       env["LINK"] = nic.nicparams[constants.NIC_LINK]
747
748     if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
749       env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
750
751     result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
752     if result.failed:
753       raise errors.HypervisorError("Failed to configure interface %s: %s."
754                                    " Network configuration script output: %s" %
755                                    (tap, result.fail_reason, result.output))
756
757   @staticmethod
758   def _VerifyAffinityPackage():
759     if affinity is None:
760       raise errors.HypervisorError("affinity Python package not"
761         " found; cannot use CPU pinning under KVM")
762
763   @staticmethod
764   def _BuildAffinityCpuMask(cpu_list):
765     """Create a CPU mask suitable for sched_setaffinity from a list of
766     CPUs.
767
768     See man taskset for more info on sched_setaffinity masks.
769     For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
770
771     @type cpu_list: list of int
772     @param cpu_list: list of physical CPU numbers to map to vCPUs in order
773     @rtype: int
774     @return: a bit mask of CPU affinities
775
776     """
777     if cpu_list == constants.CPU_PINNING_OFF:
778       return constants.CPU_PINNING_ALL_KVM
779     else:
780       return sum(2 ** cpu for cpu in cpu_list)
781
782   @classmethod
783   def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
784     """Change CPU affinity for running VM according to given CPU mask.
785
786     @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
787     @type cpu_mask: string
788     @param process_id: process ID of KVM process. Used to pin entire VM
789                        to physical CPUs.
790     @type process_id: int
791     @param thread_dict: map of virtual CPUs to KVM thread IDs
792     @type thread_dict: dict int:int
793
794     """
795
796     # Convert the string CPU mask to a list of list of int's
797     cpu_list = utils.ParseMultiCpuMask(cpu_mask)
798
799     if len(cpu_list) == 1:
800       all_cpu_mapping = cpu_list[0]
801       if all_cpu_mapping == constants.CPU_PINNING_OFF:
802         # If CPU pinning has 1 entry that's "all", then do nothing
803         pass
804       else:
805         # If CPU pinning has one non-all entry, map the entire VM to
806         # one set of physical CPUs
807         cls._VerifyAffinityPackage()
808         affinity.set_process_affinity_mask(process_id,
809           cls._BuildAffinityCpuMask(all_cpu_mapping))
810     else:
811       # The number of vCPUs mapped should match the number of vCPUs
812       # reported by KVM. This was already verified earlier, so
813       # here only as a sanity check.
814       assert len(thread_dict) == len(cpu_list)
815       cls._VerifyAffinityPackage()
816
817       # For each vCPU, map it to the proper list of physical CPUs
818       for vcpu, i in zip(cpu_list, range(len(cpu_list))):
819         affinity.set_process_affinity_mask(thread_dict[i],
820           cls._BuildAffinityCpuMask(vcpu))
821
822   def _GetVcpuThreadIds(self, instance_name):
823     """Get a mapping of vCPU no. to thread IDs for the instance
824
825     @type instance_name: string
826     @param instance_name: instance in question
827     @rtype: dictionary of int:int
828     @return: a dictionary mapping vCPU numbers to thread IDs
829
830     """
831     result = {}
832     output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
833     for line in output.stdout.splitlines():
834       match = self._CPU_INFO_RE.search(line)
835       if not match:
836         continue
837       grp = map(int, match.groups())
838       result[grp[0]] = grp[1]
839
840     return result
841
842   def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
843     """Complete CPU pinning.
844
845     @type instance_name: string
846     @param instance_name: name of instance
847     @type cpu_mask: string
848     @param cpu_mask: CPU pinning mask as entered by user
849
850     """
851     # Get KVM process ID, to be used if need to pin entire VM
852     _, pid, _ = self._InstancePidAlive(instance_name)
853     # Get vCPU thread IDs, to be used if need to pin vCPUs separately
854     thread_dict = self._GetVcpuThreadIds(instance_name)
855     # Run CPU pinning, based on configured mask
856     self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
857
858   def ListInstances(self):
859     """Get the list of running instances.
860
861     We can do this by listing our live instances directory and
862     checking whether the associated kvm process is still alive.
863
864     """
865     result = []
866     for name in os.listdir(self._PIDS_DIR):
867       if self._InstancePidAlive(name)[2]:
868         result.append(name)
869     return result
870
871   def GetInstanceInfo(self, instance_name):
872     """Get instance properties.
873
874     @type instance_name: string
875     @param instance_name: the instance name
876     @rtype: tuple of strings
877     @return: (name, id, memory, vcpus, stat, times)
878
879     """
880     _, pid, alive = self._InstancePidAlive(instance_name)
881     if not alive:
882       return None
883
884     _, memory, vcpus = self._InstancePidInfo(pid)
885     stat = "---b-"
886     times = "0"
887
888     return (instance_name, pid, memory, vcpus, stat, times)
889
890   def GetAllInstancesInfo(self):
891     """Get properties of all instances.
892
893     @return: list of tuples (name, id, memory, vcpus, stat, times)
894
895     """
896     data = []
897     for name in os.listdir(self._PIDS_DIR):
898       try:
899         info = self.GetInstanceInfo(name)
900       except errors.HypervisorError:
901         continue
902       if info:
903         data.append(info)
904     return data
905
906   def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
907     """Generate KVM information to start an instance.
908
909     """
910     # pylint: disable=R0914,R0915
911     _, v_major, v_min, _ = self._GetKVMVersion()
912
913     pidfile = self._InstancePidFile(instance.name)
914     kvm = constants.KVM_PATH
915     kvm_cmd = [kvm]
916     # used just by the vnc server, if enabled
917     kvm_cmd.extend(["-name", instance.name])
918     kvm_cmd.extend(["-m", instance.beparams[constants.BE_MEMORY]])
919     kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
920     kvm_cmd.extend(["-pidfile", pidfile])
921     kvm_cmd.extend(["-daemonize"])
922     if not instance.hvparams[constants.HV_ACPI]:
923       kvm_cmd.extend(["-no-acpi"])
924     if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
925         constants.INSTANCE_REBOOT_EXIT:
926       kvm_cmd.extend(["-no-reboot"])
927
928     hvp = instance.hvparams
929     boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
930     boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
931     boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
932     boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
933
934     self.ValidateParameters(hvp)
935
936     if startup_paused:
937       kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
938
939     if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
940       kvm_cmd.extend(["-enable-kvm"])
941     elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
942       kvm_cmd.extend(["-disable-kvm"])
943
944     if boot_network:
945       kvm_cmd.extend(["-boot", "n"])
946
947     disk_type = hvp[constants.HV_DISK_TYPE]
948     if disk_type == constants.HT_DISK_PARAVIRTUAL:
949       if_val = ",if=virtio"
950     else:
951       if_val = ",if=%s" % disk_type
952     # Cache mode
953     disk_cache = hvp[constants.HV_DISK_CACHE]
954     if instance.disk_template in constants.DTS_EXT_MIRROR:
955       if disk_cache != "none":
956         # TODO: make this a hard error, instead of a silent overwrite
957         logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
958                         " to prevent shared storage corruption on migration",
959                         disk_cache)
960       cache_val = ",cache=none"
961     elif disk_cache != constants.HT_CACHE_DEFAULT:
962       cache_val = ",cache=%s" % disk_cache
963     else:
964       cache_val = ""
965     for cfdev, dev_path in block_devices:
966       if cfdev.mode != constants.DISK_RDWR:
967         raise errors.HypervisorError("Instance has read-only disks which"
968                                      " are not supported by KVM")
969       # TODO: handle FD_LOOP and FD_BLKTAP (?)
970       boot_val = ""
971       if boot_disk:
972         kvm_cmd.extend(["-boot", "c"])
973         boot_disk = False
974         if (v_major, v_min) < (0, 14) and disk_type != constants.HT_DISK_IDE:
975           boot_val = ",boot=on"
976
977       drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
978                                                 cache_val)
979       kvm_cmd.extend(["-drive", drive_val])
980
981     #Now we can specify a different device type for CDROM devices.
982     cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
983     if not cdrom_disk_type:
984       cdrom_disk_type = disk_type
985
986     iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
987     if iso_image:
988       options = ",format=raw,media=cdrom"
989       if boot_cdrom:
990         kvm_cmd.extend(["-boot", "d"])
991         if cdrom_disk_type != constants.HT_DISK_IDE:
992           options = "%s,boot=on,if=%s" % (options, constants.HT_DISK_IDE)
993         else:
994           options = "%s,boot=on" % options
995       else:
996         if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
997           if_val = ",if=virtio"
998         else:
999           if_val = ",if=%s" % cdrom_disk_type
1000         options = "%s%s" % (options, if_val)
1001       drive_val = "file=%s%s" % (iso_image, options)
1002       kvm_cmd.extend(["-drive", drive_val])
1003
1004     iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1005     if iso_image2:
1006       options = ",format=raw,media=cdrom"
1007       if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1008         if_val = ",if=virtio"
1009       else:
1010         if_val = ",if=%s" % cdrom_disk_type
1011       options = "%s%s" % (options, if_val)
1012       drive_val = "file=%s%s" % (iso_image2, options)
1013       kvm_cmd.extend(["-drive", drive_val])
1014
1015     floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1016     if floppy_image:
1017       options = ",format=raw,media=disk"
1018       if boot_floppy:
1019         kvm_cmd.extend(["-boot", "a"])
1020         options = "%s,boot=on" % options
1021       if_val = ",if=floppy"
1022       options = "%s%s" % (options, if_val)
1023       drive_val = "file=%s%s" % (floppy_image, options)
1024       kvm_cmd.extend(["-drive", drive_val])
1025
1026     kernel_path = hvp[constants.HV_KERNEL_PATH]
1027     if kernel_path:
1028       kvm_cmd.extend(["-kernel", kernel_path])
1029       initrd_path = hvp[constants.HV_INITRD_PATH]
1030       if initrd_path:
1031         kvm_cmd.extend(["-initrd", initrd_path])
1032       root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1033                      hvp[constants.HV_KERNEL_ARGS]]
1034       if hvp[constants.HV_SERIAL_CONSOLE]:
1035         root_append.append("console=ttyS0,38400")
1036       kvm_cmd.extend(["-append", " ".join(root_append)])
1037
1038     mem_path = hvp[constants.HV_MEM_PATH]
1039     if mem_path:
1040       kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1041
1042     mouse_type = hvp[constants.HV_USB_MOUSE]
1043     vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1044
1045     if mouse_type:
1046       kvm_cmd.extend(["-usb"])
1047       kvm_cmd.extend(["-usbdevice", mouse_type])
1048     elif vnc_bind_address:
1049       kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1050
1051     keymap = hvp[constants.HV_KEYMAP]
1052     if keymap:
1053       keymap_path = self._InstanceKeymapFile(instance.name)
1054       # If a keymap file is specified, KVM won't use its internal defaults. By
1055       # first including the "en-us" layout, an error on loading the actual
1056       # layout (e.g. because it can't be found) won't lead to a non-functional
1057       # keyboard. A keyboard with incorrect keys is still better than none.
1058       utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1059       kvm_cmd.extend(["-k", keymap_path])
1060
1061     if vnc_bind_address:
1062       if netutils.IP4Address.IsValid(vnc_bind_address):
1063         if instance.network_port > constants.VNC_BASE_PORT:
1064           display = instance.network_port - constants.VNC_BASE_PORT
1065           if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1066             vnc_arg = ":%d" % (display)
1067           else:
1068             vnc_arg = "%s:%d" % (vnc_bind_address, display)
1069         else:
1070           logging.error("Network port is not a valid VNC display (%d < %d)."
1071                         " Not starting VNC", instance.network_port,
1072                         constants.VNC_BASE_PORT)
1073           vnc_arg = "none"
1074
1075         # Only allow tls and other option when not binding to a file, for now.
1076         # kvm/qemu gets confused otherwise about the filename to use.
1077         vnc_append = ""
1078         if hvp[constants.HV_VNC_TLS]:
1079           vnc_append = "%s,tls" % vnc_append
1080           if hvp[constants.HV_VNC_X509_VERIFY]:
1081             vnc_append = "%s,x509verify=%s" % (vnc_append,
1082                                                hvp[constants.HV_VNC_X509])
1083           elif hvp[constants.HV_VNC_X509]:
1084             vnc_append = "%s,x509=%s" % (vnc_append,
1085                                          hvp[constants.HV_VNC_X509])
1086         if hvp[constants.HV_VNC_PASSWORD_FILE]:
1087           vnc_append = "%s,password" % vnc_append
1088
1089         vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1090
1091       else:
1092         vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1093
1094       kvm_cmd.extend(["-vnc", vnc_arg])
1095     else:
1096       kvm_cmd.extend(["-nographic"])
1097
1098     monitor_dev = ("unix:%s,server,nowait" %
1099                    self._InstanceMonitor(instance.name))
1100     kvm_cmd.extend(["-monitor", monitor_dev])
1101     if hvp[constants.HV_SERIAL_CONSOLE]:
1102       serial_dev = ("unix:%s,server,nowait" %
1103                     self._InstanceSerial(instance.name))
1104       kvm_cmd.extend(["-serial", serial_dev])
1105     else:
1106       kvm_cmd.extend(["-serial", "none"])
1107
1108     spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1109     spice_ip_version = None
1110     if spice_bind:
1111       if netutils.IsValidInterface(spice_bind):
1112         # The user specified a network interface, we have to figure out the IP
1113         # address.
1114         addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1115         spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1116
1117         # if the user specified an IP version and the interface does not
1118         # have that kind of IP addresses, throw an exception
1119         if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1120           if not addresses[spice_ip_version]:
1121             raise errors.HypervisorError("spice: unable to get an IPv%s address"
1122                                          " for %s" % (spice_ip_version,
1123                                                       spice_bind))
1124
1125         # the user did not specify an IP version, we have to figure it out
1126         elif (addresses[constants.IP4_VERSION] and
1127               addresses[constants.IP6_VERSION]):
1128           # we have both ipv4 and ipv6, let's use the cluster default IP
1129           # version
1130           cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1131           spice_ip_version = netutils.IPAddress.GetVersionFromAddressFamily(
1132               cluster_family)
1133         elif addresses[constants.IP4_VERSION]:
1134           spice_ip_version = constants.IP4_VERSION
1135         elif addresses[constants.IP6_VERSION]:
1136           spice_ip_version = constants.IP6_VERSION
1137         else:
1138           raise errors.HypervisorError("spice: unable to get an IP address"
1139                                        " for %s" % (spice_bind))
1140
1141         spice_address = addresses[spice_ip_version][0]
1142
1143       else:
1144         # spice_bind is known to be a valid IP address, because
1145         # ValidateParameters checked it.
1146         spice_address = spice_bind
1147
1148       spice_arg = "addr=%s" % spice_address
1149       if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1150         spice_arg = "%s,tls-port=%s,x509-cacert-file=%s" % (spice_arg,
1151             instance.network_port, constants.SPICE_CACERT_FILE)
1152         spice_arg = "%s,x509-key-file=%s,x509-cert-file=%s" % (spice_arg,
1153             constants.SPICE_CERT_FILE, constants.SPICE_CERT_FILE)
1154         tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1155         if tls_ciphers:
1156           spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1157       else:
1158         spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1159
1160       if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1161         spice_arg = "%s,disable-ticketing" % spice_arg
1162
1163       if spice_ip_version:
1164         spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1165
1166       # Image compression options
1167       img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1168       img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1169       img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1170       if img_lossless:
1171         spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1172       if img_jpeg:
1173         spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1174       if img_zlib_glz:
1175         spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1176
1177       # Video stream detection
1178       video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1179       if video_streaming:
1180         spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1181
1182       # Audio compression, by default in qemu-kvm it is on
1183       if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1184         spice_arg = "%s,playback-compression=off" % spice_arg
1185       if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1186         spice_arg = "%s,agent-mouse=off" % spice_arg
1187
1188       logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1189       kvm_cmd.extend(["-spice", spice_arg])
1190
1191       # Tell kvm to use the paravirtualized graphic card, optimized for SPICE
1192       kvm_cmd.extend(["-vga", "qxl"])
1193
1194     if hvp[constants.HV_USE_LOCALTIME]:
1195       kvm_cmd.extend(["-localtime"])
1196
1197     if hvp[constants.HV_KVM_USE_CHROOT]:
1198       kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1199
1200     # Save the current instance nics, but defer their expansion as parameters,
1201     # as we'll need to generate executable temp files for them.
1202     kvm_nics = instance.nics
1203     hvparams = hvp
1204
1205     return (kvm_cmd, kvm_nics, hvparams)
1206
1207   def _WriteKVMRuntime(self, instance_name, data):
1208     """Write an instance's KVM runtime
1209
1210     """
1211     try:
1212       utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1213                       data=data)
1214     except EnvironmentError, err:
1215       raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1216
1217   def _ReadKVMRuntime(self, instance_name):
1218     """Read an instance's KVM runtime
1219
1220     """
1221     try:
1222       file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1223     except EnvironmentError, err:
1224       raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1225     return file_content
1226
1227   def _SaveKVMRuntime(self, instance, kvm_runtime):
1228     """Save an instance's KVM runtime
1229
1230     """
1231     kvm_cmd, kvm_nics, hvparams = kvm_runtime
1232     serialized_nics = [nic.ToDict() for nic in kvm_nics]
1233     serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1234     self._WriteKVMRuntime(instance.name, serialized_form)
1235
1236   def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1237     """Load an instance's KVM runtime
1238
1239     """
1240     if not serialized_runtime:
1241       serialized_runtime = self._ReadKVMRuntime(instance.name)
1242     loaded_runtime = serializer.Load(serialized_runtime)
1243     kvm_cmd, serialized_nics, hvparams = loaded_runtime
1244     kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1245     return (kvm_cmd, kvm_nics, hvparams)
1246
1247   def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1248     """Run the KVM cmd and check for errors
1249
1250     @type name: string
1251     @param name: instance name
1252     @type kvm_cmd: list of strings
1253     @param kvm_cmd: runcmd input for kvm
1254     @type tap_fds: list of int
1255     @param tap_fds: fds of tap devices opened by Ganeti
1256
1257     """
1258     try:
1259       result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1260     finally:
1261       for fd in tap_fds:
1262         utils_wrapper.CloseFdNoError(fd)
1263
1264     if result.failed:
1265       raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1266                                    (name, result.fail_reason, result.output))
1267     if not self._InstancePidAlive(name)[2]:
1268       raise errors.HypervisorError("Failed to start instance %s" % name)
1269
1270   def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
1271     """Execute a KVM cmd, after completing it with some last minute data
1272
1273     @type incoming: tuple of strings
1274     @param incoming: (target_host_ip, port)
1275
1276     """
1277     # Small _ExecuteKVMRuntime hv parameters programming howto:
1278     #  - conf_hvp contains the parameters as configured on ganeti. they might
1279     #    have changed since the instance started; only use them if the change
1280     #    won't affect the inside of the instance (which hasn't been rebooted).
1281     #  - up_hvp contains the parameters as they were when the instance was
1282     #    started, plus any new parameter which has been added between ganeti
1283     #    versions: it is paramount that those default to a value which won't
1284     #    affect the inside of the instance as well.
1285     conf_hvp = instance.hvparams
1286     name = instance.name
1287     self._CheckDown(name)
1288
1289     temp_files = []
1290
1291     kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1292     up_hvp = objects.FillDict(conf_hvp, up_hvp)
1293
1294     _, v_major, v_min, _ = self._GetKVMVersion()
1295
1296     # We know it's safe to run as a different user upon migration, so we'll use
1297     # the latest conf, from conf_hvp.
1298     security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1299     if security_model == constants.HT_SM_USER:
1300       kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1301
1302     # We have reasons to believe changing something like the nic driver/type
1303     # upon migration won't exactly fly with the instance kernel, so for nic
1304     # related parameters we'll use up_hvp
1305     tapfds = []
1306     taps = []
1307     if not kvm_nics:
1308       kvm_cmd.extend(["-net", "none"])
1309     else:
1310       vnet_hdr = False
1311       tap_extra = ""
1312       nic_type = up_hvp[constants.HV_NIC_TYPE]
1313       if nic_type == constants.HT_NIC_PARAVIRTUAL:
1314         # From version 0.12.0, kvm uses a new sintax for network configuration.
1315         if (v_major, v_min) >= (0, 12):
1316           nic_model = "virtio-net-pci"
1317           vnet_hdr = True
1318         else:
1319           nic_model = "virtio"
1320
1321         if up_hvp[constants.HV_VHOST_NET]:
1322           # vhost_net is only available from version 0.13.0 or newer
1323           if (v_major, v_min) >= (0, 13):
1324             tap_extra = ",vhost=on"
1325           else:
1326             raise errors.HypervisorError("vhost_net is configured"
1327                                         " but it is not available")
1328       else:
1329         nic_model = nic_type
1330
1331       for nic_seq, nic in enumerate(kvm_nics):
1332         tapname, tapfd = _OpenTap(vnet_hdr)
1333         tapfds.append(tapfd)
1334         taps.append(tapname)
1335         if (v_major, v_min) >= (0, 12):
1336           nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
1337           tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
1338           kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1339         else:
1340           nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1341                                                          nic.mac, nic_model)
1342           tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1343           kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1344
1345     if incoming:
1346       target, port = incoming
1347       kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1348
1349     # Changing the vnc password doesn't bother the guest that much. At most it
1350     # will surprise people who connect to it. Whether positively or negatively
1351     # it's debatable.
1352     vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1353     vnc_pwd = None
1354     if vnc_pwd_file:
1355       try:
1356         vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1357       except EnvironmentError, err:
1358         raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1359                                      % (vnc_pwd_file, err))
1360
1361     if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1362       utils.EnsureDirs([(self._InstanceChrootDir(name),
1363                          constants.SECURE_DIR_MODE)])
1364
1365     # Automatically enable QMP if version is >= 0.14
1366     if (v_major, v_min) >= (0, 14):
1367       logging.debug("Enabling QMP")
1368       kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1369                     self._InstanceQmpMonitor(instance.name)])
1370
1371     # Configure the network now for starting instances and bridged interfaces,
1372     # during FinalizeMigration for incoming instances' routed interfaces
1373     for nic_seq, nic in enumerate(kvm_nics):
1374       if (incoming and
1375           nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1376         continue
1377       self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1378
1379     # CPU affinity requires kvm to start paused, so we set this flag if the
1380     # instance is not already paused and if we are not going to accept a
1381     # migrating instance. In the latter case, pausing is not needed.
1382     start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1383
1384     # Note: CPU pinning is using up_hvp since changes take effect
1385     # during instance startup anyway, and to avoid problems when soft
1386     # rebooting the instance.
1387     cpu_pinning = False
1388     if up_hvp.get(constants.HV_CPU_MASK, None):
1389       cpu_pinning = True
1390       if start_kvm_paused:
1391         kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1392
1393     if security_model == constants.HT_SM_POOL:
1394       ss = ssconf.SimpleStore()
1395       uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1396       all_uids = set(uidpool.ExpandUidPool(uid_pool))
1397       uid = uidpool.RequestUnusedUid(all_uids)
1398       try:
1399         username = pwd.getpwuid(uid.GetUid()).pw_name
1400         kvm_cmd.extend(["-runas", username])
1401         self._RunKVMCmd(name, kvm_cmd, tapfds)
1402       except:
1403         uidpool.ReleaseUid(uid)
1404         raise
1405       else:
1406         uid.Unlock()
1407         utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1408     else:
1409       self._RunKVMCmd(name, kvm_cmd, tapfds)
1410
1411     utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1412                      constants.RUN_DIRS_MODE)])
1413     for nic_seq, tap in enumerate(taps):
1414       utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1415                       data=tap)
1416
1417     if vnc_pwd:
1418       change_cmd = "change vnc password %s" % vnc_pwd
1419       self._CallMonitorCommand(instance.name, change_cmd)
1420
1421     # Setting SPICE password. We are not vulnerable to malicious passwordless
1422     # connection attempts because SPICE by default does not allow connections
1423     # if neither a password nor the "disable_ticketing" options are specified.
1424     # As soon as we send the password via QMP, that password is a valid ticket
1425     # for connection.
1426     spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1427     if spice_password_file:
1428       try:
1429         spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1430         qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1431         qmp.connect()
1432         arguments = {
1433             "protocol": "spice",
1434             "password": spice_pwd,
1435         }
1436         qmp.Execute("set_password", arguments)
1437       except EnvironmentError, err:
1438         raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1439                                      % (spice_password_file, err))
1440
1441     for filename in temp_files:
1442       utils.RemoveFile(filename)
1443
1444     # If requested, set CPU affinity and resume instance execution
1445     if cpu_pinning:
1446       try:
1447         self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1448       finally:
1449         if start_kvm_paused:
1450           # To control CPU pinning, the VM was started frozen, so we need
1451           # to resume its execution, but only if freezing was not
1452           # explicitly requested.
1453           # Note: this is done even when an exception occurred so the VM
1454           # is not unintentionally frozen.
1455           self._CallMonitorCommand(instance.name, self._CONT_CMD)
1456
1457   def StartInstance(self, instance, block_devices, startup_paused):
1458     """Start an instance.
1459
1460     """
1461     self._CheckDown(instance.name)
1462     kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1463                                            startup_paused)
1464     self._SaveKVMRuntime(instance, kvm_runtime)
1465     self._ExecuteKVMRuntime(instance, kvm_runtime)
1466
1467   def _CallMonitorCommand(self, instance_name, command):
1468     """Invoke a command on the instance monitor.
1469
1470     """
1471     socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1472              (utils.ShellQuote(command),
1473               constants.SOCAT_PATH,
1474               utils.ShellQuote(self._InstanceMonitor(instance_name))))
1475     result = utils.RunCmd(socat)
1476     if result.failed:
1477       msg = ("Failed to send command '%s' to instance %s."
1478              " output: %s, error: %s, fail_reason: %s" %
1479              (command, instance_name,
1480               result.stdout, result.stderr, result.fail_reason))
1481       raise errors.HypervisorError(msg)
1482
1483     return result
1484
1485   @classmethod
1486   def _GetKVMVersion(cls):
1487     """Return the installed KVM version.
1488
1489     @return: (version, v_maj, v_min, v_rev)
1490     @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1491
1492     """
1493     result = utils.RunCmd([constants.KVM_PATH, "--help"])
1494     if result.failed:
1495       raise errors.HypervisorError("Unable to get KVM version")
1496     match = cls._VERSION_RE.search(result.output.splitlines()[0])
1497     if not match:
1498       raise errors.HypervisorError("Unable to get KVM version")
1499
1500     return (match.group(0), int(match.group(1)), int(match.group(2)),
1501             int(match.group(3)))
1502
1503   def StopInstance(self, instance, force=False, retry=False, name=None):
1504     """Stop an instance.
1505
1506     """
1507     if name is not None and not force:
1508       raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1509     if name is None:
1510       name = instance.name
1511       acpi = instance.hvparams[constants.HV_ACPI]
1512     else:
1513       acpi = False
1514     _, pid, alive = self._InstancePidAlive(name)
1515     if pid > 0 and alive:
1516       if force or not acpi:
1517         utils.KillProcess(pid)
1518       else:
1519         self._CallMonitorCommand(name, "system_powerdown")
1520
1521   def CleanupInstance(self, instance_name):
1522     """Cleanup after a stopped instance
1523
1524     """
1525     pidfile, pid, alive = self._InstancePidAlive(instance_name)
1526     if pid > 0 and alive:
1527       raise errors.HypervisorError("Cannot cleanup a live instance")
1528     self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1529
1530   def RebootInstance(self, instance):
1531     """Reboot an instance.
1532
1533     """
1534     # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1535     # socket the instance will stop, but now power up again. So we'll resort
1536     # to shutdown and restart.
1537     _, _, alive = self._InstancePidAlive(instance.name)
1538     if not alive:
1539       raise errors.HypervisorError("Failed to reboot instance %s:"
1540                                    " not running" % instance.name)
1541     # StopInstance will delete the saved KVM runtime so:
1542     # ...first load it...
1543     kvm_runtime = self._LoadKVMRuntime(instance)
1544     # ...now we can safely call StopInstance...
1545     if not self.StopInstance(instance):
1546       self.StopInstance(instance, force=True)
1547     # ...and finally we can save it again, and execute it...
1548     self._SaveKVMRuntime(instance, kvm_runtime)
1549     self._ExecuteKVMRuntime(instance, kvm_runtime)
1550
1551   def MigrationInfo(self, instance):
1552     """Get instance information to perform a migration.
1553
1554     @type instance: L{objects.Instance}
1555     @param instance: instance to be migrated
1556     @rtype: string
1557     @return: content of the KVM runtime file
1558
1559     """
1560     return self._ReadKVMRuntime(instance.name)
1561
1562   def AcceptInstance(self, instance, info, target):
1563     """Prepare to accept an instance.
1564
1565     @type instance: L{objects.Instance}
1566     @param instance: instance to be accepted
1567     @type info: string
1568     @param info: content of the KVM runtime file on the source node
1569     @type target: string
1570     @param target: target host (usually ip), on this node
1571
1572     """
1573     kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1574     incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1575     self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1576
1577   def FinalizeMigration(self, instance, info, success):
1578     """Finalize an instance migration.
1579
1580     Stop the incoming mode KVM.
1581
1582     @type instance: L{objects.Instance}
1583     @param instance: instance whose migration is being finalized
1584
1585     """
1586     if success:
1587       kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1588       kvm_nics = kvm_runtime[1]
1589
1590       for nic_seq, nic in enumerate(kvm_nics):
1591         if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1592           # Bridged interfaces have already been configured
1593           continue
1594         try:
1595           tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1596         except EnvironmentError, err:
1597           logging.warning("Failed to find host interface for %s NIC #%d: %s",
1598                           instance.name, nic_seq, str(err))
1599           continue
1600         try:
1601           self._ConfigureNIC(instance, nic_seq, nic, tap)
1602         except errors.HypervisorError, err:
1603           logging.warning(str(err))
1604
1605       self._WriteKVMRuntime(instance.name, info)
1606     else:
1607       self.StopInstance(instance, force=True)
1608
1609   def MigrateInstance(self, instance, target, live):
1610     """Migrate an instance to a target node.
1611
1612     The migration will not be attempted if the instance is not
1613     currently running.
1614
1615     @type instance: L{objects.Instance}
1616     @param instance: the instance to be migrated
1617     @type target: string
1618     @param target: ip address of the target node
1619     @type live: boolean
1620     @param live: perform a live migration
1621
1622     """
1623     instance_name = instance.name
1624     port = instance.hvparams[constants.HV_MIGRATION_PORT]
1625     pidfile, pid, alive = self._InstancePidAlive(instance_name)
1626     if not alive:
1627       raise errors.HypervisorError("Instance not running, cannot migrate")
1628
1629     if not live:
1630       self._CallMonitorCommand(instance_name, "stop")
1631
1632     migrate_command = ("migrate_set_speed %dm" %
1633         instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1634     self._CallMonitorCommand(instance_name, migrate_command)
1635
1636     migrate_command = ("migrate_set_downtime %dms" %
1637         instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1638     self._CallMonitorCommand(instance_name, migrate_command)
1639
1640     migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1641     self._CallMonitorCommand(instance_name, migrate_command)
1642
1643     info_command = "info migrate"
1644     done = False
1645     broken_answers = 0
1646     while not done:
1647       result = self._CallMonitorCommand(instance_name, info_command)
1648       match = self._MIGRATION_STATUS_RE.search(result.stdout)
1649       if not match:
1650         broken_answers += 1
1651         if not result.stdout:
1652           logging.info("KVM: empty 'info migrate' result")
1653         else:
1654           logging.warning("KVM: unknown 'info migrate' result: %s",
1655                           result.stdout)
1656         time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1657       else:
1658         status = match.group(1)
1659         if status == "completed":
1660           done = True
1661         elif status == "active":
1662           # reset the broken answers count
1663           broken_answers = 0
1664           time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1665         elif status == "failed" or status == "cancelled":
1666           if not live:
1667             self._CallMonitorCommand(instance_name, self._CONT_CMD)
1668           raise errors.HypervisorError("Migration %s at the kvm level" %
1669                                        status)
1670         else:
1671           logging.warning("KVM: unknown migration status '%s'", status)
1672           broken_answers += 1
1673           time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1674       if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1675         raise errors.HypervisorError("Too many 'info migrate' broken answers")
1676
1677     utils.KillProcess(pid)
1678     self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1679
1680   def GetNodeInfo(self):
1681     """Return information about the node.
1682
1683     This is just a wrapper over the base GetLinuxNodeInfo method.
1684
1685     @return: a dict with the following keys (values in MiB):
1686           - memory_total: the total memory size on the node
1687           - memory_free: the available memory on the node for instances
1688           - memory_dom0: the memory used by the node itself, if available
1689
1690     """
1691     return self.GetLinuxNodeInfo()
1692
1693   @classmethod
1694   def GetInstanceConsole(cls, instance, hvparams, beparams):
1695     """Return a command for connecting to the console of an instance.
1696
1697     """
1698     if hvparams[constants.HV_SERIAL_CONSOLE]:
1699       cmd = [constants.KVM_CONSOLE_WRAPPER,
1700              constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1701              utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1702              "STDIO,%s" % cls._SocatUnixConsoleParams(),
1703              "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1704       return objects.InstanceConsole(instance=instance.name,
1705                                      kind=constants.CONS_SSH,
1706                                      host=instance.primary_node,
1707                                      user=constants.GANETI_RUNAS,
1708                                      command=cmd)
1709
1710     vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1711     if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1712       display = instance.network_port - constants.VNC_BASE_PORT
1713       return objects.InstanceConsole(instance=instance.name,
1714                                      kind=constants.CONS_VNC,
1715                                      host=vnc_bind_address,
1716                                      port=instance.network_port,
1717                                      display=display)
1718
1719     spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1720     if spice_bind:
1721       return objects.InstanceConsole(instance=instance.name,
1722                                      kind=constants.CONS_SPICE,
1723                                      host=spice_bind,
1724                                      port=instance.network_port)
1725
1726     return objects.InstanceConsole(instance=instance.name,
1727                                    kind=constants.CONS_MESSAGE,
1728                                    message=("No serial shell for instance %s" %
1729                                             instance.name))
1730
1731   def Verify(self):
1732     """Verify the hypervisor.
1733
1734     Check that the binary exists.
1735
1736     """
1737     if not os.path.exists(constants.KVM_PATH):
1738       return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1739     if not os.path.exists(constants.SOCAT_PATH):
1740       return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1741
1742   @classmethod
1743   def CheckParameterSyntax(cls, hvparams):
1744     """Check the given parameters for validity.
1745
1746     @type hvparams:  dict
1747     @param hvparams: dictionary with parameter names/value
1748     @raise errors.HypervisorError: when a parameter is not valid
1749
1750     """
1751     super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1752
1753     kernel_path = hvparams[constants.HV_KERNEL_PATH]
1754     if kernel_path:
1755       if not hvparams[constants.HV_ROOT_PATH]:
1756         raise errors.HypervisorError("Need a root partition for the instance,"
1757                                      " if a kernel is defined")
1758
1759     if (hvparams[constants.HV_VNC_X509_VERIFY] and
1760         not hvparams[constants.HV_VNC_X509]):
1761       raise errors.HypervisorError("%s must be defined, if %s is" %
1762                                    (constants.HV_VNC_X509,
1763                                     constants.HV_VNC_X509_VERIFY))
1764
1765     boot_order = hvparams[constants.HV_BOOT_ORDER]
1766     if (boot_order == constants.HT_BO_CDROM and
1767         not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1768       raise errors.HypervisorError("Cannot boot from cdrom without an"
1769                                    " ISO path")
1770
1771     security_model = hvparams[constants.HV_SECURITY_MODEL]
1772     if security_model == constants.HT_SM_USER:
1773       if not hvparams[constants.HV_SECURITY_DOMAIN]:
1774         raise errors.HypervisorError("A security domain (user to run kvm as)"
1775                                      " must be specified")
1776     elif (security_model == constants.HT_SM_NONE or
1777           security_model == constants.HT_SM_POOL):
1778       if hvparams[constants.HV_SECURITY_DOMAIN]:
1779         raise errors.HypervisorError("Cannot have a security domain when the"
1780                                      " security model is 'none' or 'pool'")
1781
1782     spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1783     spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
1784     if spice_bind:
1785       if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1786         # if an IP version is specified, the spice_bind parameter must be an
1787         # IP of that family
1788         if (netutils.IP4Address.IsValid(spice_bind) and
1789             spice_ip_version != constants.IP4_VERSION):
1790           raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
1791                                        " the specified IP version is %s" %
1792                                        (spice_bind, spice_ip_version))
1793
1794         if (netutils.IP6Address.IsValid(spice_bind) and
1795             spice_ip_version != constants.IP6_VERSION):
1796           raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
1797                                        " the specified IP version is %s" %
1798                                        (spice_bind, spice_ip_version))
1799     else:
1800       # All the other SPICE parameters depend on spice_bind being set. Raise an
1801       # error if any of them is set without it.
1802       spice_additional_params = frozenset([
1803         constants.HV_KVM_SPICE_IP_VERSION,
1804         constants.HV_KVM_SPICE_PASSWORD_FILE,
1805         constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
1806         constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
1807         constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
1808         constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
1809         constants.HV_KVM_SPICE_USE_TLS,
1810         ])
1811       for param in spice_additional_params:
1812         if hvparams[param]:
1813           raise errors.HypervisorError("spice: %s requires %s to be set" %
1814                                        (param, constants.HV_KVM_SPICE_BIND))
1815
1816   @classmethod
1817   def ValidateParameters(cls, hvparams):
1818     """Check the given parameters for validity.
1819
1820     @type hvparams:  dict
1821     @param hvparams: dictionary with parameter names/value
1822     @raise errors.HypervisorError: when a parameter is not valid
1823
1824     """
1825     super(KVMHypervisor, cls).ValidateParameters(hvparams)
1826
1827     security_model = hvparams[constants.HV_SECURITY_MODEL]
1828     if security_model == constants.HT_SM_USER:
1829       username = hvparams[constants.HV_SECURITY_DOMAIN]
1830       try:
1831         pwd.getpwnam(username)
1832       except KeyError:
1833         raise errors.HypervisorError("Unknown security domain user %s"
1834                                      % username)
1835
1836     spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1837     if spice_bind:
1838       # only one of VNC and SPICE can be used currently.
1839       if hvparams[constants.HV_VNC_BIND_ADDRESS]:
1840         raise errors.HypervisorError("both SPICE and VNC are configured, but"
1841                                      " only one of them can be used at a"
1842                                      " given time.")
1843
1844       # KVM version should be >= 0.14.0
1845       _, v_major, v_min, _ = cls._GetKVMVersion()
1846       if (v_major, v_min) < (0, 14):
1847         raise errors.HypervisorError("spice is configured, but it is not"
1848                                      " available in versions of KVM < 0.14")
1849
1850       # if spice_bind is not an IP address, it must be a valid interface
1851       bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
1852                        or netutils.IP6Address.IsValid(spice_bind))
1853       if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
1854         raise errors.HypervisorError("spice: the %s parameter must be either"
1855                                      " a valid IP address or interface name" %
1856                                      constants.HV_KVM_SPICE_BIND)
1857
1858   @classmethod
1859   def PowercycleNode(cls):
1860     """KVM powercycle, just a wrapper over Linux powercycle.
1861
1862     """
1863     cls.LinuxPowercycle()