Package ganeti :: Package hypervisor :: Module hv_kvm
[hide private]
[frames] | no frames]

Source Code for Module ganeti.hypervisor.hv_kvm

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. 
   5  # All rights reserved. 
   6  # 
   7  # Redistribution and use in source and binary forms, with or without 
   8  # modification, are permitted provided that the following conditions are 
   9  # met: 
  10  # 
  11  # 1. Redistributions of source code must retain the above copyright notice, 
  12  # this list of conditions and the following disclaimer. 
  13  # 
  14  # 2. Redistributions in binary form must reproduce the above copyright 
  15  # notice, this list of conditions and the following disclaimer in the 
  16  # documentation and/or other materials provided with the distribution. 
  17  # 
  18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
  19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
  20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
  22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
  28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  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   # pylint: disable=F0401 
  52  except ImportError: 
  53    affinity = None 
  54  try: 
  55    import fdsend   # pylint: disable=F0401 
  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  # TUN/TAP driver constants, taken from <linux/if_tun.h> 
  76  # They are architecture-independent and already hardcoded in qemu-kvm source, 
  77  # so we can safely include them here. 
  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  #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND} 
  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  # Constant bitarray that reflects to a free pci slot 
  98  # Use it with bitarray.search() 
  99  _AVAILABLE_PCI_SLOT = bitarray("0") 
 100   
 101  # below constants show the format of runtime file 
 102  # the nics are in second possition, while the disks in 4th (last) 
 103  # moreover disk entries are stored as a list of in tuples 
 104  # (L{objects.Disk}, link_name, uri) 
 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    } 
126 127 128 -def _GetDriveURI(disk, link, uri):
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
148 149 -def _GenerateDeviceKVMId(dev_type, dev):
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
170 171 -def _GetFreeSlot(slots, slot=None, reserve=False):
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
202 203 -def _GetExistingDeviceInfo(dev_type, device, runtime):
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
228 229 -def _UpgradeSerializedRuntime(serialized_runtime):
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 # Add a dummy uuid slot if an pre-2.8 NIC is found 251 if "uuid" not in nic: 252 nic["uuid"] = utils.NewUUID() 253 254 return kvm_cmd, serialized_nics, hvparams, serialized_disks
255
256 257 -def _AnalyzeSerializedRuntime(serialized_runtime):
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
274 275 -def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
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
291 292 -def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
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 # Not supported 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
322 323 -def _OpenTap(vnet_hdr=True):
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 # The struct ifreq ioctl request (see netdevice(7)) 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 # Get the interface name from the ioctl 355 ifname = struct.unpack("16sh", res)[0].strip("\x00") 356 return (ifname, tapfd)
357
358 359 -class QmpMessage:
360 """QEMU Messaging Protocol (QMP) message. 361 362 """
363 - def __init__(self, data):
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
372 - def __getitem__(self, field_name):
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
383 - def __setitem__(self, field_name, field_value):
384 """Set the value of the required field_name to field_value. 385 386 """ 387 self.data[field_name] = field_value
388
389 - def __len__(self):
390 """Return the number of fields stored in this QmpMessage. 391 392 """ 393 return len(self.data)
394
395 - def __delitem__(self, key):
396 """Delete the specified element from the QmpMessage. 397 398 """ 399 del(self.data[key])
400 401 @staticmethod
402 - def BuildFromJsonString(json_string):
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 # Parse the string 412 data = serializer.LoadJson(json_string) 413 return QmpMessage(data)
414
415 - def __str__(self):
416 # The protocol expects the JSON object to be sent as a single line. 417 return serializer.DumpJson(self.data)
418
419 - def __eq__(self, other):
420 # When comparing two QmpMessages, we are interested in comparing 421 # their internal representation of the message data 422 return self.data == other.data
423
424 425 -class MonitorSocket(object):
426 _SOCKET_TIMEOUT = 5 427
428 - def __init__(self, monitor_filename):
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 # We want to fail if the server doesn't send a complete message 439 # in a reasonable amount of time 440 self.sock.settimeout(self._SOCKET_TIMEOUT) 441 self._connected = False
442
443 - def _check_socket(self):
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
456 - def _check_connection(self):
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
464 - def connect(self):
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 # Check file existance/stuff 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
484 - def close(self):
485 """Closes the socket 486 487 It cannot be used after this call. 488 489 """ 490 self.sock.close()
491
492 493 -class QmpConnection(MonitorSocket):
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
509 - def __init__(self, monitor_filename):
510 super(QmpConnection, self).__init__(monitor_filename) 511 self._buf = ""
512
513 - def connect(self):
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 # Check if we receive a correct greeting message from the server 525 # (As per the QEMU Protocol Specification 0.1 - section 2.2) 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 # This is needed because QMP can return more than one greetings 533 # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ 534 self._buf = "" 535 536 # Let's put the monitor in command mode using the qmp_capabilities 537 # command, or else no command will be executable. 538 # (As per the QEMU Protocol Specification 0.1 - section 4) 539 self.Execute(self._CAPABILITIES_COMMAND)
540
541 - def _ParseMessage(self, buf):
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 # Check if we got the message end token (CRLF, as per the QEMU Protocol 553 # Specification 0.1 - Section 2.1.1) 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
564 - def _Recv(self):
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 # Check if there is already a message in the buffer 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 # Events can occur between the sending of the command and the reception 644 # of the response, so we need to filter out messages with the event key. 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
658 659 -class KVMHypervisor(hv_base.BaseHypervisor):
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" # contains live instances pids 667 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids 668 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets 669 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data 670 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations 671 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps 672 # KVM instances with chroot enabled are started in empty chroot directories. 673 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories 674 # After an instance is stopped, its chroot directory is removed. 675 # If the chroot directory is not empty, it can't be removed. 676 # A non-empty chroot directory indicates a possible security incident. 677 # To support forensics, the non-empty chroot directory is quarantined in 678 # a separate directory, called 'chroot-quarantine'. 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, # will be checked later 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, # will be checked later 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 # match -drive.*boot=on|off on different lines, but in between accept only 799 # dashes not preceeded by a new line (which would mean another option 800 # different than -drive is starting) 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 # Slot 0 for Host bridge, Slot 1 for ISA bridge, Slot 2 for VGA controller 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 # Supported kvm options to get output from 827 _KVMOPT_HELP = "help" 828 _KVMOPT_MLIST = "mlist" 829 _KVMOPT_DEVICELIST = "devicelist" 830 831 # Command to execute to get the output from kvm, and whether to 832 # accept the output even on failure. 833 _KVMOPTS_CMDS = { 834 _KVMOPT_HELP: (["--help"], False), 835 _KVMOPT_MLIST: (["-M", "?"], False), 836 _KVMOPT_DEVICELIST: (["-device", "?"], True), 837 } 838
839 - def __init__(self):
840 hv_base.BaseHypervisor.__init__(self) 841 # Let's make sure the directories we need exist, even if the RUN_DIR lives 842 # in a tmpfs filesystem or has been otherwise wiped out. 843 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS] 844 utils.EnsureDirs(dirs)
845 846 @classmethod
847 - def _InstancePidFile(cls, instance_name):
848 """Returns the instance pidfile. 849 850 """ 851 return utils.PathJoin(cls._PIDS_DIR, instance_name)
852 853 @classmethod
854 - def _InstanceUidFile(cls, instance_name):
855 """Returns the instance uidfile. 856 857 """ 858 return utils.PathJoin(cls._UIDS_DIR, instance_name)
859 860 @classmethod
861 - def _InstancePidInfo(cls, pid):
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
905 - def _InstancePidAlive(self, instance_name):
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
926 - def _CheckDown(self, instance_name):
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
936 - def _InstanceMonitor(cls, instance_name):
937 """Returns the instance monitor socket name 938 939 """ 940 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
941 942 @classmethod
943 - def _InstanceSerial(cls, instance_name):
944 """Returns the instance serial socket name 945 946 """ 947 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
948 949 @classmethod
950 - def _InstanceQmpMonitor(cls, instance_name):
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
969 - def _InstanceKVMRuntime(cls, instance_name):
970 """Returns the instance KVM runtime filename 971 972 """ 973 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
974 975 @classmethod
976 - def _InstanceChrootDir(cls, instance_name):
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
983 - def _InstanceNICDir(cls, instance_name):
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
991 - def _InstanceNICFile(cls, instance_name, seq):
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
998 - def _InstanceKeymapFile(cls, instance_name):
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
1005 - def _TryReadUidFile(cls, uid_file):
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
1020 - def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
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 # The chroot directory is expected to be empty, but it isn't. 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
1060 - def _ConfigureNIC(instance, seq, nic, tap):
1061 """Run the network configuration script for a specified NIC 1062 1063 @param instance: instance we're acting on 1064 @type instance: instance object 1065 @param seq: nic sequence number 1066 @type seq: int 1067 @param nic: nic we're acting on 1068 @type nic: nic object 1069 @param tap: the host's tap interface this NIC corresponds to 1070 @type tap: str 1071 1072 """ 1073 env = { 1074 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"], 1075 "INSTANCE": instance.name, 1076 "MAC": nic.mac, 1077 "MODE": nic.nicparams[constants.NIC_MODE], 1078 "INTERFACE": tap, 1079 "INTERFACE_INDEX": str(seq), 1080 "INTERFACE_UUID": nic.uuid, 1081 "TAGS": " ".join(instance.GetTags()), 1082 } 1083 1084 if nic.ip: 1085 env["IP"] = nic.ip 1086 1087 if nic.name: 1088 env["INTERFACE_NAME"] = nic.name 1089 1090 if nic.nicparams[constants.NIC_LINK]: 1091 env["LINK"] = nic.nicparams[constants.NIC_LINK] 1092 1093 if constants.NIC_VLAN in nic.nicparams: 1094 env["VLAN"] = nic.nicparams[constants.NIC_VLAN] 1095 1096 if nic.network: 1097 n = objects.Network.FromDict(nic.netinfo) 1098 env.update(n.HooksDict()) 1099 1100 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1101 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK] 1102 1103 result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env) 1104 if result.failed: 1105 raise errors.HypervisorError("Failed to configure interface %s: %s;" 1106 " network configuration script output: %s" % 1107 (tap, result.fail_reason, result.output))
1108 1109 @staticmethod
1111 if affinity is None: 1112 raise errors.HypervisorError("affinity Python package not" 1113 " found; cannot use CPU pinning under KVM")
1114 1115 @staticmethod
1116 - def _BuildAffinityCpuMask(cpu_list):
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
1135 - def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
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 # Convert the string CPU mask to a list of list of int's 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 # If CPU pinning has 1 entry that's "all", then do nothing 1154 pass 1155 else: 1156 # If CPU pinning has one non-all entry, map the entire VM to 1157 # one set of physical CPUs 1158 cls._VerifyAffinityPackage() 1159 affinity.set_process_affinity_mask( 1160 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping)) 1161 else: 1162 # The number of vCPUs mapped should match the number of vCPUs 1163 # reported by KVM. This was already verified earlier, so 1164 # here only as a sanity check. 1165 assert len(thread_dict) == len(cpu_list) 1166 cls._VerifyAffinityPackage() 1167 1168 # For each vCPU, map it to the proper list of physical CPUs 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
1173 - def _GetVcpuThreadIds(self, instance_name):
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
1193 - def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
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 # Get KVM process ID, to be used if need to pin entire VM 1203 _, pid, _ = self._InstancePidAlive(instance_name) 1204 # Get vCPU thread IDs, to be used if need to pin vCPUs separately 1205 thread_dict = self._GetVcpuThreadIds(instance_name) 1206 # Run CPU pinning, based on configured mask 1207 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1208
1209 - def ListInstances(self, hvparams=None):
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
1222 - def GetInstanceInfo(self, instance_name, hvparams=None):
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 # Will fail if ballooning is not enabled, but we can then just resort to 1246 # the value above. 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
1254 - def GetAllInstancesInfo(self, hvparams=None):
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 # Ignore exceptions due to instances being shut down 1268 continue 1269 if info: 1270 data.append(info) 1271 return data
1272
1273 - def _GenerateKVMBlockDevicesOptions(self, instance, up_hvp, kvm_disks, 1274 kvmhelp, devlist):
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 # whether this is an older KVM version that uses the boot=on flag 1298 # on devices 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 # will be passed in -device option as driver 1310 device_driver = self._VIRTIO_BLK_PCI 1311 except errors.HypervisorError, _: 1312 pass 1313 else: 1314 if_val = ",if=%s" % disk_type 1315 # Cache mode 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 # TODO: make this a hard error, instead of a silent overwrite 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 # TODO: handle FD_LOOP and FD_BLKTAP (?) 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 # kvm_disks are the 4th entry of runtime file that did not exist in 1347 # the past. That means that cfdev should always have pci slot and 1348 # _GenerateDeviceKVMId() will not raise a exception. 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
1361 - def _GenerateKVMRuntime(self, instance, block_devices, startup_paused, 1362 kvmhelp):
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 # pylint: disable=R0912,R0914,R0915 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 # used just by the vnc server, if enabled 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 # As requested by music lovers 1401 if hvp[constants.HV_SOUNDHW]: 1402 soundhw = hvp[constants.HV_SOUNDHW] 1403 # For some reason only few sound devices require a PCI slot 1404 # while the Audio controller *must* be in slot 3. 1405 # That's why we bridge this option early in command line 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 # The SCSI controller requires another PCI slot. 1412 _ = _GetFreeSlot(pci_reservations, reserve=True) 1413 1414 # Add id to ballon and place to the first available slot (3 or 4) 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 # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as 1430 # extra hypervisor parameters. We should also investigate whether and how 1431 # shadow_mem should be considered for the resource model. 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 # whether this is an older KVM version that uses the boot=on flag 1462 # on devices 1463 needs_boot_flag = self._BOOT_RE.search(kvmhelp) 1464 1465 disk_type = hvp[constants.HV_DISK_TYPE] 1466 1467 #Now we can specify a different device type for CDROM devices. 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 # set cdrom 'if' type 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 # set boot flag, if needed 1484 boot_val = "" 1485 if boot_cdrom: 1486 kvm_cmd.extend(["-boot", "d"]) 1487 if needs_boot_flag: 1488 boot_val = ",boot=on" 1489 # and finally build the entire '-drive' value 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 # Only allow tls and other option when not binding to a file, for now. 1575 # kvm/qemu gets confused otherwise about the filename to use. 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 # FIXME: this is wrong here; the iface ip address differs 1596 # between systems, so it should be done in _ExecuteKVMRuntime 1597 if netutils.IsValidInterface(spice_bind): 1598 # The user specified a network interface, we have to figure out the IP 1599 # address. 1600 addresses = netutils.GetInterfaceIpAddresses(spice_bind) 1601 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION] 1602 1603 # if the user specified an IP version and the interface does not 1604 # have that kind of IP addresses, throw an exception 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 # the user did not specify an IP version, we have to figure it out 1612 elif (addresses[constants.IP4_VERSION] and 1613 addresses[constants.IP6_VERSION]): 1614 # we have both ipv4 and ipv6, let's use the cluster default IP 1615 # version 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 # spice_bind is known to be a valid IP address, because 1631 # ValidateParameters checked it. 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 # Image compression options 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 # Video stream detection 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 # Audio compression, by default in qemu-kvm it is on 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 # Enable the spice agent communication channel between the host and the 1677 # agent. 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 # From qemu 1.4 -nographic is incompatible with -daemonize. The new way 1692 # also works in earlier versions though (tested with 1.1 and 1.3) 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 # Add qemu-KVM -cpu param 1705 if hvp[constants.HV_CPU_TYPE]: 1706 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]]) 1707 1708 # Pass a -vga option if requested, or if spice is used, for backwards 1709 # compatibility. 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 # Various types of usb devices, comma separated 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 # Set system UUID to instance UUID 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
1741 - def _WriteKVMRuntime(self, instance_name, data):
1742 """Write an instance's KVM runtime 1743 1744 """ 1745 try: 1746 utils.WriteFile(self._InstanceKVMRuntime(instance_name), 1747 data=data) 1748 except EnvironmentError, err: 1749 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1750
1751 - def _ReadKVMRuntime(self, instance_name):
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
1761 - def _SaveKVMRuntime(self, instance, kvm_runtime):
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
1775 - def _LoadKVMRuntime(self, instance, serialized_runtime=None):
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 # too many local variables 1808 # pylint: disable=R0914
1809 - def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
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 # Small _ExecuteKVMRuntime hv parameters programming howto: 1819 # - conf_hvp contains the parameters as configured on ganeti. they might 1820 # have changed since the instance started; only use them if the change 1821 # won't affect the inside of the instance (which hasn't been rebooted). 1822 # - up_hvp contains the parameters as they were when the instance was 1823 # started, plus any new parameter which has been added between ganeti 1824 # versions: it is paramount that those default to a value which won't 1825 # affect the inside of the instance as well. 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 # the first element of kvm_cmd is always the path to the kvm binary 1834 kvm_path = kvm_cmd[0] 1835 up_hvp = objects.FillDict(conf_hvp, up_hvp) 1836 1837 # We know it's safe to run as a different user upon migration, so we'll use 1838 # the latest conf, from conf_hvp. 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 # If a keymap file is specified, KVM won't use its internal defaults. By 1847 # first including the "en-us" layout, an error on loading the actual 1848 # layout (e.g. because it can't be found) won't lead to a non-functional 1849 # keyboard. A keyboard with incorrect keys is still better than none. 1850 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap) 1851 kvm_cmd.extend(["-k", keymap_path]) 1852 1853 # We have reasons to believe changing something like the nic driver/type 1854 # upon migration won't exactly fly with the instance kernel, so for nic 1855 # related parameters we'll use up_hvp 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 # Older versions of kvm don't support DEVICE_LIST, but they don't 1873 # have new virtio syntax either. 1874 pass 1875 1876 if up_hvp[constants.HV_VHOST_NET]: 1877 # check for vhost_net support 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 # kvm_nics already exist in old runtime files and thus there might 1896 # be some entries without pci slot (therefore try: except:) 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 # Changing the vnc password doesn't bother the guest that much. At most it 1917 # will surprise people who connect to it. Whether positively or negatively 1918 # it's debatable. 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 # Automatically enable QMP if version is >= 0.14 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 # Configure the network now for starting instances and bridged/OVS 1939 # interfaces, during FinalizeMigration for incoming instances' routed 1940 # interfaces. 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 # CPU affinity requires kvm to start paused, so we set this flag if the 1954 # instance is not already paused and if we are not going to accept a 1955 # migrating instance. In the latter case, pausing is not needed. 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 # Note: CPU pinning is using up_hvp since changes take effect 1961 # during instance startup anyway, and to avoid problems when soft 1962 # rebooting the instance. 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 # Setting SPICE password. We are not vulnerable to malicious passwordless 1996 # connection attempts because SPICE by default does not allow connections 1997 # if neither a password nor the "disable_ticketing" options are specified. 1998 # As soon as we send the password via QMP, that password is a valid ticket 1999 # for connection. 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 # If requested, set CPU affinity and resume instance execution 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 # To control CPU pinning, ballooning, and vnc/spice passwords 2030 # the VM was started in a frozen state. If freezing was not 2031 # explicitly requested resume the vm status. 2032 self._CallMonitorCommand(instance.name, self._CONT_CMD)
2033
2034 - def StartInstance(self, instance, block_devices, startup_paused):
2035 """Start an instance. 2036 2037 """ 2038 self._CheckDown(instance.name) 2039 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 2040 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 2041 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, 2042 startup_paused, kvmhelp) 2043 self._SaveKVMRuntime(instance, kvm_runtime) 2044 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2045
2046 - def _CallMonitorCommand(self, instance_name, command, timeout=None):
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 # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum 2056 # version. The monitor protocol is designed for human consumption, whereas 2057 # QMP is made for programmatic usage. In the worst case QMP can also 2058 # execute monitor commands. As it is, all calls to socat take at least 2059 # 500ms and likely more: socat can't detect the end of the reply and waits 2060 # for 500ms of no data received before exiting (500 ms is the default for 2061 # the "-t" parameter). 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
2077 - def _GetFreePCISlot(self, instance, dev):
2078 """Get the first available pci slot of a runnung instance. 2079 2080 """ 2081 slots = bitarray(32) 2082 slots.setall(False) # pylint: disable=E1101 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
2092 - def VerifyHotplugSupport(self, instance, action, dev_type):
2093 """Verifies that hotplug is supported. 2094 2095 Hotplug is *not* supported in case of: 2096 - security models and chroot (disk hotplug) 2097 - fdsend module is missing (nic hot-add) 2098 2099 @raise errors.HypervisorError: in one of the previous cases 2100 2101 """ 2102 if dev_type == constants.HOTPLUG_TARGET_DISK: 2103 hvp = instance.hvparams 2104 security_model = hvp[constants.HV_SECURITY_MODEL] 2105 use_chroot = hvp[constants.HV_KVM_USE_CHROOT] 2106 if action == constants.HOTPLUG_ACTION_ADD: 2107 if use_chroot: 2108 raise errors.HotplugError("Disk hotplug is not supported" 2109 " in case of chroot.") 2110 if security_model != constants.HT_SM_NONE: 2111 raise errors.HotplugError("Disk Hotplug is not supported in case" 2112 " security models are used.") 2113 2114 if (dev_type == constants.HOTPLUG_TARGET_NIC and 2115 action == constants.HOTPLUG_ACTION_ADD and not fdsend): 2116 raise errors.HotplugError("Cannot hot-add NIC." 2117 " fdsend python module is missing.")
2118
2119 - def HotplugSupported(self, instance):
2120 """Checks if hotplug is generally supported. 2121 2122 Hotplug is *not* supported in case of: 2123 - qemu versions < 1.0 2124 - for stopped instances 2125 2126 @raise errors.HypervisorError: in one of the previous cases 2127 2128 """ 2129 try: 2130 output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD) 2131 except errors.HypervisorError: 2132 raise errors.HotplugError("Instance is probably down") 2133 2134 # TODO: search for netdev_add, drive_add, device_add..... 2135 match = self._INFO_VERSION_RE.search(output.stdout) 2136 if not match: 2137 raise errors.HotplugError("Cannot parse qemu version via monitor") 2138 2139 v_major, v_min, _, _ = match.groups() 2140 if (int(v_major), int(v_min)) < (1, 0): 2141 raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2142
2143 - def _CallHotplugCommands(self, name, cmds):
2144 for c in cmds: 2145 self._CallMonitorCommand(name, c) 2146 time.sleep(1)
2147
2148 - def _VerifyHotplugCommand(self, instance_name, device, dev_type, 2149 should_exist):
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 # in case of hot-mod this is given 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 # update relevant entries in runtime file 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 # putting it back in the same pci slot 2243 device.pci = self.HotDelDevice(instance, dev_type, device, _, seq) 2244 self.HotAddDevice(instance, dev_type, device, _, seq)
2245
2246 - def _PassTapFd(self, instance, fd, nic):
2247 """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS 2248 2249 """ 2250 # TODO: factor out code related to unix sockets. 2251 # squash common parts between monitor and qmp 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
2264 - def _ParseKVMVersion(cls, text):
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
2287 - def _GetKVMOutput(cls, kvm_path, option):
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
2309 - def _GetKVMVersion(cls, kvm_path):
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
2319 - def _GetDefaultMachineVersion(cls, kvm_path):
2320 """Return the default hardware revision (e.g. pc-1.1) 2321 2322 """ 2323 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST) 2324 match = cls._DEFAULT_MACHINE_VERSION_RE.search(output) 2325 if match: 2326 return match.group(1) 2327 else: 2328 return "pc"
2329
2330 - def StopInstance(self, instance, force=False, retry=False, name=None, 2331 timeout=None):
2332 """Stop an instance. 2333 2334 """ 2335 assert(timeout is None or force is not None) 2336 2337 if name is not None and not force: 2338 raise errors.HypervisorError("Cannot shutdown cleanly by name only") 2339 if name is None: 2340 name = instance.name 2341 acpi = instance.hvparams[constants.HV_ACPI] 2342 else: 2343 acpi = False 2344 _, pid, alive = self._InstancePidAlive(name) 2345 if pid > 0 and alive: 2346 if force or not acpi: 2347 utils.KillProcess(pid) 2348 else: 2349 self._CallMonitorCommand(name, "system_powerdown", timeout)
2350
2351 - def CleanupInstance(self, instance_name):
2352 """Cleanup after a stopped instance 2353 2354 """ 2355 pidfile, pid, alive = self._InstancePidAlive(instance_name) 2356 if pid > 0 and alive: 2357 raise errors.HypervisorError("Cannot cleanup a live instance") 2358 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2359
2360 - def RebootInstance(self, instance):
2361 """Reboot an instance. 2362 2363 """ 2364 # For some reason if we do a 'send-key ctrl-alt-delete' to the control 2365 # socket the instance will stop, but now power up again. So we'll resort 2366 # to shutdown and restart. 2367 _, _, alive = self._InstancePidAlive(instance.name) 2368 if not alive: 2369 raise errors.HypervisorError("Failed to reboot instance %s:" 2370 " not running" % instance.name) 2371 # StopInstance will delete the saved KVM runtime so: 2372 # ...first load it... 2373 kvm_runtime = self._LoadKVMRuntime(instance) 2374 # ...now we can safely call StopInstance... 2375 if not self.StopInstance(instance): 2376 self.StopInstance(instance, force=True) 2377 # ...and finally we can save it again, and execute it... 2378 self._SaveKVMRuntime(instance, kvm_runtime) 2379 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 2380 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 2381 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2382
2383 - def MigrationInfo(self, instance):
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
2394 - def AcceptInstance(self, instance, info, target):
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
2412 - def FinalizeMigrationDst(self, instance, info, success):
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 # Bridged/OVS interfaces have already been configured 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
2444 - def MigrateInstance(self, cluster_name, instance, target, live):
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
2480 - def FinalizeMigrationSource(self, instance, success, live):
2481 """Finalize the instance migration on the source node. 2482 2483 @type instance: L{objects.Instance} 2484 @param instance: the instance that was migrated 2485 @type success: bool 2486 @param success: whether the migration succeeded or not 2487 @type live: bool 2488 @param live: whether the user requested a live migration or not 2489 2490 """ 2491 if success: 2492 pidfile, pid, _ = self._InstancePidAlive(instance.name) 2493 utils.KillProcess(pid) 2494 self._RemoveInstanceRuntimeFiles(pidfile, instance.name) 2495 elif live: 2496 self._CallMonitorCommand(instance.name, self._CONT_CMD)
2497
2498 - def GetMigrationStatus(self, instance):
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
2536 - def BalloonInstanceMemory(self, instance, mem):
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
2547 - def GetNodeInfo(self, hvparams=None):
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
2568 - def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
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
2629 - def CheckParameterSyntax(cls, hvparams):
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 # if an IP version is specified, the spice_bind parameter must be an 2681 # IP of that family 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 # All the other SPICE parameters depend on spice_bind being set. Raise an 2695 # error if any of them is set without it. 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
2702 - def ValidateParameters(cls, hvparams):
2703 """Check the given parameters for validity. 2704 2705 @type hvparams: dict 2706 @param hvparams: dictionary with parameter names/value 2707 @raise errors.HypervisorError: when a parameter is not valid 2708 2709 """ 2710 super(KVMHypervisor, cls).ValidateParameters(hvparams) 2711 2712 kvm_path = hvparams[constants.HV_KVM_PATH] 2713 2714 security_model = hvparams[constants.HV_SECURITY_MODEL] 2715 if security_model == constants.HT_SM_USER: 2716 username = hvparams[constants.HV_SECURITY_DOMAIN] 2717 try: 2718 pwd.getpwnam(username) 2719 except KeyError: 2720 raise errors.HypervisorError("Unknown security domain user %s" 2721 % username) 2722 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS] 2723 if vnc_bind_address: 2724 bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address) 2725 is_interface = netutils.IsValidInterface(vnc_bind_address) 2726 is_path = utils.IsNormAbsPath(vnc_bind_address) 2727 if not bound_to_addr and not is_interface and not is_path: 2728 raise errors.HypervisorError("VNC: The %s parameter must be either" 2729 " a valid IP address, an interface name," 2730 " or an absolute path" % 2731 constants.HV_KVM_SPICE_BIND) 2732 2733 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 2734 if spice_bind: 2735 # only one of VNC and SPICE can be used currently. 2736 if hvparams[constants.HV_VNC_BIND_ADDRESS]: 2737 raise errors.HypervisorError("Both SPICE and VNC are configured, but" 2738 " only one of them can be used at a" 2739 " given time") 2740 2741 # check that KVM supports SPICE 2742 kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP) 2743 if not cls._SPICE_RE.search(kvmhelp): 2744 raise errors.HypervisorError("SPICE is configured, but it is not" 2745 " supported according to 'kvm --help'") 2746 2747 # if spice_bind is not an IP address, it must be a valid interface 2748 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or 2749 netutils.IP6Address.IsValid(spice_bind)) 2750 if not bound_to_addr and not netutils.IsValidInterface(spice_bind): 2751 raise errors.HypervisorError("SPICE: The %s parameter must be either" 2752 " a valid IP address or interface name" % 2753 constants.HV_KVM_SPICE_BIND) 2754 2755 machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION] 2756 if machine_version: 2757 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST) 2758 if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output): 2759 raise errors.HypervisorError("Unsupported machine version: %s" % 2760 machine_version)
2761 2762 @classmethod
2763 - def PowercycleNode(cls, hvparams=None):
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