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