1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """KVM hypervisor
23
24 """
25
26 import errno
27 import os
28 import os.path
29 import re
30 import tempfile
31 import time
32 import logging
33 import pwd
34 import struct
35 import fcntl
36 import shutil
37 import socket
38 import stat
39 import StringIO
40 try:
41 import affinity
42 except ImportError:
43 affinity = None
44
45 from ganeti import utils
46 from ganeti import constants
47 from ganeti import errors
48 from ganeti import serializer
49 from ganeti import objects
50 from ganeti import uidpool
51 from ganeti import ssconf
52 from ganeti import netutils
53 from ganeti import pathutils
54 from ganeti.hypervisor import hv_base
55 from ganeti.utils import wrapper as utils_wrapper
56
57
58 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
59 _KVM_START_PAUSED_FLAG = "-S"
60
61
62
63
64 TUNSETIFF = 0x400454ca
65 TUNGETIFF = 0x800454d2
66 TUNGETFEATURES = 0x800454cf
67 IFF_TAP = 0x0002
68 IFF_NO_PI = 0x1000
69 IFF_VNET_HDR = 0x4000
70
71
72 _SPICE_ADDITIONAL_PARAMS = frozenset([
73 constants.HV_KVM_SPICE_IP_VERSION,
74 constants.HV_KVM_SPICE_PASSWORD_FILE,
75 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
76 constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
77 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
78 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
79 constants.HV_KVM_SPICE_USE_TLS,
80 ])
84 """Check whether to enable the IFF_VNET_HDR flag.
85
86 To do this, _all_ of the following conditions must be met:
87 1. TUNGETFEATURES ioctl() *must* be implemented
88 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
89 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
90 drivers/net/tun.c there is no way to test this until after the tap device
91 has been created using TUNSETIFF, and there is no way to change the
92 IFF_VNET_HDR flag after creating the interface, catch-22! However both
93 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
94 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
95
96 @type fd: int
97 @param fd: the file descriptor of /dev/net/tun
98
99 """
100 req = struct.pack("I", 0)
101 try:
102 res = fcntl.ioctl(fd, TUNGETFEATURES, req)
103 except EnvironmentError:
104 logging.warning("TUNGETFEATURES ioctl() not implemented")
105 return False
106
107 tunflags = struct.unpack("I", res)[0]
108 if tunflags & IFF_VNET_HDR:
109 return True
110 else:
111 logging.warning("Host does not support IFF_VNET_HDR, not enabling")
112 return False
113
116 """Open a new tap device and return its file descriptor.
117
118 This is intended to be used by a qemu-type hypervisor together with the -net
119 tap,fd=<fd> command line parameter.
120
121 @type vnet_hdr: boolean
122 @param vnet_hdr: Enable the VNET Header
123 @return: (ifname, tapfd)
124 @rtype: tuple
125
126 """
127 try:
128 tapfd = os.open("/dev/net/tun", os.O_RDWR)
129 except EnvironmentError:
130 raise errors.HypervisorError("Failed to open /dev/net/tun")
131
132 flags = IFF_TAP | IFF_NO_PI
133
134 if vnet_hdr and _ProbeTapVnetHdr(tapfd):
135 flags |= IFF_VNET_HDR
136
137
138 ifr = struct.pack("16sh", "", flags)
139
140 try:
141 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
142 except EnvironmentError, err:
143 raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
144 err)
145
146
147 ifname = struct.unpack("16sh", res)[0].strip("\x00")
148 return (ifname, tapfd)
149
152 """QEMU Messaging Protocol (QMP) message.
153
154 """
156 """Creates a new QMP message based on the passed data.
157
158 """
159 if not isinstance(data, dict):
160 raise TypeError("QmpMessage must be initialized with a dict")
161
162 self.data = data
163
165 """Get the value of the required field if present, or None.
166
167 Overrides the [] operator to provide access to the message data,
168 returning None if the required item is not in the message
169 @return: the value of the field_name field, or None if field_name
170 is not contained in the message
171
172 """
173 return self.data.get(field_name, None)
174
176 """Set the value of the required field_name to field_value.
177
178 """
179 self.data[field_name] = field_value
180
181 @staticmethod
183 """Build a QmpMessage from a JSON encoded string.
184
185 @type json_string: str
186 @param json_string: JSON string representing the message
187 @rtype: L{QmpMessage}
188 @return: a L{QmpMessage} built from json_string
189
190 """
191
192 data = serializer.LoadJson(json_string)
193 return QmpMessage(data)
194
198
200
201
202 return self.data == other.data
203
206 """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
207
208 """
209 _FIRST_MESSAGE_KEY = "QMP"
210 _EVENT_KEY = "event"
211 _ERROR_KEY = "error"
212 _RETURN_KEY = RETURN_KEY = "return"
213 _ACTUAL_KEY = ACTUAL_KEY = "actual"
214 _ERROR_CLASS_KEY = "class"
215 _ERROR_DATA_KEY = "data"
216 _ERROR_DESC_KEY = "desc"
217 _EXECUTE_KEY = "execute"
218 _ARGUMENTS_KEY = "arguments"
219 _CAPABILITIES_COMMAND = "qmp_capabilities"
220 _MESSAGE_END_TOKEN = "\r\n"
221 _SOCKET_TIMEOUT = 5
222
224 """Instantiates the QmpConnection object.
225
226 @type monitor_filename: string
227 @param monitor_filename: the filename of the UNIX raw socket on which the
228 QMP monitor is listening
229
230 """
231 self.monitor_filename = monitor_filename
232 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
233
234
235 self.sock.settimeout(self._SOCKET_TIMEOUT)
236 self._connected = False
237 self._buf = ""
238
240 sock_stat = None
241 try:
242 sock_stat = os.stat(self.monitor_filename)
243 except EnvironmentError, err:
244 if err.errno == errno.ENOENT:
245 raise errors.HypervisorError("No qmp socket found")
246 else:
247 raise errors.HypervisorError("Error checking qmp socket: %s",
248 utils.ErrnoOrStr(err))
249 if not stat.S_ISSOCK(sock_stat.st_mode):
250 raise errors.HypervisorError("Qmp socket is not a socket")
251
253 """Make sure that the connection is established.
254
255 """
256 if not self._connected:
257 raise errors.ProgrammerError("To use a QmpConnection you need to first"
258 " invoke connect() on it")
259
261 """Connects to the QMP monitor.
262
263 Connects to the UNIX socket and makes sure that we can actually send and
264 receive data to the kvm instance via QMP.
265
266 @raise errors.HypervisorError: when there are communication errors
267 @raise errors.ProgrammerError: when there are data serialization errors
268
269 """
270 if self._connected:
271 raise errors.ProgrammerError("Cannot connect twice")
272
273 self._check_socket()
274
275
276 try:
277 self.sock.connect(self.monitor_filename)
278 except EnvironmentError:
279 raise errors.HypervisorError("Can't connect to qmp socket")
280 self._connected = True
281
282
283
284 greeting = self._Recv()
285 if not greeting[self._FIRST_MESSAGE_KEY]:
286 self._connected = False
287 raise errors.HypervisorError("kvm: qmp communication error (wrong"
288 " server greeting")
289
290
291
292
293 self.Execute(self._CAPABILITIES_COMMAND)
294
296 """Extract and parse a QMP message from the given buffer.
297
298 Seeks for a QMP message in the given buf. If found, it parses it and
299 returns it together with the rest of the characters in the buf.
300 If no message is found, returns None and the whole buffer.
301
302 @raise errors.ProgrammerError: when there are data serialization errors
303
304 """
305 message = None
306
307
308 pos = buf.find(self._MESSAGE_END_TOKEN)
309 if pos >= 0:
310 try:
311 message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
312 except Exception, err:
313 raise errors.ProgrammerError("QMP data serialization error: %s" % err)
314 buf = buf[pos + 1:]
315
316 return (message, buf)
317
319 """Receives a message from QMP and decodes the received JSON object.
320
321 @rtype: QmpMessage
322 @return: the received message
323 @raise errors.HypervisorError: when there are communication errors
324 @raise errors.ProgrammerError: when there are data serialization errors
325
326 """
327 self._check_connection()
328
329
330 (message, self._buf) = self._ParseMessage(self._buf)
331 if message:
332 return message
333
334 recv_buffer = StringIO.StringIO(self._buf)
335 recv_buffer.seek(len(self._buf))
336 try:
337 while True:
338 data = self.sock.recv(4096)
339 if not data:
340 break
341 recv_buffer.write(data)
342
343 (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
344 if message:
345 return message
346
347 except socket.timeout, err:
348 raise errors.HypervisorError("Timeout while receiving a QMP message: "
349 "%s" % (err))
350 except socket.error, err:
351 raise errors.HypervisorError("Unable to receive data from KVM using the"
352 " QMP protocol: %s" % err)
353
354 - def _Send(self, message):
355 """Encodes and sends a message to KVM using QMP.
356
357 @type message: QmpMessage
358 @param message: message to send to KVM
359 @raise errors.HypervisorError: when there are communication errors
360 @raise errors.ProgrammerError: when there are data serialization errors
361
362 """
363 self._check_connection()
364 try:
365 message_str = str(message)
366 except Exception, err:
367 raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
368
369 try:
370 self.sock.sendall(message_str)
371 except socket.timeout, err:
372 raise errors.HypervisorError("Timeout while sending a QMP message: "
373 "%s (%s)" % (err.string, err.errno))
374 except socket.error, err:
375 raise errors.HypervisorError("Unable to send data from KVM using the"
376 " QMP protocol: %s" % err)
377
378 - def Execute(self, command, arguments=None):
379 """Executes a QMP command and returns the response of the server.
380
381 @type command: str
382 @param command: the command to execute
383 @type arguments: dict
384 @param arguments: dictionary of arguments to be passed to the command
385 @rtype: dict
386 @return: dictionary representing the received JSON object
387 @raise errors.HypervisorError: when there are communication errors
388 @raise errors.ProgrammerError: when there are data serialization errors
389
390 """
391 self._check_connection()
392 message = QmpMessage({self._EXECUTE_KEY: command})
393 if arguments:
394 message[self._ARGUMENTS_KEY] = arguments
395 self._Send(message)
396
397
398
399 while True:
400 response = self._Recv()
401 err = response[self._ERROR_KEY]
402 if err:
403 raise errors.HypervisorError("kvm: error executing the %s"
404 " command: %s (%s, %s):" %
405 (command,
406 err[self._ERROR_DESC_KEY],
407 err[self._ERROR_CLASS_KEY],
408 err[self._ERROR_DATA_KEY]))
409
410 elif not response[self._EVENT_KEY]:
411 return response
412
415 """KVM hypervisor interface
416
417 """
418 CAN_MIGRATE = True
419
420 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
421 _PIDS_DIR = _ROOT_DIR + "/pid"
422 _UIDS_DIR = _ROOT_DIR + "/uid"
423 _CTRL_DIR = _ROOT_DIR + "/ctrl"
424 _CONF_DIR = _ROOT_DIR + "/conf"
425 _NICS_DIR = _ROOT_DIR + "/nic"
426 _KEYMAP_DIR = _ROOT_DIR + "/keymap"
427
428 _CHROOT_DIR = _ROOT_DIR + "/chroot"
429
430
431
432
433
434 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
435 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
436 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
437
438 PARAMETERS = {
439 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
440 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
441 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
442 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
443 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
444 constants.HV_ACPI: hv_base.NO_CHECK,
445 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
446 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
447 constants.HV_VNC_BIND_ADDRESS:
448 (False, lambda x: (netutils.IP4Address.IsValid(x) or
449 utils.IsNormAbsPath(x)),
450 "the VNC bind address must be either a valid IP address or an absolute"
451 " pathname", None, None),
452 constants.HV_VNC_TLS: hv_base.NO_CHECK,
453 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
454 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
455 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
456 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK,
457 constants.HV_KVM_SPICE_IP_VERSION:
458 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
459 x in constants.VALID_IP_VERSIONS),
460 "the SPICE IP version should be 4 or 6",
461 None, None),
462 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
463 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
464 hv_base.ParamInSet(
465 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
466 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
467 hv_base.ParamInSet(
468 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
469 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
470 hv_base.ParamInSet(
471 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
472 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
473 hv_base.ParamInSet(
474 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
475 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
476 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
477 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
478 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
479 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
480 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
481 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
482 constants.HV_BOOT_ORDER:
483 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
484 constants.HV_NIC_TYPE:
485 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
486 constants.HV_DISK_TYPE:
487 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
488 constants.HV_KVM_CDROM_DISK_TYPE:
489 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
490 constants.HV_USB_MOUSE:
491 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
492 constants.HV_KEYMAP: hv_base.NO_CHECK,
493 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
494 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
495 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
496 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
497 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
498 constants.HV_DISK_CACHE:
499 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
500 constants.HV_SECURITY_MODEL:
501 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
502 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
503 constants.HV_KVM_FLAG:
504 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
505 constants.HV_VHOST_NET: hv_base.NO_CHECK,
506 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
507 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
508 constants.HV_REBOOT_BEHAVIOR:
509 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
510 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
511 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
512 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
513 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
514 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
515 constants.HV_SOUNDHW: hv_base.NO_CHECK,
516 constants.HV_USB_DEVICES: hv_base.NO_CHECK,
517 constants.HV_VGA: hv_base.NO_CHECK,
518 constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
519 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
520 }
521
522 _VIRTIO = "virtio"
523 _VIRTIO_NET_PCI = "virtio-net-pci"
524
525 _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
526 re.M | re.I)
527 _MIGRATION_PROGRESS_RE = \
528 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
529 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
530 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
531
532 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
533 _MIGRATION_INFO_RETRY_DELAY = 2
534
535 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
536
537 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
538 _CPU_INFO_CMD = "info cpus"
539 _CONT_CMD = "cont"
540
541 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
542 _CHECK_MACHINE_VERSION_RE = \
543 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
544
545 _QMP_RE = re.compile(r"^-qmp\s", re.M)
546 _SPICE_RE = re.compile(r"^-spice\s", re.M)
547 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
548 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
549 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
550 _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
551 _DISPLAY_RE = re.compile(r"^-display\s", re.M)
552 _MACHINE_RE = re.compile(r"^-machine\s", re.M)
553 _NEW_VIRTIO_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M)
554
555
556
557 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
558
559 ANCILLARY_FILES = [
560 _KVM_NETWORK_SCRIPT,
561 ]
562 ANCILLARY_FILES_OPT = [
563 _KVM_NETWORK_SCRIPT,
564 ]
565
566
567 _KVMOPT_HELP = "help"
568 _KVMOPT_MLIST = "mlist"
569 _KVMOPT_DEVICELIST = "devicelist"
570
571
572
573 _KVMOPTS_CMDS = {
574 _KVMOPT_HELP: (["--help"], False),
575 _KVMOPT_MLIST: (["-M", "?"], False),
576 _KVMOPT_DEVICELIST: (["-device", "?"], True),
577 }
578
585
586 @classmethod
588 """Returns the instance pidfile.
589
590 """
591 return utils.PathJoin(cls._PIDS_DIR, instance_name)
592
593 @classmethod
595 """Returns the instance uidfile.
596
597 """
598 return utils.PathJoin(cls._UIDS_DIR, instance_name)
599
600 @classmethod
602 """Check pid file for instance information.
603
604 Check that a pid file is associated with an instance, and retrieve
605 information from its command line.
606
607 @type pid: string or int
608 @param pid: process id of the instance to check
609 @rtype: tuple
610 @return: (instance_name, memory, vcpus)
611 @raise errors.HypervisorError: when an instance cannot be found
612
613 """
614 alive = utils.IsProcessAlive(pid)
615 if not alive:
616 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
617
618 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
619 try:
620 cmdline = utils.ReadFile(cmdline_file)
621 except EnvironmentError, err:
622 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
623 (pid, err))
624
625 instance = None
626 memory = 0
627 vcpus = 0
628
629 arg_list = cmdline.split("\x00")
630 while arg_list:
631 arg = arg_list.pop(0)
632 if arg == "-name":
633 instance = arg_list.pop(0)
634 elif arg == "-m":
635 memory = int(arg_list.pop(0))
636 elif arg == "-smp":
637 vcpus = int(arg_list.pop(0).split(",")[0])
638
639 if instance is None:
640 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
641 " instance" % pid)
642
643 return (instance, memory, vcpus)
644
646 """Returns the instance pidfile, pid, and liveness.
647
648 @type instance_name: string
649 @param instance_name: instance name
650 @rtype: tuple
651 @return: (pid file name, pid, liveness)
652
653 """
654 pidfile = self._InstancePidFile(instance_name)
655 pid = utils.ReadPidFile(pidfile)
656
657 alive = False
658 try:
659 cmd_instance = self._InstancePidInfo(pid)[0]
660 alive = (cmd_instance == instance_name)
661 except errors.HypervisorError:
662 pass
663
664 return (pidfile, pid, alive)
665
667 """Raises an error unless the given instance is down.
668
669 """
670 alive = self._InstancePidAlive(instance_name)[2]
671 if alive:
672 raise errors.HypervisorError("Failed to start instance %s: %s" %
673 (instance_name, "already running"))
674
675 @classmethod
677 """Returns the instance monitor socket name
678
679 """
680 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
681
682 @classmethod
684 """Returns the instance serial socket name
685
686 """
687 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
688
689 @classmethod
691 """Returns the instance serial QMP socket name
692
693 """
694 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
695
696 @staticmethod
698 """Returns the correct parameters for socat
699
700 If we have a new-enough socat we can use raw mode with an escape character.
701
702 """
703 if constants.SOCAT_USE_ESCAPE:
704 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
705 else:
706 return "echo=0,icanon=0"
707
708 @classmethod
710 """Returns the instance KVM runtime filename
711
712 """
713 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
714
715 @classmethod
717 """Returns the name of the KVM chroot dir of the instance
718
719 """
720 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
721
722 @classmethod
724 """Returns the name of the directory holding the tap device files for a
725 given instance.
726
727 """
728 return utils.PathJoin(cls._NICS_DIR, instance_name)
729
730 @classmethod
732 """Returns the name of the file containing the tap device for a given NIC
733
734 """
735 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
736
737 @classmethod
739 """Returns the name of the file containing the keymap for a given instance
740
741 """
742 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
743
744 @classmethod
746 """Try to read a uid file
747
748 """
749 if os.path.exists(uid_file):
750 try:
751 uid = int(utils.ReadOneLineFile(uid_file))
752 return uid
753 except EnvironmentError:
754 logging.warning("Can't read uid file", exc_info=True)
755 except (TypeError, ValueError):
756 logging.warning("Can't parse uid file contents", exc_info=True)
757 return None
758
759 @classmethod
761 """Removes an instance's rutime sockets/files/dirs.
762
763 """
764 utils.RemoveFile(pidfile)
765 utils.RemoveFile(cls._InstanceMonitor(instance_name))
766 utils.RemoveFile(cls._InstanceSerial(instance_name))
767 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
768 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
769 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
770 uid_file = cls._InstanceUidFile(instance_name)
771 uid = cls._TryReadUidFile(uid_file)
772 utils.RemoveFile(uid_file)
773 if uid is not None:
774 uidpool.ReleaseUid(uid)
775 try:
776 shutil.rmtree(cls._InstanceNICDir(instance_name))
777 except OSError, err:
778 if err.errno != errno.ENOENT:
779 raise
780 try:
781 chroot_dir = cls._InstanceChrootDir(instance_name)
782 utils.RemoveDir(chroot_dir)
783 except OSError, err:
784 if err.errno == errno.ENOTEMPTY:
785
786 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
787 prefix="%s-%s-" %
788 (instance_name,
789 utils.TimestampForFilename()))
790 logging.warning("The chroot directory of instance %s can not be"
791 " removed as it is not empty. Moving it to the"
792 " quarantine instead. Please investigate the"
793 " contents (%s) and clean up manually",
794 instance_name, new_chroot_dir)
795 utils.RenameFile(chroot_dir, new_chroot_dir)
796 else:
797 raise
798
799 @staticmethod
846
847 @staticmethod
852
853 @staticmethod
855 """Create a CPU mask suitable for sched_setaffinity from a list of
856 CPUs.
857
858 See man taskset for more info on sched_setaffinity masks.
859 For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
860
861 @type cpu_list: list of int
862 @param cpu_list: list of physical CPU numbers to map to vCPUs in order
863 @rtype: int
864 @return: a bit mask of CPU affinities
865
866 """
867 if cpu_list == constants.CPU_PINNING_OFF:
868 return constants.CPU_PINNING_ALL_KVM
869 else:
870 return sum(2 ** cpu for cpu in cpu_list)
871
872 @classmethod
874 """Change CPU affinity for running VM according to given CPU mask.
875
876 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
877 @type cpu_mask: string
878 @param process_id: process ID of KVM process. Used to pin entire VM
879 to physical CPUs.
880 @type process_id: int
881 @param thread_dict: map of virtual CPUs to KVM thread IDs
882 @type thread_dict: dict int:int
883
884 """
885
886 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
887
888 if len(cpu_list) == 1:
889 all_cpu_mapping = cpu_list[0]
890 if all_cpu_mapping == constants.CPU_PINNING_OFF:
891
892 pass
893 else:
894
895
896 cls._VerifyAffinityPackage()
897 affinity.set_process_affinity_mask(
898 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
899 else:
900
901
902
903 assert len(thread_dict) == len(cpu_list)
904 cls._VerifyAffinityPackage()
905
906
907 for vcpu, i in zip(cpu_list, range(len(cpu_list))):
908 affinity.set_process_affinity_mask(thread_dict[i],
909 cls._BuildAffinityCpuMask(vcpu))
910
912 """Get a mapping of vCPU no. to thread IDs for the instance
913
914 @type instance_name: string
915 @param instance_name: instance in question
916 @rtype: dictionary of int:int
917 @return: a dictionary mapping vCPU numbers to thread IDs
918
919 """
920 result = {}
921 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
922 for line in output.stdout.splitlines():
923 match = self._CPU_INFO_RE.search(line)
924 if not match:
925 continue
926 grp = map(int, match.groups())
927 result[grp[0]] = grp[1]
928
929 return result
930
932 """Complete CPU pinning.
933
934 @type instance_name: string
935 @param instance_name: name of instance
936 @type cpu_mask: string
937 @param cpu_mask: CPU pinning mask as entered by user
938
939 """
940
941 _, pid, _ = self._InstancePidAlive(instance_name)
942
943 thread_dict = self._GetVcpuThreadIds(instance_name)
944
945 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
946
948 """Get the list of running instances.
949
950 We can do this by listing our live instances directory and
951 checking whether the associated kvm process is still alive.
952
953 """
954 result = []
955 for name in os.listdir(self._PIDS_DIR):
956 if self._InstancePidAlive(name)[2]:
957 result.append(name)
958 return result
959
961 """Get instance properties.
962
963 @type instance_name: string
964 @param instance_name: the instance name
965 @rtype: tuple of strings
966 @return: (name, id, memory, vcpus, stat, times)
967
968 """
969 _, pid, alive = self._InstancePidAlive(instance_name)
970 if not alive:
971 return None
972
973 _, memory, vcpus = self._InstancePidInfo(pid)
974 istat = "---b-"
975 times = "0"
976
977 try:
978 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
979 qmp.connect()
980 vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
981
982
983 mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
984 memory = mem_bytes / 1048576
985 except errors.HypervisorError:
986 pass
987
988 return (instance_name, pid, memory, vcpus, istat, times)
989
991 """Get properties of all instances.
992
993 @return: list of tuples (name, id, memory, vcpus, stat, times)
994
995 """
996 data = []
997 for name in os.listdir(self._PIDS_DIR):
998 try:
999 info = self.GetInstanceInfo(name)
1000 except errors.HypervisorError:
1001
1002 continue
1003 if info:
1004 data.append(info)
1005 return data
1006
1009 """Generate KVM information to start an instance.
1010
1011 @type kvmhelp: string
1012 @param kvmhelp: output of kvm --help
1013 @attention: this function must not have any side-effects; for
1014 example, it must not write to the filesystem, or read values
1015 from the current system the are expected to differ between
1016 nodes, since it is only run once at instance startup;
1017 actions/kvm arguments that can vary between systems should be
1018 done in L{_ExecuteKVMRuntime}
1019
1020 """
1021
1022 hvp = instance.hvparams
1023 self.ValidateParameters(hvp)
1024
1025 pidfile = self._InstancePidFile(instance.name)
1026 kvm = hvp[constants.HV_KVM_PATH]
1027 kvm_cmd = [kvm]
1028
1029 kvm_cmd.extend(["-name", instance.name])
1030 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1031
1032 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1033 if hvp[constants.HV_CPU_CORES]:
1034 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1035 if hvp[constants.HV_CPU_THREADS]:
1036 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1037 if hvp[constants.HV_CPU_SOCKETS]:
1038 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1039
1040 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1041
1042 kvm_cmd.extend(["-pidfile", pidfile])
1043 kvm_cmd.extend(["-balloon", "virtio"])
1044 kvm_cmd.extend(["-daemonize"])
1045 if not instance.hvparams[constants.HV_ACPI]:
1046 kvm_cmd.extend(["-no-acpi"])
1047 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1048 constants.INSTANCE_REBOOT_EXIT:
1049 kvm_cmd.extend(["-no-reboot"])
1050
1051 mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1052 if not mversion:
1053 mversion = self._GetDefaultMachineVersion(kvm)
1054 if self._MACHINE_RE.search(kvmhelp):
1055
1056
1057
1058 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1059 specprop = ",accel=kvm"
1060 else:
1061 specprop = ""
1062 machinespec = "%s%s" % (mversion, specprop)
1063 kvm_cmd.extend(["-machine", machinespec])
1064 else:
1065 kvm_cmd.extend(["-M", mversion])
1066 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1067 self._ENABLE_KVM_RE.search(kvmhelp)):
1068 kvm_cmd.extend(["-enable-kvm"])
1069 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1070 self._DISABLE_KVM_RE.search(kvmhelp)):
1071 kvm_cmd.extend(["-disable-kvm"])
1072
1073 kernel_path = hvp[constants.HV_KERNEL_PATH]
1074 if kernel_path:
1075 boot_disk = boot_cdrom = boot_floppy = boot_network = False
1076 else:
1077 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1078 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1079 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1080 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1081
1082 if startup_paused:
1083 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1084
1085 if boot_network:
1086 kvm_cmd.extend(["-boot", "n"])
1087
1088
1089
1090 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1091
1092 disk_type = hvp[constants.HV_DISK_TYPE]
1093 if disk_type == constants.HT_DISK_PARAVIRTUAL:
1094 if_val = ",if=virtio"
1095 else:
1096 if_val = ",if=%s" % disk_type
1097
1098 disk_cache = hvp[constants.HV_DISK_CACHE]
1099 if instance.disk_template in constants.DTS_EXT_MIRROR:
1100 if disk_cache != "none":
1101
1102 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1103 " to prevent shared storage corruption on migration",
1104 disk_cache)
1105 cache_val = ",cache=none"
1106 elif disk_cache != constants.HT_CACHE_DEFAULT:
1107 cache_val = ",cache=%s" % disk_cache
1108 else:
1109 cache_val = ""
1110 for cfdev, dev_path in block_devices:
1111 if cfdev.mode != constants.DISK_RDWR:
1112 raise errors.HypervisorError("Instance has read-only disks which"
1113 " are not supported by KVM")
1114
1115 boot_val = ""
1116 if boot_disk:
1117 kvm_cmd.extend(["-boot", "c"])
1118 boot_disk = False
1119 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1120 boot_val = ",boot=on"
1121
1122 drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
1123 cache_val)
1124 kvm_cmd.extend(["-drive", drive_val])
1125
1126
1127 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1128 if not cdrom_disk_type:
1129 cdrom_disk_type = disk_type
1130
1131 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1132 if iso_image:
1133 options = ",format=raw,media=cdrom"
1134
1135 if boot_cdrom:
1136 actual_cdrom_type = constants.HT_DISK_IDE
1137 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1138 actual_cdrom_type = "virtio"
1139 else:
1140 actual_cdrom_type = cdrom_disk_type
1141 if_val = ",if=%s" % actual_cdrom_type
1142
1143 boot_val = ""
1144 if boot_cdrom:
1145 kvm_cmd.extend(["-boot", "d"])
1146 if needs_boot_flag:
1147 boot_val = ",boot=on"
1148
1149 drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1150 kvm_cmd.extend(["-drive", drive_val])
1151
1152 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1153 if iso_image2:
1154 options = ",format=raw,media=cdrom"
1155 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1156 if_val = ",if=virtio"
1157 else:
1158 if_val = ",if=%s" % cdrom_disk_type
1159 drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1160 kvm_cmd.extend(["-drive", drive_val])
1161
1162 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1163 if floppy_image:
1164 options = ",format=raw,media=disk"
1165 if boot_floppy:
1166 kvm_cmd.extend(["-boot", "a"])
1167 options = "%s,boot=on" % options
1168 if_val = ",if=floppy"
1169 options = "%s%s" % (options, if_val)
1170 drive_val = "file=%s%s" % (floppy_image, options)
1171 kvm_cmd.extend(["-drive", drive_val])
1172
1173 if kernel_path:
1174 kvm_cmd.extend(["-kernel", kernel_path])
1175 initrd_path = hvp[constants.HV_INITRD_PATH]
1176 if initrd_path:
1177 kvm_cmd.extend(["-initrd", initrd_path])
1178 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1179 hvp[constants.HV_KERNEL_ARGS]]
1180 if hvp[constants.HV_SERIAL_CONSOLE]:
1181 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1182 root_append.append("console=ttyS0,%s" % serial_speed)
1183 kvm_cmd.extend(["-append", " ".join(root_append)])
1184
1185 mem_path = hvp[constants.HV_MEM_PATH]
1186 if mem_path:
1187 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1188
1189 monitor_dev = ("unix:%s,server,nowait" %
1190 self._InstanceMonitor(instance.name))
1191 kvm_cmd.extend(["-monitor", monitor_dev])
1192 if hvp[constants.HV_SERIAL_CONSOLE]:
1193 serial_dev = ("unix:%s,server,nowait" %
1194 self._InstanceSerial(instance.name))
1195 kvm_cmd.extend(["-serial", serial_dev])
1196 else:
1197 kvm_cmd.extend(["-serial", "none"])
1198
1199 mouse_type = hvp[constants.HV_USB_MOUSE]
1200 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1201 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1202 spice_ip_version = None
1203
1204 kvm_cmd.extend(["-usb"])
1205
1206 if mouse_type:
1207 kvm_cmd.extend(["-usbdevice", mouse_type])
1208 elif vnc_bind_address:
1209 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1210
1211 if vnc_bind_address:
1212 if netutils.IP4Address.IsValid(vnc_bind_address):
1213 if instance.network_port > constants.VNC_BASE_PORT:
1214 display = instance.network_port - constants.VNC_BASE_PORT
1215 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1216 vnc_arg = ":%d" % (display)
1217 else:
1218 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1219 else:
1220 logging.error("Network port is not a valid VNC display (%d < %d)."
1221 " Not starting VNC", instance.network_port,
1222 constants.VNC_BASE_PORT)
1223 vnc_arg = "none"
1224
1225
1226
1227 vnc_append = ""
1228 if hvp[constants.HV_VNC_TLS]:
1229 vnc_append = "%s,tls" % vnc_append
1230 if hvp[constants.HV_VNC_X509_VERIFY]:
1231 vnc_append = "%s,x509verify=%s" % (vnc_append,
1232 hvp[constants.HV_VNC_X509])
1233 elif hvp[constants.HV_VNC_X509]:
1234 vnc_append = "%s,x509=%s" % (vnc_append,
1235 hvp[constants.HV_VNC_X509])
1236 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1237 vnc_append = "%s,password" % vnc_append
1238
1239 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1240
1241 else:
1242 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1243
1244 kvm_cmd.extend(["-vnc", vnc_arg])
1245 elif spice_bind:
1246
1247
1248 if netutils.IsValidInterface(spice_bind):
1249
1250
1251 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1252 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1253
1254
1255
1256 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1257 if not addresses[spice_ip_version]:
1258 raise errors.HypervisorError("spice: unable to get an IPv%s address"
1259 " for %s" % (spice_ip_version,
1260 spice_bind))
1261
1262
1263 elif (addresses[constants.IP4_VERSION] and
1264 addresses[constants.IP6_VERSION]):
1265
1266
1267 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1268 spice_ip_version = \
1269 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1270 elif addresses[constants.IP4_VERSION]:
1271 spice_ip_version = constants.IP4_VERSION
1272 elif addresses[constants.IP6_VERSION]:
1273 spice_ip_version = constants.IP6_VERSION
1274 else:
1275 raise errors.HypervisorError("spice: unable to get an IP address"
1276 " for %s" % (spice_bind))
1277
1278 spice_address = addresses[spice_ip_version][0]
1279
1280 else:
1281
1282
1283 spice_address = spice_bind
1284
1285 spice_arg = "addr=%s" % spice_address
1286 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1287 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1288 (spice_arg, instance.network_port,
1289 pathutils.SPICE_CACERT_FILE))
1290 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1291 (spice_arg, pathutils.SPICE_CERT_FILE,
1292 pathutils.SPICE_CERT_FILE))
1293 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1294 if tls_ciphers:
1295 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1296 else:
1297 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1298
1299 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1300 spice_arg = "%s,disable-ticketing" % spice_arg
1301
1302 if spice_ip_version:
1303 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1304
1305
1306 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1307 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1308 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1309 if img_lossless:
1310 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1311 if img_jpeg:
1312 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1313 if img_zlib_glz:
1314 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1315
1316
1317 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1318 if video_streaming:
1319 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1320
1321
1322 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1323 spice_arg = "%s,playback-compression=off" % spice_arg
1324 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1325 spice_arg = "%s,agent-mouse=off" % spice_arg
1326 else:
1327
1328
1329 kvm_cmd.extend(["-device", "virtio-serial-pci"])
1330 kvm_cmd.extend(["-device", "virtserialport,chardev=spicechannel0,"
1331 "name=com.redhat.spice.0"])
1332 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1333
1334 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1335 kvm_cmd.extend(["-spice", spice_arg])
1336
1337 else:
1338
1339
1340 if self._DISPLAY_RE.search(kvmhelp):
1341 kvm_cmd.extend(["-display", "none"])
1342 else:
1343 kvm_cmd.extend(["-nographic"])
1344
1345 if hvp[constants.HV_USE_LOCALTIME]:
1346 kvm_cmd.extend(["-localtime"])
1347
1348 if hvp[constants.HV_KVM_USE_CHROOT]:
1349 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1350
1351
1352 if hvp[constants.HV_CPU_TYPE]:
1353 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1354
1355
1356 if hvp[constants.HV_SOUNDHW]:
1357 kvm_cmd.extend(["-soundhw", hvp[constants.HV_SOUNDHW]])
1358
1359
1360
1361 if hvp[constants.HV_VGA]:
1362 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1363 elif spice_bind:
1364 kvm_cmd.extend(["-vga", "qxl"])
1365
1366
1367 if hvp[constants.HV_USB_DEVICES]:
1368 for dev in hvp[constants.HV_USB_DEVICES].split(","):
1369 kvm_cmd.extend(["-usbdevice", dev])
1370
1371 if hvp[constants.HV_KVM_EXTRA]:
1372 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1373
1374
1375
1376 kvm_nics = instance.nics
1377 hvparams = hvp
1378
1379 return (kvm_cmd, kvm_nics, hvparams)
1380
1390
1392 """Read an instance's KVM runtime
1393
1394 """
1395 try:
1396 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1397 except EnvironmentError, err:
1398 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1399 return file_content
1400
1402 """Save an instance's KVM runtime
1403
1404 """
1405 kvm_cmd, kvm_nics, hvparams = kvm_runtime
1406 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1407 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1408 self._WriteKVMRuntime(instance.name, serialized_form)
1409
1411 """Load an instance's KVM runtime
1412
1413 """
1414 if not serialized_runtime:
1415 serialized_runtime = self._ReadKVMRuntime(instance.name)
1416 loaded_runtime = serializer.Load(serialized_runtime)
1417 kvm_cmd, serialized_nics, hvparams = loaded_runtime
1418 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1419 return (kvm_cmd, kvm_nics, hvparams)
1420
1421 - def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1422 """Run the KVM cmd and check for errors
1423
1424 @type name: string
1425 @param name: instance name
1426 @type kvm_cmd: list of strings
1427 @param kvm_cmd: runcmd input for kvm
1428 @type tap_fds: list of int
1429 @param tap_fds: fds of tap devices opened by Ganeti
1430
1431 """
1432 try:
1433 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1434 finally:
1435 for fd in tap_fds:
1436 utils_wrapper.CloseFdNoError(fd)
1437
1438 if result.failed:
1439 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1440 (name, result.fail_reason, result.output))
1441 if not self._InstancePidAlive(name)[2]:
1442 raise errors.HypervisorError("Failed to start instance %s" % name)
1443
1445 """Execute a KVM cmd, after completing it with some last minute data.
1446
1447 @type incoming: tuple of strings
1448 @param incoming: (target_host_ip, port)
1449 @type kvmhelp: string
1450 @param kvmhelp: output of kvm --help
1451
1452 """
1453
1454
1455
1456
1457
1458
1459
1460
1461 conf_hvp = instance.hvparams
1462 name = instance.name
1463 self._CheckDown(name)
1464
1465 temp_files = []
1466
1467 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1468
1469 kvm_path = kvm_cmd[0]
1470 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1471
1472
1473
1474 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1475 if security_model == constants.HT_SM_USER:
1476 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1477
1478 keymap = conf_hvp[constants.HV_KEYMAP]
1479 if keymap:
1480 keymap_path = self._InstanceKeymapFile(name)
1481
1482
1483
1484
1485 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1486 kvm_cmd.extend(["-k", keymap_path])
1487
1488
1489
1490
1491 tapfds = []
1492 taps = []
1493 if not kvm_nics:
1494 kvm_cmd.extend(["-net", "none"])
1495 else:
1496 vnet_hdr = False
1497 tap_extra = ""
1498 nic_type = up_hvp[constants.HV_NIC_TYPE]
1499 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1500 nic_model = self._VIRTIO
1501 try:
1502 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1503 if self._NEW_VIRTIO_RE.search(devlist):
1504 nic_model = self._VIRTIO_NET_PCI
1505 vnet_hdr = True
1506 except errors.HypervisorError, _:
1507
1508
1509 pass
1510
1511 if up_hvp[constants.HV_VHOST_NET]:
1512
1513 if self._VHOST_RE.search(kvmhelp):
1514 tap_extra = ",vhost=on"
1515 else:
1516 raise errors.HypervisorError("vhost_net is configured"
1517 " but it is not available")
1518 else:
1519 nic_model = nic_type
1520
1521 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1522
1523 for nic_seq, nic in enumerate(kvm_nics):
1524 tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr)
1525 tapfds.append(tapfd)
1526 taps.append(tapname)
1527 if kvm_supports_netdev:
1528 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
1529 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
1530 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1531 else:
1532 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1533 nic.mac, nic_model)
1534 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1535 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1536
1537 if incoming:
1538 target, port = incoming
1539 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1540
1541
1542
1543
1544 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1545 vnc_pwd = None
1546 if vnc_pwd_file:
1547 try:
1548 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1549 except EnvironmentError, err:
1550 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1551 % (vnc_pwd_file, err))
1552
1553 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1554 utils.EnsureDirs([(self._InstanceChrootDir(name),
1555 constants.SECURE_DIR_MODE)])
1556
1557
1558 if self._QMP_RE.search(kvmhelp):
1559 logging.debug("Enabling QMP")
1560 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1561 self._InstanceQmpMonitor(instance.name)])
1562
1563
1564
1565 for nic_seq, nic in enumerate(kvm_nics):
1566 if (incoming and
1567 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1568 continue
1569 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1570
1571
1572
1573
1574 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1575 if start_kvm_paused:
1576 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1577
1578
1579
1580
1581 cpu_pinning = False
1582 if up_hvp.get(constants.HV_CPU_MASK, None):
1583 cpu_pinning = True
1584
1585 if security_model == constants.HT_SM_POOL:
1586 ss = ssconf.SimpleStore()
1587 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1588 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1589 uid = uidpool.RequestUnusedUid(all_uids)
1590 try:
1591 username = pwd.getpwuid(uid.GetUid()).pw_name
1592 kvm_cmd.extend(["-runas", username])
1593 self._RunKVMCmd(name, kvm_cmd, tapfds)
1594 except:
1595 uidpool.ReleaseUid(uid)
1596 raise
1597 else:
1598 uid.Unlock()
1599 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1600 else:
1601 self._RunKVMCmd(name, kvm_cmd, tapfds)
1602
1603 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1604 constants.RUN_DIRS_MODE)])
1605 for nic_seq, tap in enumerate(taps):
1606 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1607 data=tap)
1608
1609 if vnc_pwd:
1610 change_cmd = "change vnc password %s" % vnc_pwd
1611 self._CallMonitorCommand(instance.name, change_cmd)
1612
1613
1614
1615
1616
1617
1618 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1619 if spice_password_file:
1620 spice_pwd = ""
1621 try:
1622 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1623 except EnvironmentError, err:
1624 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1625 % (spice_password_file, err))
1626
1627 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1628 qmp.connect()
1629 arguments = {
1630 "protocol": "spice",
1631 "password": spice_pwd,
1632 }
1633 qmp.Execute("set_password", arguments)
1634
1635 for filename in temp_files:
1636 utils.RemoveFile(filename)
1637
1638
1639 if cpu_pinning:
1640 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1641
1642 start_memory = self._InstanceStartupMemory(instance)
1643 if start_memory < instance.beparams[constants.BE_MAXMEM]:
1644 self.BalloonInstanceMemory(instance, start_memory)
1645
1646 if start_kvm_paused:
1647
1648
1649
1650 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1651
1652 - def StartInstance(self, instance, block_devices, startup_paused):
1663
1665 """Invoke a command on the instance monitor.
1666
1667 """
1668 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1669 (utils.ShellQuote(command),
1670 constants.SOCAT_PATH,
1671 utils.ShellQuote(self._InstanceMonitor(instance_name))))
1672 result = utils.RunCmd(socat)
1673 if result.failed:
1674 msg = ("Failed to send command '%s' to instance %s."
1675 " output: %s, error: %s, fail_reason: %s" %
1676 (command, instance_name,
1677 result.stdout, result.stderr, result.fail_reason))
1678 raise errors.HypervisorError(msg)
1679
1680 return result
1681
1682 @classmethod
1684 """Parse the KVM version from the --help output.
1685
1686 @type text: string
1687 @param text: output of kvm --help
1688 @return: (version, v_maj, v_min, v_rev)
1689 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1690
1691 """
1692 match = cls._VERSION_RE.search(text.splitlines()[0])
1693 if not match:
1694 raise errors.HypervisorError("Unable to get KVM version")
1695
1696 v_all = match.group(0)
1697 v_maj = int(match.group(1))
1698 v_min = int(match.group(2))
1699 if match.group(4):
1700 v_rev = int(match.group(4))
1701 else:
1702 v_rev = 0
1703 return (v_all, v_maj, v_min, v_rev)
1704
1705 @classmethod
1707 """Return the output of a kvm invocation
1708
1709 @type kvm_path: string
1710 @param kvm_path: path to the kvm executable
1711 @type option: a key of _KVMOPTS_CMDS
1712 @param option: kvm option to fetch the output from
1713 @return: output a supported kvm invocation
1714 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
1715
1716 """
1717 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
1718
1719 optlist, can_fail = cls._KVMOPTS_CMDS[option]
1720
1721 result = utils.RunCmd([kvm_path] + optlist)
1722 if result.failed and not can_fail:
1723 raise errors.HypervisorError("Unable to get KVM %s output" %
1724 " ".join(cls._KVMOPTS_CMDS[option]))
1725 return result.output
1726
1727 @classmethod
1729 """Return the installed KVM version.
1730
1731 @return: (version, v_maj, v_min, v_rev)
1732 @raise errors.HypervisorError: when the KVM version cannot be retrieved
1733
1734 """
1735 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
1736
1737 @classmethod
1748
1749 - def StopInstance(self, instance, force=False, retry=False, name=None):
1766
1775
1798
1800 """Get instance information to perform a migration.
1801
1802 @type instance: L{objects.Instance}
1803 @param instance: instance to be migrated
1804 @rtype: string
1805 @return: content of the KVM runtime file
1806
1807 """
1808 return self._ReadKVMRuntime(instance.name)
1809
1811 """Prepare to accept an instance.
1812
1813 @type instance: L{objects.Instance}
1814 @param instance: instance to be accepted
1815 @type info: string
1816 @param info: content of the KVM runtime file on the source node
1817 @type target: string
1818 @param target: target host (usually ip), on this node
1819
1820 """
1821 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1822 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1823 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
1824 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
1825 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
1826 incoming=incoming_address)
1827
1829 """Finalize the instance migration on the target node.
1830
1831 Stop the incoming mode KVM.
1832
1833 @type instance: L{objects.Instance}
1834 @param instance: instance whose migration is being finalized
1835
1836 """
1837 if success:
1838 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1839 kvm_nics = kvm_runtime[1]
1840
1841 for nic_seq, nic in enumerate(kvm_nics):
1842 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1843
1844 continue
1845 try:
1846 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1847 except EnvironmentError, err:
1848 logging.warning("Failed to find host interface for %s NIC #%d: %s",
1849 instance.name, nic_seq, str(err))
1850 continue
1851 try:
1852 self._ConfigureNIC(instance, nic_seq, nic, tap)
1853 except errors.HypervisorError, err:
1854 logging.warning(str(err))
1855
1856 self._WriteKVMRuntime(instance.name, info)
1857 else:
1858 self.StopInstance(instance, force=True)
1859
1861 """Migrate an instance to a target node.
1862
1863 The migration will not be attempted if the instance is not
1864 currently running.
1865
1866 @type instance: L{objects.Instance}
1867 @param instance: the instance to be migrated
1868 @type target: string
1869 @param target: ip address of the target node
1870 @type live: boolean
1871 @param live: perform a live migration
1872
1873 """
1874 instance_name = instance.name
1875 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1876 _, _, alive = self._InstancePidAlive(instance_name)
1877 if not alive:
1878 raise errors.HypervisorError("Instance not running, cannot migrate")
1879
1880 if not live:
1881 self._CallMonitorCommand(instance_name, "stop")
1882
1883 migrate_command = ("migrate_set_speed %dm" %
1884 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1885 self._CallMonitorCommand(instance_name, migrate_command)
1886
1887 migrate_command = ("migrate_set_downtime %dms" %
1888 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1889 self._CallMonitorCommand(instance_name, migrate_command)
1890
1891 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1892 self._CallMonitorCommand(instance_name, migrate_command)
1893
1911
1913 """Get the migration status
1914
1915 @type instance: L{objects.Instance}
1916 @param instance: the instance that is being migrated
1917 @rtype: L{objects.MigrationStatus}
1918 @return: the status of the current migration (one of
1919 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1920 progress info that can be retrieved from the hypervisor
1921
1922 """
1923 info_command = "info migrate"
1924 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
1925 result = self._CallMonitorCommand(instance.name, info_command)
1926 match = self._MIGRATION_STATUS_RE.search(result.stdout)
1927 if not match:
1928 if not result.stdout:
1929 logging.info("KVM: empty 'info migrate' result")
1930 else:
1931 logging.warning("KVM: unknown 'info migrate' result: %s",
1932 result.stdout)
1933 else:
1934 status = match.group(1)
1935 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
1936 migration_status = objects.MigrationStatus(status=status)
1937 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
1938 if match:
1939 migration_status.transferred_ram = match.group("transferred")
1940 migration_status.total_ram = match.group("total")
1941
1942 return migration_status
1943
1944 logging.warning("KVM: unknown migration status '%s'", status)
1945
1946 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1947
1948 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
1949
1951 """Balloon an instance memory to a certain value.
1952
1953 @type instance: L{objects.Instance}
1954 @param instance: instance to be accepted
1955 @type mem: int
1956 @param mem: actual memory size to use for instance runtime
1957
1958 """
1959 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
1960
1962 """Return information about the node.
1963
1964 @return: a dict with the following keys (values in MiB):
1965 - memory_total: the total memory size on the node
1966 - memory_free: the available memory on the node for instances
1967 - memory_dom0: the memory used by the node itself, if available
1968 - hv_version: the hypervisor version in the form (major, minor,
1969 revision)
1970
1971 """
1972 result = self.GetLinuxNodeInfo()
1973
1974
1975
1976 _, v_major, v_min, v_rev = self._GetKVMVersion(constants.KVM_PATH)
1977 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
1978 return result
1979
1980 @classmethod
1982 """Return a command for connecting to the console of an instance.
1983
1984 """
1985 if hvparams[constants.HV_SERIAL_CONSOLE]:
1986 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
1987 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1988 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1989 "STDIO,%s" % cls._SocatUnixConsoleParams(),
1990 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1991 return objects.InstanceConsole(instance=instance.name,
1992 kind=constants.CONS_SSH,
1993 host=instance.primary_node,
1994 user=constants.SSH_CONSOLE_USER,
1995 command=cmd)
1996
1997 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1998 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1999 display = instance.network_port - constants.VNC_BASE_PORT
2000 return objects.InstanceConsole(instance=instance.name,
2001 kind=constants.CONS_VNC,
2002 host=vnc_bind_address,
2003 port=instance.network_port,
2004 display=display)
2005
2006 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2007 if spice_bind:
2008 return objects.InstanceConsole(instance=instance.name,
2009 kind=constants.CONS_SPICE,
2010 host=spice_bind,
2011 port=instance.network_port)
2012
2013 return objects.InstanceConsole(instance=instance.name,
2014 kind=constants.CONS_MESSAGE,
2015 message=("No serial shell for instance %s" %
2016 instance.name))
2017
2019 """Verify the hypervisor.
2020
2021 Check that the required binaries exist.
2022
2023 @return: Problem description if something is wrong, C{None} otherwise
2024
2025 """
2026 msgs = []
2027
2028
2029
2030 if not os.path.exists(constants.KVM_PATH):
2031 msgs.append("The KVM binary ('%s') does not exist" % constants.KVM_PATH)
2032 if not os.path.exists(constants.SOCAT_PATH):
2033 msgs.append("The socat binary ('%s') does not exist" %
2034 constants.SOCAT_PATH)
2035
2036 return self._FormatVerifyResults(msgs)
2037
2038 @classmethod
2040 """Check the given parameters for validity.
2041
2042 @type hvparams: dict
2043 @param hvparams: dictionary with parameter names/value
2044 @raise errors.HypervisorError: when a parameter is not valid
2045
2046 """
2047 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2048
2049 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2050 if kernel_path:
2051 if not hvparams[constants.HV_ROOT_PATH]:
2052 raise errors.HypervisorError("Need a root partition for the instance,"
2053 " if a kernel is defined")
2054
2055 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2056 not hvparams[constants.HV_VNC_X509]):
2057 raise errors.HypervisorError("%s must be defined, if %s is" %
2058 (constants.HV_VNC_X509,
2059 constants.HV_VNC_X509_VERIFY))
2060
2061 if hvparams[constants.HV_SERIAL_CONSOLE]:
2062 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2063 valid_speeds = constants.VALID_SERIAL_SPEEDS
2064 if not serial_speed or serial_speed not in valid_speeds:
2065 raise errors.HypervisorError("Invalid serial console speed, must be"
2066 " one of: %s" %
2067 utils.CommaJoin(valid_speeds))
2068
2069 boot_order = hvparams[constants.HV_BOOT_ORDER]
2070 if (boot_order == constants.HT_BO_CDROM and
2071 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2072 raise errors.HypervisorError("Cannot boot from cdrom without an"
2073 " ISO path")
2074
2075 security_model = hvparams[constants.HV_SECURITY_MODEL]
2076 if security_model == constants.HT_SM_USER:
2077 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2078 raise errors.HypervisorError("A security domain (user to run kvm as)"
2079 " must be specified")
2080 elif (security_model == constants.HT_SM_NONE or
2081 security_model == constants.HT_SM_POOL):
2082 if hvparams[constants.HV_SECURITY_DOMAIN]:
2083 raise errors.HypervisorError("Cannot have a security domain when the"
2084 " security model is 'none' or 'pool'")
2085
2086 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2087 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2088 if spice_bind:
2089 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2090
2091
2092 if (netutils.IP4Address.IsValid(spice_bind) and
2093 spice_ip_version != constants.IP4_VERSION):
2094 raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
2095 " the specified IP version is %s" %
2096 (spice_bind, spice_ip_version))
2097
2098 if (netutils.IP6Address.IsValid(spice_bind) and
2099 spice_ip_version != constants.IP6_VERSION):
2100 raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
2101 " the specified IP version is %s" %
2102 (spice_bind, spice_ip_version))
2103 else:
2104
2105
2106 for param in _SPICE_ADDITIONAL_PARAMS:
2107 if hvparams[param]:
2108 raise errors.HypervisorError("spice: %s requires %s to be set" %
2109 (param, constants.HV_KVM_SPICE_BIND))
2110
2111 @classmethod
2161
2162 @classmethod
2164 """KVM powercycle, just a wrapper over Linux powercycle.
2165
2166 """
2167 cls.LinuxPowercycle()
2168