Convert hv_kvm to a package
[ganeti-github.git] / lib / hypervisor / hv_kvm / __init__.py
1 #
2 #
3
4 # Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 urllib2
38 import socket
39 import stat
40 import StringIO
41 from bitarray import bitarray
42 try:
43 import affinity # pylint: disable=F0401
44 except ImportError:
45 affinity = None
46 try:
47 import fdsend # pylint: disable=F0401
48 except ImportError:
49 fdsend = None
50
51 from ganeti import utils
52 from ganeti import constants
53 from ganeti import errors
54 from ganeti import serializer
55 from ganeti import objects
56 from ganeti import uidpool
57 from ganeti import ssconf
58 from ganeti import netutils
59 from ganeti import pathutils
60 from ganeti.hypervisor import hv_base
61 from ganeti.utils import wrapper as utils_wrapper
62
63
64 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
65 _KVM_START_PAUSED_FLAG = "-S"
66
67 # TUN/TAP driver constants, taken from <linux/if_tun.h>
68 # They are architecture-independent and already hardcoded in qemu-kvm source,
69 # so we can safely include them here.
70 TUNSETIFF = 0x400454ca
71 TUNGETIFF = 0x800454d2
72 TUNGETFEATURES = 0x800454cf
73 IFF_TAP = 0x0002
74 IFF_NO_PI = 0x1000
75 IFF_VNET_HDR = 0x4000
76
77 #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND}
78 _SPICE_ADDITIONAL_PARAMS = frozenset([
79 constants.HV_KVM_SPICE_IP_VERSION,
80 constants.HV_KVM_SPICE_PASSWORD_FILE,
81 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
82 constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
83 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
84 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
85 constants.HV_KVM_SPICE_USE_TLS,
86 ])
87
88 # Constant bitarray that reflects to a free pci slot
89 # Use it with bitarray.search()
90 _AVAILABLE_PCI_SLOT = bitarray("0")
91
92 # below constants show the format of runtime file
93 # the nics are in second possition, while the disks in 4th (last)
94 # moreover disk entries are stored as a list of in tuples
95 # (L{objects.Disk}, link_name, uri)
96 _KVM_NICS_RUNTIME_INDEX = 1
97 _KVM_DISKS_RUNTIME_INDEX = 3
98 _DEVICE_RUNTIME_INDEX = {
99 constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX,
100 constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX
101 }
102 _FIND_RUNTIME_ENTRY = {
103 constants.HOTPLUG_TARGET_NIC:
104 lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid],
105 constants.HOTPLUG_TARGET_DISK:
106 lambda disk, kvm_disks: [(d, l, u) for (d, l, u) in kvm_disks
107 if d.uuid == disk.uuid]
108 }
109 _RUNTIME_DEVICE = {
110 constants.HOTPLUG_TARGET_NIC: lambda d: d,
111 constants.HOTPLUG_TARGET_DISK: lambda (d, e, _): d
112 }
113 _RUNTIME_ENTRY = {
114 constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
115 constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e, None)
116 }
117
118 _MIGRATION_CAPS_DELIM = ":"
119
120
121 def _GenerateDeviceKVMId(dev_type, dev):
122 """Helper function to generate a unique device name used by KVM
123
124 QEMU monitor commands use names to identify devices. Here we use their pci
125 slot and a part of their UUID to name them. dev.pci might be None for old
126 devices in the cluster.
127
128 @type dev_type: sting
129 @param dev_type: device type of param dev
130 @type dev: L{objects.Disk} or L{objects.NIC}
131 @param dev: the device object for which we generate a kvm name
132 @raise errors.HotplugError: in case a device has no pci slot (old devices)
133
134 """
135
136 if not dev.pci:
137 raise errors.HotplugError("Hotplug is not supported for %s with UUID %s" %
138 (dev_type, dev.uuid))
139
140 return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
141
142
143 def _GetFreeSlot(slots, slot=None, reserve=False):
144 """Helper method to get first available slot in a bitarray
145
146 @type slots: bitarray
147 @param slots: the bitarray to operate on
148 @type slot: integer
149 @param slot: if given we check whether the slot is free
150 @type reserve: boolean
151 @param reserve: whether to reserve the first available slot or not
152 @return: the idx of the (first) available slot
153 @raise errors.HotplugError: If all slots in a bitarray are occupied
154 or the given slot is not free.
155
156 """
157 if slot is not None:
158 assert slot < len(slots)
159 if slots[slot]:
160 raise errors.HypervisorError("Slots %d occupied" % slot)
161
162 else:
163 avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
164 if not avail:
165 raise errors.HypervisorError("All slots occupied")
166
167 slot = int(avail[0])
168
169 if reserve:
170 slots[slot] = True
171
172 return slot
173
174
175 def _GetExistingDeviceInfo(dev_type, device, runtime):
176 """Helper function to get an existing device inside the runtime file
177
178 Used when an instance is running. Load kvm runtime file and search
179 for a device based on its type and uuid.
180
181 @type dev_type: sting
182 @param dev_type: device type of param dev
183 @type device: L{objects.Disk} or L{objects.NIC}
184 @param device: the device object for which we generate a kvm name
185 @type runtime: tuple (cmd, nics, hvparams, disks)
186 @param runtime: the runtime data to search for the device
187 @raise errors.HotplugError: in case the requested device does not
188 exist (e.g. device has been added without --hotplug option) or
189 device info has not pci slot (e.g. old devices in the cluster)
190
191 """
192 index = _DEVICE_RUNTIME_INDEX[dev_type]
193 found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
194 if not found:
195 raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
196 (dev_type, device.uuid))
197
198 return found[0]
199
200
201 def _UpgradeSerializedRuntime(serialized_runtime):
202 """Upgrade runtime data
203
204 Remove any deprecated fields or change the format of the data.
205 The runtime files are not upgraded when Ganeti is upgraded, so the required
206 modification have to be performed here.
207
208 @type serialized_runtime: string
209 @param serialized_runtime: raw text data read from actual runtime file
210 @return: (cmd, nic dicts, hvparams, bdev dicts)
211 @rtype: tuple
212
213 """
214 loaded_runtime = serializer.Load(serialized_runtime)
215 kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
216 if len(loaded_runtime) >= 4:
217 serialized_disks = loaded_runtime[3]
218 else:
219 serialized_disks = []
220
221 for nic in serialized_nics:
222 # Add a dummy uuid slot if an pre-2.8 NIC is found
223 if "uuid" not in nic:
224 nic["uuid"] = utils.NewUUID()
225
226 return kvm_cmd, serialized_nics, hvparams, serialized_disks
227
228
229 def _AnalyzeSerializedRuntime(serialized_runtime):
230 """Return runtime entries for a serialized runtime file
231
232 @type serialized_runtime: string
233 @param serialized_runtime: raw text data read from actual runtime file
234 @return: (cmd, nics, hvparams, bdevs)
235 @rtype: tuple
236
237 """
238 kvm_cmd, serialized_nics, hvparams, serialized_disks = \
239 _UpgradeSerializedRuntime(serialized_runtime)
240 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
241 kvm_disks = [(objects.Disk.FromDict(sdisk), link, uri)
242 for sdisk, link, uri in serialized_disks]
243
244 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
245
246
247 def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
248 """Retrieves supported TUN features from file descriptor.
249
250 @see: L{_ProbeTapVnetHdr}
251
252 """
253 req = struct.pack("I", 0)
254 try:
255 buf = _ioctl(fd, TUNGETFEATURES, req)
256 except EnvironmentError, err:
257 logging.warning("ioctl(TUNGETFEATURES) failed: %s", err)
258 return None
259 else:
260 (flags, ) = struct.unpack("I", buf)
261 return flags
262
263
264 def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
265 """Check whether to enable the IFF_VNET_HDR flag.
266
267 To do this, _all_ of the following conditions must be met:
268 1. TUNGETFEATURES ioctl() *must* be implemented
269 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
270 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
271 drivers/net/tun.c there is no way to test this until after the tap device
272 has been created using TUNSETIFF, and there is no way to change the
273 IFF_VNET_HDR flag after creating the interface, catch-22! However both
274 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
275 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
276
277 @type fd: int
278 @param fd: the file descriptor of /dev/net/tun
279
280 """
281 flags = _features_fn(fd)
282
283 if flags is None:
284 # Not supported
285 return False
286
287 result = bool(flags & IFF_VNET_HDR)
288
289 if not result:
290 logging.warning("Kernel does not support IFF_VNET_HDR, not enabling")
291
292 return result
293
294
295 def _OpenTap(vnet_hdr=True, name=""):
296 """Open a new tap device and return its file descriptor.
297
298 This is intended to be used by a qemu-type hypervisor together with the -net
299 tap,fd=<fd> command line parameter.
300
301 @type vnet_hdr: boolean
302 @param vnet_hdr: Enable the VNET Header
303
304 @type name: string
305 @param name: name for the TAP interface being created; if an empty
306 string is passed, the OS will generate a unique name
307
308 @return: (ifname, tapfd)
309 @rtype: tuple
310
311 """
312 try:
313 tapfd = os.open("/dev/net/tun", os.O_RDWR)
314 except EnvironmentError:
315 raise errors.HypervisorError("Failed to open /dev/net/tun")
316
317 flags = IFF_TAP | IFF_NO_PI
318
319 if vnet_hdr and _ProbeTapVnetHdr(tapfd):
320 flags |= IFF_VNET_HDR
321
322 # The struct ifreq ioctl request (see netdevice(7))
323 ifr = struct.pack("16sh", name, flags)
324
325 try:
326 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
327 except EnvironmentError, err:
328 raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
329 err)
330
331 # Get the interface name from the ioctl
332 ifname = struct.unpack("16sh", res)[0].strip("\x00")
333 return (ifname, tapfd)
334
335
336 class HeadRequest(urllib2.Request):
337 def get_method(self):
338 return "HEAD"
339
340
341 def _CheckUrl(url):
342 """Check if a given URL exists on the server
343
344 """
345 try:
346 urllib2.urlopen(HeadRequest(url))
347 return True
348 except urllib2.URLError:
349 return False
350
351
352 class QmpMessage:
353 """QEMU Messaging Protocol (QMP) message.
354
355 """
356 def __init__(self, data):
357 """Creates a new QMP message based on the passed data.
358
359 """
360 if not isinstance(data, dict):
361 raise TypeError("QmpMessage must be initialized with a dict")
362
363 self.data = data
364
365 def __getitem__(self, field_name):
366 """Get the value of the required field if present, or None.
367
368 Overrides the [] operator to provide access to the message data,
369 returning None if the required item is not in the message
370 @return: the value of the field_name field, or None if field_name
371 is not contained in the message
372
373 """
374 return self.data.get(field_name, None)
375
376 def __setitem__(self, field_name, field_value):
377 """Set the value of the required field_name to field_value.
378
379 """
380 self.data[field_name] = field_value
381
382 def __len__(self):
383 """Return the number of fields stored in this QmpMessage.
384
385 """
386 return len(self.data)
387
388 def __delitem__(self, key):
389 """Delete the specified element from the QmpMessage.
390
391 """
392 del(self.data[key])
393
394 @staticmethod
395 def BuildFromJsonString(json_string):
396 """Build a QmpMessage from a JSON encoded string.
397
398 @type json_string: str
399 @param json_string: JSON string representing the message
400 @rtype: L{QmpMessage}
401 @return: a L{QmpMessage} built from json_string
402
403 """
404 # Parse the string
405 data = serializer.LoadJson(json_string)
406 return QmpMessage(data)
407
408 def __str__(self):
409 # The protocol expects the JSON object to be sent as a single line.
410 return serializer.DumpJson(self.data)
411
412 def __eq__(self, other):
413 # When comparing two QmpMessages, we are interested in comparing
414 # their internal representation of the message data
415 return self.data == other.data
416
417
418 class MonitorSocket(object):
419 _SOCKET_TIMEOUT = 5
420
421 def __init__(self, monitor_filename):
422 """Instantiates the MonitorSocket object.
423
424 @type monitor_filename: string
425 @param monitor_filename: the filename of the UNIX raw socket on which the
426 monitor (QMP or simple one) is listening
427
428 """
429 self.monitor_filename = monitor_filename
430 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
431 # We want to fail if the server doesn't send a complete message
432 # in a reasonable amount of time
433 self.sock.settimeout(self._SOCKET_TIMEOUT)
434 self._connected = False
435
436 def _check_socket(self):
437 sock_stat = None
438 try:
439 sock_stat = os.stat(self.monitor_filename)
440 except EnvironmentError, err:
441 if err.errno == errno.ENOENT:
442 raise errors.HypervisorError("No monitor socket found")
443 else:
444 raise errors.HypervisorError("Error checking monitor socket: %s",
445 utils.ErrnoOrStr(err))
446 if not stat.S_ISSOCK(sock_stat.st_mode):
447 raise errors.HypervisorError("Monitor socket is not a socket")
448
449 def _check_connection(self):
450 """Make sure that the connection is established.
451
452 """
453 if not self._connected:
454 raise errors.ProgrammerError("To use a MonitorSocket you need to first"
455 " invoke connect() on it")
456
457 def connect(self):
458 """Connects to the monitor.
459
460 Connects to the UNIX socket
461
462 @raise errors.HypervisorError: when there are communication errors
463
464 """
465 if self._connected:
466 raise errors.ProgrammerError("Cannot connect twice")
467
468 self._check_socket()
469
470 # Check file existance/stuff
471 try:
472 self.sock.connect(self.monitor_filename)
473 except EnvironmentError:
474 raise errors.HypervisorError("Can't connect to qmp socket")
475 self._connected = True
476
477 def close(self):
478 """Closes the socket
479
480 It cannot be used after this call.
481
482 """
483 self.sock.close()
484
485
486 class QmpConnection(MonitorSocket):
487 """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
488
489 """
490 _FIRST_MESSAGE_KEY = "QMP"
491 _EVENT_KEY = "event"
492 _ERROR_KEY = "error"
493 _RETURN_KEY = RETURN_KEY = "return"
494 _ACTUAL_KEY = ACTUAL_KEY = "actual"
495 _ERROR_CLASS_KEY = "class"
496 _ERROR_DESC_KEY = "desc"
497 _EXECUTE_KEY = "execute"
498 _ARGUMENTS_KEY = "arguments"
499 _CAPABILITIES_COMMAND = "qmp_capabilities"
500 _MESSAGE_END_TOKEN = "\r\n"
501
502 def __init__(self, monitor_filename):
503 super(QmpConnection, self).__init__(monitor_filename)
504 self._buf = ""
505
506 def connect(self):
507 """Connects to the QMP monitor.
508
509 Connects to the UNIX socket and makes sure that we can actually send and
510 receive data to the kvm instance via QMP.
511
512 @raise errors.HypervisorError: when there are communication errors
513 @raise errors.ProgrammerError: when there are data serialization errors
514
515 """
516 super(QmpConnection, self).connect()
517 # Check if we receive a correct greeting message from the server
518 # (As per the QEMU Protocol Specification 0.1 - section 2.2)
519 greeting = self._Recv()
520 if not greeting[self._FIRST_MESSAGE_KEY]:
521 self._connected = False
522 raise errors.HypervisorError("kvm: QMP communication error (wrong"
523 " server greeting")
524
525 # This is needed because QMP can return more than one greetings
526 # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ
527 self._buf = ""
528
529 # Let's put the monitor in command mode using the qmp_capabilities
530 # command, or else no command will be executable.
531 # (As per the QEMU Protocol Specification 0.1 - section 4)
532 self.Execute(self._CAPABILITIES_COMMAND)
533
534 def _ParseMessage(self, buf):
535 """Extract and parse a QMP message from the given buffer.
536
537 Seeks for a QMP message in the given buf. If found, it parses it and
538 returns it together with the rest of the characters in the buf.
539 If no message is found, returns None and the whole buffer.
540
541 @raise errors.ProgrammerError: when there are data serialization errors
542
543 """
544 message = None
545 # Check if we got the message end token (CRLF, as per the QEMU Protocol
546 # Specification 0.1 - Section 2.1.1)
547 pos = buf.find(self._MESSAGE_END_TOKEN)
548 if pos >= 0:
549 try:
550 message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
551 except Exception, err:
552 raise errors.ProgrammerError("QMP data serialization error: %s" % err)
553 buf = buf[pos + 1:]
554
555 return (message, buf)
556
557 def _Recv(self):
558 """Receives a message from QMP and decodes the received JSON object.
559
560 @rtype: QmpMessage
561 @return: the received message
562 @raise errors.HypervisorError: when there are communication errors
563 @raise errors.ProgrammerError: when there are data serialization errors
564
565 """
566 self._check_connection()
567
568 # Check if there is already a message in the buffer
569 (message, self._buf) = self._ParseMessage(self._buf)
570 if message:
571 return message
572
573 recv_buffer = StringIO.StringIO(self._buf)
574 recv_buffer.seek(len(self._buf))
575 try:
576 while True:
577 data = self.sock.recv(4096)
578 if not data:
579 break
580 recv_buffer.write(data)
581
582 (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
583 if message:
584 return message
585
586 except socket.timeout, err:
587 raise errors.HypervisorError("Timeout while receiving a QMP message: "
588 "%s" % (err))
589 except socket.error, err:
590 raise errors.HypervisorError("Unable to receive data from KVM using the"
591 " QMP protocol: %s" % err)
592
593 def _Send(self, message):
594 """Encodes and sends a message to KVM using QMP.
595
596 @type message: QmpMessage
597 @param message: message to send to KVM
598 @raise errors.HypervisorError: when there are communication errors
599 @raise errors.ProgrammerError: when there are data serialization errors
600
601 """
602 self._check_connection()
603 try:
604 message_str = str(message)
605 except Exception, err:
606 raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
607
608 try:
609 self.sock.sendall(message_str)
610 except socket.timeout, err:
611 raise errors.HypervisorError("Timeout while sending a QMP message: "
612 "%s (%s)" % (err.string, err.errno))
613 except socket.error, err:
614 raise errors.HypervisorError("Unable to send data from KVM using the"
615 " QMP protocol: %s" % err)
616
617 def Execute(self, command, arguments=None):
618 """Executes a QMP command and returns the response of the server.
619
620 @type command: str
621 @param command: the command to execute
622 @type arguments: dict
623 @param arguments: dictionary of arguments to be passed to the command
624 @rtype: dict
625 @return: dictionary representing the received JSON object
626 @raise errors.HypervisorError: when there are communication errors
627 @raise errors.ProgrammerError: when there are data serialization errors
628
629 """
630 self._check_connection()
631 message = QmpMessage({self._EXECUTE_KEY: command})
632 if arguments:
633 message[self._ARGUMENTS_KEY] = arguments
634 self._Send(message)
635
636 # Events can occur between the sending of the command and the reception
637 # of the response, so we need to filter out messages with the event key.
638 while True:
639 response = self._Recv()
640 err = response[self._ERROR_KEY]
641 if err:
642 raise errors.HypervisorError("kvm: error executing the %s"
643 " command: %s (%s):" %
644 (command,
645 err[self._ERROR_DESC_KEY],
646 err[self._ERROR_CLASS_KEY]))
647
648 elif not response[self._EVENT_KEY]:
649 return response
650
651
652 class KVMHypervisor(hv_base.BaseHypervisor):
653 """KVM hypervisor interface
654
655 """
656 CAN_MIGRATE = True
657
658 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
659 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
660 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
661 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
662 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
663 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
664 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
665 # KVM instances with chroot enabled are started in empty chroot directories.
666 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
667 # After an instance is stopped, its chroot directory is removed.
668 # If the chroot directory is not empty, it can't be removed.
669 # A non-empty chroot directory indicates a possible security incident.
670 # To support forensics, the non-empty chroot directory is quarantined in
671 # a separate directory, called 'chroot-quarantine'.
672 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
673 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
674 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
675
676 PARAMETERS = {
677 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
678 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
679 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
680 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
681 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
682 constants.HV_ACPI: hv_base.NO_CHECK,
683 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
684 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
685 constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK, # will be checked later
686 constants.HV_VNC_TLS: hv_base.NO_CHECK,
687 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
688 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
689 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
690 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
691 constants.HV_KVM_SPICE_IP_VERSION:
692 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
693 x in constants.VALID_IP_VERSIONS),
694 "The SPICE IP version should be 4 or 6",
695 None, None),
696 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
697 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
698 hv_base.ParamInSet(
699 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
700 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
701 hv_base.ParamInSet(
702 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
703 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
704 hv_base.ParamInSet(
705 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
706 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
707 hv_base.ParamInSet(
708 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
709 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
710 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
711 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
712 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
713 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
714 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
715 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
716 constants.HV_BOOT_ORDER:
717 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
718 constants.HV_NIC_TYPE:
719 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
720 constants.HV_DISK_TYPE:
721 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
722 constants.HV_KVM_CDROM_DISK_TYPE:
723 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
724 constants.HV_USB_MOUSE:
725 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
726 constants.HV_KEYMAP: hv_base.NO_CHECK,
727 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
728 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
729 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
730 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
731 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
732 constants.HV_DISK_CACHE:
733 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
734 constants.HV_SECURITY_MODEL:
735 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
736 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
737 constants.HV_KVM_FLAG:
738 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
739 constants.HV_VHOST_NET: hv_base.NO_CHECK,
740 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
741 constants.HV_KVM_USER_SHUTDOWN: hv_base.NO_CHECK,
742 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
743 constants.HV_REBOOT_BEHAVIOR:
744 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
745 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
746 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
747 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
748 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
749 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
750 constants.HV_SOUNDHW: hv_base.NO_CHECK,
751 constants.HV_USB_DEVICES: hv_base.NO_CHECK,
752 constants.HV_VGA: hv_base.NO_CHECK,
753 constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
754 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
755 constants.HV_KVM_MIGRATION_CAPS: hv_base.NO_CHECK,
756 constants.HV_VNET_HDR: hv_base.NO_CHECK,
757 }
758
759 _VIRTIO = "virtio"
760 _VIRTIO_NET_PCI = "virtio-net-pci"
761 _VIRTIO_BLK_PCI = "virtio-blk-pci"
762
763 _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)",
764 re.M | re.I)
765 _MIGRATION_PROGRESS_RE = \
766 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
767 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
768 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
769
770 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
771 _MIGRATION_INFO_RETRY_DELAY = 2
772
773 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
774
775 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
776 _CPU_INFO_CMD = "info cpus"
777 _CONT_CMD = "cont"
778
779 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
780 _CHECK_MACHINE_VERSION_RE = \
781 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
782
783 _QMP_RE = re.compile(r"^-qmp\s", re.M)
784 _SPICE_RE = re.compile(r"^-spice\s", re.M)
785 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
786 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
787 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
788 _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
789 _DISPLAY_RE = re.compile(r"^-display\s", re.M)
790 _MACHINE_RE = re.compile(r"^-machine\s", re.M)
791 _VIRTIO_NET_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M)
792 _VIRTIO_BLK_RE = re.compile(r"^name \"%s\"" % _VIRTIO_BLK_PCI, re.M)
793 # match -drive.*boot=on|off on different lines, but in between accept only
794 # dashes not preceeded by a new line (which would mean another option
795 # different than -drive is starting)
796 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
797 _UUID_RE = re.compile(r"^-uuid\s", re.M)
798
799 _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
800 _INFO_PCI_CMD = "info pci"
801 _FIND_PCI_DEVICE_RE = \
802 staticmethod(
803 lambda pci, devid: re.compile(r'Bus.*device[ ]*%d,(.*\n){5,6}.*id "%s"' %
804 (pci, devid), re.M))
805
806 _INFO_VERSION_RE = \
807 re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
808 _INFO_VERSION_CMD = "info version"
809
810 # Slot 0 for Host bridge, Slot 1 for ISA bridge, Slot 2 for VGA controller
811 _DEFAULT_PCI_RESERVATIONS = "11100000000000000000000000000000"
812 _SOUNDHW_WITH_PCI_SLOT = ["ac97", "es1370", "hda"]
813
814 ANCILLARY_FILES = [
815 _KVM_NETWORK_SCRIPT,
816 ]
817 ANCILLARY_FILES_OPT = [
818 _KVM_NETWORK_SCRIPT,
819 ]
820
821 # Supported kvm options to get output from
822 _KVMOPT_HELP = "help"
823 _KVMOPT_MLIST = "mlist"
824 _KVMOPT_DEVICELIST = "devicelist"
825
826 # Command to execute to get the output from kvm, and whether to
827 # accept the output even on failure.
828 _KVMOPTS_CMDS = {
829 _KVMOPT_HELP: (["--help"], False),
830 _KVMOPT_MLIST: (["-M", "?"], False),
831 _KVMOPT_DEVICELIST: (["-device", "?"], True),
832 }
833
834 def __init__(self):
835 hv_base.BaseHypervisor.__init__(self)
836 # Let's make sure the directories we need exist, even if the RUN_DIR lives
837 # in a tmpfs filesystem or has been otherwise wiped out.
838 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
839 utils.EnsureDirs(dirs)
840
841 @classmethod
842 def _InstancePidFile(cls, instance_name):
843 """Returns the instance pidfile.
844
845 """
846 return utils.PathJoin(cls._PIDS_DIR, instance_name)
847
848 @classmethod
849 def _InstanceUidFile(cls, instance_name):
850 """Returns the instance uidfile.
851
852 """
853 return utils.PathJoin(cls._UIDS_DIR, instance_name)
854
855 @classmethod
856 def _InstancePidInfo(cls, pid):
857 """Check pid file for instance information.
858
859 Check that a pid file is associated with an instance, and retrieve
860 information from its command line.
861
862 @type pid: string or int
863 @param pid: process id of the instance to check
864 @rtype: tuple
865 @return: (instance_name, memory, vcpus)
866 @raise errors.HypervisorError: when an instance cannot be found
867
868 """
869 alive = utils.IsProcessAlive(pid)
870 if not alive:
871 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
872
873 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
874 try:
875 cmdline = utils.ReadFile(cmdline_file)
876 except EnvironmentError, err:
877 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
878 (pid, err))
879
880 instance = None
881 memory = 0
882 vcpus = 0
883
884 arg_list = cmdline.split("\x00")
885 while arg_list:
886 arg = arg_list.pop(0)
887 if arg == "-name":
888 instance = arg_list.pop(0)
889 elif arg == "-m":
890 memory = int(arg_list.pop(0))
891 elif arg == "-smp":
892 vcpus = int(arg_list.pop(0).split(",")[0])
893
894 if instance is None:
895 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
896 " instance" % pid)
897
898 return (instance, memory, vcpus)
899
900 @classmethod
901 def _InstancePidAlive(cls, instance_name):
902 """Returns the instance pidfile, pid, and liveness.
903
904 @type instance_name: string
905 @param instance_name: instance name
906 @rtype: tuple
907 @return: (pid file name, pid, liveness)
908
909 """
910 pidfile = cls._InstancePidFile(instance_name)
911 pid = utils.ReadPidFile(pidfile)
912
913 alive = False
914 try:
915 cmd_instance = cls._InstancePidInfo(pid)[0]
916 alive = (cmd_instance == instance_name)
917 except errors.HypervisorError:
918 pass
919
920 return (pidfile, pid, alive)
921
922 @classmethod
923 def _CheckDown(cls, instance_name):
924 """Raises an error unless the given instance is down.
925
926 """
927 alive = cls._InstancePidAlive(instance_name)[2]
928 if alive:
929 raise errors.HypervisorError("Failed to start instance %s: %s" %
930 (instance_name, "already running"))
931
932 @classmethod
933 def _InstanceMonitor(cls, instance_name):
934 """Returns the instance monitor socket name
935
936 """
937 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
938
939 @classmethod
940 def _InstanceSerial(cls, instance_name):
941 """Returns the instance serial socket name
942
943 """
944 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
945
946 @classmethod
947 def _InstanceQmpMonitor(cls, instance_name):
948 """Returns the instance serial QMP socket name
949
950 """
951 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
952
953 @classmethod
954 def _InstanceShutdownMonitor(cls, instance_name):
955 """Returns the instance QMP output filename
956
957 """
958 return utils.PathJoin(cls._CTRL_DIR, "%s.shutdown" % instance_name)
959
960 @staticmethod
961 def _SocatUnixConsoleParams():
962 """Returns the correct parameters for socat
963
964 If we have a new-enough socat we can use raw mode with an escape character.
965
966 """
967 if constants.SOCAT_USE_ESCAPE:
968 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
969 else:
970 return "echo=0,icanon=0"
971
972 @classmethod
973 def _InstanceKVMRuntime(cls, instance_name):
974 """Returns the instance KVM runtime filename
975
976 """
977 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
978
979 @classmethod
980 def _InstanceChrootDir(cls, instance_name):
981 """Returns the name of the KVM chroot dir of the instance
982
983 """
984 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
985
986 @classmethod
987 def _InstanceNICDir(cls, instance_name):
988 """Returns the name of the directory holding the tap device files for a
989 given instance.
990
991 """
992 return utils.PathJoin(cls._NICS_DIR, instance_name)
993
994 @classmethod
995 def _InstanceNICFile(cls, instance_name, seq):
996 """Returns the name of the file containing the tap device for a given NIC
997
998 """
999 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
1000
1001 @classmethod
1002 def _InstanceKeymapFile(cls, instance_name):
1003 """Returns the name of the file containing the keymap for a given instance
1004
1005 """
1006 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
1007
1008 @classmethod
1009 def _TryReadUidFile(cls, uid_file):
1010 """Try to read a uid file
1011
1012 """
1013 if os.path.exists(uid_file):
1014 try:
1015 uid = int(utils.ReadOneLineFile(uid_file))
1016 return uid
1017 except EnvironmentError:
1018 logging.warning("Can't read uid file", exc_info=True)
1019 except (TypeError, ValueError):
1020 logging.warning("Can't parse uid file contents", exc_info=True)
1021 return None
1022
1023 @classmethod
1024 def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
1025 """Removes an instance's rutime sockets/files/dirs.
1026
1027 """
1028 utils.RemoveFile(pidfile)
1029 utils.RemoveFile(cls._InstanceMonitor(instance_name))
1030 utils.RemoveFile(cls._InstanceSerial(instance_name))
1031 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
1032 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
1033 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
1034 uid_file = cls._InstanceUidFile(instance_name)
1035 uid = cls._TryReadUidFile(uid_file)
1036 utils.RemoveFile(uid_file)
1037 if uid is not None:
1038 uidpool.ReleaseUid(uid)
1039 try:
1040 shutil.rmtree(cls._InstanceNICDir(instance_name))
1041 except OSError, err:
1042 if err.errno != errno.ENOENT:
1043 raise
1044 try:
1045 chroot_dir = cls._InstanceChrootDir(instance_name)
1046 utils.RemoveDir(chroot_dir)
1047 except OSError, err:
1048 if err.errno == errno.ENOTEMPTY:
1049 # The chroot directory is expected to be empty, but it isn't.
1050 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
1051 prefix="%s-%s-" %
1052 (instance_name,
1053 utils.TimestampForFilename()))
1054 logging.warning("The chroot directory of instance %s can not be"
1055 " removed as it is not empty. Moving it to the"
1056 " quarantine instead. Please investigate the"
1057 " contents (%s) and clean up manually",
1058 instance_name, new_chroot_dir)
1059 utils.RenameFile(chroot_dir, new_chroot_dir)
1060 else:
1061 raise
1062
1063 @staticmethod
1064 def _ConfigureNIC(instance, seq, nic, tap):
1065 """Run the network configuration script for a specified NIC
1066
1067 @param instance: instance we're acting on
1068 @type instance: instance object
1069 @param seq: nic sequence number
1070 @type seq: int
1071 @param nic: nic we're acting on
1072 @type nic: nic object
1073 @param tap: the host's tap interface this NIC corresponds to
1074 @type tap: str
1075
1076 """
1077 env = {
1078 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
1079 "INSTANCE": instance.name,
1080 "MAC": nic.mac,
1081 "MODE": nic.nicparams[constants.NIC_MODE],
1082 "INTERFACE": tap,
1083 "INTERFACE_INDEX": str(seq),
1084 "INTERFACE_UUID": nic.uuid,
1085 "TAGS": " ".join(instance.GetTags()),
1086 }
1087
1088 if nic.ip:
1089 env["IP"] = nic.ip
1090
1091 if nic.name:
1092 env["INTERFACE_NAME"] = nic.name
1093
1094 if nic.nicparams[constants.NIC_LINK]:
1095 env["LINK"] = nic.nicparams[constants.NIC_LINK]
1096
1097 if nic.nicparams[constants.NIC_VLAN]:
1098 env["VLAN"] = nic.nicparams[constants.NIC_VLAN]
1099
1100 if nic.network:
1101 n = objects.Network.FromDict(nic.netinfo)
1102 env.update(n.HooksDict())
1103
1104 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1105 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
1106
1107 result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
1108 if result.failed:
1109 raise errors.HypervisorError("Failed to configure interface %s: %s;"
1110 " network configuration script output: %s" %
1111 (tap, result.fail_reason, result.output))
1112
1113 @staticmethod
1114 def _VerifyAffinityPackage():
1115 if affinity is None:
1116 raise errors.HypervisorError("affinity Python package not"
1117 " found; cannot use CPU pinning under KVM")
1118
1119 @staticmethod
1120 def _BuildAffinityCpuMask(cpu_list):
1121 """Create a CPU mask suitable for sched_setaffinity from a list of
1122 CPUs.
1123
1124 See man taskset for more info on sched_setaffinity masks.
1125 For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
1126
1127 @type cpu_list: list of int
1128 @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1129 @rtype: int
1130 @return: a bit mask of CPU affinities
1131
1132 """
1133 if cpu_list == constants.CPU_PINNING_OFF:
1134 return constants.CPU_PINNING_ALL_KVM
1135 else:
1136 return sum(2 ** cpu for cpu in cpu_list)
1137
1138 @classmethod
1139 def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1140 """Change CPU affinity for running VM according to given CPU mask.
1141
1142 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1143 @type cpu_mask: string
1144 @param process_id: process ID of KVM process. Used to pin entire VM
1145 to physical CPUs.
1146 @type process_id: int
1147 @param thread_dict: map of virtual CPUs to KVM thread IDs
1148 @type thread_dict: dict int:int
1149
1150 """
1151 # Convert the string CPU mask to a list of list of int's
1152 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1153
1154 if len(cpu_list) == 1:
1155 all_cpu_mapping = cpu_list[0]
1156 if all_cpu_mapping == constants.CPU_PINNING_OFF:
1157 # If CPU pinning has 1 entry that's "all", then do nothing
1158 pass
1159 else:
1160 # If CPU pinning has one non-all entry, map the entire VM to
1161 # one set of physical CPUs
1162 cls._VerifyAffinityPackage()
1163 affinity.set_process_affinity_mask(
1164 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
1165 else:
1166 # The number of vCPUs mapped should match the number of vCPUs
1167 # reported by KVM. This was already verified earlier, so
1168 # here only as a sanity check.
1169 assert len(thread_dict) == len(cpu_list)
1170 cls._VerifyAffinityPackage()
1171
1172 # For each vCPU, map it to the proper list of physical CPUs
1173 for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1174 affinity.set_process_affinity_mask(thread_dict[i],
1175 cls._BuildAffinityCpuMask(vcpu))
1176
1177 def _GetVcpuThreadIds(self, instance_name):
1178 """Get a mapping of vCPU no. to thread IDs for the instance
1179
1180 @type instance_name: string
1181 @param instance_name: instance in question
1182 @rtype: dictionary of int:int
1183 @return: a dictionary mapping vCPU numbers to thread IDs
1184
1185 """
1186 result = {}
1187 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1188 for line in output.stdout.splitlines():
1189 match = self._CPU_INFO_RE.search(line)
1190 if not match:
1191 continue
1192 grp = map(int, match.groups())
1193 result[grp[0]] = grp[1]
1194
1195 return result
1196
1197 def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1198 """Complete CPU pinning.
1199
1200 @type instance_name: string
1201 @param instance_name: name of instance
1202 @type cpu_mask: string
1203 @param cpu_mask: CPU pinning mask as entered by user
1204
1205 """
1206 # Get KVM process ID, to be used if need to pin entire VM
1207 _, pid, _ = self._InstancePidAlive(instance_name)
1208 # Get vCPU thread IDs, to be used if need to pin vCPUs separately
1209 thread_dict = self._GetVcpuThreadIds(instance_name)
1210 # Run CPU pinning, based on configured mask
1211 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1212
1213 def ListInstances(self, hvparams=None):
1214 """Get the list of running instances.
1215
1216 We can do this by listing our live instances directory and
1217 checking whether the associated kvm process is still alive.
1218
1219 """
1220 result = []
1221 for name in os.listdir(self._PIDS_DIR):
1222 if self._InstancePidAlive(name)[2] or self._IsUserShutdown(name):
1223 result.append(name)
1224 return result
1225
1226 @classmethod
1227 def _IsUserShutdown(cls, instance_name):
1228 return os.path.exists(cls._InstanceShutdownMonitor(instance_name))
1229
1230 @classmethod
1231 def _ClearUserShutdown(cls, instance_name):
1232 utils.RemoveFile(cls._InstanceShutdownMonitor(instance_name))
1233
1234 def GetInstanceInfo(self, instance_name, hvparams=None):
1235 """Get instance properties.
1236
1237 @type instance_name: string
1238 @param instance_name: the instance name
1239 @type hvparams: dict of strings
1240 @param hvparams: hvparams to be used with this instance
1241 @rtype: tuple of strings
1242 @return: (name, id, memory, vcpus, stat, times)
1243
1244 """
1245 _, pid, alive = self._InstancePidAlive(instance_name)
1246 if not alive:
1247 if self._IsUserShutdown(instance_name):
1248 return (instance_name, -1, 0, 0, hv_base.HvInstanceState.SHUTDOWN, 0)
1249 else:
1250 return None
1251
1252 _, memory, vcpus = self._InstancePidInfo(pid)
1253 istat = hv_base.HvInstanceState.RUNNING
1254 times = 0
1255
1256 try:
1257 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1258 qmp.connect()
1259 vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1260 # Will fail if ballooning is not enabled, but we can then just resort to
1261 # the value above.
1262 mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1263 memory = mem_bytes / 1048576
1264 except errors.HypervisorError:
1265 pass
1266
1267 return (instance_name, pid, memory, vcpus, istat, times)
1268
1269 def GetAllInstancesInfo(self, hvparams=None):
1270 """Get properties of all instances.
1271
1272 @type hvparams: dict of strings
1273 @param hvparams: hypervisor parameter
1274 @return: list of tuples (name, id, memory, vcpus, stat, times)
1275
1276 """
1277 data = []
1278 for name in os.listdir(self._PIDS_DIR):
1279 try:
1280 info = self.GetInstanceInfo(name)
1281 except errors.HypervisorError:
1282 # Ignore exceptions due to instances being shut down
1283 continue
1284 if info:
1285 data.append(info)
1286 return data
1287
1288 def _GenerateKVMBlockDevicesOptions(self, instance, kvm_disks,
1289 kvmhelp, devlist):
1290 """Generate KVM options regarding instance's block devices.
1291
1292 @type instance: L{objects.Instance}
1293 @param instance: the instance object
1294 @type kvm_disks: list of tuples
1295 @param kvm_disks: list of tuples [(disk, link_name, uri)..]
1296 @type kvmhelp: string
1297 @param kvmhelp: output of kvm --help
1298 @type devlist: string
1299 @param devlist: output of kvm -device ?
1300 @rtype: list
1301 @return: list of command line options eventually used by kvm executable
1302
1303 """
1304 hvp = instance.hvparams
1305 kernel_path = hvp[constants.HV_KERNEL_PATH]
1306 if kernel_path:
1307 boot_disk = False
1308 else:
1309 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1310
1311 # whether this is an older KVM version that uses the boot=on flag
1312 # on devices
1313 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1314
1315 dev_opts = []
1316 device_driver = None
1317 disk_type = hvp[constants.HV_DISK_TYPE]
1318 if disk_type == constants.HT_DISK_PARAVIRTUAL:
1319 if_val = ",if=%s" % self._VIRTIO
1320 try:
1321 if self._VIRTIO_BLK_RE.search(devlist):
1322 if_val = ",if=none"
1323 # will be passed in -device option as driver
1324 device_driver = self._VIRTIO_BLK_PCI
1325 except errors.HypervisorError, _:
1326 pass
1327 else:
1328 if_val = ",if=%s" % disk_type
1329 # Cache mode
1330 disk_cache = hvp[constants.HV_DISK_CACHE]
1331 if instance.disk_template in constants.DTS_EXT_MIRROR:
1332 if disk_cache != "none":
1333 # TODO: make this a hard error, instead of a silent overwrite
1334 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1335 " to prevent shared storage corruption on migration",
1336 disk_cache)
1337 cache_val = ",cache=none"
1338 elif disk_cache != constants.HT_CACHE_DEFAULT:
1339 cache_val = ",cache=%s" % disk_cache
1340 else:
1341 cache_val = ""
1342 for cfdev, link_name, uri in kvm_disks:
1343 if cfdev.mode != constants.DISK_RDWR:
1344 raise errors.HypervisorError("Instance has read-only disks which"
1345 " are not supported by KVM")
1346 # TODO: handle FD_LOOP and FD_BLKTAP (?)
1347 boot_val = ""
1348 if boot_disk:
1349 dev_opts.extend(["-boot", "c"])
1350 boot_disk = False
1351 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1352 boot_val = ",boot=on"
1353
1354 access_mode = cfdev.params.get(constants.LDP_ACCESS,
1355 constants.DISK_KERNELSPACE)
1356 if (uri and access_mode == constants.DISK_USERSPACE):
1357 drive_uri = uri
1358 else:
1359 drive_uri = link_name
1360
1361 drive_val = "file=%s,format=raw%s%s%s" % \
1362 (drive_uri, if_val, boot_val, cache_val)
1363
1364 if device_driver:
1365 # kvm_disks are the 4th entry of runtime file that did not exist in
1366 # the past. That means that cfdev should always have pci slot and
1367 # _GenerateDeviceKVMId() will not raise a exception.
1368 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK, cfdev)
1369 drive_val += (",id=%s" % kvm_devid)
1370 drive_val += (",bus=0,unit=%d" % cfdev.pci)
1371 dev_val = ("%s,drive=%s,id=%s" %
1372 (device_driver, kvm_devid, kvm_devid))
1373 dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1374 dev_opts.extend(["-device", dev_val])
1375
1376 dev_opts.extend(["-drive", drive_val])
1377
1378 return dev_opts
1379
1380 @staticmethod
1381 def _CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image, cdrom_boot,
1382 needs_boot_flag):
1383 """Extends L{kvm_cmd} with the '-drive' option for a cdrom, and
1384 optionally the '-boot' option.
1385
1386 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide -boot d
1387
1388 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide,boot=on
1389
1390 Example: -drive file=http://hostname.com/cdrom.iso,media=cdrom
1391
1392 @type kvm_cmd: string
1393 @param kvm_cmd: KVM command line
1394
1395 @type cdrom_disk_type:
1396 @param cdrom_disk_type:
1397
1398 @type cdrom_image:
1399 @param cdrom_image:
1400
1401 @type cdrom_boot:
1402 @param cdrom_boot:
1403
1404 @type needs_boot_flag:
1405 @param needs_boot_flag:
1406
1407 """
1408 # Check that the ISO image is accessible
1409 # See https://bugs.launchpad.net/qemu/+bug/597575
1410 if utils.IsUrl(cdrom_image) and not _CheckUrl(cdrom_image):
1411 raise errors.HypervisorError("Cdrom ISO image '%s' is not accessible" %
1412 cdrom_image)
1413
1414 # set cdrom 'media' and 'format', if needed
1415 if utils.IsUrl(cdrom_image):
1416 options = ",media=cdrom"
1417 else:
1418 options = ",media=cdrom,format=raw"
1419
1420 # set cdrom 'if' type
1421 if cdrom_boot:
1422 if_val = ",if=" + constants.HT_DISK_IDE
1423 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1424 if_val = ",if=virtio"
1425 else:
1426 if_val = ",if=" + cdrom_disk_type
1427
1428 # set boot flag, if needed
1429 boot_val = ""
1430 if cdrom_boot:
1431 kvm_cmd.extend(["-boot", "d"])
1432
1433 # whether this is an older KVM version that requires the 'boot=on' flag
1434 # on devices
1435 if needs_boot_flag:
1436 boot_val = ",boot=on"
1437
1438 # build '-drive' option
1439 drive_val = "file=%s%s%s%s" % (cdrom_image, options, if_val, boot_val)
1440 kvm_cmd.extend(["-drive", drive_val])
1441
1442 def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1443 kvmhelp):
1444 """Generate KVM information to start an instance.
1445
1446 @type kvmhelp: string
1447 @param kvmhelp: output of kvm --help
1448 @attention: this function must not have any side-effects; for
1449 example, it must not write to the filesystem, or read values
1450 from the current system the are expected to differ between
1451 nodes, since it is only run once at instance startup;
1452 actions/kvm arguments that can vary between systems should be
1453 done in L{_ExecuteKVMRuntime}
1454
1455 """
1456 # pylint: disable=R0912,R0914,R0915
1457 hvp = instance.hvparams
1458 self.ValidateParameters(hvp)
1459
1460 pidfile = self._InstancePidFile(instance.name)
1461 kvm = hvp[constants.HV_KVM_PATH]
1462 kvm_cmd = [kvm]
1463 # used just by the vnc server, if enabled
1464 kvm_cmd.extend(["-name", instance.name])
1465 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1466
1467 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1468 if hvp[constants.HV_CPU_CORES]:
1469 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1470 if hvp[constants.HV_CPU_THREADS]:
1471 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1472 if hvp[constants.HV_CPU_SOCKETS]:
1473 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1474
1475 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1476
1477 kvm_cmd.extend(["-pidfile", pidfile])
1478
1479 pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1480
1481 # As requested by music lovers
1482 if hvp[constants.HV_SOUNDHW]:
1483 soundhw = hvp[constants.HV_SOUNDHW]
1484 # For some reason only few sound devices require a PCI slot
1485 # while the Audio controller *must* be in slot 3.
1486 # That's why we bridge this option early in command line
1487 if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
1488 _ = _GetFreeSlot(pci_reservations, reserve=True)
1489 kvm_cmd.extend(["-soundhw", soundhw])
1490
1491 # Add id to ballon and place to the first available slot (3 or 4)
1492 addr = _GetFreeSlot(pci_reservations, reserve=True)
1493 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1494 kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
1495 kvm_cmd.extend(["-daemonize"])
1496 if not instance.hvparams[constants.HV_ACPI]:
1497 kvm_cmd.extend(["-no-acpi"])
1498 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1499 constants.INSTANCE_REBOOT_EXIT:
1500 kvm_cmd.extend(["-no-reboot"])
1501
1502 mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1503 if not mversion:
1504 mversion = self._GetDefaultMachineVersion(kvm)
1505 if self._MACHINE_RE.search(kvmhelp):
1506 # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
1507 # extra hypervisor parameters. We should also investigate whether and how
1508 # shadow_mem should be considered for the resource model.
1509 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1510 specprop = ",accel=kvm"
1511 else:
1512 specprop = ""
1513 machinespec = "%s%s" % (mversion, specprop)
1514 kvm_cmd.extend(["-machine", machinespec])
1515 else:
1516 kvm_cmd.extend(["-M", mversion])
1517 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1518 self._ENABLE_KVM_RE.search(kvmhelp)):
1519 kvm_cmd.extend(["-enable-kvm"])
1520 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1521 self._DISABLE_KVM_RE.search(kvmhelp)):
1522 kvm_cmd.extend(["-disable-kvm"])
1523
1524 kernel_path = hvp[constants.HV_KERNEL_PATH]
1525 if kernel_path:
1526 boot_cdrom = boot_floppy = boot_network = False
1527 else:
1528 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1529 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1530 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1531
1532 if startup_paused:
1533 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1534
1535 if boot_network:
1536 kvm_cmd.extend(["-boot", "n"])
1537
1538 disk_type = hvp[constants.HV_DISK_TYPE]
1539
1540 # Now we can specify a different device type for CDROM devices.
1541 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1542 if not cdrom_disk_type:
1543 cdrom_disk_type = disk_type
1544
1545 cdrom_image1 = hvp[constants.HV_CDROM_IMAGE_PATH]
1546 if cdrom_image1:
1547 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1548 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image1, boot_cdrom,
1549 needs_boot_flag)
1550
1551 cdrom_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1552 if cdrom_image2:
1553 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image2, False, False)
1554
1555 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1556 if floppy_image:
1557 options = ",format=raw,media=disk"
1558 if boot_floppy:
1559 kvm_cmd.extend(["-boot", "a"])
1560 options = "%s,boot=on" % options
1561 if_val = ",if=floppy"
1562 options = "%s%s" % (options, if_val)
1563 drive_val = "file=%s%s" % (floppy_image, options)
1564 kvm_cmd.extend(["-drive", drive_val])
1565
1566 if kernel_path:
1567 kvm_cmd.extend(["-kernel", kernel_path])
1568 initrd_path = hvp[constants.HV_INITRD_PATH]
1569 if initrd_path:
1570 kvm_cmd.extend(["-initrd", initrd_path])
1571 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1572 hvp[constants.HV_KERNEL_ARGS]]
1573 if hvp[constants.HV_SERIAL_CONSOLE]:
1574 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1575 root_append.append("console=ttyS0,%s" % serial_speed)
1576 kvm_cmd.extend(["-append", " ".join(root_append)])
1577
1578 mem_path = hvp[constants.HV_MEM_PATH]
1579 if mem_path:
1580 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1581
1582 monitor_dev = ("unix:%s,server,nowait" %
1583 self._InstanceMonitor(instance.name))
1584 kvm_cmd.extend(["-monitor", monitor_dev])
1585 if hvp[constants.HV_SERIAL_CONSOLE]:
1586 serial_dev = ("unix:%s,server,nowait" %
1587 self._InstanceSerial(instance.name))
1588 kvm_cmd.extend(["-serial", serial_dev])
1589 else:
1590 kvm_cmd.extend(["-serial", "none"])
1591
1592 mouse_type = hvp[constants.HV_USB_MOUSE]
1593 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1594 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1595 spice_ip_version = None
1596
1597 kvm_cmd.extend(["-usb"])
1598
1599 if mouse_type:
1600 kvm_cmd.extend(["-usbdevice", mouse_type])
1601 elif vnc_bind_address:
1602 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1603
1604 if vnc_bind_address:
1605 if netutils.IsValidInterface(vnc_bind_address):
1606 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1607 if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1608 if len(if_ip4_addresses) < 1:
1609 logging.error("Could not determine IPv4 address of interface %s",
1610 vnc_bind_address)
1611 else:
1612 vnc_bind_address = if_ip4_addresses[0]
1613 if netutils.IP4Address.IsValid(vnc_bind_address):
1614 if instance.network_port > constants.VNC_BASE_PORT:
1615 display = instance.network_port - constants.VNC_BASE_PORT
1616 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1617 vnc_arg = ":%d" % (display)
1618 else:
1619 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1620 else:
1621 logging.error("Network port is not a valid VNC display (%d < %d),"
1622 " not starting VNC",
1623 instance.network_port, constants.VNC_BASE_PORT)
1624 vnc_arg = "none"
1625
1626 # Only allow tls and other option when not binding to a file, for now.
1627 # kvm/qemu gets confused otherwise about the filename to use.
1628 vnc_append = ""
1629 if hvp[constants.HV_VNC_TLS]:
1630 vnc_append = "%s,tls" % vnc_append
1631 if hvp[constants.HV_VNC_X509_VERIFY]:
1632 vnc_append = "%s,x509verify=%s" % (vnc_append,
1633 hvp[constants.HV_VNC_X509])
1634 elif hvp[constants.HV_VNC_X509]:
1635 vnc_append = "%s,x509=%s" % (vnc_append,
1636 hvp[constants.HV_VNC_X509])
1637 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1638 vnc_append = "%s,password" % vnc_append
1639
1640 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1641
1642 else:
1643 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1644
1645 kvm_cmd.extend(["-vnc", vnc_arg])
1646 elif spice_bind:
1647 # FIXME: this is wrong here; the iface ip address differs
1648 # between systems, so it should be done in _ExecuteKVMRuntime
1649 if netutils.IsValidInterface(spice_bind):
1650 # The user specified a network interface, we have to figure out the IP
1651 # address.
1652 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1653 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1654
1655 # if the user specified an IP version and the interface does not
1656 # have that kind of IP addresses, throw an exception
1657 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1658 if not addresses[spice_ip_version]:
1659 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1660 " for %s" % (spice_ip_version,
1661 spice_bind))
1662
1663 # the user did not specify an IP version, we have to figure it out
1664 elif (addresses[constants.IP4_VERSION] and
1665 addresses[constants.IP6_VERSION]):
1666 # we have both ipv4 and ipv6, let's use the cluster default IP
1667 # version
1668 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1669 spice_ip_version = \
1670 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1671 elif addresses[constants.IP4_VERSION]:
1672 spice_ip_version = constants.IP4_VERSION
1673 elif addresses[constants.IP6_VERSION]:
1674 spice_ip_version = constants.IP6_VERSION
1675 else:
1676 raise errors.HypervisorError("SPICE: Unable to get an IP address"
1677 " for %s" % (spice_bind))
1678
1679 spice_address = addresses[spice_ip_version][0]
1680
1681 else:
1682 # spice_bind is known to be a valid IP address, because
1683 # ValidateParameters checked it.
1684 spice_address = spice_bind
1685
1686 spice_arg = "addr=%s" % spice_address
1687 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1688 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1689 (spice_arg, instance.network_port,
1690 pathutils.SPICE_CACERT_FILE))
1691 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1692 (spice_arg, pathutils.SPICE_CERT_FILE,
1693 pathutils.SPICE_CERT_FILE))
1694 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1695 if tls_ciphers:
1696 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1697 else:
1698 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1699
1700 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1701 spice_arg = "%s,disable-ticketing" % spice_arg
1702
1703 if spice_ip_version:
1704 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1705
1706 # Image compression options
1707 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1708 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1709 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1710 if img_lossless:
1711 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1712 if img_jpeg:
1713 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1714 if img_zlib_glz:
1715 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1716
1717 # Video stream detection
1718 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1719 if video_streaming:
1720 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1721
1722 # Audio compression, by default in qemu-kvm it is on
1723 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1724 spice_arg = "%s,playback-compression=off" % spice_arg
1725 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1726 spice_arg = "%s,agent-mouse=off" % spice_arg
1727 else:
1728 # Enable the spice agent communication channel between the host and the
1729 # agent.
1730 addr = _GetFreeSlot(pci_reservations, reserve=True)
1731 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1732 kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
1733 kvm_cmd.extend([
1734 "-device",
1735 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1736 ])
1737 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1738
1739 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1740 kvm_cmd.extend(["-spice", spice_arg])
1741
1742 else:
1743 # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1744 # also works in earlier versions though (tested with 1.1 and 1.3)
1745 if self._DISPLAY_RE.search(kvmhelp):
1746 kvm_cmd.extend(["-display", "none"])
1747 else:
1748 kvm_cmd.extend(["-nographic"])
1749
1750 if hvp[constants.HV_USE_LOCALTIME]:
1751 kvm_cmd.extend(["-localtime"])
1752
1753 if hvp[constants.HV_KVM_USE_CHROOT]:
1754 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1755
1756 # Add qemu-KVM -cpu param
1757 if hvp[constants.HV_CPU_TYPE]:
1758 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1759
1760 # Pass a -vga option if requested, or if spice is used, for backwards
1761 # compatibility.
1762 if hvp[constants.HV_VGA]:
1763 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1764 elif spice_bind:
1765 kvm_cmd.extend(["-vga", "qxl"])
1766
1767 # Various types of usb devices, comma separated
1768 if hvp[constants.HV_USB_DEVICES]:
1769 for dev in hvp[constants.HV_USB_DEVICES].split(","):
1770 kvm_cmd.extend(["-usbdevice", dev])
1771
1772 # Set system UUID to instance UUID
1773 if self._UUID_RE.search(kvmhelp):
1774 kvm_cmd.extend(["-uuid", instance.uuid])
1775
1776 if hvp[constants.HV_KVM_EXTRA]:
1777 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1778
1779 kvm_disks = []
1780 for disk, link_name, uri in block_devices:
1781 disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
1782 kvm_disks.append((disk, link_name, uri))
1783
1784 kvm_nics = []
1785 for nic in instance.nics:
1786 nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True)
1787 kvm_nics.append(nic)
1788
1789 hvparams = hvp
1790
1791 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1792
1793 def _WriteKVMRuntime(self, instance_name, data):
1794 """Write an instance's KVM runtime
1795
1796 """
1797 try:
1798 utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1799 data=data)
1800 except EnvironmentError, err:
1801 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1802
1803 def _ReadKVMRuntime(self, instance_name):
1804 """Read an instance's KVM runtime
1805
1806 """
1807 try:
1808 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1809 except EnvironmentError, err:
1810 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1811 return file_content
1812
1813 def _SaveKVMRuntime(self, instance, kvm_runtime):
1814 """Save an instance's KVM runtime
1815
1816 """
1817 kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1818
1819 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1820 serialized_disks = [(blk.ToDict(), link, uri)
1821 for blk, link, uri in kvm_disks]
1822 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1823 serialized_disks))
1824
1825 self._WriteKVMRuntime(instance.name, serialized_form)
1826
1827 def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1828 """Load an instance's KVM runtime
1829
1830 """
1831 if not serialized_runtime:
1832 serialized_runtime = self._ReadKVMRuntime(instance.name)
1833
1834 return _AnalyzeSerializedRuntime(serialized_runtime)
1835
1836 def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1837 """Run the KVM cmd and check for errors
1838
1839 @type name: string
1840 @param name: instance name
1841 @type kvm_cmd: list of strings
1842 @param kvm_cmd: runcmd input for kvm
1843 @type tap_fds: list of int
1844 @param tap_fds: fds of tap devices opened by Ganeti
1845
1846 """
1847 try:
1848 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1849 finally:
1850 for fd in tap_fds:
1851 utils_wrapper.CloseFdNoError(fd)
1852
1853 if result.failed:
1854 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1855 (name, result.fail_reason, result.output))
1856 if not self._InstancePidAlive(name)[2]:
1857 raise errors.HypervisorError("Failed to start instance %s" % name)
1858
1859 @staticmethod
1860 def _GenerateTapName(nic):
1861 """Generate a TAP network interface name for a NIC.
1862
1863 This helper function generates a special TAP network interface
1864 name for NICs that are meant to be used in instance communication.
1865 This function checks the existing TAP interfaces in order to find
1866 a unique name for the new TAP network interface. The TAP network
1867 interface names are of the form 'gnt.com.%d', where '%d' is a
1868 unique number within the node.
1869
1870 @type nic: ganeti.objects.NIC
1871 @param nic: NIC object for the name should be generated
1872
1873 @rtype: string
1874 @return: TAP network interface name, or the empty string if the
1875 NIC is not used in instance communication
1876
1877 """
1878 if nic.name is None or not \
1879 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
1880 return ""
1881
1882 result = utils.RunCmd(["ip", "tuntap", "list"])
1883
1884 if result.failed:
1885 raise errors.HypervisorError("Failed to list TUN/TAP interfaces")
1886
1887 idxs = set()
1888
1889 for line in result.output.splitlines():
1890 parts = line.split(": ", 1)
1891
1892 if len(parts) < 2:
1893 raise errors.HypervisorError("Failed to parse TUN/TAP interfaces")
1894
1895 r = re.match(r"gnt\.com\.([0-9]+)", parts[0])
1896
1897 if r is not None:
1898 idxs.add(int(r.group(1)))
1899
1900 if idxs:
1901 idx = max(idxs) + 1
1902 else:
1903 idx = 0
1904
1905 return "gnt.com.%d" % idx
1906
1907 # too many local variables
1908 # pylint: disable=R0914
1909 def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1910 """Execute a KVM cmd, after completing it with some last minute data.
1911
1912 @type incoming: tuple of strings
1913 @param incoming: (target_host_ip, port)
1914 @type kvmhelp: string
1915 @param kvmhelp: output of kvm --help
1916
1917 """
1918 # Small _ExecuteKVMRuntime hv parameters programming howto:
1919 # - conf_hvp contains the parameters as configured on ganeti. they might
1920 # have changed since the instance started; only use them if the change
1921 # won't affect the inside of the instance (which hasn't been rebooted).
1922 # - up_hvp contains the parameters as they were when the instance was
1923 # started, plus any new parameter which has been added between ganeti
1924 # versions: it is paramount that those default to a value which won't
1925 # affect the inside of the instance as well.
1926 conf_hvp = instance.hvparams
1927 name = instance.name
1928 self._CheckDown(name)
1929
1930 self._ClearUserShutdown(instance.name)
1931 self._StartKvmd(instance.hvparams)
1932
1933 temp_files = []
1934
1935 kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1936 # the first element of kvm_cmd is always the path to the kvm binary
1937 kvm_path = kvm_cmd[0]
1938 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1939
1940 # We know it's safe to run as a different user upon migration, so we'll use
1941 # the latest conf, from conf_hvp.
1942 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1943 if security_model == constants.HT_SM_USER:
1944 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1945
1946 keymap = conf_hvp[constants.HV_KEYMAP]
1947 if keymap:
1948 keymap_path = self._InstanceKeymapFile(name)
1949 # If a keymap file is specified, KVM won't use its internal defaults. By
1950 # first including the "en-us" layout, an error on loading the actual
1951 # layout (e.g. because it can't be found) won't lead to a non-functional
1952 # keyboard. A keyboard with incorrect keys is still better than none.
1953 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1954 kvm_cmd.extend(["-k", keymap_path])
1955
1956 # We have reasons to believe changing something like the nic driver/type
1957 # upon migration won't exactly fly with the instance kernel, so for nic
1958 # related parameters we'll use up_hvp
1959 tapfds = []
1960 taps = []
1961 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1962 if not kvm_nics:
1963 kvm_cmd.extend(["-net", "none"])
1964 else:
1965 vnet_hdr = False
1966 tap_extra = ""
1967 nic_type = up_hvp[constants.HV_NIC_TYPE]
1968 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1969 nic_model = self._VIRTIO
1970 try:
1971 if self._VIRTIO_NET_RE.search(devlist):
1972 nic_model = self._VIRTIO_NET_PCI
1973 vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1974 except errors.HypervisorError, _:
1975 # Older versions of kvm don't support DEVICE_LIST, but they don't
1976 # have new virtio syntax either.
1977 pass
1978
1979 if up_hvp[constants.HV_VHOST_NET]:
1980 # check for vhost_net support
1981 if self._VHOST_RE.search(kvmhelp):
1982 tap_extra = ",vhost=on"
1983 else:
1984 raise errors.HypervisorError("vhost_net is configured"
1985 " but it is not available")
1986 else:
1987 nic_model = nic_type
1988
1989 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1990
1991 for nic_seq, nic in enumerate(kvm_nics):
1992 tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr,
1993 name=self._GenerateTapName(nic))
1994 tapfds.append(tapfd)
1995 taps.append(tapname)
1996 if kvm_supports_netdev:
1997 nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1998 try:
1999 # kvm_nics already exist in old runtime files and thus there might
2000 # be some entries without pci slot (therefore try: except:)
2001 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2002 netdev = kvm_devid
2003 nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
2004 except errors.HotplugError:
2005 netdev = "netdev%d" % nic_seq
2006 nic_val += (",netdev=%s" % netdev)
2007 tap_val = ("type=tap,id=%s,fd=%d%s" %
2008 (netdev, tapfd, tap_extra))
2009 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
2010 else:
2011 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
2012 nic.mac, nic_model)
2013 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
2014 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
2015
2016 if incoming:
2017 target, port = incoming
2018 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
2019
2020 # Changing the vnc password doesn't bother the guest that much. At most it
2021 # will surprise people who connect to it. Whether positively or negatively
2022 # it's debatable.
2023 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
2024 vnc_pwd = None
2025 if vnc_pwd_file:
2026 try:
2027 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
2028 except EnvironmentError, err:
2029 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
2030 % (vnc_pwd_file, err))
2031
2032 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
2033 utils.EnsureDirs([(self._InstanceChrootDir(name),
2034 constants.SECURE_DIR_MODE)])
2035
2036 # Automatically enable QMP if version is >= 0.14
2037 if self._QMP_RE.search(kvmhelp):
2038 logging.debug("Enabling QMP")
2039 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
2040 self._InstanceQmpMonitor(instance.name)])
2041
2042 # Configure the network now for starting instances and bridged interfaces,
2043 # during FinalizeMigration for incoming instances' routed interfaces
2044 for nic_seq, nic in enumerate(kvm_nics):
2045 if (incoming and
2046 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
2047 continue
2048 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
2049
2050 bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
2051 kvm_disks,
2052 kvmhelp,
2053 devlist)
2054 kvm_cmd.extend(bdev_opts)
2055 # CPU affinity requires kvm to start paused, so we set this flag if the
2056 # instance is not already paused and if we are not going to accept a
2057 # migrating instance. In the latter case, pausing is not needed.
2058 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
2059 if start_kvm_paused:
2060 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
2061
2062 # Note: CPU pinning is using up_hvp since changes take effect
2063 # during instance startup anyway, and to avoid problems when soft
2064 # rebooting the instance.
2065 cpu_pinning = False
2066 if up_hvp.get(constants.HV_CPU_MASK, None):
2067 cpu_pinning = True
2068
2069 if security_model == constants.HT_SM_POOL:
2070 ss = ssconf.SimpleStore()
2071 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
2072 all_uids = set(uidpool.ExpandUidPool(uid_pool))
2073 uid = uidpool.RequestUnusedUid(all_uids)
2074 try:
2075 username = pwd.getpwuid(uid.GetUid()).pw_name
2076 kvm_cmd.extend(["-runas", username])
2077 self._RunKVMCmd(name, kvm_cmd, tapfds)
2078 except:
2079 uidpool.ReleaseUid(uid)
2080 raise
2081 else:
2082 uid.Unlock()
2083 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
2084 else:
2085 self._RunKVMCmd(name, kvm_cmd, tapfds)
2086
2087 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
2088 constants.RUN_DIRS_MODE)])
2089 for nic_seq, tap in enumerate(taps):
2090 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
2091 data=tap)
2092
2093 if vnc_pwd:
2094 change_cmd = "change vnc password %s" % vnc_pwd
2095 self._CallMonitorCommand(instance.name, change_cmd)
2096
2097 # Setting SPICE password. We are not vulnerable to malicious passwordless
2098 # connection attempts because SPICE by default does not allow connections
2099 # if neither a password nor the "disable_ticketing" options are specified.
2100 # As soon as we send the password via QMP, that password is a valid ticket
2101 # for connection.
2102 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
2103 if spice_password_file:
2104 spice_pwd = ""
2105 try:
2106 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
2107 except EnvironmentError, err:
2108 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
2109 % (spice_password_file, err))
2110
2111 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
2112 qmp.connect()
2113 arguments = {
2114 "protocol": "spice",
2115 "password": spice_pwd,
2116 }
2117 qmp.Execute("set_password", arguments)
2118
2119 for filename in temp_files:
2120 utils.RemoveFile(filename)
2121
2122 # If requested, set CPU affinity and resume instance execution
2123 if cpu_pinning:
2124 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
2125
2126 start_memory = self._InstanceStartupMemory(instance)
2127 if start_memory < instance.beparams[constants.BE_MAXMEM]:
2128 self.BalloonInstanceMemory(instance, start_memory)
2129
2130 if start_kvm_paused:
2131 # To control CPU pinning, ballooning, and vnc/spice passwords
2132 # the VM was started in a frozen state. If freezing was not
2133 # explicitly requested resume the vm status.
2134 self._CallMonitorCommand(instance.name, self._CONT_CMD)
2135
2136 @staticmethod
2137 def _StartKvmd(hvparams):
2138 """Ensure that the Kvm daemon is running.
2139
2140 """
2141 if hvparams is None \
2142 or not hvparams[constants.HV_KVM_USER_SHUTDOWN] \
2143 or utils.IsDaemonAlive(constants.KVMD):
2144 return
2145
2146 result = utils.RunCmd(constants.KVMD)
2147
2148 if result.failed:
2149 raise errors.HypervisorError("Failed to start KVM daemon")
2150
2151 def StartInstance(self, instance, block_devices, startup_paused):
2152 """Start an instance.
2153
2154 """
2155 self._CheckDown(instance.name)
2156 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2157 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2158 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
2159 startup_paused, kvmhelp)
2160 self._SaveKVMRuntime(instance, kvm_runtime)
2161 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2162
2163 @classmethod
2164 def _CallMonitorCommand(cls, instance_name, command, timeout=None):
2165 """Invoke a command on the instance monitor.
2166
2167 """
2168 if timeout is not None:
2169 timeout_cmd = "timeout %s" % (timeout, )
2170 else:
2171 timeout_cmd = ""
2172
2173 # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2174 # version. The monitor protocol is designed for human consumption, whereas
2175 # QMP is made for programmatic usage. In the worst case QMP can also
2176 # execute monitor commands. As it is, all calls to socat take at least
2177 # 500ms and likely more: socat can't detect the end of the reply and waits
2178 # for 500ms of no data received before exiting (500 ms is the default for
2179 # the "-t" parameter).
2180 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
2181 (utils.ShellQuote(command),
2182 timeout_cmd,
2183 constants.SOCAT_PATH,
2184 utils.ShellQuote(cls._InstanceMonitor(instance_name))))
2185 result = utils.RunCmd(socat)
2186 if result.failed:
2187 msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2188 " output: %s" %
2189 (command, instance_name, result.fail_reason, result.output))
2190 raise errors.HypervisorError(msg)
2191
2192 return result
2193
2194 def _GetFreePCISlot(self, instance, dev):
2195 """Get the first available pci slot of a runnung instance.
2196
2197 """
2198 slots = bitarray(32)
2199 slots.setall(False) # pylint: disable=E1101
2200 output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2201 for line in output.stdout.splitlines():
2202 match = self._INFO_PCI_RE.search(line)
2203 if match:
2204 slot = int(match.group(1))
2205 slots[slot] = True
2206
2207 dev.pci = _GetFreeSlot(slots)
2208
2209 def VerifyHotplugSupport(self, instance, action, dev_type):
2210 """Verifies that hotplug is supported.
2211
2212 Hotplug is *not* supported in case of:
2213 - security models and chroot (disk hotplug)
2214 - fdsend module is missing (nic hot-add)
2215
2216 @raise errors.HypervisorError: in one of the previous cases
2217
2218 """
2219 if dev_type == constants.HOTPLUG_TARGET_DISK:
2220 hvp = instance.hvparams
2221 security_model = hvp[constants.HV_SECURITY_MODEL]
2222 use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2223 if use_chroot:
2224 raise errors.HotplugError("Disk hotplug is not supported"
2225 " in case of chroot.")
2226 if security_model != constants.HT_SM_NONE:
2227 raise errors.HotplugError("Disk Hotplug is not supported in case"
2228 " security models are used.")
2229
2230 if (dev_type == constants.HOTPLUG_TARGET_NIC and
2231 action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2232 raise errors.HotplugError("Cannot hot-add NIC."
2233 " fdsend python module is missing.")
2234
2235 def HotplugSupported(self, instance):
2236 """Checks if hotplug is generally supported.
2237
2238 Hotplug is *not* supported in case of:
2239 - qemu versions < 1.0
2240 - for stopped instances
2241
2242 @raise errors.HypervisorError: in one of the previous cases
2243
2244 """
2245 try:
2246 output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2247 except errors.HypervisorError:
2248 raise errors.HotplugError("Instance is probably down")
2249
2250 # TODO: search for netdev_add, drive_add, device_add.....
2251 match = self._INFO_VERSION_RE.search(output.stdout)
2252 if not match:
2253 raise errors.HotplugError("Cannot parse qemu version via monitor")
2254
2255 v_major, v_min, _, _ = match.groups()
2256 if (int(v_major), int(v_min)) < (1, 0):
2257 raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2258
2259 def _CallHotplugCommands(self, name, cmds):
2260 for c in cmds:
2261 self._CallMonitorCommand(name, c)
2262 time.sleep(1)
2263
2264 def _VerifyHotplugCommand(self, instance_name, device, dev_type,
2265 should_exist):
2266 """Checks if a previous hotplug command has succeeded.
2267
2268 It issues info pci monitor command and checks depending on should_exist
2269 value if an entry with PCI slot and device ID is found or not.
2270
2271 @raise errors.HypervisorError: if result is not the expected one
2272
2273 """
2274 output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2275 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2276 match = \
2277 self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2278 if match and not should_exist:
2279 msg = "Device %s should have been removed but is still there" % kvm_devid
2280 raise errors.HypervisorError(msg)
2281
2282 if not match and should_exist:
2283 msg = "Device %s should have been added but is missing" % kvm_devid
2284 raise errors.HypervisorError(msg)
2285
2286 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
2287
2288 def HotAddDevice(self, instance, dev_type, device, extra, seq):
2289 """ Helper method to hot-add a new device
2290
2291 It gets free pci slot generates the device name and invokes the
2292 device specific method.
2293
2294 """
2295 # in case of hot-mod this is given
2296 if device.pci is None:
2297 self._GetFreePCISlot(instance, device)
2298 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2299 runtime = self._LoadKVMRuntime(instance)
2300 if dev_type == constants.HOTPLUG_TARGET_DISK:
2301 cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
2302 (extra, kvm_devid)]
2303 cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2304 (hex(device.pci), kvm_devid, kvm_devid)]
2305 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2306 (tap, fd) = _OpenTap()
2307 self._ConfigureNIC(instance, seq, device, tap)
2308 self._PassTapFd(instance, fd, device)
2309 cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
2310 args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2311 (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2312 cmds += ["device_add %s" % args]
2313 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2314
2315 self._CallHotplugCommands(instance.name, cmds)
2316 self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2317 # update relevant entries in runtime file
2318 index = _DEVICE_RUNTIME_INDEX[dev_type]
2319 entry = _RUNTIME_ENTRY[dev_type](device, extra)
2320 runtime[index].append(entry)
2321 self._SaveKVMRuntime(instance, runtime)
2322
2323 def HotDelDevice(self, instance, dev_type, device, _, seq):
2324 """ Helper method for hot-del device
2325
2326 It gets device info from runtime file, generates the device name and
2327 invokes the device specific method.
2328
2329 """
2330 runtime = self._LoadKVMRuntime(instance)
2331 entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2332 kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2333 kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2334 if dev_type == constants.HOTPLUG_TARGET_DISK:
2335 cmds = ["device_del %s" % kvm_devid]
2336 cmds += ["drive_del %s" % kvm_devid]
2337 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2338 cmds = ["device_del %s" % kvm_devid]
2339 cmds += ["netdev_del %s" % kvm_devid]
2340 utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2341 self._CallHotplugCommands(instance.name, cmds)
2342 self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2343 index = _DEVICE_RUNTIME_INDEX[dev_type]
2344 runtime[index].remove(entry)
2345 self._SaveKVMRuntime(instance, runtime)
2346
2347 return kvm_device.pci
2348
2349 def HotModDevice(self, instance, dev_type, device, _, seq):
2350 """ Helper method for hot-mod device
2351
2352 It gets device info from runtime file, generates the device name and
2353 invokes the device specific method. Currently only NICs support hot-mod
2354
2355 """
2356 if dev_type == constants.HOTPLUG_TARGET_NIC:
2357 # putting it back in the same pci slot
2358 device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2359 self.HotAddDevice(instance, dev_type, device, _, seq)
2360
2361 def _PassTapFd(self, instance, fd, nic):
2362 """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2363
2364 """
2365 # TODO: factor out code related to unix sockets.
2366 # squash common parts between monitor and qmp
2367 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2368 command = "getfd %s\n" % kvm_devid
2369 fds = [fd]
2370 logging.info("%s", fds)
2371 try:
2372 monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2373 monsock.connect()
2374 fdsend.sendfds(monsock.sock, command, fds=fds)
2375 finally:
2376 monsock.close()
2377
2378 @classmethod
2379 def _ParseKVMVersion(cls, text):
2380 """Parse the KVM version from the --help output.
2381
2382 @type text: string
2383 @param text: output of kvm --help
2384 @return: (version, v_maj, v_min, v_rev)
2385 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2386
2387 """
2388 match = cls._VERSION_RE.search(text.splitlines()[0])
2389 if not match:
2390 raise errors.HypervisorError("Unable to get KVM version")
2391
2392 v_all = match.group(0)
2393 v_maj = int(match.group(1))
2394 v_min = int(match.group(2))
2395 if match.group(4):
2396 v_rev = int(match.group(4))
2397 else:
2398 v_rev = 0
2399 return (v_all, v_maj, v_min, v_rev)
2400
2401 @classmethod
2402 def _GetKVMOutput(cls, kvm_path, option):
2403 """Return the output of a kvm invocation
2404
2405 @type kvm_path: string
2406 @param kvm_path: path to the kvm executable
2407 @type option: a key of _KVMOPTS_CMDS
2408 @param option: kvm option to fetch the output from
2409 @return: output a supported kvm invocation
2410 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2411
2412 """
2413 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2414
2415 optlist, can_fail = cls._KVMOPTS_CMDS[option]
2416
2417 result = utils.RunCmd([kvm_path] + optlist)
2418 if result.failed and not can_fail:
2419 raise errors.HypervisorError("Unable to get KVM %s output" %
2420 " ".join(optlist))
2421 return result.output
2422
2423 @classmethod
2424 def _GetKVMVersion(cls, kvm_path):
2425 """Return the installed KVM version.
2426
2427 @return: (version, v_maj, v_min, v_rev)
2428 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2429
2430 """
2431 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2432
2433 @classmethod
2434 def _GetDefaultMachineVersion(cls, kvm_path):
2435 """Return the default hardware revision (e.g. pc-1.1)
2436
2437 """
2438 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2439 match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2440 if match:
2441 return match.group(1)
2442 else:
2443 return "pc"
2444
2445 @classmethod
2446 def _StopInstance(cls, instance, force=False, name=None, timeout=None):
2447 """Stop an instance.
2448
2449 """
2450 assert(timeout is None or force is not None)
2451
2452 if name is not None and not force:
2453 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2454 if name is None:
2455 name = instance.name
2456 acpi = instance.hvparams[constants.HV_ACPI]
2457 else:
2458 acpi = False
2459 _, pid, alive = cls._InstancePidAlive(name)
2460 if pid > 0 and alive:
2461 if force or not acpi:
2462 utils.KillProcess(pid)
2463 else:
2464 cls._CallMonitorCommand(name, "system_powerdown", timeout)
2465 cls._ClearUserShutdown(instance.name)
2466
2467 def StopInstance(self, instance, force=False, retry=False, name=None,
2468 timeout=None):
2469 """Stop an instance.
2470
2471 """
2472 self._StopInstance(instance, force, name=name, timeout=timeout)
2473
2474 def CleanupInstance(self, instance_name):
2475 """Cleanup after a stopped instance
2476
2477 """
2478 pidfile, pid, alive = self._InstancePidAlive(instance_name)
2479 if pid > 0 and alive:
2480 raise errors.HypervisorError("Cannot cleanup a live instance")
2481 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2482 self._ClearUserShutdown(instance_name)
2483
2484 def RebootInstance(self, instance):
2485 """Reboot an instance.
2486
2487 """
2488 # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2489 # socket the instance will stop, but now power up again. So we'll resort
2490 # to shutdown and restart.
2491 _, _, alive = self._InstancePidAlive(instance.name)
2492 if not alive:
2493 raise errors.HypervisorError("Failed to reboot instance %s:"
2494 " not running" % instance.name)
2495 # StopInstance will delete the saved KVM runtime so:
2496 # ...first load it...
2497 kvm_runtime = self._LoadKVMRuntime(instance)
2498 # ...now we can safely call StopInstance...
2499 if not self.StopInstance(instance):
2500 self.StopInstance(instance, force=True)
2501 # ...and finally we can save it again, and execute it...
2502 self._SaveKVMRuntime(instance, kvm_runtime)
2503 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2504 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2505 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2506
2507 def MigrationInfo(self, instance):
2508 """Get instance information to perform a migration.
2509
2510 @type instance: L{objects.Instance}
2511 @param instance: instance to be migrated
2512 @rtype: string
2513 @return: content of the KVM runtime file
2514
2515 """
2516 return self._ReadKVMRuntime(instance.name)
2517
2518 def AcceptInstance(self, instance, info, target):
2519 """Prepare to accept an instance.
2520
2521 @type instance: L{objects.Instance}
2522 @param instance: instance to be accepted
2523 @type info: string
2524 @param info: content of the KVM runtime file on the source node
2525 @type target: string
2526 @param target: target host (usually ip), on this node
2527
2528 """
2529 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2530 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2531 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2532 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2533 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2534 incoming=incoming_address)
2535
2536 def FinalizeMigrationDst(self, instance, info, success):
2537 """Finalize the instance migration on the target node.
2538
2539 Stop the incoming mode KVM.
2540
2541 @type instance: L{objects.Instance}
2542 @param instance: instance whose migration is being finalized
2543
2544 """
2545 if success:
2546 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2547 kvm_nics = kvm_runtime[1]
2548
2549 for nic_seq, nic in enumerate(kvm_nics):
2550 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2551 # Bridged interfaces have already been configured
2552 continue
2553 try:
2554 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2555 except EnvironmentError, err:
2556 logging.warning("Failed to find host interface for %s NIC #%d: %s",
2557 instance.name, nic_seq, str(err))
2558 continue
2559 try:
2560 self._ConfigureNIC(instance, nic_seq, nic, tap)
2561 except errors.HypervisorError, err:
2562 logging.warning(str(err))
2563
2564 self._WriteKVMRuntime(instance.name, info)
2565 else:
2566 self.StopInstance(instance, force=True)
2567
2568 def MigrateInstance(self, cluster_name, instance, target, live):
2569 """Migrate an instance to a target node.
2570
2571 The migration will not be attempted if the instance is not
2572 currently running.
2573
2574 @type cluster_name: string
2575 @param cluster_name: name of the cluster
2576 @type instance: L{objects.Instance}
2577 @param instance: the instance to be migrated
2578 @type target: string
2579 @param target: ip address of the target node
2580 @type live: boolean
2581 @param live: perform a live migration
2582
2583 """
2584 instance_name = instance.name
2585 port = instance.hvparams[constants.HV_MIGRATION_PORT]
2586 _, _, alive = self._InstancePidAlive(instance_name)
2587 if not alive:
2588 raise errors.HypervisorError("Instance not running, cannot migrate")
2589
2590 if not live:
2591 self._CallMonitorCommand(instance_name, "stop")
2592
2593 migrate_command = ("migrate_set_speed %dm" %
2594 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2595 self._CallMonitorCommand(instance_name, migrate_command)
2596
2597 migrate_command = ("migrate_set_downtime %dms" %
2598 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2599 self._CallMonitorCommand(instance_name, migrate_command)
2600
2601 migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS]
2602 if migration_caps:
2603 for c in migration_caps.split(_MIGRATION_CAPS_DELIM):
2604 migrate_command = ("migrate_set_capability %s on" % c)
2605 self._CallMonitorCommand(instance_name, migrate_command)
2606
2607 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2608 self._CallMonitorCommand(instance_name, migrate_command)
2609
2610 def FinalizeMigrationSource(self, instance, success, live):
2611 """Finalize the instance migration on the source node.
2612
2613 @type instance: L{objects.Instance}
2614 @param instance: the instance that was migrated
2615 @type success: bool
2616 @param success: whether the migration succeeded or not
2617 @type live: bool
2618 @param live: whether the user requested a live migration or not
2619
2620 """
2621 if success:
2622 pidfile, pid, _ = self._InstancePidAlive(instance.name)
2623 utils.KillProcess(pid)
2624 self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2625 elif live:
2626 self._CallMonitorCommand(instance.name, self._CONT_CMD)
2627 self._ClearUserShutdown(instance.name)
2628
2629 def GetMigrationStatus(self, instance):
2630 """Get the migration status
2631
2632 @type instance: L{objects.Instance}
2633 @param instance: the instance that is being migrated
2634 @rtype: L{objects.MigrationStatus}
2635 @return: the status of the current migration (one of
2636 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2637 progress info that can be retrieved from the hypervisor
2638
2639 """
2640 info_command = "info migrate"
2641 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2642 result = self._CallMonitorCommand(instance.name, info_command)
2643 match = self._MIGRATION_STATUS_RE.search(result.stdout)
2644 if not match:
2645 if not result.stdout:
2646 logging.info("KVM: empty 'info migrate' result")
2647 else:
2648 logging.warning("KVM: unknown 'info migrate' result: %s",
2649 result.stdout)
2650 else:
2651 status = match.group(1)
2652 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2653 migration_status = objects.MigrationStatus(status=status)
2654 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2655 if match:
2656 migration_status.transferred_ram = match.group("transferred")
2657 migration_status.total_ram = match.group("total")
2658
2659 return migration_status
2660
2661 logging.warning("KVM: unknown migration status '%s'", status)
2662
2663 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2664
2665 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2666
2667 def BalloonInstanceMemory(self, instance, mem):
2668 """Balloon an instance memory to a certain value.
2669
2670 @type instance: L{objects.Instance}
2671 @param instance: instance to be accepted
2672 @type mem: int
2673 @param mem: actual memory size to use for instance runtime
2674
2675 """
2676 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2677
2678 def GetNodeInfo(self, hvparams=None):
2679 """Return information about the node.
2680
2681 @type hvparams: dict of strings
2682 @param hvparams: hypervisor parameters, not used in this class
2683
2684 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2685 the following keys:
2686 - hv_version: the hypervisor version in the form (major, minor,
2687 revision)
2688
2689 """
2690 result = self.GetLinuxNodeInfo()
2691 kvmpath = constants.KVM_PATH
2692 if hvparams is not None:
2693 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2694 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2695 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2696 return result
2697
2698 @classmethod
2699 def GetInstanceConsole(cls, instance, primary_node, node_group,
2700 hvparams, beparams):
2701 """Return a command for connecting to the console of an instance.
2702
2703 """
2704 if hvparams[constants.HV_SERIAL_CONSOLE]:
2705 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2706 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2707 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2708 "STDIO,%s" % cls._SocatUnixConsoleParams(),
2709 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2710 ndparams = node_group.FillND(primary_node)
2711 return objects.InstanceConsole(instance=instance.name,
2712 kind=constants.CONS_SSH,
2713 host=primary_node.name,
2714 port=ndparams.get(constants.ND_SSH_PORT),
2715 user=constants.SSH_CONSOLE_USER,
2716 command=cmd)
2717
2718 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2719 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2720 display = instance.network_port - constants.VNC_BASE_PORT
2721 return objects.InstanceConsole(instance=instance.name,
2722 kind=constants.CONS_VNC,
2723 host=vnc_bind_address,
2724 port=instance.network_port,
2725 display=display)
2726
2727 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2728 if spice_bind:
2729 return objects.InstanceConsole(instance=instance.name,
2730 kind=constants.CONS_SPICE,
2731 host=spice_bind,
2732 port=instance.network_port)
2733
2734 return objects.InstanceConsole(instance=instance.name,
2735 kind=constants.CONS_MESSAGE,
2736 message=("No serial shell for instance %s" %
2737 instance.name))
2738
2739 def Verify(self, hvparams=None):
2740 """Verify the hypervisor.
2741
2742 Check that the required binaries exist.
2743
2744 @type hvparams: dict of strings
2745 @param hvparams: hypervisor parameters to be verified against, not used here
2746
2747 @return: Problem description if something is wrong, C{None} otherwise
2748
2749 """
2750 msgs = []
2751 kvmpath = constants.KVM_PATH
2752 if hvparams is not None:
2753 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2754 if not os.path.exists(kvmpath):
2755 msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2756 if not os.path.exists(constants.SOCAT_PATH):
2757 msgs.append("The socat binary ('%s') does not exist" %
2758 constants.SOCAT_PATH)
2759
2760 return self._FormatVerifyResults(msgs)
2761
2762 @classmethod
2763 def CheckParameterSyntax(cls, hvparams):
2764 """Check the given parameters for validity.
2765
2766 @type hvparams: dict
2767 @param hvparams: dictionary with parameter names/value
2768 @raise errors.HypervisorError: when a parameter is not valid
2769
2770 """
2771 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2772
2773 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2774 if kernel_path:
2775 if not hvparams[constants.HV_ROOT_PATH]:
2776 raise errors.HypervisorError("Need a root partition for the instance,"
2777 " if a kernel is defined")
2778
2779 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2780 not hvparams[constants.HV_VNC_X509]):
2781 raise errors.HypervisorError("%s must be defined, if %s is" %
2782 (constants.HV_VNC_X509,
2783 constants.HV_VNC_X509_VERIFY))
2784
2785 if hvparams[constants.HV_SERIAL_CONSOLE]:
2786 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2787 valid_speeds = constants.VALID_SERIAL_SPEEDS
2788 if not serial_speed or serial_speed not in valid_speeds:
2789 raise errors.HypervisorError("Invalid serial console speed, must be"
2790 " one of: %s" %
2791 utils.CommaJoin(valid_speeds))
2792
2793 boot_order = hvparams[constants.HV_BOOT_ORDER]
2794 if (boot_order == constants.HT_BO_CDROM and
2795 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2796 raise errors.HypervisorError("Cannot boot from cdrom without an"
2797 " ISO path")
2798
2799 security_model = hvparams[constants.HV_SECURITY_MODEL]
2800 if security_model == constants.HT_SM_USER:
2801 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2802 raise errors.HypervisorError("A security domain (user to run kvm as)"
2803 " must be specified")
2804 elif (security_model == constants.HT_SM_NONE or
2805 security_model == constants.HT_SM_POOL):
2806 if hvparams[constants.HV_SECURITY_DOMAIN]:
2807 raise errors.HypervisorError("Cannot have a security domain when the"
2808 " security model is 'none' or 'pool'")
2809
2810 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2811 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2812 if spice_bind:
2813 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2814 # if an IP version is specified, the spice_bind parameter must be an
2815 # IP of that family
2816 if (netutils.IP4Address.IsValid(spice_bind) and
2817 spice_ip_version != constants.IP4_VERSION):
2818 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2819 " the specified IP version is %s" %
2820 (spice_bind, spice_ip_version))
2821
2822 if (netutils.IP6Address.IsValid(spice_bind) and
2823 spice_ip_version != constants.IP6_VERSION):
2824 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2825 " the specified IP version is %s" %
2826 (spice_bind, spice_ip_version))
2827 else:
2828 # All the other SPICE parameters depend on spice_bind being set. Raise an
2829 # error if any of them is set without it.
2830 for param in _SPICE_ADDITIONAL_PARAMS:
2831 if hvparams[param]:
2832 raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2833 (param, constants.HV_KVM_SPICE_BIND))
2834
2835 @classmethod
2836 def ValidateParameters(cls, hvparams):
2837 """Check the given parameters for validity.
2838
2839 @type hvparams: dict
2840 @param hvparams: dictionary with parameter names/value
2841 @raise errors.HypervisorError: when a parameter is not valid
2842
2843 """
2844 super(KVMHypervisor, cls).ValidateParameters(hvparams)
2845
2846 kvm_path = hvparams[constants.HV_KVM_PATH]
2847
2848 security_model = hvparams[constants.HV_SECURITY_MODEL]
2849 if security_model == constants.HT_SM_USER:
2850 username = hvparams[constants.HV_SECURITY_DOMAIN]
2851 try:
2852 pwd.getpwnam(username)
2853 except KeyError:
2854 raise errors.HypervisorError("Unknown security domain user %s"
2855 % username)
2856 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2857 if vnc_bind_address:
2858 bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address)
2859 is_interface = netutils.IsValidInterface(vnc_bind_address)
2860 is_path = utils.IsNormAbsPath(vnc_bind_address)
2861 if not bound_to_addr and not is_interface and not is_path:
2862 raise errors.HypervisorError("VNC: The %s parameter must be either"
2863 " a valid IP address, an interface name,"
2864 " or an absolute path" %
2865 constants.HV_KVM_SPICE_BIND)
2866
2867 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2868 if spice_bind:
2869 # only one of VNC and SPICE can be used currently.
2870 if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2871 raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2872 " only one of them can be used at a"
2873 " given time")
2874
2875 # check that KVM supports SPICE
2876 kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2877 if not cls._SPICE_RE.search(kvmhelp):
2878 raise errors.HypervisorError("SPICE is configured, but it is not"
2879 " supported according to 'kvm --help'")
2880
2881 # if spice_bind is not an IP address, it must be a valid interface
2882 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2883 netutils.IP6Address.IsValid(spice_bind))
2884 if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2885 raise errors.HypervisorError("SPICE: The %s parameter must be either"
2886 " a valid IP address or interface name" %
2887 constants.HV_KVM_SPICE_BIND)
2888
2889 machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2890 if machine_version:
2891 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2892 if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2893 raise errors.HypervisorError("Unsupported machine version: %s" %
2894 machine_version)
2895
2896 @classmethod
2897 def PowercycleNode(cls, hvparams=None):
2898 """KVM powercycle, just a wrapper over Linux powercycle.
2899
2900 @type hvparams: dict of strings
2901 @param hvparams: hypervisor params to be used on this node
2902
2903 """
2904 cls.LinuxPowercycle()