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