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  # 
   6  # This program is free software; you can redistribute it and/or modify 
   7  # it under the terms of the GNU General Public License as published by 
   8  # the Free Software Foundation; either version 2 of the License, or 
   9  # (at your option) any later version. 
  10  # 
  11  # This program is distributed in the hope that it will be useful, but 
  12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  14  # General Public License for more details. 
  15  # 
  16  # You should have received a copy of the GNU General Public License 
  17  # along with this program; if not, write to the Free Software 
  18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
  19  # 02110-1301, USA. 
  20   
  21   
  22  """KVM hypervisor 
  23   
  24  """ 
  25   
  26  import errno 
  27  import os 
  28  import os.path 
  29  import re 
  30  import tempfile 
  31  import time 
  32  import logging 
  33  import pwd 
  34  import struct 
  35  import fcntl 
  36  import shutil 
  37  import socket 
  38  import stat 
  39  import StringIO 
  40  try: 
  41    import affinity   # pylint: disable=F0401 
  42  except ImportError: 
  43    affinity = None 
  44   
  45  from ganeti import utils 
  46  from ganeti import constants 
  47  from ganeti import errors 
  48  from ganeti import serializer 
  49  from ganeti import objects 
  50  from ganeti import uidpool 
  51  from ganeti import ssconf 
  52  from ganeti import netutils 
  53  from ganeti import pathutils 
  54  from ganeti.hypervisor import hv_base 
  55  from ganeti.utils import wrapper as utils_wrapper 
  56   
  57   
  58  _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge" 
  59  _KVM_START_PAUSED_FLAG = "-S" 
  60   
  61  # TUN/TAP driver constants, taken from <linux/if_tun.h> 
  62  # They are architecture-independent and already hardcoded in qemu-kvm source, 
  63  # so we can safely include them here. 
  64  TUNSETIFF = 0x400454ca 
  65  TUNGETIFF = 0x800454d2 
  66  TUNGETFEATURES = 0x800454cf 
  67  IFF_TAP = 0x0002 
  68  IFF_NO_PI = 0x1000 
  69  IFF_ONE_QUEUE = 0x2000 
  70  IFF_VNET_HDR = 0x4000 
  71   
  72  #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND} 
  73  _SPICE_ADDITIONAL_PARAMS = frozenset([ 
  74    constants.HV_KVM_SPICE_IP_VERSION, 
  75    constants.HV_KVM_SPICE_PASSWORD_FILE, 
  76    constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR, 
  77    constants.HV_KVM_SPICE_JPEG_IMG_COMPR, 
  78    constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR, 
  79    constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION, 
  80    constants.HV_KVM_SPICE_USE_TLS, 
  81    ]) 
82 83 84 -def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
85 """Retrieves supported TUN features from file descriptor. 86 87 @see: L{_ProbeTapVnetHdr} 88 89 """ 90 req = struct.pack("I", 0) 91 try: 92 buf = _ioctl(fd, TUNGETFEATURES, req) 93 except EnvironmentError, err: 94 logging.warning("ioctl(TUNGETFEATURES) failed: %s", err) 95 return None 96 else: 97 (flags, ) = struct.unpack("I", buf) 98 return flags
99
100 101 -def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
102 """Check whether to enable the IFF_VNET_HDR flag. 103 104 To do this, _all_ of the following conditions must be met: 105 1. TUNGETFEATURES ioctl() *must* be implemented 106 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag 107 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in 108 drivers/net/tun.c there is no way to test this until after the tap device 109 has been created using TUNSETIFF, and there is no way to change the 110 IFF_VNET_HDR flag after creating the interface, catch-22! However both 111 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27, 112 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is. 113 114 @type fd: int 115 @param fd: the file descriptor of /dev/net/tun 116 117 """ 118 flags = _features_fn(fd) 119 120 if flags is None: 121 # Not supported 122 return False 123 124 result = bool(flags & IFF_VNET_HDR) 125 126 if not result: 127 logging.warning("Kernel does not support IFF_VNET_HDR, not enabling") 128 129 return result
130
131 132 -def _OpenTap(vnet_hdr=True):
133 """Open a new tap device and return its file descriptor. 134 135 This is intended to be used by a qemu-type hypervisor together with the -net 136 tap,fd=<fd> command line parameter. 137 138 @type vnet_hdr: boolean 139 @param vnet_hdr: Enable the VNET Header 140 @return: (ifname, tapfd) 141 @rtype: tuple 142 143 """ 144 try: 145 tapfd = os.open("/dev/net/tun", os.O_RDWR) 146 except EnvironmentError: 147 raise errors.HypervisorError("Failed to open /dev/net/tun") 148 149 flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE 150 151 if vnet_hdr and _ProbeTapVnetHdr(tapfd): 152 flags |= IFF_VNET_HDR 153 154 # The struct ifreq ioctl request (see netdevice(7)) 155 ifr = struct.pack("16sh", "", flags) 156 157 try: 158 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr) 159 except EnvironmentError, err: 160 raise errors.HypervisorError("Failed to allocate a new TAP device: %s" % 161 err) 162 163 # Get the interface name from the ioctl 164 ifname = struct.unpack("16sh", res)[0].strip("\x00") 165 return (ifname, tapfd)
166
167 168 -class QmpMessage:
169 """QEMU Messaging Protocol (QMP) message. 170 171 """
172 - def __init__(self, data):
173 """Creates a new QMP message based on the passed data. 174 175 """ 176 if not isinstance(data, dict): 177 raise TypeError("QmpMessage must be initialized with a dict") 178 179 self.data = data
180
181 - def __getitem__(self, field_name):
182 """Get the value of the required field if present, or None. 183 184 Overrides the [] operator to provide access to the message data, 185 returning None if the required item is not in the message 186 @return: the value of the field_name field, or None if field_name 187 is not contained in the message 188 189 """ 190 return self.data.get(field_name, None)
191
192 - def __setitem__(self, field_name, field_value):
193 """Set the value of the required field_name to field_value. 194 195 """ 196 self.data[field_name] = field_value
197
198 - def __len__(self):
199 """Return the number of fields stored in this QmpMessage. 200 201 """ 202 return len(self.data)
203
204 - def __delitem__(self, key):
205 """Delete the specified element from the QmpMessage. 206 207 """ 208 del(self.data[key])
209 210 @staticmethod
211 - def BuildFromJsonString(json_string):
212 """Build a QmpMessage from a JSON encoded string. 213 214 @type json_string: str 215 @param json_string: JSON string representing the message 216 @rtype: L{QmpMessage} 217 @return: a L{QmpMessage} built from json_string 218 219 """ 220 # Parse the string 221 data = serializer.LoadJson(json_string) 222 return QmpMessage(data)
223
224 - def __str__(self):
225 # The protocol expects the JSON object to be sent as a single line. 226 return serializer.DumpJson(self.data)
227
228 - def __eq__(self, other):
229 # When comparing two QmpMessages, we are interested in comparing 230 # their internal representation of the message data 231 return self.data == other.data
232
233 234 -class QmpConnection:
235 """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP). 236 237 """ 238 _FIRST_MESSAGE_KEY = "QMP" 239 _EVENT_KEY = "event" 240 _ERROR_KEY = "error" 241 _RETURN_KEY = RETURN_KEY = "return" 242 _ACTUAL_KEY = ACTUAL_KEY = "actual" 243 _ERROR_CLASS_KEY = "class" 244 _ERROR_DESC_KEY = "desc" 245 _EXECUTE_KEY = "execute" 246 _ARGUMENTS_KEY = "arguments" 247 _CAPABILITIES_COMMAND = "qmp_capabilities" 248 _MESSAGE_END_TOKEN = "\r\n" 249 _SOCKET_TIMEOUT = 5 250
251 - def __init__(self, monitor_filename):
252 """Instantiates the QmpConnection object. 253 254 @type monitor_filename: string 255 @param monitor_filename: the filename of the UNIX raw socket on which the 256 QMP monitor is listening 257 258 """ 259 self.monitor_filename = monitor_filename 260 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 261 # We want to fail if the server doesn't send a complete message 262 # in a reasonable amount of time 263 self.sock.settimeout(self._SOCKET_TIMEOUT) 264 self._connected = False 265 self._buf = ""
266
267 - def _check_socket(self):
268 sock_stat = None 269 try: 270 sock_stat = os.stat(self.monitor_filename) 271 except EnvironmentError, err: 272 if err.errno == errno.ENOENT: 273 raise errors.HypervisorError("No qmp socket found") 274 else: 275 raise errors.HypervisorError("Error checking qmp socket: %s", 276 utils.ErrnoOrStr(err)) 277 if not stat.S_ISSOCK(sock_stat.st_mode): 278 raise errors.HypervisorError("Qmp socket is not a socket")
279
280 - def _check_connection(self):
281 """Make sure that the connection is established. 282 283 """ 284 if not self._connected: 285 raise errors.ProgrammerError("To use a QmpConnection you need to first" 286 " invoke connect() on it")
287
288 - def connect(self):
289 """Connects to the QMP monitor. 290 291 Connects to the UNIX socket and makes sure that we can actually send and 292 receive data to the kvm instance via QMP. 293 294 @raise errors.HypervisorError: when there are communication errors 295 @raise errors.ProgrammerError: when there are data serialization errors 296 297 """ 298 if self._connected: 299 raise errors.ProgrammerError("Cannot connect twice") 300 301 self._check_socket() 302 303 # Check file existance/stuff 304 try: 305 self.sock.connect(self.monitor_filename) 306 except EnvironmentError: 307 raise errors.HypervisorError("Can't connect to qmp socket") 308 self._connected = True 309 310 # Check if we receive a correct greeting message from the server 311 # (As per the QEMU Protocol Specification 0.1 - section 2.2) 312 greeting = self._Recv() 313 if not greeting[self._FIRST_MESSAGE_KEY]: 314 self._connected = False 315 raise errors.HypervisorError("kvm: QMP communication error (wrong" 316 " server greeting") 317 318 # Let's put the monitor in command mode using the qmp_capabilities 319 # command, or else no command will be executable. 320 # (As per the QEMU Protocol Specification 0.1 - section 4) 321 self.Execute(self._CAPABILITIES_COMMAND)
322
323 - def _ParseMessage(self, buf):
324 """Extract and parse a QMP message from the given buffer. 325 326 Seeks for a QMP message in the given buf. If found, it parses it and 327 returns it together with the rest of the characters in the buf. 328 If no message is found, returns None and the whole buffer. 329 330 @raise errors.ProgrammerError: when there are data serialization errors 331 332 """ 333 message = None 334 # Check if we got the message end token (CRLF, as per the QEMU Protocol 335 # Specification 0.1 - Section 2.1.1) 336 pos = buf.find(self._MESSAGE_END_TOKEN) 337 if pos >= 0: 338 try: 339 message = QmpMessage.BuildFromJsonString(buf[:pos + 1]) 340 except Exception, err: 341 raise errors.ProgrammerError("QMP data serialization error: %s" % err) 342 buf = buf[pos + 1:] 343 344 return (message, buf)
345
346 - def _Recv(self):
347 """Receives a message from QMP and decodes the received JSON object. 348 349 @rtype: QmpMessage 350 @return: the received message 351 @raise errors.HypervisorError: when there are communication errors 352 @raise errors.ProgrammerError: when there are data serialization errors 353 354 """ 355 self._check_connection() 356 357 # Check if there is already a message in the buffer 358 (message, self._buf) = self._ParseMessage(self._buf) 359 if message: 360 return message 361 362 recv_buffer = StringIO.StringIO(self._buf) 363 recv_buffer.seek(len(self._buf)) 364 try: 365 while True: 366 data = self.sock.recv(4096) 367 if not data: 368 break 369 recv_buffer.write(data) 370 371 (message, self._buf) = self._ParseMessage(recv_buffer.getvalue()) 372 if message: 373 return message 374 375 except socket.timeout, err: 376 raise errors.HypervisorError("Timeout while receiving a QMP message: " 377 "%s" % (err)) 378 except socket.error, err: 379 raise errors.HypervisorError("Unable to receive data from KVM using the" 380 " QMP protocol: %s" % err)
381
382 - def _Send(self, message):
383 """Encodes and sends a message to KVM using QMP. 384 385 @type message: QmpMessage 386 @param message: message to send to KVM 387 @raise errors.HypervisorError: when there are communication errors 388 @raise errors.ProgrammerError: when there are data serialization errors 389 390 """ 391 self._check_connection() 392 try: 393 message_str = str(message) 394 except Exception, err: 395 raise errors.ProgrammerError("QMP data deserialization error: %s" % err) 396 397 try: 398 self.sock.sendall(message_str) 399 except socket.timeout, err: 400 raise errors.HypervisorError("Timeout while sending a QMP message: " 401 "%s (%s)" % (err.string, err.errno)) 402 except socket.error, err: 403 raise errors.HypervisorError("Unable to send data from KVM using the" 404 " QMP protocol: %s" % err)
405
406 - def Execute(self, command, arguments=None):
407 """Executes a QMP command and returns the response of the server. 408 409 @type command: str 410 @param command: the command to execute 411 @type arguments: dict 412 @param arguments: dictionary of arguments to be passed to the command 413 @rtype: dict 414 @return: dictionary representing the received JSON object 415 @raise errors.HypervisorError: when there are communication errors 416 @raise errors.ProgrammerError: when there are data serialization errors 417 418 """ 419 self._check_connection() 420 message = QmpMessage({self._EXECUTE_KEY: command}) 421 if arguments: 422 message[self._ARGUMENTS_KEY] = arguments 423 self._Send(message) 424 425 # Events can occur between the sending of the command and the reception 426 # of the response, so we need to filter out messages with the event key. 427 while True: 428 response = self._Recv() 429 err = response[self._ERROR_KEY] 430 if err: 431 raise errors.HypervisorError("kvm: error executing the %s" 432 " command: %s (%s):" % 433 (command, 434 err[self._ERROR_DESC_KEY], 435 err[self._ERROR_CLASS_KEY])) 436 437 elif not response[self._EVENT_KEY]: 438 return response
439
440 441 -class KVMHypervisor(hv_base.BaseHypervisor):
442 """KVM hypervisor interface 443 444 """ 445 CAN_MIGRATE = True 446 447 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor" 448 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids 449 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids 450 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets 451 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data 452 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations 453 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps 454 # KVM instances with chroot enabled are started in empty chroot directories. 455 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories 456 # After an instance is stopped, its chroot directory is removed. 457 # If the chroot directory is not empty, it can't be removed. 458 # A non-empty chroot directory indicates a possible security incident. 459 # To support forensics, the non-empty chroot directory is quarantined in 460 # a separate directory, called 'chroot-quarantine'. 461 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine" 462 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR, 463 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR] 464 465 PARAMETERS = { 466 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK, 467 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK, 468 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK, 469 constants.HV_ROOT_PATH: hv_base.NO_CHECK, 470 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK, 471 constants.HV_ACPI: hv_base.NO_CHECK, 472 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK, 473 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK, 474 constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK, # will be checked later 475 constants.HV_VNC_TLS: hv_base.NO_CHECK, 476 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK, 477 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK, 478 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK, 479 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later 480 constants.HV_KVM_SPICE_IP_VERSION: 481 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or 482 x in constants.VALID_IP_VERSIONS), 483 "The SPICE IP version should be 4 or 6", 484 None, None), 485 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK, 486 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR: 487 hv_base.ParamInSet( 488 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS), 489 constants.HV_KVM_SPICE_JPEG_IMG_COMPR: 490 hv_base.ParamInSet( 491 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS), 492 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR: 493 hv_base.ParamInSet( 494 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS), 495 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION: 496 hv_base.ParamInSet( 497 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS), 498 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK, 499 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK, 500 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK, 501 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK, 502 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 503 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 504 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 505 constants.HV_BOOT_ORDER: 506 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES), 507 constants.HV_NIC_TYPE: 508 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES), 509 constants.HV_DISK_TYPE: 510 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES), 511 constants.HV_KVM_CDROM_DISK_TYPE: 512 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES), 513 constants.HV_USB_MOUSE: 514 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES), 515 constants.HV_KEYMAP: hv_base.NO_CHECK, 516 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 517 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK, 518 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK, 519 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 520 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK, 521 constants.HV_DISK_CACHE: 522 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES), 523 constants.HV_SECURITY_MODEL: 524 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES), 525 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK, 526 constants.HV_KVM_FLAG: 527 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES), 528 constants.HV_VHOST_NET: hv_base.NO_CHECK, 529 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK, 530 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK, 531 constants.HV_REBOOT_BEHAVIOR: 532 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS), 533 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK, 534 constants.HV_CPU_TYPE: hv_base.NO_CHECK, 535 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK, 536 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK, 537 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK, 538 constants.HV_SOUNDHW: hv_base.NO_CHECK, 539 constants.HV_USB_DEVICES: hv_base.NO_CHECK, 540 constants.HV_VGA: hv_base.NO_CHECK, 541 constants.HV_KVM_EXTRA: hv_base.NO_CHECK, 542 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK, 543 constants.HV_VNET_HDR: hv_base.NO_CHECK, 544 } 545 546 _VIRTIO = "virtio" 547 _VIRTIO_NET_PCI = "virtio-net-pci" 548 549 _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)", 550 re.M | re.I) 551 _MIGRATION_PROGRESS_RE = \ 552 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n" 553 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n" 554 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I) 555 556 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5 557 _MIGRATION_INFO_RETRY_DELAY = 2 558 559 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b") 560 561 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I) 562 _CPU_INFO_CMD = "info cpus" 563 _CONT_CMD = "cont" 564 565 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M) 566 _CHECK_MACHINE_VERSION_RE = \ 567 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M)) 568 569 _QMP_RE = re.compile(r"^-qmp\s", re.M) 570 _SPICE_RE = re.compile(r"^-spice\s", re.M) 571 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M) 572 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M) 573 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M) 574 _NETDEV_RE = re.compile(r"^-netdev\s", re.M) 575 _DISPLAY_RE = re.compile(r"^-display\s", re.M) 576 _MACHINE_RE = re.compile(r"^-machine\s", re.M) 577 _NEW_VIRTIO_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M) 578 # match -drive.*boot=on|off on different lines, but in between accept only 579 # dashes not preceeded by a new line (which would mean another option 580 # different than -drive is starting) 581 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S) 582 _UUID_RE = re.compile(r"^-uuid\s", re.M) 583 584 ANCILLARY_FILES = [ 585 _KVM_NETWORK_SCRIPT, 586 ] 587 ANCILLARY_FILES_OPT = [ 588 _KVM_NETWORK_SCRIPT, 589 ] 590 591 # Supported kvm options to get output from 592 _KVMOPT_HELP = "help" 593 _KVMOPT_MLIST = "mlist" 594 _KVMOPT_DEVICELIST = "devicelist" 595 596 # Command to execute to get the output from kvm, and whether to 597 # accept the output even on failure. 598 _KVMOPTS_CMDS = { 599 _KVMOPT_HELP: (["--help"], False), 600 _KVMOPT_MLIST: (["-M", "?"], False), 601 _KVMOPT_DEVICELIST: (["-device", "?"], True), 602 } 603
604 - def __init__(self):
605 hv_base.BaseHypervisor.__init__(self) 606 # Let's make sure the directories we need exist, even if the RUN_DIR lives 607 # in a tmpfs filesystem or has been otherwise wiped out. 608 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS] 609 utils.EnsureDirs(dirs)
610 611 @classmethod
612 - def _InstancePidFile(cls, instance_name):
613 """Returns the instance pidfile. 614 615 """ 616 return utils.PathJoin(cls._PIDS_DIR, instance_name)
617 618 @classmethod
619 - def _InstanceUidFile(cls, instance_name):
620 """Returns the instance uidfile. 621 622 """ 623 return utils.PathJoin(cls._UIDS_DIR, instance_name)
624 625 @classmethod
626 - def _InstancePidInfo(cls, pid):
627 """Check pid file for instance information. 628 629 Check that a pid file is associated with an instance, and retrieve 630 information from its command line. 631 632 @type pid: string or int 633 @param pid: process id of the instance to check 634 @rtype: tuple 635 @return: (instance_name, memory, vcpus) 636 @raise errors.HypervisorError: when an instance cannot be found 637 638 """ 639 alive = utils.IsProcessAlive(pid) 640 if not alive: 641 raise errors.HypervisorError("Cannot get info for pid %s" % pid) 642 643 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline") 644 try: 645 cmdline = utils.ReadFile(cmdline_file) 646 except EnvironmentError, err: 647 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" % 648 (pid, err)) 649 650 instance = None 651 memory = 0 652 vcpus = 0 653 654 arg_list = cmdline.split("\x00") 655 while arg_list: 656 arg = arg_list.pop(0) 657 if arg == "-name": 658 instance = arg_list.pop(0) 659 elif arg == "-m": 660 memory = int(arg_list.pop(0)) 661 elif arg == "-smp": 662 vcpus = int(arg_list.pop(0).split(",")[0]) 663 664 if instance is None: 665 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm" 666 " instance" % pid) 667 668 return (instance, memory, vcpus)
669
670 - def _InstancePidAlive(self, instance_name):
671 """Returns the instance pidfile, pid, and liveness. 672 673 @type instance_name: string 674 @param instance_name: instance name 675 @rtype: tuple 676 @return: (pid file name, pid, liveness) 677 678 """ 679 pidfile = self._InstancePidFile(instance_name) 680 pid = utils.ReadPidFile(pidfile) 681 682 alive = False 683 try: 684 cmd_instance = self._InstancePidInfo(pid)[0] 685 alive = (cmd_instance == instance_name) 686 except errors.HypervisorError: 687 pass 688 689 return (pidfile, pid, alive)
690
691 - def _CheckDown(self, instance_name):
692 """Raises an error unless the given instance is down. 693 694 """ 695 alive = self._InstancePidAlive(instance_name)[2] 696 if alive: 697 raise errors.HypervisorError("Failed to start instance %s: %s" % 698 (instance_name, "already running"))
699 700 @classmethod
701 - def _InstanceMonitor(cls, instance_name):
702 """Returns the instance monitor socket name 703 704 """ 705 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
706 707 @classmethod
708 - def _InstanceSerial(cls, instance_name):
709 """Returns the instance serial socket name 710 711 """ 712 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
713 714 @classmethod
715 - def _InstanceQmpMonitor(cls, instance_name):
716 """Returns the instance serial QMP socket name 717 718 """ 719 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
720 721 @staticmethod
723 """Returns the correct parameters for socat 724 725 If we have a new-enough socat we can use raw mode with an escape character. 726 727 """ 728 if constants.SOCAT_USE_ESCAPE: 729 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE 730 else: 731 return "echo=0,icanon=0"
732 733 @classmethod
734 - def _InstanceKVMRuntime(cls, instance_name):
735 """Returns the instance KVM runtime filename 736 737 """ 738 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
739 740 @classmethod
741 - def _InstanceChrootDir(cls, instance_name):
742 """Returns the name of the KVM chroot dir of the instance 743 744 """ 745 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
746 747 @classmethod
748 - def _InstanceNICDir(cls, instance_name):
749 """Returns the name of the directory holding the tap device files for a 750 given instance. 751 752 """ 753 return utils.PathJoin(cls._NICS_DIR, instance_name)
754 755 @classmethod
756 - def _InstanceNICFile(cls, instance_name, seq):
757 """Returns the name of the file containing the tap device for a given NIC 758 759 """ 760 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
761 762 @classmethod
763 - def _InstanceKeymapFile(cls, instance_name):
764 """Returns the name of the file containing the keymap for a given instance 765 766 """ 767 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
768 769 @classmethod
770 - def _TryReadUidFile(cls, uid_file):
771 """Try to read a uid file 772 773 """ 774 if os.path.exists(uid_file): 775 try: 776 uid = int(utils.ReadOneLineFile(uid_file)) 777 return uid 778 except EnvironmentError: 779 logging.warning("Can't read uid file", exc_info=True) 780 except (TypeError, ValueError): 781 logging.warning("Can't parse uid file contents", exc_info=True) 782 return None
783 784 @classmethod
785 - def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
786 """Removes an instance's rutime sockets/files/dirs. 787 788 """ 789 utils.RemoveFile(pidfile) 790 utils.RemoveFile(cls._InstanceMonitor(instance_name)) 791 utils.RemoveFile(cls._InstanceSerial(instance_name)) 792 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name)) 793 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name)) 794 utils.RemoveFile(cls._InstanceKeymapFile(instance_name)) 795 uid_file = cls._InstanceUidFile(instance_name) 796 uid = cls._TryReadUidFile(uid_file) 797 utils.RemoveFile(uid_file) 798 if uid is not None: 799 uidpool.ReleaseUid(uid) 800 try: 801 shutil.rmtree(cls._InstanceNICDir(instance_name)) 802 except OSError, err: 803 if err.errno != errno.ENOENT: 804 raise 805 try: 806 chroot_dir = cls._InstanceChrootDir(instance_name) 807 utils.RemoveDir(chroot_dir) 808 except OSError, err: 809 if err.errno == errno.ENOTEMPTY: 810 # The chroot directory is expected to be empty, but it isn't. 811 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR, 812 prefix="%s-%s-" % 813 (instance_name, 814 utils.TimestampForFilename())) 815 logging.warning("The chroot directory of instance %s can not be" 816 " removed as it is not empty. Moving it to the" 817 " quarantine instead. Please investigate the" 818 " contents (%s) and clean up manually", 819 instance_name, new_chroot_dir) 820 utils.RenameFile(chroot_dir, new_chroot_dir) 821 else: 822 raise
823 824 @staticmethod
825 - def _ConfigureNIC(instance, seq, nic, tap):
826 """Run the network configuration script for a specified NIC 827 828 @param instance: instance we're acting on 829 @type instance: instance object 830 @param seq: nic sequence number 831 @type seq: int 832 @param nic: nic we're acting on 833 @type nic: nic object 834 @param tap: the host's tap interface this NIC corresponds to 835 @type tap: str 836 837 """ 838 if instance.tags: 839 tags = " ".join(instance.tags) 840 else: 841 tags = "" 842 843 env = { 844 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"], 845 "INSTANCE": instance.name, 846 "MAC": nic.mac, 847 "MODE": nic.nicparams[constants.NIC_MODE], 848 "INTERFACE": tap, 849 "INTERFACE_INDEX": str(seq), 850 "TAGS": tags, 851 } 852 853 if nic.ip: 854 env["IP"] = nic.ip 855 856 if nic.nicparams[constants.NIC_LINK]: 857 env["LINK"] = nic.nicparams[constants.NIC_LINK] 858 859 if nic.network: 860 n = objects.Network.FromDict(nic.netinfo) 861 env.update(n.HooksDict()) 862 863 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 864 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK] 865 866 result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env) 867 if result.failed: 868 raise errors.HypervisorError("Failed to configure interface %s: %s;" 869 " network configuration script output: %s" % 870 (tap, result.fail_reason, result.output))
871 872 @staticmethod
874 if affinity is None: 875 raise errors.HypervisorError("affinity Python package not" 876 " found; cannot use CPU pinning under KVM")
877 878 @staticmethod
879 - def _BuildAffinityCpuMask(cpu_list):
880 """Create a CPU mask suitable for sched_setaffinity from a list of 881 CPUs. 882 883 See man taskset for more info on sched_setaffinity masks. 884 For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101). 885 886 @type cpu_list: list of int 887 @param cpu_list: list of physical CPU numbers to map to vCPUs in order 888 @rtype: int 889 @return: a bit mask of CPU affinities 890 891 """ 892 if cpu_list == constants.CPU_PINNING_OFF: 893 return constants.CPU_PINNING_ALL_KVM 894 else: 895 return sum(2 ** cpu for cpu in cpu_list)
896 897 @classmethod
898 - def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
899 """Change CPU affinity for running VM according to given CPU mask. 900 901 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3" 902 @type cpu_mask: string 903 @param process_id: process ID of KVM process. Used to pin entire VM 904 to physical CPUs. 905 @type process_id: int 906 @param thread_dict: map of virtual CPUs to KVM thread IDs 907 @type thread_dict: dict int:int 908 909 """ 910 # Convert the string CPU mask to a list of list of int's 911 cpu_list = utils.ParseMultiCpuMask(cpu_mask) 912 913 if len(cpu_list) == 1: 914 all_cpu_mapping = cpu_list[0] 915 if all_cpu_mapping == constants.CPU_PINNING_OFF: 916 # If CPU pinning has 1 entry that's "all", then do nothing 917 pass 918 else: 919 # If CPU pinning has one non-all entry, map the entire VM to 920 # one set of physical CPUs 921 cls._VerifyAffinityPackage() 922 affinity.set_process_affinity_mask( 923 process_id, cls._BuildAffinityCpuMask(all_cpu_mapping)) 924 else: 925 # The number of vCPUs mapped should match the number of vCPUs 926 # reported by KVM. This was already verified earlier, so 927 # here only as a sanity check. 928 assert len(thread_dict) == len(cpu_list) 929 cls._VerifyAffinityPackage() 930 931 # For each vCPU, map it to the proper list of physical CPUs 932 for vcpu, i in zip(cpu_list, range(len(cpu_list))): 933 affinity.set_process_affinity_mask(thread_dict[i], 934 cls._BuildAffinityCpuMask(vcpu))
935
936 - def _GetVcpuThreadIds(self, instance_name):
937 """Get a mapping of vCPU no. to thread IDs for the instance 938 939 @type instance_name: string 940 @param instance_name: instance in question 941 @rtype: dictionary of int:int 942 @return: a dictionary mapping vCPU numbers to thread IDs 943 944 """ 945 result = {} 946 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD) 947 for line in output.stdout.splitlines(): 948 match = self._CPU_INFO_RE.search(line) 949 if not match: 950 continue 951 grp = map(int, match.groups()) 952 result[grp[0]] = grp[1] 953 954 return result
955
956 - def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
957 """Complete CPU pinning. 958 959 @type instance_name: string 960 @param instance_name: name of instance 961 @type cpu_mask: string 962 @param cpu_mask: CPU pinning mask as entered by user 963 964 """ 965 # Get KVM process ID, to be used if need to pin entire VM 966 _, pid, _ = self._InstancePidAlive(instance_name) 967 # Get vCPU thread IDs, to be used if need to pin vCPUs separately 968 thread_dict = self._GetVcpuThreadIds(instance_name) 969 # Run CPU pinning, based on configured mask 970 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
971
972 - def ListInstances(self, hvparams=None):
973 """Get the list of running instances. 974 975 We can do this by listing our live instances directory and 976 checking whether the associated kvm process is still alive. 977 978 """ 979 result = [] 980 for name in os.listdir(self._PIDS_DIR): 981 if self._InstancePidAlive(name)[2]: 982 result.append(name) 983 return result
984
985 - def GetInstanceInfo(self, instance_name, hvparams=None):
986 """Get instance properties. 987 988 @type instance_name: string 989 @param instance_name: the instance name 990 @type hvparams: dict of strings 991 @param hvparams: hvparams to be used with this instance 992 @rtype: tuple of strings 993 @return: (name, id, memory, vcpus, stat, times) 994 995 """ 996 _, pid, alive = self._InstancePidAlive(instance_name) 997 if not alive: 998 return None 999 1000 _, memory, vcpus = self._InstancePidInfo(pid) 1001 istat = "---b-" 1002 times = "0" 1003 1004 try: 1005 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name)) 1006 qmp.connect() 1007 vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY]) 1008 # Will fail if ballooning is not enabled, but we can then just resort to 1009 # the value above. 1010 mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY] 1011 memory = mem_bytes / 1048576 1012 except errors.HypervisorError: 1013 pass 1014 1015 return (instance_name, pid, memory, vcpus, istat, times)
1016
1017 - def GetAllInstancesInfo(self, hvparams=None):
1018 """Get properties of all instances. 1019 1020 @type hvparams: dict of strings 1021 @param hvparams: hypervisor parameter 1022 @return: list of tuples (name, id, memory, vcpus, stat, times) 1023 1024 """ 1025 data = [] 1026 for name in os.listdir(self._PIDS_DIR): 1027 try: 1028 info = self.GetInstanceInfo(name) 1029 except errors.HypervisorError: 1030 # Ignore exceptions due to instances being shut down 1031 continue 1032 if info: 1033 data.append(info) 1034 return data
1035
1036 - def _GenerateKVMRuntime(self, instance, block_devices, startup_paused, 1037 kvmhelp):
1038 """Generate KVM information to start an instance. 1039 1040 @type kvmhelp: string 1041 @param kvmhelp: output of kvm --help 1042 @attention: this function must not have any side-effects; for 1043 example, it must not write to the filesystem, or read values 1044 from the current system the are expected to differ between 1045 nodes, since it is only run once at instance startup; 1046 actions/kvm arguments that can vary between systems should be 1047 done in L{_ExecuteKVMRuntime} 1048 1049 """ 1050 # pylint: disable=R0912,R0914,R0915 1051 hvp = instance.hvparams 1052 self.ValidateParameters(hvp) 1053 1054 pidfile = self._InstancePidFile(instance.name) 1055 kvm = hvp[constants.HV_KVM_PATH] 1056 kvm_cmd = [kvm] 1057 # used just by the vnc server, if enabled 1058 kvm_cmd.extend(["-name", instance.name]) 1059 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]]) 1060 1061 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]] 1062 if hvp[constants.HV_CPU_CORES]: 1063 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES]) 1064 if hvp[constants.HV_CPU_THREADS]: 1065 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS]) 1066 if hvp[constants.HV_CPU_SOCKETS]: 1067 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS]) 1068 1069 kvm_cmd.extend(["-smp", ",".join(smp_list)]) 1070 1071 kvm_cmd.extend(["-pidfile", pidfile]) 1072 kvm_cmd.extend(["-balloon", "virtio"]) 1073 kvm_cmd.extend(["-daemonize"]) 1074 if not instance.hvparams[constants.HV_ACPI]: 1075 kvm_cmd.extend(["-no-acpi"]) 1076 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \ 1077 constants.INSTANCE_REBOOT_EXIT: 1078 kvm_cmd.extend(["-no-reboot"]) 1079 1080 mversion = hvp[constants.HV_KVM_MACHINE_VERSION] 1081 if not mversion: 1082 mversion = self._GetDefaultMachineVersion(kvm) 1083 if self._MACHINE_RE.search(kvmhelp): 1084 # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as 1085 # extra hypervisor parameters. We should also investigate whether and how 1086 # shadow_mem should be considered for the resource model. 1087 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED): 1088 specprop = ",accel=kvm" 1089 else: 1090 specprop = "" 1091 machinespec = "%s%s" % (mversion, specprop) 1092 kvm_cmd.extend(["-machine", machinespec]) 1093 else: 1094 kvm_cmd.extend(["-M", mversion]) 1095 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and 1096 self._ENABLE_KVM_RE.search(kvmhelp)): 1097 kvm_cmd.extend(["-enable-kvm"]) 1098 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and 1099 self._DISABLE_KVM_RE.search(kvmhelp)): 1100 kvm_cmd.extend(["-disable-kvm"]) 1101 1102 kernel_path = hvp[constants.HV_KERNEL_PATH] 1103 if kernel_path: 1104 boot_disk = boot_cdrom = boot_floppy = boot_network = False 1105 else: 1106 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK 1107 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM 1108 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY 1109 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK 1110 1111 if startup_paused: 1112 kvm_cmd.extend([_KVM_START_PAUSED_FLAG]) 1113 1114 if boot_network: 1115 kvm_cmd.extend(["-boot", "n"]) 1116 1117 # whether this is an older KVM version that uses the boot=on flag 1118 # on devices 1119 needs_boot_flag = self._BOOT_RE.search(kvmhelp) 1120 1121 disk_type = hvp[constants.HV_DISK_TYPE] 1122 if disk_type == constants.HT_DISK_PARAVIRTUAL: 1123 if_val = ",if=virtio" 1124 else: 1125 if_val = ",if=%s" % disk_type 1126 # Cache mode 1127 disk_cache = hvp[constants.HV_DISK_CACHE] 1128 if instance.disk_template in constants.DTS_EXT_MIRROR: 1129 if disk_cache != "none": 1130 # TODO: make this a hard error, instead of a silent overwrite 1131 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'" 1132 " to prevent shared storage corruption on migration", 1133 disk_cache) 1134 cache_val = ",cache=none" 1135 elif disk_cache != constants.HT_CACHE_DEFAULT: 1136 cache_val = ",cache=%s" % disk_cache 1137 else: 1138 cache_val = "" 1139 for cfdev, dev_path in block_devices: 1140 if cfdev.mode != constants.DISK_RDWR: 1141 raise errors.HypervisorError("Instance has read-only disks which" 1142 " are not supported by KVM") 1143 # TODO: handle FD_LOOP and FD_BLKTAP (?) 1144 boot_val = "" 1145 if boot_disk: 1146 kvm_cmd.extend(["-boot", "c"]) 1147 boot_disk = False 1148 if needs_boot_flag and disk_type != constants.HT_DISK_IDE: 1149 boot_val = ",boot=on" 1150 1151 drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val, 1152 cache_val) 1153 kvm_cmd.extend(["-drive", drive_val]) 1154 1155 #Now we can specify a different device type for CDROM devices. 1156 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE] 1157 if not cdrom_disk_type: 1158 cdrom_disk_type = disk_type 1159 1160 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH] 1161 if iso_image: 1162 options = ",format=raw,media=cdrom" 1163 # set cdrom 'if' type 1164 if boot_cdrom: 1165 actual_cdrom_type = constants.HT_DISK_IDE 1166 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL: 1167 actual_cdrom_type = "virtio" 1168 else: 1169 actual_cdrom_type = cdrom_disk_type 1170 if_val = ",if=%s" % actual_cdrom_type 1171 # set boot flag, if needed 1172 boot_val = "" 1173 if boot_cdrom: 1174 kvm_cmd.extend(["-boot", "d"]) 1175 if needs_boot_flag: 1176 boot_val = ",boot=on" 1177 # and finally build the entire '-drive' value 1178 drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val) 1179 kvm_cmd.extend(["-drive", drive_val]) 1180 1181 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH] 1182 if iso_image2: 1183 options = ",format=raw,media=cdrom" 1184 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL: 1185 if_val = ",if=virtio" 1186 else: 1187 if_val = ",if=%s" % cdrom_disk_type 1188 drive_val = "file=%s%s%s" % (iso_image2, options, if_val) 1189 kvm_cmd.extend(["-drive", drive_val]) 1190 1191 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH] 1192 if floppy_image: 1193 options = ",format=raw,media=disk" 1194 if boot_floppy: 1195 kvm_cmd.extend(["-boot", "a"]) 1196 options = "%s,boot=on" % options 1197 if_val = ",if=floppy" 1198 options = "%s%s" % (options, if_val) 1199 drive_val = "file=%s%s" % (floppy_image, options) 1200 kvm_cmd.extend(["-drive", drive_val]) 1201 1202 if kernel_path: 1203 kvm_cmd.extend(["-kernel", kernel_path]) 1204 initrd_path = hvp[constants.HV_INITRD_PATH] 1205 if initrd_path: 1206 kvm_cmd.extend(["-initrd", initrd_path]) 1207 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH], 1208 hvp[constants.HV_KERNEL_ARGS]] 1209 if hvp[constants.HV_SERIAL_CONSOLE]: 1210 serial_speed = hvp[constants.HV_SERIAL_SPEED] 1211 root_append.append("console=ttyS0,%s" % serial_speed) 1212 kvm_cmd.extend(["-append", " ".join(root_append)]) 1213 1214 mem_path = hvp[constants.HV_MEM_PATH] 1215 if mem_path: 1216 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"]) 1217 1218 monitor_dev = ("unix:%s,server,nowait" % 1219 self._InstanceMonitor(instance.name)) 1220 kvm_cmd.extend(["-monitor", monitor_dev]) 1221 if hvp[constants.HV_SERIAL_CONSOLE]: 1222 serial_dev = ("unix:%s,server,nowait" % 1223 self._InstanceSerial(instance.name)) 1224 kvm_cmd.extend(["-serial", serial_dev]) 1225 else: 1226 kvm_cmd.extend(["-serial", "none"]) 1227 1228 mouse_type = hvp[constants.HV_USB_MOUSE] 1229 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS] 1230 spice_bind = hvp[constants.HV_KVM_SPICE_BIND] 1231 spice_ip_version = None 1232 1233 kvm_cmd.extend(["-usb"]) 1234 1235 if mouse_type: 1236 kvm_cmd.extend(["-usbdevice", mouse_type]) 1237 elif vnc_bind_address: 1238 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET]) 1239 1240 if vnc_bind_address: 1241 if netutils.IsValidInterface(vnc_bind_address): 1242 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address) 1243 if_ip4_addresses = if_addresses[constants.IP4_VERSION] 1244 if len(if_ip4_addresses) < 1: 1245 logging.error("Could not determine IPv4 address of interface %s", 1246 vnc_bind_address) 1247 else: 1248 vnc_bind_address = if_ip4_addresses[0] 1249 if netutils.IP4Address.IsValid(vnc_bind_address): 1250 if instance.network_port > constants.VNC_BASE_PORT: 1251 display = instance.network_port - constants.VNC_BASE_PORT 1252 if vnc_bind_address == constants.IP4_ADDRESS_ANY: 1253 vnc_arg = ":%d" % (display) 1254 else: 1255 vnc_arg = "%s:%d" % (vnc_bind_address, display) 1256 else: 1257 logging.error("Network port is not a valid VNC display (%d < %d)," 1258 " not starting VNC", 1259 instance.network_port, constants.VNC_BASE_PORT) 1260 vnc_arg = "none" 1261 1262 # Only allow tls and other option when not binding to a file, for now. 1263 # kvm/qemu gets confused otherwise about the filename to use. 1264 vnc_append = "" 1265 if hvp[constants.HV_VNC_TLS]: 1266 vnc_append = "%s,tls" % vnc_append 1267 if hvp[constants.HV_VNC_X509_VERIFY]: 1268 vnc_append = "%s,x509verify=%s" % (vnc_append, 1269 hvp[constants.HV_VNC_X509]) 1270 elif hvp[constants.HV_VNC_X509]: 1271 vnc_append = "%s,x509=%s" % (vnc_append, 1272 hvp[constants.HV_VNC_X509]) 1273 if hvp[constants.HV_VNC_PASSWORD_FILE]: 1274 vnc_append = "%s,password" % vnc_append 1275 1276 vnc_arg = "%s%s" % (vnc_arg, vnc_append) 1277 1278 else: 1279 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name) 1280 1281 kvm_cmd.extend(["-vnc", vnc_arg]) 1282 elif spice_bind: 1283 # FIXME: this is wrong here; the iface ip address differs 1284 # between systems, so it should be done in _ExecuteKVMRuntime 1285 if netutils.IsValidInterface(spice_bind): 1286 # The user specified a network interface, we have to figure out the IP 1287 # address. 1288 addresses = netutils.GetInterfaceIpAddresses(spice_bind) 1289 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION] 1290 1291 # if the user specified an IP version and the interface does not 1292 # have that kind of IP addresses, throw an exception 1293 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED: 1294 if not addresses[spice_ip_version]: 1295 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address" 1296 " for %s" % (spice_ip_version, 1297 spice_bind)) 1298 1299 # the user did not specify an IP version, we have to figure it out 1300 elif (addresses[constants.IP4_VERSION] and 1301 addresses[constants.IP6_VERSION]): 1302 # we have both ipv4 and ipv6, let's use the cluster default IP 1303 # version 1304 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily() 1305 spice_ip_version = \ 1306 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family) 1307 elif addresses[constants.IP4_VERSION]: 1308 spice_ip_version = constants.IP4_VERSION 1309 elif addresses[constants.IP6_VERSION]: 1310 spice_ip_version = constants.IP6_VERSION 1311 else: 1312 raise errors.HypervisorError("SPICE: Unable to get an IP address" 1313 " for %s" % (spice_bind)) 1314 1315 spice_address = addresses[spice_ip_version][0] 1316 1317 else: 1318 # spice_bind is known to be a valid IP address, because 1319 # ValidateParameters checked it. 1320 spice_address = spice_bind 1321 1322 spice_arg = "addr=%s" % spice_address 1323 if hvp[constants.HV_KVM_SPICE_USE_TLS]: 1324 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" % 1325 (spice_arg, instance.network_port, 1326 pathutils.SPICE_CACERT_FILE)) 1327 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" % 1328 (spice_arg, pathutils.SPICE_CERT_FILE, 1329 pathutils.SPICE_CERT_FILE)) 1330 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS] 1331 if tls_ciphers: 1332 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers) 1333 else: 1334 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port) 1335 1336 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]: 1337 spice_arg = "%s,disable-ticketing" % spice_arg 1338 1339 if spice_ip_version: 1340 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version) 1341 1342 # Image compression options 1343 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR] 1344 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR] 1345 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR] 1346 if img_lossless: 1347 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless) 1348 if img_jpeg: 1349 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg) 1350 if img_zlib_glz: 1351 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz) 1352 1353 # Video stream detection 1354 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION] 1355 if video_streaming: 1356 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming) 1357 1358 # Audio compression, by default in qemu-kvm it is on 1359 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]: 1360 spice_arg = "%s,playback-compression=off" % spice_arg 1361 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]: 1362 spice_arg = "%s,agent-mouse=off" % spice_arg 1363 else: 1364 # Enable the spice agent communication channel between the host and the 1365 # agent. 1366 kvm_cmd.extend(["-device", "virtio-serial-pci"]) 1367 kvm_cmd.extend([ 1368 "-device", 1369 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0", 1370 ]) 1371 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"]) 1372 1373 logging.info("KVM: SPICE will listen on port %s", instance.network_port) 1374 kvm_cmd.extend(["-spice", spice_arg]) 1375 1376 else: 1377 # From qemu 1.4 -nographic is incompatible with -daemonize. The new way 1378 # also works in earlier versions though (tested with 1.1 and 1.3) 1379 if self._DISPLAY_RE.search(kvmhelp): 1380 kvm_cmd.extend(["-display", "none"]) 1381 else: 1382 kvm_cmd.extend(["-nographic"]) 1383 1384 if hvp[constants.HV_USE_LOCALTIME]: 1385 kvm_cmd.extend(["-localtime"]) 1386 1387 if hvp[constants.HV_KVM_USE_CHROOT]: 1388 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)]) 1389 1390 # Add qemu-KVM -cpu param 1391 if hvp[constants.HV_CPU_TYPE]: 1392 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]]) 1393 1394 # As requested by music lovers 1395 if hvp[constants.HV_SOUNDHW]: 1396 kvm_cmd.extend(["-soundhw", hvp[constants.HV_SOUNDHW]]) 1397 1398 # Pass a -vga option if requested, or if spice is used, for backwards 1399 # compatibility. 1400 if hvp[constants.HV_VGA]: 1401 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]]) 1402 elif spice_bind: 1403 kvm_cmd.extend(["-vga", "qxl"]) 1404 1405 # Various types of usb devices, comma separated 1406 if hvp[constants.HV_USB_DEVICES]: 1407 for dev in hvp[constants.HV_USB_DEVICES].split(","): 1408 kvm_cmd.extend(["-usbdevice", dev]) 1409 1410 # Set system UUID to instance UUID 1411 if self._UUID_RE.search(kvmhelp): 1412 kvm_cmd.extend(["-uuid", instance.uuid]) 1413 1414 if hvp[constants.HV_KVM_EXTRA]: 1415 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" ")) 1416 1417 # Save the current instance nics, but defer their expansion as parameters, 1418 # as we'll need to generate executable temp files for them. 1419 kvm_nics = instance.nics 1420 hvparams = hvp 1421 1422 return (kvm_cmd, kvm_nics, hvparams)
1423
1424 - def _WriteKVMRuntime(self, instance_name, data):
1425 """Write an instance's KVM runtime 1426 1427 """ 1428 try: 1429 utils.WriteFile(self._InstanceKVMRuntime(instance_name), 1430 data=data) 1431 except EnvironmentError, err: 1432 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1433
1434 - def _ReadKVMRuntime(self, instance_name):
1435 """Read an instance's KVM runtime 1436 1437 """ 1438 try: 1439 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name)) 1440 except EnvironmentError, err: 1441 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err) 1442 return file_content
1443
1444 - def _SaveKVMRuntime(self, instance, kvm_runtime):
1445 """Save an instance's KVM runtime 1446 1447 """ 1448 kvm_cmd, kvm_nics, hvparams = kvm_runtime 1449 serialized_nics = [nic.ToDict() for nic in kvm_nics] 1450 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams)) 1451 self._WriteKVMRuntime(instance.name, serialized_form)
1452
1453 - def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1454 """Load an instance's KVM runtime 1455 1456 """ 1457 if not serialized_runtime: 1458 serialized_runtime = self._ReadKVMRuntime(instance.name) 1459 loaded_runtime = serializer.Load(serialized_runtime) 1460 kvm_cmd, serialized_nics, hvparams = loaded_runtime 1461 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics] 1462 return (kvm_cmd, kvm_nics, hvparams)
1463
1464 - def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1465 """Run the KVM cmd and check for errors 1466 1467 @type name: string 1468 @param name: instance name 1469 @type kvm_cmd: list of strings 1470 @param kvm_cmd: runcmd input for kvm 1471 @type tap_fds: list of int 1472 @param tap_fds: fds of tap devices opened by Ganeti 1473 1474 """ 1475 try: 1476 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds) 1477 finally: 1478 for fd in tap_fds: 1479 utils_wrapper.CloseFdNoError(fd) 1480 1481 if result.failed: 1482 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" % 1483 (name, result.fail_reason, result.output)) 1484 if not self._InstancePidAlive(name)[2]: 1485 raise errors.HypervisorError("Failed to start instance %s" % name)
1486
1487 - def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1488 """Execute a KVM cmd, after completing it with some last minute data. 1489 1490 @type incoming: tuple of strings 1491 @param incoming: (target_host_ip, port) 1492 @type kvmhelp: string 1493 @param kvmhelp: output of kvm --help 1494 1495 """ 1496 # Small _ExecuteKVMRuntime hv parameters programming howto: 1497 # - conf_hvp contains the parameters as configured on ganeti. they might 1498 # have changed since the instance started; only use them if the change 1499 # won't affect the inside of the instance (which hasn't been rebooted). 1500 # - up_hvp contains the parameters as they were when the instance was 1501 # started, plus any new parameter which has been added between ganeti 1502 # versions: it is paramount that those default to a value which won't 1503 # affect the inside of the instance as well. 1504 conf_hvp = instance.hvparams 1505 name = instance.name 1506 self._CheckDown(name) 1507 1508 temp_files = [] 1509 1510 kvm_cmd, kvm_nics, up_hvp = kvm_runtime 1511 # the first element of kvm_cmd is always the path to the kvm binary 1512 kvm_path = kvm_cmd[0] 1513 up_hvp = objects.FillDict(conf_hvp, up_hvp) 1514 1515 # We know it's safe to run as a different user upon migration, so we'll use 1516 # the latest conf, from conf_hvp. 1517 security_model = conf_hvp[constants.HV_SECURITY_MODEL] 1518 if security_model == constants.HT_SM_USER: 1519 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]]) 1520 1521 keymap = conf_hvp[constants.HV_KEYMAP] 1522 if keymap: 1523 keymap_path = self._InstanceKeymapFile(name) 1524 # If a keymap file is specified, KVM won't use its internal defaults. By 1525 # first including the "en-us" layout, an error on loading the actual 1526 # layout (e.g. because it can't be found) won't lead to a non-functional 1527 # keyboard. A keyboard with incorrect keys is still better than none. 1528 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap) 1529 kvm_cmd.extend(["-k", keymap_path]) 1530 1531 # We have reasons to believe changing something like the nic driver/type 1532 # upon migration won't exactly fly with the instance kernel, so for nic 1533 # related parameters we'll use up_hvp 1534 tapfds = [] 1535 taps = [] 1536 if not kvm_nics: 1537 kvm_cmd.extend(["-net", "none"]) 1538 else: 1539 vnet_hdr = False 1540 tap_extra = "" 1541 nic_type = up_hvp[constants.HV_NIC_TYPE] 1542 if nic_type == constants.HT_NIC_PARAVIRTUAL: 1543 nic_model = self._VIRTIO 1544 try: 1545 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST) 1546 if self._NEW_VIRTIO_RE.search(devlist): 1547 nic_model = self._VIRTIO_NET_PCI 1548 vnet_hdr = up_hvp[constants.HV_VNET_HDR] 1549 except errors.HypervisorError, _: 1550 # Older versions of kvm don't support DEVICE_LIST, but they don't 1551 # have new virtio syntax either. 1552 pass 1553 1554 if up_hvp[constants.HV_VHOST_NET]: 1555 # check for vhost_net support 1556 if self._VHOST_RE.search(kvmhelp): 1557 tap_extra = ",vhost=on" 1558 else: 1559 raise errors.HypervisorError("vhost_net is configured" 1560 " but it is not available") 1561 else: 1562 nic_model = nic_type 1563 1564 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp) 1565 1566 for nic_seq, nic in enumerate(kvm_nics): 1567 tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr) 1568 tapfds.append(tapfd) 1569 taps.append(tapname) 1570 if kvm_supports_netdev: 1571 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq) 1572 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra) 1573 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val]) 1574 else: 1575 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq, 1576 nic.mac, nic_model) 1577 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd) 1578 kvm_cmd.extend(["-net", tap_val, "-net", nic_val]) 1579 1580 if incoming: 1581 target, port = incoming 1582 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)]) 1583 1584 # Changing the vnc password doesn't bother the guest that much. At most it 1585 # will surprise people who connect to it. Whether positively or negatively 1586 # it's debatable. 1587 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE] 1588 vnc_pwd = None 1589 if vnc_pwd_file: 1590 try: 1591 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True) 1592 except EnvironmentError, err: 1593 raise errors.HypervisorError("Failed to open VNC password file %s: %s" 1594 % (vnc_pwd_file, err)) 1595 1596 if conf_hvp[constants.HV_KVM_USE_CHROOT]: 1597 utils.EnsureDirs([(self._InstanceChrootDir(name), 1598 constants.SECURE_DIR_MODE)]) 1599 1600 # Automatically enable QMP if version is >= 0.14 1601 if self._QMP_RE.search(kvmhelp): 1602 logging.debug("Enabling QMP") 1603 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" % 1604 self._InstanceQmpMonitor(instance.name)]) 1605 1606 # Configure the network now for starting instances and bridged interfaces, 1607 # during FinalizeMigration for incoming instances' routed interfaces 1608 for nic_seq, nic in enumerate(kvm_nics): 1609 if (incoming and 1610 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED): 1611 continue 1612 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq]) 1613 1614 # CPU affinity requires kvm to start paused, so we set this flag if the 1615 # instance is not already paused and if we are not going to accept a 1616 # migrating instance. In the latter case, pausing is not needed. 1617 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming 1618 if start_kvm_paused: 1619 kvm_cmd.extend([_KVM_START_PAUSED_FLAG]) 1620 1621 # Note: CPU pinning is using up_hvp since changes take effect 1622 # during instance startup anyway, and to avoid problems when soft 1623 # rebooting the instance. 1624 cpu_pinning = False 1625 if up_hvp.get(constants.HV_CPU_MASK, None): 1626 cpu_pinning = True 1627 1628 if security_model == constants.HT_SM_POOL: 1629 ss = ssconf.SimpleStore() 1630 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n") 1631 all_uids = set(uidpool.ExpandUidPool(uid_pool)) 1632 uid = uidpool.RequestUnusedUid(all_uids) 1633 try: 1634 username = pwd.getpwuid(uid.GetUid()).pw_name 1635 kvm_cmd.extend(["-runas", username]) 1636 self._RunKVMCmd(name, kvm_cmd, tapfds) 1637 except: 1638 uidpool.ReleaseUid(uid) 1639 raise 1640 else: 1641 uid.Unlock() 1642 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr()) 1643 else: 1644 self._RunKVMCmd(name, kvm_cmd, tapfds) 1645 1646 utils.EnsureDirs([(self._InstanceNICDir(instance.name), 1647 constants.RUN_DIRS_MODE)]) 1648 for nic_seq, tap in enumerate(taps): 1649 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq), 1650 data=tap) 1651 1652 if vnc_pwd: 1653 change_cmd = "change vnc password %s" % vnc_pwd 1654 self._CallMonitorCommand(instance.name, change_cmd) 1655 1656 # Setting SPICE password. We are not vulnerable to malicious passwordless 1657 # connection attempts because SPICE by default does not allow connections 1658 # if neither a password nor the "disable_ticketing" options are specified. 1659 # As soon as we send the password via QMP, that password is a valid ticket 1660 # for connection. 1661 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE] 1662 if spice_password_file: 1663 spice_pwd = "" 1664 try: 1665 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True) 1666 except EnvironmentError, err: 1667 raise errors.HypervisorError("Failed to open SPICE password file %s: %s" 1668 % (spice_password_file, err)) 1669 1670 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name)) 1671 qmp.connect() 1672 arguments = { 1673 "protocol": "spice", 1674 "password": spice_pwd, 1675 } 1676 qmp.Execute("set_password", arguments) 1677 1678 for filename in temp_files: 1679 utils.RemoveFile(filename) 1680 1681 # If requested, set CPU affinity and resume instance execution 1682 if cpu_pinning: 1683 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK]) 1684 1685 start_memory = self._InstanceStartupMemory(instance) 1686 if start_memory < instance.beparams[constants.BE_MAXMEM]: 1687 self.BalloonInstanceMemory(instance, start_memory) 1688 1689 if start_kvm_paused: 1690 # To control CPU pinning, ballooning, and vnc/spice passwords 1691 # the VM was started in a frozen state. If freezing was not 1692 # explicitly requested resume the vm status. 1693 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1694
1695 - def StartInstance(self, instance, block_devices, startup_paused):
1696 """Start an instance. 1697 1698 """ 1699 self._CheckDown(instance.name) 1700 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 1701 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 1702 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, 1703 startup_paused, kvmhelp) 1704 self._SaveKVMRuntime(instance, kvm_runtime) 1705 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
1706
1707 - def _CallMonitorCommand(self, instance_name, command, timeout=None):
1708 """Invoke a command on the instance monitor. 1709 1710 """ 1711 if timeout is not None: 1712 timeout_cmd = "timeout %s" % (timeout, ) 1713 else: 1714 timeout_cmd = "" 1715 1716 # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum 1717 # version. The monitor protocol is designed for human consumption, whereas 1718 # QMP is made for programmatic usage. In the worst case QMP can also 1719 # execute monitor commands. As it is, all calls to socat take at least 1720 # 500ms and likely more: socat can't detect the end of the reply and waits 1721 # for 500ms of no data received before exiting (500 ms is the default for 1722 # the "-t" parameter). 1723 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" % 1724 (utils.ShellQuote(command), 1725 timeout_cmd, 1726 constants.SOCAT_PATH, 1727 utils.ShellQuote(self._InstanceMonitor(instance_name)))) 1728 1729 result = utils.RunCmd(socat) 1730 if result.failed: 1731 msg = ("Failed to send command '%s' to instance '%s', reason '%s'," 1732 " output: %s" % 1733 (command, instance_name, result.fail_reason, result.output)) 1734 raise errors.HypervisorError(msg) 1735 1736 return result
1737 1738 @classmethod
1739 - def _ParseKVMVersion(cls, text):
1740 """Parse the KVM version from the --help output. 1741 1742 @type text: string 1743 @param text: output of kvm --help 1744 @return: (version, v_maj, v_min, v_rev) 1745 @raise errors.HypervisorError: when the KVM version cannot be retrieved 1746 1747 """ 1748 match = cls._VERSION_RE.search(text.splitlines()[0]) 1749 if not match: 1750 raise errors.HypervisorError("Unable to get KVM version") 1751 1752 v_all = match.group(0) 1753 v_maj = int(match.group(1)) 1754 v_min = int(match.group(2)) 1755 if match.group(4): 1756 v_rev = int(match.group(4)) 1757 else: 1758 v_rev = 0 1759 return (v_all, v_maj, v_min, v_rev)
1760 1761 @classmethod
1762 - def _GetKVMOutput(cls, kvm_path, option):
1763 """Return the output of a kvm invocation 1764 1765 @type kvm_path: string 1766 @param kvm_path: path to the kvm executable 1767 @type option: a key of _KVMOPTS_CMDS 1768 @param option: kvm option to fetch the output from 1769 @return: output a supported kvm invocation 1770 @raise errors.HypervisorError: when the KVM help output cannot be retrieved 1771 1772 """ 1773 assert option in cls._KVMOPTS_CMDS, "Invalid output option" 1774 1775 optlist, can_fail = cls._KVMOPTS_CMDS[option] 1776 1777 result = utils.RunCmd([kvm_path] + optlist) 1778 if result.failed and not can_fail: 1779 raise errors.HypervisorError("Unable to get KVM %s output" % 1780 " ".join(optlist)) 1781 return result.output
1782 1783 @classmethod
1784 - def _GetKVMVersion(cls, kvm_path):
1785 """Return the installed KVM version. 1786 1787 @return: (version, v_maj, v_min, v_rev) 1788 @raise errors.HypervisorError: when the KVM version cannot be retrieved 1789 1790 """ 1791 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
1792 1793 @classmethod
1794 - def _GetDefaultMachineVersion(cls, kvm_path):
1795 """Return the default hardware revision (e.g. pc-1.1) 1796 1797 """ 1798 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST) 1799 match = cls._DEFAULT_MACHINE_VERSION_RE.search(output) 1800 if match: 1801 return match.group(1) 1802 else: 1803 return "pc"
1804
1805 - def StopInstance(self, instance, force=False, retry=False, name=None, 1806 timeout=None):
1807 """Stop an instance. 1808 1809 """ 1810 assert(timeout is None or force is not None) 1811 1812 if name is not None and not force: 1813 raise errors.HypervisorError("Cannot shutdown cleanly by name only") 1814 if name is None: 1815 name = instance.name 1816 acpi = instance.hvparams[constants.HV_ACPI] 1817 else: 1818 acpi = False 1819 _, pid, alive = self._InstancePidAlive(name) 1820 if pid > 0 and alive: 1821 if force or not acpi: 1822 utils.KillProcess(pid) 1823 else: 1824 self._CallMonitorCommand(name, "system_powerdown", timeout)
1825
1826 - def CleanupInstance(self, instance_name):
1827 """Cleanup after a stopped instance 1828 1829 """ 1830 pidfile, pid, alive = self._InstancePidAlive(instance_name) 1831 if pid > 0 and alive: 1832 raise errors.HypervisorError("Cannot cleanup a live instance") 1833 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1834
1835 - def RebootInstance(self, instance):
1836 """Reboot an instance. 1837 1838 """ 1839 # For some reason if we do a 'send-key ctrl-alt-delete' to the control 1840 # socket the instance will stop, but now power up again. So we'll resort 1841 # to shutdown and restart. 1842 _, _, alive = self._InstancePidAlive(instance.name) 1843 if not alive: 1844 raise errors.HypervisorError("Failed to reboot instance %s:" 1845 " not running" % instance.name) 1846 # StopInstance will delete the saved KVM runtime so: 1847 # ...first load it... 1848 kvm_runtime = self._LoadKVMRuntime(instance) 1849 # ...now we can safely call StopInstance... 1850 if not self.StopInstance(instance): 1851 self.StopInstance(instance, force=True) 1852 # ...and finally we can save it again, and execute it... 1853 self._SaveKVMRuntime(instance, kvm_runtime) 1854 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 1855 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 1856 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
1857
1858 - def MigrationInfo(self, instance):
1859 """Get instance information to perform a migration. 1860 1861 @type instance: L{objects.Instance} 1862 @param instance: instance to be migrated 1863 @rtype: string 1864 @return: content of the KVM runtime file 1865 1866 """ 1867 return self._ReadKVMRuntime(instance.name)
1868
1869 - def AcceptInstance(self, instance, info, target):
1870 """Prepare to accept an instance. 1871 1872 @type instance: L{objects.Instance} 1873 @param instance: instance to be accepted 1874 @type info: string 1875 @param info: content of the KVM runtime file on the source node 1876 @type target: string 1877 @param target: target host (usually ip), on this node 1878 1879 """ 1880 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info) 1881 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT]) 1882 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 1883 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 1884 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp, 1885 incoming=incoming_address)
1886
1887 - def FinalizeMigrationDst(self, instance, info, success):
1888 """Finalize the instance migration on the target node. 1889 1890 Stop the incoming mode KVM. 1891 1892 @type instance: L{objects.Instance} 1893 @param instance: instance whose migration is being finalized 1894 1895 """ 1896 if success: 1897 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info) 1898 kvm_nics = kvm_runtime[1] 1899 1900 for nic_seq, nic in enumerate(kvm_nics): 1901 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1902 # Bridged interfaces have already been configured 1903 continue 1904 try: 1905 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq)) 1906 except EnvironmentError, err: 1907 logging.warning("Failed to find host interface for %s NIC #%d: %s", 1908 instance.name, nic_seq, str(err)) 1909 continue 1910 try: 1911 self._ConfigureNIC(instance, nic_seq, nic, tap) 1912 except errors.HypervisorError, err: 1913 logging.warning(str(err)) 1914 1915 self._WriteKVMRuntime(instance.name, info) 1916 else: 1917 self.StopInstance(instance, force=True)
1918
1919 - def MigrateInstance(self, cluster_name, instance, target, live):
1920 """Migrate an instance to a target node. 1921 1922 The migration will not be attempted if the instance is not 1923 currently running. 1924 1925 @type cluster_name: string 1926 @param cluster_name: name of the cluster 1927 @type instance: L{objects.Instance} 1928 @param instance: the instance to be migrated 1929 @type target: string 1930 @param target: ip address of the target node 1931 @type live: boolean 1932 @param live: perform a live migration 1933 1934 """ 1935 instance_name = instance.name 1936 port = instance.hvparams[constants.HV_MIGRATION_PORT] 1937 _, _, alive = self._InstancePidAlive(instance_name) 1938 if not alive: 1939 raise errors.HypervisorError("Instance not running, cannot migrate") 1940 1941 if not live: 1942 self._CallMonitorCommand(instance_name, "stop") 1943 1944 migrate_command = ("migrate_set_speed %dm" % 1945 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH]) 1946 self._CallMonitorCommand(instance_name, migrate_command) 1947 1948 migrate_command = ("migrate_set_downtime %dms" % 1949 instance.hvparams[constants.HV_MIGRATION_DOWNTIME]) 1950 self._CallMonitorCommand(instance_name, migrate_command) 1951 1952 migrate_command = "migrate -d tcp:%s:%s" % (target, port) 1953 self._CallMonitorCommand(instance_name, migrate_command)
1954
1955 - def FinalizeMigrationSource(self, instance, success, live):
1956 """Finalize the instance migration on the source node. 1957 1958 @type instance: L{objects.Instance} 1959 @param instance: the instance that was migrated 1960 @type success: bool 1961 @param success: whether the migration succeeded or not 1962 @type live: bool 1963 @param live: whether the user requested a live migration or not 1964 1965 """ 1966 if success: 1967 pidfile, pid, _ = self._InstancePidAlive(instance.name) 1968 utils.KillProcess(pid) 1969 self._RemoveInstanceRuntimeFiles(pidfile, instance.name) 1970 elif live: 1971 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1972
1973 - def GetMigrationStatus(self, instance):
1974 """Get the migration status 1975 1976 @type instance: L{objects.Instance} 1977 @param instance: the instance that is being migrated 1978 @rtype: L{objects.MigrationStatus} 1979 @return: the status of the current migration (one of 1980 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional 1981 progress info that can be retrieved from the hypervisor 1982 1983 """ 1984 info_command = "info migrate" 1985 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS): 1986 result = self._CallMonitorCommand(instance.name, info_command) 1987 match = self._MIGRATION_STATUS_RE.search(result.stdout) 1988 if not match: 1989 if not result.stdout: 1990 logging.info("KVM: empty 'info migrate' result") 1991 else: 1992 logging.warning("KVM: unknown 'info migrate' result: %s", 1993 result.stdout) 1994 else: 1995 status = match.group(1) 1996 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES: 1997 migration_status = objects.MigrationStatus(status=status) 1998 match = self._MIGRATION_PROGRESS_RE.search(result.stdout) 1999 if match: 2000 migration_status.transferred_ram = match.group("transferred") 2001 migration_status.total_ram = match.group("total") 2002 2003 return migration_status 2004 2005 logging.warning("KVM: unknown migration status '%s'", status) 2006 2007 time.sleep(self._MIGRATION_INFO_RETRY_DELAY) 2008 2009 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2010
2011 - def BalloonInstanceMemory(self, instance, mem):
2012 """Balloon an instance memory to a certain value. 2013 2014 @type instance: L{objects.Instance} 2015 @param instance: instance to be accepted 2016 @type mem: int 2017 @param mem: actual memory size to use for instance runtime 2018 2019 """ 2020 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2021
2022 - def GetNodeInfo(self, hvparams=None):
2023 """Return information about the node. 2024 2025 @type hvparams: dict of strings 2026 @param hvparams: hypervisor parameters, not used in this class 2027 2028 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus 2029 the following keys: 2030 - hv_version: the hypervisor version in the form (major, minor, 2031 revision) 2032 2033 """ 2034 result = self.GetLinuxNodeInfo() 2035 kvmpath = constants.KVM_PATH 2036 if hvparams is not None: 2037 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH) 2038 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath) 2039 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev) 2040 return result
2041 2042 @classmethod
2043 - def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
2044 """Return a command for connecting to the console of an instance. 2045 2046 """ 2047 if hvparams[constants.HV_SERIAL_CONSOLE]: 2048 cmd = [pathutils.KVM_CONSOLE_WRAPPER, 2049 constants.SOCAT_PATH, utils.ShellQuote(instance.name), 2050 utils.ShellQuote(cls._InstanceMonitor(instance.name)), 2051 "STDIO,%s" % cls._SocatUnixConsoleParams(), 2052 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)] 2053 return objects.InstanceConsole(instance=instance.name, 2054 kind=constants.CONS_SSH, 2055 host=primary_node.name, 2056 user=constants.SSH_CONSOLE_USER, 2057 command=cmd) 2058 2059 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS] 2060 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT: 2061 display = instance.network_port - constants.VNC_BASE_PORT 2062 return objects.InstanceConsole(instance=instance.name, 2063 kind=constants.CONS_VNC, 2064 host=vnc_bind_address, 2065 port=instance.network_port, 2066 display=display) 2067 2068 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 2069 if spice_bind: 2070 return objects.InstanceConsole(instance=instance.name, 2071 kind=constants.CONS_SPICE, 2072 host=spice_bind, 2073 port=instance.network_port) 2074 2075 return objects.InstanceConsole(instance=instance.name, 2076 kind=constants.CONS_MESSAGE, 2077 message=("No serial shell for instance %s" % 2078 instance.name))
2079
2080 - def Verify(self, hvparams=None):
2081 """Verify the hypervisor. 2082 2083 Check that the required binaries exist. 2084 2085 @type hvparams: dict of strings 2086 @param hvparams: hypervisor parameters to be verified against, not used here 2087 2088 @return: Problem description if something is wrong, C{None} otherwise 2089 2090 """ 2091 msgs = [] 2092 kvmpath = constants.KVM_PATH 2093 if hvparams is not None: 2094 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH) 2095 if not os.path.exists(kvmpath): 2096 msgs.append("The KVM binary ('%s') does not exist" % kvmpath) 2097 if not os.path.exists(constants.SOCAT_PATH): 2098 msgs.append("The socat binary ('%s') does not exist" % 2099 constants.SOCAT_PATH) 2100 2101 return self._FormatVerifyResults(msgs)
2102 2103 @classmethod
2104 - def CheckParameterSyntax(cls, hvparams):
2105 """Check the given parameters for validity. 2106 2107 @type hvparams: dict 2108 @param hvparams: dictionary with parameter names/value 2109 @raise errors.HypervisorError: when a parameter is not valid 2110 2111 """ 2112 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams) 2113 2114 kernel_path = hvparams[constants.HV_KERNEL_PATH] 2115 if kernel_path: 2116 if not hvparams[constants.HV_ROOT_PATH]: 2117 raise errors.HypervisorError("Need a root partition for the instance," 2118 " if a kernel is defined") 2119 2120 if (hvparams[constants.HV_VNC_X509_VERIFY] and 2121 not hvparams[constants.HV_VNC_X509]): 2122 raise errors.HypervisorError("%s must be defined, if %s is" % 2123 (constants.HV_VNC_X509, 2124 constants.HV_VNC_X509_VERIFY)) 2125 2126 if hvparams[constants.HV_SERIAL_CONSOLE]: 2127 serial_speed = hvparams[constants.HV_SERIAL_SPEED] 2128 valid_speeds = constants.VALID_SERIAL_SPEEDS 2129 if not serial_speed or serial_speed not in valid_speeds: 2130 raise errors.HypervisorError("Invalid serial console speed, must be" 2131 " one of: %s" % 2132 utils.CommaJoin(valid_speeds)) 2133 2134 boot_order = hvparams[constants.HV_BOOT_ORDER] 2135 if (boot_order == constants.HT_BO_CDROM and 2136 not hvparams[constants.HV_CDROM_IMAGE_PATH]): 2137 raise errors.HypervisorError("Cannot boot from cdrom without an" 2138 " ISO path") 2139 2140 security_model = hvparams[constants.HV_SECURITY_MODEL] 2141 if security_model == constants.HT_SM_USER: 2142 if not hvparams[constants.HV_SECURITY_DOMAIN]: 2143 raise errors.HypervisorError("A security domain (user to run kvm as)" 2144 " must be specified") 2145 elif (security_model == constants.HT_SM_NONE or 2146 security_model == constants.HT_SM_POOL): 2147 if hvparams[constants.HV_SECURITY_DOMAIN]: 2148 raise errors.HypervisorError("Cannot have a security domain when the" 2149 " security model is 'none' or 'pool'") 2150 2151 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 2152 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION] 2153 if spice_bind: 2154 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED: 2155 # if an IP version is specified, the spice_bind parameter must be an 2156 # IP of that family 2157 if (netutils.IP4Address.IsValid(spice_bind) and 2158 spice_ip_version != constants.IP4_VERSION): 2159 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but" 2160 " the specified IP version is %s" % 2161 (spice_bind, spice_ip_version)) 2162 2163 if (netutils.IP6Address.IsValid(spice_bind) and 2164 spice_ip_version != constants.IP6_VERSION): 2165 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but" 2166 " the specified IP version is %s" % 2167 (spice_bind, spice_ip_version)) 2168 else: 2169 # All the other SPICE parameters depend on spice_bind being set. Raise an 2170 # error if any of them is set without it. 2171 for param in _SPICE_ADDITIONAL_PARAMS: 2172 if hvparams[param]: 2173 raise errors.HypervisorError("SPICE: %s requires %s to be set" % 2174 (param, constants.HV_KVM_SPICE_BIND))
2175 2176 @classmethod
2177 - def ValidateParameters(cls, hvparams):
2178 """Check the given parameters for validity. 2179 2180 @type hvparams: dict 2181 @param hvparams: dictionary with parameter names/value 2182 @raise errors.HypervisorError: when a parameter is not valid 2183 2184 """ 2185 super(KVMHypervisor, cls).ValidateParameters(hvparams) 2186 2187 kvm_path = hvparams[constants.HV_KVM_PATH] 2188 2189 security_model = hvparams[constants.HV_SECURITY_MODEL] 2190 if security_model == constants.HT_SM_USER: 2191 username = hvparams[constants.HV_SECURITY_DOMAIN] 2192 try: 2193 pwd.getpwnam(username) 2194 except KeyError: 2195 raise errors.HypervisorError("Unknown security domain user %s" 2196 % username) 2197 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS] 2198 if vnc_bind_address: 2199 bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address) 2200 is_interface = netutils.IsValidInterface(vnc_bind_address) 2201 is_path = utils.IsNormAbsPath(vnc_bind_address) 2202 if not bound_to_addr and not is_interface and not is_path: 2203 raise errors.HypervisorError("VNC: The %s parameter must be either" 2204 " a valid IP address, an interface name," 2205 " or an absolute path" % 2206 constants.HV_KVM_SPICE_BIND) 2207 2208 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 2209 if spice_bind: 2210 # only one of VNC and SPICE can be used currently. 2211 if hvparams[constants.HV_VNC_BIND_ADDRESS]: 2212 raise errors.HypervisorError("Both SPICE and VNC are configured, but" 2213 " only one of them can be used at a" 2214 " given time") 2215 2216 # check that KVM supports SPICE 2217 kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP) 2218 if not cls._SPICE_RE.search(kvmhelp): 2219 raise errors.HypervisorError("SPICE is configured, but it is not" 2220 " supported according to 'kvm --help'") 2221 2222 # if spice_bind is not an IP address, it must be a valid interface 2223 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or 2224 netutils.IP6Address.IsValid(spice_bind)) 2225 if not bound_to_addr and not netutils.IsValidInterface(spice_bind): 2226 raise errors.HypervisorError("SPICE: The %s parameter must be either" 2227 " a valid IP address or interface name" % 2228 constants.HV_KVM_SPICE_BIND) 2229 2230 machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION] 2231 if machine_version: 2232 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST) 2233 if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output): 2234 raise errors.HypervisorError("Unsupported machine version: %s" % 2235 machine_version)
2236 2237 @classmethod
2238 - def PowercycleNode(cls, hvparams=None):
2239 """KVM powercycle, just a wrapper over Linux powercycle. 2240 2241 @type hvparams: dict of strings 2242 @param hvparams: hypervisor params to be used on this node 2243 2244 """ 2245 cls.LinuxPowercycle()
2246