1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """KVM hypervisor
32
33 """
34
35 import errno
36 import os
37 import os.path
38 import re
39 import tempfile
40 import time
41 import logging
42 import pwd
43 import struct
44 import fcntl
45 import shutil
46 import socket
47 import stat
48 import StringIO
49 from bitarray import bitarray
50 try:
51 import affinity
52 except ImportError:
53 affinity = None
54 try:
55 import fdsend
56 except ImportError:
57 fdsend = None
58
59 from ganeti import utils
60 from ganeti import constants
61 from ganeti import errors
62 from ganeti import serializer
63 from ganeti import objects
64 from ganeti import uidpool
65 from ganeti import ssconf
66 from ganeti import netutils
67 from ganeti import pathutils
68 from ganeti.hypervisor import hv_base
69 from ganeti.utils import wrapper as utils_wrapper
70
71
72 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
73 _KVM_START_PAUSED_FLAG = "-S"
74
75
76
77
78 TUNSETIFF = 0x400454ca
79 TUNGETIFF = 0x800454d2
80 TUNGETFEATURES = 0x800454cf
81 IFF_TAP = 0x0002
82 IFF_NO_PI = 0x1000
83 IFF_ONE_QUEUE = 0x2000
84 IFF_VNET_HDR = 0x4000
85
86
87 _SPICE_ADDITIONAL_PARAMS = frozenset([
88 constants.HV_KVM_SPICE_IP_VERSION,
89 constants.HV_KVM_SPICE_PASSWORD_FILE,
90 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
91 constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
92 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
93 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
94 constants.HV_KVM_SPICE_USE_TLS,
95 ])
96
97
98
99 _AVAILABLE_PCI_SLOT = bitarray("0")
100
101
102
103
104
105 _KVM_NICS_RUNTIME_INDEX = 1
106 _KVM_DISKS_RUNTIME_INDEX = 3
107 _DEVICE_RUNTIME_INDEX = {
108 constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX,
109 constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX
110 }
111 _FIND_RUNTIME_ENTRY = {
112 constants.HOTPLUG_TARGET_NIC:
113 lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid],
114 constants.HOTPLUG_TARGET_DISK:
115 lambda disk, kvm_disks: [(d, l, u) for (d, l, u) in kvm_disks
116 if d.uuid == disk.uuid]
117 }
118 _RUNTIME_DEVICE = {
119 constants.HOTPLUG_TARGET_NIC: lambda d: d,
120 constants.HOTPLUG_TARGET_DISK: lambda (d, e, _): d
121 }
122 _RUNTIME_ENTRY = {
123 constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
124 constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e[0], e[1])
125 }
129 """Helper function to get the drive uri to be used in --drive kvm option
130
131 @type disk: L{objects.Disk}
132 @param disk: A disk configuration object
133 @type link: string
134 @param link: The device link as returned by _SymlinkBlockDev()
135 @type uri: string
136 @param uri: The drive uri as returned by _CalculateDeviceURI()
137
138 """
139 access_mode = disk.params.get(constants.LDP_ACCESS,
140 constants.DISK_KERNELSPACE)
141 if (uri and access_mode == constants.DISK_USERSPACE):
142 drive_uri = uri
143 else:
144 drive_uri = link
145
146 return drive_uri
147
150 """Helper function to generate a unique device name used by KVM
151
152 QEMU monitor commands use names to identify devices. Here we use their pci
153 slot and a part of their UUID to name them. dev.pci might be None for old
154 devices in the cluster.
155
156 @type dev_type: sting
157 @param dev_type: device type of param dev
158 @type dev: L{objects.Disk} or L{objects.NIC}
159 @param dev: the device object for which we generate a kvm name
160 @raise errors.HotplugError: in case a device has no pci slot (old devices)
161
162 """
163
164 if not dev.pci:
165 raise errors.HotplugError("Hotplug is not supported for %s with UUID %s" %
166 (dev_type, dev.uuid))
167
168 return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
169
172 """Helper method to get first available slot in a bitarray
173
174 @type slots: bitarray
175 @param slots: the bitarray to operate on
176 @type slot: integer
177 @param slot: if given we check whether the slot is free
178 @type reserve: boolean
179 @param reserve: whether to reserve the first available slot or not
180 @return: the idx of the (first) available slot
181 @raise errors.HotplugError: If all slots in a bitarray are occupied
182 or the given slot is not free.
183
184 """
185 if slot is not None:
186 assert slot < len(slots)
187 if slots[slot]:
188 raise errors.HypervisorError("Slots %d occupied" % slot)
189
190 else:
191 avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
192 if not avail:
193 raise errors.HypervisorError("All slots occupied")
194
195 slot = int(avail[0])
196
197 if reserve:
198 slots[slot] = True
199
200 return slot
201
204 """Helper function to get an existing device inside the runtime file
205
206 Used when an instance is running. Load kvm runtime file and search
207 for a device based on its type and uuid.
208
209 @type dev_type: sting
210 @param dev_type: device type of param dev
211 @type device: L{objects.Disk} or L{objects.NIC}
212 @param device: the device object for which we generate a kvm name
213 @type runtime: tuple (cmd, nics, hvparams, disks)
214 @param runtime: the runtime data to search for the device
215 @raise errors.HotplugError: in case the requested device does not
216 exist (e.g. device has been added without --hotplug option) or
217 device info has not pci slot (e.g. old devices in the cluster)
218
219 """
220 index = _DEVICE_RUNTIME_INDEX[dev_type]
221 found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
222 if not found:
223 raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
224 (dev_type, device.uuid))
225
226 return found[0]
227
230 """Upgrade runtime data
231
232 Remove any deprecated fields or change the format of the data.
233 The runtime files are not upgraded when Ganeti is upgraded, so the required
234 modification have to be performed here.
235
236 @type serialized_runtime: string
237 @param serialized_runtime: raw text data read from actual runtime file
238 @return: (cmd, nic dicts, hvparams, bdev dicts)
239 @rtype: tuple
240
241 """
242 loaded_runtime = serializer.Load(serialized_runtime)
243 kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
244 if len(loaded_runtime) >= 4:
245 serialized_disks = loaded_runtime[3]
246 else:
247 serialized_disks = []
248
249 for nic in serialized_nics:
250
251 if "uuid" not in nic:
252 nic["uuid"] = utils.NewUUID()
253
254 return kvm_cmd, serialized_nics, hvparams, serialized_disks
255
258 """Return runtime entries for a serialized runtime file
259
260 @type serialized_runtime: string
261 @param serialized_runtime: raw text data read from actual runtime file
262 @return: (cmd, nics, hvparams, bdevs)
263 @rtype: tuple
264
265 """
266 kvm_cmd, serialized_nics, hvparams, serialized_disks = \
267 _UpgradeSerializedRuntime(serialized_runtime)
268 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
269 kvm_disks = [(objects.Disk.FromDict(sdisk), link, uri)
270 for sdisk, link, uri in serialized_disks]
271
272 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
273
276 """Retrieves supported TUN features from file descriptor.
277
278 @see: L{_ProbeTapVnetHdr}
279
280 """
281 req = struct.pack("I", 0)
282 try:
283 buf = _ioctl(fd, TUNGETFEATURES, req)
284 except EnvironmentError, err:
285 logging.warning("ioctl(TUNGETFEATURES) failed: %s", err)
286 return None
287 else:
288 (flags, ) = struct.unpack("I", buf)
289 return flags
290
293 """Check whether to enable the IFF_VNET_HDR flag.
294
295 To do this, _all_ of the following conditions must be met:
296 1. TUNGETFEATURES ioctl() *must* be implemented
297 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
298 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
299 drivers/net/tun.c there is no way to test this until after the tap device
300 has been created using TUNSETIFF, and there is no way to change the
301 IFF_VNET_HDR flag after creating the interface, catch-22! However both
302 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
303 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
304
305 @type fd: int
306 @param fd: the file descriptor of /dev/net/tun
307
308 """
309 flags = _features_fn(fd)
310
311 if flags is None:
312
313 return False
314
315 result = bool(flags & IFF_VNET_HDR)
316
317 if not result:
318 logging.warning("Kernel does not support IFF_VNET_HDR, not enabling")
319
320 return result
321
324 """Open a new tap device and return its file descriptor.
325
326 This is intended to be used by a qemu-type hypervisor together with the -net
327 tap,fd=<fd> command line parameter.
328
329 @type vnet_hdr: boolean
330 @param vnet_hdr: Enable the VNET Header
331 @return: (ifname, tapfd)
332 @rtype: tuple
333
334 """
335 try:
336 tapfd = os.open("/dev/net/tun", os.O_RDWR)
337 except EnvironmentError:
338 raise errors.HypervisorError("Failed to open /dev/net/tun")
339
340 flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE
341
342 if vnet_hdr and _ProbeTapVnetHdr(tapfd):
343 flags |= IFF_VNET_HDR
344
345
346 ifr = struct.pack("16sh", "", flags)
347
348 try:
349 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
350 except EnvironmentError, err:
351 raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
352 err)
353
354
355 ifname = struct.unpack("16sh", res)[0].strip("\x00")
356 return (ifname, tapfd)
357
360 """QEMU Messaging Protocol (QMP) message.
361
362 """
364 """Creates a new QMP message based on the passed data.
365
366 """
367 if not isinstance(data, dict):
368 raise TypeError("QmpMessage must be initialized with a dict")
369
370 self.data = data
371
373 """Get the value of the required field if present, or None.
374
375 Overrides the [] operator to provide access to the message data,
376 returning None if the required item is not in the message
377 @return: the value of the field_name field, or None if field_name
378 is not contained in the message
379
380 """
381 return self.data.get(field_name, None)
382
384 """Set the value of the required field_name to field_value.
385
386 """
387 self.data[field_name] = field_value
388
390 """Return the number of fields stored in this QmpMessage.
391
392 """
393 return len(self.data)
394
396 """Delete the specified element from the QmpMessage.
397
398 """
399 del(self.data[key])
400
401 @staticmethod
403 """Build a QmpMessage from a JSON encoded string.
404
405 @type json_string: str
406 @param json_string: JSON string representing the message
407 @rtype: L{QmpMessage}
408 @return: a L{QmpMessage} built from json_string
409
410 """
411
412 data = serializer.LoadJson(json_string)
413 return QmpMessage(data)
414
418
420
421
422 return self.data == other.data
423
426 _SOCKET_TIMEOUT = 5
427
429 """Instantiates the MonitorSocket object.
430
431 @type monitor_filename: string
432 @param monitor_filename: the filename of the UNIX raw socket on which the
433 monitor (QMP or simple one) is listening
434
435 """
436 self.monitor_filename = monitor_filename
437 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
438
439
440 self.sock.settimeout(self._SOCKET_TIMEOUT)
441 self._connected = False
442
444 sock_stat = None
445 try:
446 sock_stat = os.stat(self.monitor_filename)
447 except EnvironmentError, err:
448 if err.errno == errno.ENOENT:
449 raise errors.HypervisorError("No monitor socket found")
450 else:
451 raise errors.HypervisorError("Error checking monitor socket: %s",
452 utils.ErrnoOrStr(err))
453 if not stat.S_ISSOCK(sock_stat.st_mode):
454 raise errors.HypervisorError("Monitor socket is not a socket")
455
457 """Make sure that the connection is established.
458
459 """
460 if not self._connected:
461 raise errors.ProgrammerError("To use a MonitorSocket you need to first"
462 " invoke connect() on it")
463
465 """Connects to the monitor.
466
467 Connects to the UNIX socket
468
469 @raise errors.HypervisorError: when there are communication errors
470
471 """
472 if self._connected:
473 raise errors.ProgrammerError("Cannot connect twice")
474
475 self._check_socket()
476
477
478 try:
479 self.sock.connect(self.monitor_filename)
480 except EnvironmentError:
481 raise errors.HypervisorError("Can't connect to qmp socket")
482 self._connected = True
483
485 """Closes the socket
486
487 It cannot be used after this call.
488
489 """
490 self.sock.close()
491
494 """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
495
496 """
497 _FIRST_MESSAGE_KEY = "QMP"
498 _EVENT_KEY = "event"
499 _ERROR_KEY = "error"
500 _RETURN_KEY = RETURN_KEY = "return"
501 _ACTUAL_KEY = ACTUAL_KEY = "actual"
502 _ERROR_CLASS_KEY = "class"
503 _ERROR_DESC_KEY = "desc"
504 _EXECUTE_KEY = "execute"
505 _ARGUMENTS_KEY = "arguments"
506 _CAPABILITIES_COMMAND = "qmp_capabilities"
507 _MESSAGE_END_TOKEN = "\r\n"
508
512
514 """Connects to the QMP monitor.
515
516 Connects to the UNIX socket and makes sure that we can actually send and
517 receive data to the kvm instance via QMP.
518
519 @raise errors.HypervisorError: when there are communication errors
520 @raise errors.ProgrammerError: when there are data serialization errors
521
522 """
523 super(QmpConnection, self).connect()
524
525
526 greeting = self._Recv()
527 if not greeting[self._FIRST_MESSAGE_KEY]:
528 self._connected = False
529 raise errors.HypervisorError("kvm: QMP communication error (wrong"
530 " server greeting")
531
532
533
534 self._buf = ""
535
536
537
538
539 self.Execute(self._CAPABILITIES_COMMAND)
540
542 """Extract and parse a QMP message from the given buffer.
543
544 Seeks for a QMP message in the given buf. If found, it parses it and
545 returns it together with the rest of the characters in the buf.
546 If no message is found, returns None and the whole buffer.
547
548 @raise errors.ProgrammerError: when there are data serialization errors
549
550 """
551 message = None
552
553
554 pos = buf.find(self._MESSAGE_END_TOKEN)
555 if pos >= 0:
556 try:
557 message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
558 except Exception, err:
559 raise errors.ProgrammerError("QMP data serialization error: %s" % err)
560 buf = buf[pos + 1:]
561
562 return (message, buf)
563
565 """Receives a message from QMP and decodes the received JSON object.
566
567 @rtype: QmpMessage
568 @return: the received message
569 @raise errors.HypervisorError: when there are communication errors
570 @raise errors.ProgrammerError: when there are data serialization errors
571
572 """
573 self._check_connection()
574
575
576 (message, self._buf) = self._ParseMessage(self._buf)
577 if message:
578 return message
579
580 recv_buffer = StringIO.StringIO(self._buf)
581 recv_buffer.seek(len(self._buf))
582 try:
583 while True:
584 data = self.sock.recv(4096)
585 if not data:
586 break
587 recv_buffer.write(data)
588
589 (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
590 if message:
591 return message
592
593 except socket.timeout, err:
594 raise errors.HypervisorError("Timeout while receiving a QMP message: "
595 "%s" % (err))
596 except socket.error, err:
597 raise errors.HypervisorError("Unable to receive data from KVM using the"
598 " QMP protocol: %s" % err)
599
600 - def _Send(self, message):
601 """Encodes and sends a message to KVM using QMP.
602
603 @type message: QmpMessage
604 @param message: message to send to KVM
605 @raise errors.HypervisorError: when there are communication errors
606 @raise errors.ProgrammerError: when there are data serialization errors
607
608 """
609 self._check_connection()
610 try:
611 message_str = str(message)
612 except Exception, err:
613 raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
614
615 try:
616 self.sock.sendall(message_str)
617 except socket.timeout, err:
618 raise errors.HypervisorError("Timeout while sending a QMP message: "
619 "%s (%s)" % (err.string, err.errno))
620 except socket.error, err:
621 raise errors.HypervisorError("Unable to send data from KVM using the"
622 " QMP protocol: %s" % err)
623
624 - def Execute(self, command, arguments=None):
625 """Executes a QMP command and returns the response of the server.
626
627 @type command: str
628 @param command: the command to execute
629 @type arguments: dict
630 @param arguments: dictionary of arguments to be passed to the command
631 @rtype: dict
632 @return: dictionary representing the received JSON object
633 @raise errors.HypervisorError: when there are communication errors
634 @raise errors.ProgrammerError: when there are data serialization errors
635
636 """
637 self._check_connection()
638 message = QmpMessage({self._EXECUTE_KEY: command})
639 if arguments:
640 message[self._ARGUMENTS_KEY] = arguments
641 self._Send(message)
642
643
644
645 while True:
646 response = self._Recv()
647 err = response[self._ERROR_KEY]
648 if err:
649 raise errors.HypervisorError("kvm: error executing the %s"
650 " command: %s (%s):" %
651 (command,
652 err[self._ERROR_DESC_KEY],
653 err[self._ERROR_CLASS_KEY]))
654
655 elif not response[self._EVENT_KEY]:
656 return response
657
660 """KVM hypervisor interface
661
662 """
663 CAN_MIGRATE = True
664
665 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
666 _PIDS_DIR = _ROOT_DIR + "/pid"
667 _UIDS_DIR = _ROOT_DIR + "/uid"
668 _CTRL_DIR = _ROOT_DIR + "/ctrl"
669 _CONF_DIR = _ROOT_DIR + "/conf"
670 _NICS_DIR = _ROOT_DIR + "/nic"
671 _KEYMAP_DIR = _ROOT_DIR + "/keymap"
672
673 _CHROOT_DIR = _ROOT_DIR + "/chroot"
674
675
676
677
678
679 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
680 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
681 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
682
683 PARAMETERS = {
684 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
685 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
686 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
687 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
688 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
689 constants.HV_ACPI: hv_base.NO_CHECK,
690 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
691 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
692 constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK,
693 constants.HV_VNC_TLS: hv_base.NO_CHECK,
694 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
695 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
696 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
697 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK,
698 constants.HV_KVM_SPICE_IP_VERSION:
699 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
700 x in constants.VALID_IP_VERSIONS),
701 "The SPICE IP version should be 4 or 6",
702 None, None),
703 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
704 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
705 hv_base.ParamInSet(
706 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
707 constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
708 hv_base.ParamInSet(
709 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
710 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
711 hv_base.ParamInSet(
712 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
713 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
714 hv_base.ParamInSet(
715 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
716 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
717 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
718 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
719 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
720 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
721 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
722 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
723 constants.HV_BOOT_ORDER:
724 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
725 constants.HV_NIC_TYPE:
726 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
727 constants.HV_DISK_TYPE:
728 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
729 constants.HV_KVM_CDROM_DISK_TYPE:
730 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
731 constants.HV_USB_MOUSE:
732 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
733 constants.HV_KEYMAP: hv_base.NO_CHECK,
734 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
735 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
736 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
737 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
738 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
739 constants.HV_DISK_CACHE:
740 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
741 constants.HV_SECURITY_MODEL:
742 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
743 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
744 constants.HV_KVM_FLAG:
745 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
746 constants.HV_VHOST_NET: hv_base.NO_CHECK,
747 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
748 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
749 constants.HV_REBOOT_BEHAVIOR:
750 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
751 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
752 constants.HV_CPU_TYPE: hv_base.NO_CHECK,
753 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
754 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
755 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
756 constants.HV_SOUNDHW: hv_base.NO_CHECK,
757 constants.HV_USB_DEVICES: hv_base.NO_CHECK,
758 constants.HV_VGA: hv_base.NO_CHECK,
759 constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
760 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
761 constants.HV_VNET_HDR: hv_base.NO_CHECK,
762 }
763
764 _VIRTIO = "virtio"
765 _VIRTIO_NET_PCI = "virtio-net-pci"
766 _VIRTIO_BLK_PCI = "virtio-blk-pci"
767
768 _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)",
769 re.M | re.I)
770 _MIGRATION_PROGRESS_RE = \
771 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
772 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
773 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
774
775 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
776 _MIGRATION_INFO_RETRY_DELAY = 2
777
778 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
779
780 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
781 _CPU_INFO_CMD = "info cpus"
782 _CONT_CMD = "cont"
783
784 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
785 _CHECK_MACHINE_VERSION_RE = \
786 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
787
788 _QMP_RE = re.compile(r"^-qmp\s", re.M)
789 _SPICE_RE = re.compile(r"^-spice\s", re.M)
790 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
791 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
792 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
793 _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
794 _DISPLAY_RE = re.compile(r"^-display\s", re.M)
795 _MACHINE_RE = re.compile(r"^-machine\s", re.M)
796 _VIRTIO_NET_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M)
797 _VIRTIO_BLK_RE = re.compile(r"^name \"%s\"" % _VIRTIO_BLK_PCI, re.M)
798
799
800
801 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
802 _UUID_RE = re.compile(r"^-uuid\s", re.M)
803
804 _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
805 _INFO_PCI_CMD = "info pci"
806 _FIND_PCI_DEVICE_RE = \
807 staticmethod(
808 lambda pci, devid: re.compile(r'Bus.*device[ ]*%d,(.*\n){5,6}.*id "%s"' %
809 (pci, devid), re.M))
810
811 _INFO_VERSION_RE = \
812 re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
813 _INFO_VERSION_CMD = "info version"
814
815
816 _DEFAULT_PCI_RESERVATIONS = "11100000000000000000000000000000"
817 _SOUNDHW_WITH_PCI_SLOT = ["ac97", "es1370", "hda"]
818
819 ANCILLARY_FILES = [
820 _KVM_NETWORK_SCRIPT,
821 ]
822 ANCILLARY_FILES_OPT = [
823 _KVM_NETWORK_SCRIPT,
824 ]
825
826
827 _KVMOPT_HELP = "help"
828 _KVMOPT_MLIST = "mlist"
829 _KVMOPT_DEVICELIST = "devicelist"
830
831
832
833 _KVMOPTS_CMDS = {
834 _KVMOPT_HELP: (["--help"], False),
835 _KVMOPT_MLIST: (["-M", "?"], False),
836 _KVMOPT_DEVICELIST: (["-device", "?"], True),
837 }
838
845
846 @classmethod
848 """Returns the instance pidfile.
849
850 """
851 return utils.PathJoin(cls._PIDS_DIR, instance_name)
852
853 @classmethod
855 """Returns the instance uidfile.
856
857 """
858 return utils.PathJoin(cls._UIDS_DIR, instance_name)
859
860 @classmethod
862 """Check pid file for instance information.
863
864 Check that a pid file is associated with an instance, and retrieve
865 information from its command line.
866
867 @type pid: string or int
868 @param pid: process id of the instance to check
869 @rtype: tuple
870 @return: (instance_name, memory, vcpus)
871 @raise errors.HypervisorError: when an instance cannot be found
872
873 """
874 alive = utils.IsProcessAlive(pid)
875 if not alive:
876 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
877
878 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
879 try:
880 cmdline = utils.ReadFile(cmdline_file)
881 except EnvironmentError, err:
882 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
883 (pid, err))
884
885 instance = None
886 memory = 0
887 vcpus = 0
888
889 arg_list = cmdline.split("\x00")
890 while arg_list:
891 arg = arg_list.pop(0)
892 if arg == "-name":
893 instance = arg_list.pop(0)
894 elif arg == "-m":
895 memory = int(arg_list.pop(0))
896 elif arg == "-smp":
897 vcpus = int(arg_list.pop(0).split(",")[0])
898
899 if instance is None:
900 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
901 " instance" % pid)
902
903 return (instance, memory, vcpus)
904
906 """Returns the instance pidfile, pid, and liveness.
907
908 @type instance_name: string
909 @param instance_name: instance name
910 @rtype: tuple
911 @return: (pid file name, pid, liveness)
912
913 """
914 pidfile = self._InstancePidFile(instance_name)
915 pid = utils.ReadPidFile(pidfile)
916
917 alive = False
918 try:
919 cmd_instance = self._InstancePidInfo(pid)[0]
920 alive = (cmd_instance == instance_name)
921 except errors.HypervisorError:
922 pass
923
924 return (pidfile, pid, alive)
925
927 """Raises an error unless the given instance is down.
928
929 """
930 alive = self._InstancePidAlive(instance_name)[2]
931 if alive:
932 raise errors.HypervisorError("Failed to start instance %s: %s" %
933 (instance_name, "already running"))
934
935 @classmethod
937 """Returns the instance monitor socket name
938
939 """
940 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
941
942 @classmethod
944 """Returns the instance serial socket name
945
946 """
947 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
948
949 @classmethod
951 """Returns the instance serial QMP socket name
952
953 """
954 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
955
956 @staticmethod
958 """Returns the correct parameters for socat
959
960 If we have a new-enough socat we can use raw mode with an escape character.
961
962 """
963 if constants.SOCAT_USE_ESCAPE:
964 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
965 else:
966 return "echo=0,icanon=0"
967
968 @classmethod
970 """Returns the instance KVM runtime filename
971
972 """
973 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
974
975 @classmethod
977 """Returns the name of the KVM chroot dir of the instance
978
979 """
980 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
981
982 @classmethod
984 """Returns the name of the directory holding the tap device files for a
985 given instance.
986
987 """
988 return utils.PathJoin(cls._NICS_DIR, instance_name)
989
990 @classmethod
992 """Returns the name of the file containing the tap device for a given NIC
993
994 """
995 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
996
997 @classmethod
999 """Returns the name of the file containing the keymap for a given instance
1000
1001 """
1002 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
1003
1004 @classmethod
1006 """Try to read a uid file
1007
1008 """
1009 if os.path.exists(uid_file):
1010 try:
1011 uid = int(utils.ReadOneLineFile(uid_file))
1012 return uid
1013 except EnvironmentError:
1014 logging.warning("Can't read uid file", exc_info=True)
1015 except (TypeError, ValueError):
1016 logging.warning("Can't parse uid file contents", exc_info=True)
1017 return None
1018
1019 @classmethod
1021 """Removes an instance's rutime sockets/files/dirs.
1022
1023 """
1024 utils.RemoveFile(pidfile)
1025 utils.RemoveFile(cls._InstanceMonitor(instance_name))
1026 utils.RemoveFile(cls._InstanceSerial(instance_name))
1027 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
1028 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
1029 utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
1030 uid_file = cls._InstanceUidFile(instance_name)
1031 uid = cls._TryReadUidFile(uid_file)
1032 utils.RemoveFile(uid_file)
1033 if uid is not None:
1034 uidpool.ReleaseUid(uid)
1035 try:
1036 shutil.rmtree(cls._InstanceNICDir(instance_name))
1037 except OSError, err:
1038 if err.errno != errno.ENOENT:
1039 raise
1040 try:
1041 chroot_dir = cls._InstanceChrootDir(instance_name)
1042 utils.RemoveDir(chroot_dir)
1043 except OSError, err:
1044 if err.errno == errno.ENOTEMPTY:
1045
1046 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
1047 prefix="%s-%s-" %
1048 (instance_name,
1049 utils.TimestampForFilename()))
1050 logging.warning("The chroot directory of instance %s can not be"
1051 " removed as it is not empty. Moving it to the"
1052 " quarantine instead. Please investigate the"
1053 " contents (%s) and clean up manually",
1054 instance_name, new_chroot_dir)
1055 utils.RenameFile(chroot_dir, new_chroot_dir)
1056 else:
1057 raise
1058
1059 @staticmethod
1108
1109 @staticmethod
1114
1115 @staticmethod
1117 """Create a CPU mask suitable for sched_setaffinity from a list of
1118 CPUs.
1119
1120 See man taskset for more info on sched_setaffinity masks.
1121 For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
1122
1123 @type cpu_list: list of int
1124 @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1125 @rtype: int
1126 @return: a bit mask of CPU affinities
1127
1128 """
1129 if cpu_list == constants.CPU_PINNING_OFF:
1130 return constants.CPU_PINNING_ALL_KVM
1131 else:
1132 return sum(2 ** cpu for cpu in cpu_list)
1133
1134 @classmethod
1136 """Change CPU affinity for running VM according to given CPU mask.
1137
1138 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1139 @type cpu_mask: string
1140 @param process_id: process ID of KVM process. Used to pin entire VM
1141 to physical CPUs.
1142 @type process_id: int
1143 @param thread_dict: map of virtual CPUs to KVM thread IDs
1144 @type thread_dict: dict int:int
1145
1146 """
1147
1148 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1149
1150 if len(cpu_list) == 1:
1151 all_cpu_mapping = cpu_list[0]
1152 if all_cpu_mapping == constants.CPU_PINNING_OFF:
1153
1154 pass
1155 else:
1156
1157
1158 cls._VerifyAffinityPackage()
1159 affinity.set_process_affinity_mask(
1160 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
1161 else:
1162
1163
1164
1165 assert len(thread_dict) == len(cpu_list)
1166 cls._VerifyAffinityPackage()
1167
1168
1169 for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1170 affinity.set_process_affinity_mask(thread_dict[i],
1171 cls._BuildAffinityCpuMask(vcpu))
1172
1174 """Get a mapping of vCPU no. to thread IDs for the instance
1175
1176 @type instance_name: string
1177 @param instance_name: instance in question
1178 @rtype: dictionary of int:int
1179 @return: a dictionary mapping vCPU numbers to thread IDs
1180
1181 """
1182 result = {}
1183 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1184 for line in output.stdout.splitlines():
1185 match = self._CPU_INFO_RE.search(line)
1186 if not match:
1187 continue
1188 grp = map(int, match.groups())
1189 result[grp[0]] = grp[1]
1190
1191 return result
1192
1194 """Complete CPU pinning.
1195
1196 @type instance_name: string
1197 @param instance_name: name of instance
1198 @type cpu_mask: string
1199 @param cpu_mask: CPU pinning mask as entered by user
1200
1201 """
1202
1203 _, pid, _ = self._InstancePidAlive(instance_name)
1204
1205 thread_dict = self._GetVcpuThreadIds(instance_name)
1206
1207 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1208
1210 """Get the list of running instances.
1211
1212 We can do this by listing our live instances directory and
1213 checking whether the associated kvm process is still alive.
1214
1215 """
1216 result = []
1217 for name in os.listdir(self._PIDS_DIR):
1218 if self._InstancePidAlive(name)[2]:
1219 result.append(name)
1220 return result
1221
1223 """Get instance properties.
1224
1225 @type instance_name: string
1226 @param instance_name: the instance name
1227 @type hvparams: dict of strings
1228 @param hvparams: hvparams to be used with this instance
1229 @rtype: tuple of strings
1230 @return: (name, id, memory, vcpus, stat, times)
1231
1232 """
1233 _, pid, alive = self._InstancePidAlive(instance_name)
1234 if not alive:
1235 return None
1236
1237 _, memory, vcpus = self._InstancePidInfo(pid)
1238 istat = "---b-"
1239 times = "0"
1240
1241 try:
1242 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1243 qmp.connect()
1244 vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1245
1246
1247 mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1248 memory = mem_bytes / 1048576
1249 except errors.HypervisorError:
1250 pass
1251
1252 return (instance_name, pid, memory, vcpus, istat, times)
1253
1255 """Get properties of all instances.
1256
1257 @type hvparams: dict of strings
1258 @param hvparams: hypervisor parameter
1259 @return: list of tuples (name, id, memory, vcpus, stat, times)
1260
1261 """
1262 data = []
1263 for name in os.listdir(self._PIDS_DIR):
1264 try:
1265 info = self.GetInstanceInfo(name)
1266 except errors.HypervisorError:
1267
1268 continue
1269 if info:
1270 data.append(info)
1271 return data
1272
1275 """Generate KVM options regarding instance's block devices.
1276
1277 @type instance: L{objects.Instance}
1278 @param instance: the instance object
1279 @type up_hvp: dict
1280 @param up_hvp: the instance's runtime hypervisor parameters
1281 @type kvm_disks: list of tuples
1282 @param kvm_disks: list of tuples [(disk, link_name, uri)..]
1283 @type kvmhelp: string
1284 @param kvmhelp: output of kvm --help
1285 @type devlist: string
1286 @param devlist: output of kvm -device ?
1287 @rtype: list
1288 @return: list of command line options eventually used by kvm executable
1289
1290 """
1291 kernel_path = up_hvp[constants.HV_KERNEL_PATH]
1292 if kernel_path:
1293 boot_disk = False
1294 else:
1295 boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1296
1297
1298
1299 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1300
1301 dev_opts = []
1302 device_driver = None
1303 disk_type = up_hvp[constants.HV_DISK_TYPE]
1304 if disk_type == constants.HT_DISK_PARAVIRTUAL:
1305 if_val = ",if=%s" % self._VIRTIO
1306 try:
1307 if self._VIRTIO_BLK_RE.search(devlist):
1308 if_val = ",if=none"
1309
1310 device_driver = self._VIRTIO_BLK_PCI
1311 except errors.HypervisorError, _:
1312 pass
1313 else:
1314 if_val = ",if=%s" % disk_type
1315
1316 disk_cache = up_hvp[constants.HV_DISK_CACHE]
1317 if instance.disk_template in constants.DTS_EXT_MIRROR:
1318 if disk_cache != "none":
1319
1320 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1321 " to prevent shared storage corruption on migration",
1322 disk_cache)
1323 cache_val = ",cache=none"
1324 elif disk_cache != constants.HT_CACHE_DEFAULT:
1325 cache_val = ",cache=%s" % disk_cache
1326 else:
1327 cache_val = ""
1328 for cfdev, link_name, uri in kvm_disks:
1329 if cfdev.mode != constants.DISK_RDWR:
1330 raise errors.HypervisorError("Instance has read-only disks which"
1331 " are not supported by KVM")
1332
1333 boot_val = ""
1334 if boot_disk:
1335 dev_opts.extend(["-boot", "c"])
1336 boot_disk = False
1337 if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1338 boot_val = ",boot=on"
1339
1340 drive_uri = _GetDriveURI(cfdev, link_name, uri)
1341
1342 drive_val = "file=%s,format=raw%s%s%s" % \
1343 (drive_uri, if_val, boot_val, cache_val)
1344
1345 if device_driver:
1346
1347
1348
1349 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK, cfdev)
1350 drive_val += (",id=%s" % kvm_devid)
1351 drive_val += (",bus=0,unit=%d" % cfdev.pci)
1352 dev_val = ("%s,drive=%s,id=%s" %
1353 (device_driver, kvm_devid, kvm_devid))
1354 dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1355 dev_opts.extend(["-device", dev_val])
1356
1357 dev_opts.extend(["-drive", drive_val])
1358
1359 return dev_opts
1360
1363 """Generate KVM information to start an instance.
1364
1365 @type kvmhelp: string
1366 @param kvmhelp: output of kvm --help
1367 @attention: this function must not have any side-effects; for
1368 example, it must not write to the filesystem, or read values
1369 from the current system the are expected to differ between
1370 nodes, since it is only run once at instance startup;
1371 actions/kvm arguments that can vary between systems should be
1372 done in L{_ExecuteKVMRuntime}
1373
1374 """
1375
1376 hvp = instance.hvparams
1377 self.ValidateParameters(hvp)
1378
1379 pidfile = self._InstancePidFile(instance.name)
1380 kvm = hvp[constants.HV_KVM_PATH]
1381 kvm_cmd = [kvm]
1382
1383 kvm_cmd.extend(["-name", instance.name])
1384 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1385
1386 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1387 if hvp[constants.HV_CPU_CORES]:
1388 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1389 if hvp[constants.HV_CPU_THREADS]:
1390 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1391 if hvp[constants.HV_CPU_SOCKETS]:
1392 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1393
1394 kvm_cmd.extend(["-smp", ",".join(smp_list)])
1395
1396 kvm_cmd.extend(["-pidfile", pidfile])
1397
1398 pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1399
1400
1401 if hvp[constants.HV_SOUNDHW]:
1402 soundhw = hvp[constants.HV_SOUNDHW]
1403
1404
1405
1406 if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
1407 _ = _GetFreeSlot(pci_reservations, reserve=True)
1408 kvm_cmd.extend(["-soundhw", soundhw])
1409
1410 if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
1411
1412 _ = _GetFreeSlot(pci_reservations, reserve=True)
1413
1414
1415 addr = _GetFreeSlot(pci_reservations, reserve=True)
1416 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1417 kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
1418 kvm_cmd.extend(["-daemonize"])
1419 if not instance.hvparams[constants.HV_ACPI]:
1420 kvm_cmd.extend(["-no-acpi"])
1421 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1422 constants.INSTANCE_REBOOT_EXIT:
1423 kvm_cmd.extend(["-no-reboot"])
1424
1425 mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1426 if not mversion:
1427 mversion = self._GetDefaultMachineVersion(kvm)
1428 if self._MACHINE_RE.search(kvmhelp):
1429
1430
1431
1432 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1433 specprop = ",accel=kvm"
1434 else:
1435 specprop = ""
1436 machinespec = "%s%s" % (mversion, specprop)
1437 kvm_cmd.extend(["-machine", machinespec])
1438 else:
1439 kvm_cmd.extend(["-M", mversion])
1440 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1441 self._ENABLE_KVM_RE.search(kvmhelp)):
1442 kvm_cmd.extend(["-enable-kvm"])
1443 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1444 self._DISABLE_KVM_RE.search(kvmhelp)):
1445 kvm_cmd.extend(["-disable-kvm"])
1446
1447 kernel_path = hvp[constants.HV_KERNEL_PATH]
1448 if kernel_path:
1449 boot_cdrom = boot_floppy = boot_network = False
1450 else:
1451 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1452 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1453 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1454
1455 if startup_paused:
1456 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1457
1458 if boot_network:
1459 kvm_cmd.extend(["-boot", "n"])
1460
1461
1462
1463 needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1464
1465 disk_type = hvp[constants.HV_DISK_TYPE]
1466
1467
1468 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1469 if not cdrom_disk_type:
1470 cdrom_disk_type = disk_type
1471
1472 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1473 if iso_image:
1474 options = ",format=raw,media=cdrom"
1475
1476 if boot_cdrom:
1477 actual_cdrom_type = constants.HT_DISK_IDE
1478 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1479 actual_cdrom_type = "virtio"
1480 else:
1481 actual_cdrom_type = cdrom_disk_type
1482 if_val = ",if=%s" % actual_cdrom_type
1483
1484 boot_val = ""
1485 if boot_cdrom:
1486 kvm_cmd.extend(["-boot", "d"])
1487 if needs_boot_flag:
1488 boot_val = ",boot=on"
1489
1490 drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1491 kvm_cmd.extend(["-drive", drive_val])
1492
1493 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1494 if iso_image2:
1495 options = ",format=raw,media=cdrom"
1496 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1497 if_val = ",if=virtio"
1498 else:
1499 if_val = ",if=%s" % cdrom_disk_type
1500 drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1501 kvm_cmd.extend(["-drive", drive_val])
1502
1503 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1504 if floppy_image:
1505 options = ",format=raw,media=disk"
1506 if boot_floppy:
1507 kvm_cmd.extend(["-boot", "a"])
1508 options = "%s,boot=on" % options
1509 if_val = ",if=floppy"
1510 options = "%s%s" % (options, if_val)
1511 drive_val = "file=%s%s" % (floppy_image, options)
1512 kvm_cmd.extend(["-drive", drive_val])
1513
1514 if kernel_path:
1515 kvm_cmd.extend(["-kernel", kernel_path])
1516 initrd_path = hvp[constants.HV_INITRD_PATH]
1517 if initrd_path:
1518 kvm_cmd.extend(["-initrd", initrd_path])
1519 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1520 hvp[constants.HV_KERNEL_ARGS]]
1521 if hvp[constants.HV_SERIAL_CONSOLE]:
1522 serial_speed = hvp[constants.HV_SERIAL_SPEED]
1523 root_append.append("console=ttyS0,%s" % serial_speed)
1524 kvm_cmd.extend(["-append", " ".join(root_append)])
1525
1526 mem_path = hvp[constants.HV_MEM_PATH]
1527 if mem_path:
1528 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1529
1530 monitor_dev = ("unix:%s,server,nowait" %
1531 self._InstanceMonitor(instance.name))
1532 kvm_cmd.extend(["-monitor", monitor_dev])
1533 if hvp[constants.HV_SERIAL_CONSOLE]:
1534 serial_dev = ("unix:%s,server,nowait" %
1535 self._InstanceSerial(instance.name))
1536 kvm_cmd.extend(["-serial", serial_dev])
1537 else:
1538 kvm_cmd.extend(["-serial", "none"])
1539
1540 mouse_type = hvp[constants.HV_USB_MOUSE]
1541 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1542 spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1543 spice_ip_version = None
1544
1545 kvm_cmd.extend(["-usb"])
1546
1547 if mouse_type:
1548 kvm_cmd.extend(["-usbdevice", mouse_type])
1549 elif vnc_bind_address:
1550 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1551
1552 if vnc_bind_address:
1553 if netutils.IsValidInterface(vnc_bind_address):
1554 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1555 if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1556 if len(if_ip4_addresses) < 1:
1557 logging.error("Could not determine IPv4 address of interface %s",
1558 vnc_bind_address)
1559 else:
1560 vnc_bind_address = if_ip4_addresses[0]
1561 if netutils.IP4Address.IsValid(vnc_bind_address):
1562 if instance.network_port > constants.VNC_BASE_PORT:
1563 display = instance.network_port - constants.VNC_BASE_PORT
1564 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1565 vnc_arg = ":%d" % (display)
1566 else:
1567 vnc_arg = "%s:%d" % (vnc_bind_address, display)
1568 else:
1569 logging.error("Network port is not a valid VNC display (%d < %d),"
1570 " not starting VNC",
1571 instance.network_port, constants.VNC_BASE_PORT)
1572 vnc_arg = "none"
1573
1574
1575
1576 vnc_append = ""
1577 if hvp[constants.HV_VNC_TLS]:
1578 vnc_append = "%s,tls" % vnc_append
1579 if hvp[constants.HV_VNC_X509_VERIFY]:
1580 vnc_append = "%s,x509verify=%s" % (vnc_append,
1581 hvp[constants.HV_VNC_X509])
1582 elif hvp[constants.HV_VNC_X509]:
1583 vnc_append = "%s,x509=%s" % (vnc_append,
1584 hvp[constants.HV_VNC_X509])
1585 if hvp[constants.HV_VNC_PASSWORD_FILE]:
1586 vnc_append = "%s,password" % vnc_append
1587
1588 vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1589
1590 else:
1591 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1592
1593 kvm_cmd.extend(["-vnc", vnc_arg])
1594 elif spice_bind:
1595
1596
1597 if netutils.IsValidInterface(spice_bind):
1598
1599
1600 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1601 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1602
1603
1604
1605 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1606 if not addresses[spice_ip_version]:
1607 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1608 " for %s" % (spice_ip_version,
1609 spice_bind))
1610
1611
1612 elif (addresses[constants.IP4_VERSION] and
1613 addresses[constants.IP6_VERSION]):
1614
1615
1616 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1617 spice_ip_version = \
1618 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1619 elif addresses[constants.IP4_VERSION]:
1620 spice_ip_version = constants.IP4_VERSION
1621 elif addresses[constants.IP6_VERSION]:
1622 spice_ip_version = constants.IP6_VERSION
1623 else:
1624 raise errors.HypervisorError("SPICE: Unable to get an IP address"
1625 " for %s" % (spice_bind))
1626
1627 spice_address = addresses[spice_ip_version][0]
1628
1629 else:
1630
1631
1632 spice_address = spice_bind
1633
1634 spice_arg = "addr=%s" % spice_address
1635 if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1636 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1637 (spice_arg, instance.network_port,
1638 pathutils.SPICE_CACERT_FILE))
1639 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1640 (spice_arg, pathutils.SPICE_CERT_FILE,
1641 pathutils.SPICE_CERT_FILE))
1642 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1643 if tls_ciphers:
1644 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1645 else:
1646 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1647
1648 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1649 spice_arg = "%s,disable-ticketing" % spice_arg
1650
1651 if spice_ip_version:
1652 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1653
1654
1655 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1656 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1657 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1658 if img_lossless:
1659 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1660 if img_jpeg:
1661 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1662 if img_zlib_glz:
1663 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1664
1665
1666 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1667 if video_streaming:
1668 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1669
1670
1671 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1672 spice_arg = "%s,playback-compression=off" % spice_arg
1673 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1674 spice_arg = "%s,agent-mouse=off" % spice_arg
1675 else:
1676
1677
1678 addr = _GetFreeSlot(pci_reservations, reserve=True)
1679 pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1680 kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
1681 kvm_cmd.extend([
1682 "-device",
1683 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1684 ])
1685 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1686
1687 logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1688 kvm_cmd.extend(["-spice", spice_arg])
1689
1690 else:
1691
1692
1693 if self._DISPLAY_RE.search(kvmhelp):
1694 kvm_cmd.extend(["-display", "none"])
1695 else:
1696 kvm_cmd.extend(["-nographic"])
1697
1698 if hvp[constants.HV_USE_LOCALTIME]:
1699 kvm_cmd.extend(["-localtime"])
1700
1701 if hvp[constants.HV_KVM_USE_CHROOT]:
1702 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1703
1704
1705 if hvp[constants.HV_CPU_TYPE]:
1706 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1707
1708
1709
1710 if hvp[constants.HV_VGA]:
1711 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1712 elif spice_bind:
1713 kvm_cmd.extend(["-vga", "qxl"])
1714
1715
1716 if hvp[constants.HV_USB_DEVICES]:
1717 for dev in hvp[constants.HV_USB_DEVICES].split(","):
1718 kvm_cmd.extend(["-usbdevice", dev])
1719
1720
1721 if self._UUID_RE.search(kvmhelp):
1722 kvm_cmd.extend(["-uuid", instance.uuid])
1723
1724 if hvp[constants.HV_KVM_EXTRA]:
1725 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1726
1727 kvm_disks = []
1728 for disk, link_name, uri in block_devices:
1729 disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
1730 kvm_disks.append((disk, link_name, uri))
1731
1732 kvm_nics = []
1733 for nic in instance.nics:
1734 nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True)
1735 kvm_nics.append(nic)
1736
1737 hvparams = hvp
1738
1739 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1740
1750
1752 """Read an instance's KVM runtime
1753
1754 """
1755 try:
1756 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1757 except EnvironmentError, err:
1758 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1759 return file_content
1760
1762 """Save an instance's KVM runtime
1763
1764 """
1765 kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1766
1767 serialized_nics = [nic.ToDict() for nic in kvm_nics]
1768 serialized_disks = [(blk.ToDict(), link, uri)
1769 for blk, link, uri in kvm_disks]
1770 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1771 serialized_disks))
1772
1773 self._WriteKVMRuntime(instance.name, serialized_form)
1774
1776 """Load an instance's KVM runtime
1777
1778 """
1779 if not serialized_runtime:
1780 serialized_runtime = self._ReadKVMRuntime(instance.name)
1781
1782 return _AnalyzeSerializedRuntime(serialized_runtime)
1783
1784 - def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1785 """Run the KVM cmd and check for errors
1786
1787 @type name: string
1788 @param name: instance name
1789 @type kvm_cmd: list of strings
1790 @param kvm_cmd: runcmd input for kvm
1791 @type tap_fds: list of int
1792 @param tap_fds: fds of tap devices opened by Ganeti
1793
1794 """
1795 try:
1796 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1797 finally:
1798 for fd in tap_fds:
1799 utils_wrapper.CloseFdNoError(fd)
1800
1801 if result.failed:
1802 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1803 (name, result.fail_reason, result.output))
1804 if not self._InstancePidAlive(name)[2]:
1805 raise errors.HypervisorError("Failed to start instance %s" % name)
1806
1807
1808
1810 """Execute a KVM cmd, after completing it with some last minute data.
1811
1812 @type incoming: tuple of strings
1813 @param incoming: (target_host_ip, port)
1814 @type kvmhelp: string
1815 @param kvmhelp: output of kvm --help
1816
1817 """
1818
1819
1820
1821
1822
1823
1824
1825
1826 conf_hvp = instance.hvparams
1827 name = instance.name
1828 self._CheckDown(name)
1829
1830 temp_files = []
1831
1832 kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1833
1834 kvm_path = kvm_cmd[0]
1835 up_hvp = objects.FillDict(conf_hvp, up_hvp)
1836
1837
1838
1839 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1840 if security_model == constants.HT_SM_USER:
1841 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1842
1843 keymap = conf_hvp[constants.HV_KEYMAP]
1844 if keymap:
1845 keymap_path = self._InstanceKeymapFile(name)
1846
1847
1848
1849
1850 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1851 kvm_cmd.extend(["-k", keymap_path])
1852
1853
1854
1855
1856 tapfds = []
1857 taps = []
1858 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1859 if not kvm_nics:
1860 kvm_cmd.extend(["-net", "none"])
1861 else:
1862 vnet_hdr = False
1863 tap_extra = ""
1864 nic_type = up_hvp[constants.HV_NIC_TYPE]
1865 if nic_type == constants.HT_NIC_PARAVIRTUAL:
1866 nic_model = self._VIRTIO
1867 try:
1868 if self._VIRTIO_NET_RE.search(devlist):
1869 nic_model = self._VIRTIO_NET_PCI
1870 vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1871 except errors.HypervisorError, _:
1872
1873
1874 pass
1875
1876 if up_hvp[constants.HV_VHOST_NET]:
1877
1878 if self._VHOST_RE.search(kvmhelp):
1879 tap_extra = ",vhost=on"
1880 else:
1881 raise errors.HypervisorError("vhost_net is configured"
1882 " but it is not available")
1883 else:
1884 nic_model = nic_type
1885
1886 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1887
1888 for nic_seq, nic in enumerate(kvm_nics):
1889 tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr)
1890 tapfds.append(tapfd)
1891 taps.append(tapname)
1892 if kvm_supports_netdev:
1893 nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1894 try:
1895
1896
1897 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1898 netdev = kvm_devid
1899 nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1900 except errors.HotplugError:
1901 netdev = "netdev%d" % nic_seq
1902 nic_val += (",netdev=%s" % netdev)
1903 tap_val = ("type=tap,id=%s,fd=%d%s" %
1904 (netdev, tapfd, tap_extra))
1905 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1906 else:
1907 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1908 nic.mac, nic_model)
1909 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1910 kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1911
1912 if incoming:
1913 target, port = incoming
1914 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1915
1916
1917
1918
1919 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1920 vnc_pwd = None
1921 if vnc_pwd_file:
1922 try:
1923 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1924 except EnvironmentError, err:
1925 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1926 % (vnc_pwd_file, err))
1927
1928 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1929 utils.EnsureDirs([(self._InstanceChrootDir(name),
1930 constants.SECURE_DIR_MODE)])
1931
1932
1933 if self._QMP_RE.search(kvmhelp):
1934 logging.debug("Enabling QMP")
1935 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1936 self._InstanceQmpMonitor(instance.name)])
1937
1938
1939
1940
1941 for nic_seq, nic in enumerate(kvm_nics):
1942 if (incoming and
1943 nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED):
1944 continue
1945 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1946
1947 bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1948 up_hvp,
1949 kvm_disks,
1950 kvmhelp,
1951 devlist)
1952 kvm_cmd.extend(bdev_opts)
1953
1954
1955
1956 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1957 if start_kvm_paused:
1958 kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1959
1960
1961
1962
1963 cpu_pinning = False
1964 if up_hvp.get(constants.HV_CPU_MASK, None):
1965 cpu_pinning = True
1966
1967 if security_model == constants.HT_SM_POOL:
1968 ss = ssconf.SimpleStore()
1969 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1970 all_uids = set(uidpool.ExpandUidPool(uid_pool))
1971 uid = uidpool.RequestUnusedUid(all_uids)
1972 try:
1973 username = pwd.getpwuid(uid.GetUid()).pw_name
1974 kvm_cmd.extend(["-runas", username])
1975 self._RunKVMCmd(name, kvm_cmd, tapfds)
1976 except:
1977 uidpool.ReleaseUid(uid)
1978 raise
1979 else:
1980 uid.Unlock()
1981 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1982 else:
1983 self._RunKVMCmd(name, kvm_cmd, tapfds)
1984
1985 utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1986 constants.RUN_DIRS_MODE)])
1987 for nic_seq, tap in enumerate(taps):
1988 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1989 data=tap)
1990
1991 if vnc_pwd:
1992 change_cmd = "change vnc password %s" % vnc_pwd
1993 self._CallMonitorCommand(instance.name, change_cmd)
1994
1995
1996
1997
1998
1999
2000 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
2001 if spice_password_file:
2002 spice_pwd = ""
2003 try:
2004 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
2005 except EnvironmentError, err:
2006 raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
2007 % (spice_password_file, err))
2008
2009 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
2010 qmp.connect()
2011 arguments = {
2012 "protocol": "spice",
2013 "password": spice_pwd,
2014 }
2015 qmp.Execute("set_password", arguments)
2016
2017 for filename in temp_files:
2018 utils.RemoveFile(filename)
2019
2020
2021 if cpu_pinning:
2022 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
2023
2024 start_memory = self._InstanceStartupMemory(instance)
2025 if start_memory < instance.beparams[constants.BE_MAXMEM]:
2026 self.BalloonInstanceMemory(instance, start_memory)
2027
2028 if start_kvm_paused:
2029
2030
2031
2032 self._CallMonitorCommand(instance.name, self._CONT_CMD)
2033
2034 - def StartInstance(self, instance, block_devices, startup_paused):
2045
2047 """Invoke a command on the instance monitor.
2048
2049 """
2050 if timeout is not None:
2051 timeout_cmd = "timeout %s" % (timeout, )
2052 else:
2053 timeout_cmd = ""
2054
2055
2056
2057
2058
2059
2060
2061
2062 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
2063 (utils.ShellQuote(command),
2064 timeout_cmd,
2065 constants.SOCAT_PATH,
2066 utils.ShellQuote(self._InstanceMonitor(instance_name))))
2067
2068 result = utils.RunCmd(socat)
2069 if result.failed:
2070 msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2071 " output: %s" %
2072 (command, instance_name, result.fail_reason, result.output))
2073 raise errors.HypervisorError(msg)
2074
2075 return result
2076
2078 """Get the first available pci slot of a runnung instance.
2079
2080 """
2081 slots = bitarray(32)
2082 slots.setall(False)
2083 output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2084 for line in output.stdout.splitlines():
2085 match = self._INFO_PCI_RE.search(line)
2086 if match:
2087 slot = int(match.group(1))
2088 slots[slot] = True
2089
2090 dev.pci = _GetFreeSlot(slots)
2091
2118
2142
2147
2150 """Checks if a previous hotplug command has succeeded.
2151
2152 It issues info pci monitor command and checks depending on should_exist
2153 value if an entry with PCI slot and device ID is found or not.
2154
2155 @raise errors.HypervisorError: if result is not the expected one
2156
2157 """
2158 output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2159 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2160 match = \
2161 self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2162 if match and not should_exist:
2163 msg = "Device %s should have been removed but is still there" % kvm_devid
2164 raise errors.HypervisorError(msg)
2165
2166 if not match and should_exist:
2167 msg = "Device %s should have been added but is missing" % kvm_devid
2168 raise errors.HypervisorError(msg)
2169
2170 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
2171
2172 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
2173 """ Helper method to hot-add a new device
2174
2175 It gets free pci slot generates the device name and invokes the
2176 device specific method.
2177
2178 """
2179
2180 if device.pci is None:
2181 self._GetFreePCISlot(instance, device)
2182 kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2183 runtime = self._LoadKVMRuntime(instance)
2184 if dev_type == constants.HOTPLUG_TARGET_DISK:
2185 drive_uri = _GetDriveURI(device, extra[0], extra[1])
2186 cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
2187 (drive_uri, kvm_devid)]
2188 cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2189 (hex(device.pci), kvm_devid, kvm_devid)]
2190 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2191 (tap, fd) = _OpenTap()
2192 self._ConfigureNIC(instance, seq, device, tap)
2193 self._PassTapFd(instance, fd, device)
2194 cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
2195 args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2196 (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2197 cmds += ["device_add %s" % args]
2198 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2199
2200 self._CallHotplugCommands(instance.name, cmds)
2201 self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2202
2203 index = _DEVICE_RUNTIME_INDEX[dev_type]
2204 entry = _RUNTIME_ENTRY[dev_type](device, extra)
2205 runtime[index].append(entry)
2206 self._SaveKVMRuntime(instance, runtime)
2207
2208 - def HotDelDevice(self, instance, dev_type, device, _, seq):
2209 """ Helper method for hot-del device
2210
2211 It gets device info from runtime file, generates the device name and
2212 invokes the device specific method.
2213
2214 """
2215 runtime = self._LoadKVMRuntime(instance)
2216 entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2217 kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2218 kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2219 if dev_type == constants.HOTPLUG_TARGET_DISK:
2220 cmds = ["device_del %s" % kvm_devid]
2221 cmds += ["drive_del %s" % kvm_devid]
2222 elif dev_type == constants.HOTPLUG_TARGET_NIC:
2223 cmds = ["device_del %s" % kvm_devid]
2224 cmds += ["netdev_del %s" % kvm_devid]
2225 utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2226 self._CallHotplugCommands(instance.name, cmds)
2227 self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2228 index = _DEVICE_RUNTIME_INDEX[dev_type]
2229 runtime[index].remove(entry)
2230 self._SaveKVMRuntime(instance, runtime)
2231
2232 return kvm_device.pci
2233
2234 - def HotModDevice(self, instance, dev_type, device, _, seq):
2235 """ Helper method for hot-mod device
2236
2237 It gets device info from runtime file, generates the device name and
2238 invokes the device specific method. Currently only NICs support hot-mod
2239
2240 """
2241 if dev_type == constants.HOTPLUG_TARGET_NIC:
2242
2243 device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2244 self.HotAddDevice(instance, dev_type, device, _, seq)
2245
2247 """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2248
2249 """
2250
2251
2252 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2253 command = "getfd %s\n" % kvm_devid
2254 fds = [fd]
2255 logging.info("%s", fds)
2256 try:
2257 monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2258 monsock.connect()
2259 fdsend.sendfds(monsock.sock, command, fds=fds)
2260 finally:
2261 monsock.close()
2262
2263 @classmethod
2265 """Parse the KVM version from the --help output.
2266
2267 @type text: string
2268 @param text: output of kvm --help
2269 @return: (version, v_maj, v_min, v_rev)
2270 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2271
2272 """
2273 match = cls._VERSION_RE.search(text.splitlines()[0])
2274 if not match:
2275 raise errors.HypervisorError("Unable to get KVM version")
2276
2277 v_all = match.group(0)
2278 v_maj = int(match.group(1))
2279 v_min = int(match.group(2))
2280 if match.group(4):
2281 v_rev = int(match.group(4))
2282 else:
2283 v_rev = 0
2284 return (v_all, v_maj, v_min, v_rev)
2285
2286 @classmethod
2288 """Return the output of a kvm invocation
2289
2290 @type kvm_path: string
2291 @param kvm_path: path to the kvm executable
2292 @type option: a key of _KVMOPTS_CMDS
2293 @param option: kvm option to fetch the output from
2294 @return: output a supported kvm invocation
2295 @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2296
2297 """
2298 assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2299
2300 optlist, can_fail = cls._KVMOPTS_CMDS[option]
2301
2302 result = utils.RunCmd([kvm_path] + optlist)
2303 if result.failed and not can_fail:
2304 raise errors.HypervisorError("Unable to get KVM %s output" %
2305 " ".join(optlist))
2306 return result.output
2307
2308 @classmethod
2310 """Return the installed KVM version.
2311
2312 @return: (version, v_maj, v_min, v_rev)
2313 @raise errors.HypervisorError: when the KVM version cannot be retrieved
2314
2315 """
2316 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2317
2318 @classmethod
2329
2330 - def StopInstance(self, instance, force=False, retry=False, name=None,
2331 timeout=None):
2350
2359
2382
2384 """Get instance information to perform a migration.
2385
2386 @type instance: L{objects.Instance}
2387 @param instance: instance to be migrated
2388 @rtype: string
2389 @return: content of the KVM runtime file
2390
2391 """
2392 return self._ReadKVMRuntime(instance.name)
2393
2395 """Prepare to accept an instance.
2396
2397 @type instance: L{objects.Instance}
2398 @param instance: instance to be accepted
2399 @type info: string
2400 @param info: content of the KVM runtime file on the source node
2401 @type target: string
2402 @param target: target host (usually ip), on this node
2403
2404 """
2405 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2406 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2407 kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2408 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2409 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2410 incoming=incoming_address)
2411
2413 """Finalize the instance migration on the target node.
2414
2415 Stop the incoming mode KVM.
2416
2417 @type instance: L{objects.Instance}
2418 @param instance: instance whose migration is being finalized
2419
2420 """
2421 if success:
2422 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2423 kvm_nics = kvm_runtime[1]
2424
2425 for nic_seq, nic in enumerate(kvm_nics):
2426 if nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_ROUTED:
2427
2428 continue
2429 try:
2430 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2431 except EnvironmentError, err:
2432 logging.warning("Failed to find host interface for %s NIC #%d: %s",
2433 instance.name, nic_seq, str(err))
2434 continue
2435 try:
2436 self._ConfigureNIC(instance, nic_seq, nic, tap)
2437 except errors.HypervisorError, err:
2438 logging.warning(str(err))
2439
2440 self._WriteKVMRuntime(instance.name, info)
2441 else:
2442 self.StopInstance(instance, force=True)
2443
2445 """Migrate an instance to a target node.
2446
2447 The migration will not be attempted if the instance is not
2448 currently running.
2449
2450 @type cluster_name: string
2451 @param cluster_name: name of the cluster
2452 @type instance: L{objects.Instance}
2453 @param instance: the instance to be migrated
2454 @type target: string
2455 @param target: ip address of the target node
2456 @type live: boolean
2457 @param live: perform a live migration
2458
2459 """
2460 instance_name = instance.name
2461 port = instance.hvparams[constants.HV_MIGRATION_PORT]
2462 _, _, alive = self._InstancePidAlive(instance_name)
2463 if not alive:
2464 raise errors.HypervisorError("Instance not running, cannot migrate")
2465
2466 if not live:
2467 self._CallMonitorCommand(instance_name, "stop")
2468
2469 migrate_command = ("migrate_set_speed %dm" %
2470 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2471 self._CallMonitorCommand(instance_name, migrate_command)
2472
2473 migrate_command = ("migrate_set_downtime %dms" %
2474 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2475 self._CallMonitorCommand(instance_name, migrate_command)
2476
2477 migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2478 self._CallMonitorCommand(instance_name, migrate_command)
2479
2497
2499 """Get the migration status
2500
2501 @type instance: L{objects.Instance}
2502 @param instance: the instance that is being migrated
2503 @rtype: L{objects.MigrationStatus}
2504 @return: the status of the current migration (one of
2505 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2506 progress info that can be retrieved from the hypervisor
2507
2508 """
2509 info_command = "info migrate"
2510 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2511 result = self._CallMonitorCommand(instance.name, info_command)
2512 match = self._MIGRATION_STATUS_RE.search(result.stdout)
2513 if not match:
2514 if not result.stdout:
2515 logging.info("KVM: empty 'info migrate' result")
2516 else:
2517 logging.warning("KVM: unknown 'info migrate' result: %s",
2518 result.stdout)
2519 else:
2520 status = match.group(1)
2521 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2522 migration_status = objects.MigrationStatus(status=status)
2523 match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2524 if match:
2525 migration_status.transferred_ram = match.group("transferred")
2526 migration_status.total_ram = match.group("total")
2527
2528 return migration_status
2529
2530 logging.warning("KVM: unknown migration status '%s'", status)
2531
2532 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2533
2534 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2535
2537 """Balloon an instance memory to a certain value.
2538
2539 @type instance: L{objects.Instance}
2540 @param instance: instance to be accepted
2541 @type mem: int
2542 @param mem: actual memory size to use for instance runtime
2543
2544 """
2545 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2546
2548 """Return information about the node.
2549
2550 @type hvparams: dict of strings
2551 @param hvparams: hypervisor parameters, not used in this class
2552
2553 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2554 the following keys:
2555 - hv_version: the hypervisor version in the form (major, minor,
2556 revision)
2557
2558 """
2559 result = self.GetLinuxNodeInfo()
2560 kvmpath = constants.KVM_PATH
2561 if hvparams is not None:
2562 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2563 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2564 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2565 return result
2566
2567 @classmethod
2569 """Return a command for connecting to the console of an instance.
2570
2571 """
2572 if hvparams[constants.HV_SERIAL_CONSOLE]:
2573 cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2574 constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2575 utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2576 "STDIO,%s" % cls._SocatUnixConsoleParams(),
2577 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2578 return objects.InstanceConsole(instance=instance.name,
2579 kind=constants.CONS_SSH,
2580 host=primary_node.name,
2581 user=constants.SSH_CONSOLE_USER,
2582 command=cmd)
2583
2584 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2585 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2586 display = instance.network_port - constants.VNC_BASE_PORT
2587 return objects.InstanceConsole(instance=instance.name,
2588 kind=constants.CONS_VNC,
2589 host=vnc_bind_address,
2590 port=instance.network_port,
2591 display=display)
2592
2593 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2594 if spice_bind:
2595 return objects.InstanceConsole(instance=instance.name,
2596 kind=constants.CONS_SPICE,
2597 host=spice_bind,
2598 port=instance.network_port)
2599
2600 return objects.InstanceConsole(instance=instance.name,
2601 kind=constants.CONS_MESSAGE,
2602 message=("No serial shell for instance %s" %
2603 instance.name))
2604
2605 - def Verify(self, hvparams=None):
2606 """Verify the hypervisor.
2607
2608 Check that the required binaries exist.
2609
2610 @type hvparams: dict of strings
2611 @param hvparams: hypervisor parameters to be verified against, not used here
2612
2613 @return: Problem description if something is wrong, C{None} otherwise
2614
2615 """
2616 msgs = []
2617 kvmpath = constants.KVM_PATH
2618 if hvparams is not None:
2619 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2620 if not os.path.exists(kvmpath):
2621 msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2622 if not os.path.exists(constants.SOCAT_PATH):
2623 msgs.append("The socat binary ('%s') does not exist" %
2624 constants.SOCAT_PATH)
2625
2626 return self._FormatVerifyResults(msgs)
2627
2628 @classmethod
2630 """Check the given parameters for validity.
2631
2632 @type hvparams: dict
2633 @param hvparams: dictionary with parameter names/value
2634 @raise errors.HypervisorError: when a parameter is not valid
2635
2636 """
2637 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2638
2639 kernel_path = hvparams[constants.HV_KERNEL_PATH]
2640 if kernel_path:
2641 if not hvparams[constants.HV_ROOT_PATH]:
2642 raise errors.HypervisorError("Need a root partition for the instance,"
2643 " if a kernel is defined")
2644
2645 if (hvparams[constants.HV_VNC_X509_VERIFY] and
2646 not hvparams[constants.HV_VNC_X509]):
2647 raise errors.HypervisorError("%s must be defined, if %s is" %
2648 (constants.HV_VNC_X509,
2649 constants.HV_VNC_X509_VERIFY))
2650
2651 if hvparams[constants.HV_SERIAL_CONSOLE]:
2652 serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2653 valid_speeds = constants.VALID_SERIAL_SPEEDS
2654 if not serial_speed or serial_speed not in valid_speeds:
2655 raise errors.HypervisorError("Invalid serial console speed, must be"
2656 " one of: %s" %
2657 utils.CommaJoin(valid_speeds))
2658
2659 boot_order = hvparams[constants.HV_BOOT_ORDER]
2660 if (boot_order == constants.HT_BO_CDROM and
2661 not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2662 raise errors.HypervisorError("Cannot boot from cdrom without an"
2663 " ISO path")
2664
2665 security_model = hvparams[constants.HV_SECURITY_MODEL]
2666 if security_model == constants.HT_SM_USER:
2667 if not hvparams[constants.HV_SECURITY_DOMAIN]:
2668 raise errors.HypervisorError("A security domain (user to run kvm as)"
2669 " must be specified")
2670 elif (security_model == constants.HT_SM_NONE or
2671 security_model == constants.HT_SM_POOL):
2672 if hvparams[constants.HV_SECURITY_DOMAIN]:
2673 raise errors.HypervisorError("Cannot have a security domain when the"
2674 " security model is 'none' or 'pool'")
2675
2676 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2677 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2678 if spice_bind:
2679 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2680
2681
2682 if (netutils.IP4Address.IsValid(spice_bind) and
2683 spice_ip_version != constants.IP4_VERSION):
2684 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2685 " the specified IP version is %s" %
2686 (spice_bind, spice_ip_version))
2687
2688 if (netutils.IP6Address.IsValid(spice_bind) and
2689 spice_ip_version != constants.IP6_VERSION):
2690 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2691 " the specified IP version is %s" %
2692 (spice_bind, spice_ip_version))
2693 else:
2694
2695
2696 for param in _SPICE_ADDITIONAL_PARAMS:
2697 if hvparams[param]:
2698 raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2699 (param, constants.HV_KVM_SPICE_BIND))
2700
2701 @classmethod
2761
2762 @classmethod
2764 """KVM powercycle, just a wrapper over Linux powercycle.
2765
2766 @type hvparams: dict of strings
2767 @param hvparams: hypervisor params to be used on this node
2768
2769 """
2770 cls.LinuxPowercycle()
2771