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