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

Source Code for Package ganeti.hypervisor.hv_kvm

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc. 
   5  # All rights reserved. 
   6  # 
   7  # Redistribution and use in source and binary forms, with or without 
   8  # modification, are permitted provided that the following conditions are 
   9  # met: 
  10  # 
  11  # 1. Redistributions of source code must retain the above copyright notice, 
  12  # this list of conditions and the following disclaimer. 
  13  # 
  14  # 2. Redistributions in binary form must reproduce the above copyright 
  15  # notice, this list of conditions and the following disclaimer in the 
  16  # documentation and/or other materials provided with the distribution. 
  17  # 
  18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
  19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
  20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
  22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
  28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  29   
  30   
  31  """KVM hypervisor 
  32   
  33  """ 
  34   
  35  import errno 
  36  import os 
  37  import os.path 
  38  import re 
  39  import tempfile 
  40  import time 
  41  import logging 
  42  import pwd 
  43  import shutil 
  44  import urllib2 
  45  from bitarray import bitarray 
  46  try: 
  47    import psutil   # pylint: disable=F0401 
  48  except ImportError: 
  49    psutil = None 
  50  try: 
  51    import fdsend   # pylint: disable=F0401 
  52  except ImportError: 
  53    fdsend = None 
  54   
  55  from ganeti import utils 
  56  from ganeti import constants 
  57  from ganeti import errors 
  58  from ganeti import serializer 
  59  from ganeti import objects 
  60  from ganeti import uidpool 
  61  from ganeti import ssconf 
  62  from ganeti import netutils 
  63  from ganeti import pathutils 
  64  from ganeti.hypervisor import hv_base 
  65  from ganeti.utils import wrapper as utils_wrapper 
  66   
  67  from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \ 
  68                                               MonitorSocket 
  69  from ganeti.hypervisor.hv_kvm.netdev import OpenTap 
  70   
  71   
  72  _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge" 
  73  _KVM_START_PAUSED_FLAG = "-S" 
  74   
  75  #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND} 
  76  _SPICE_ADDITIONAL_PARAMS = frozenset([ 
  77    constants.HV_KVM_SPICE_IP_VERSION, 
  78    constants.HV_KVM_SPICE_PASSWORD_FILE, 
  79    constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR, 
  80    constants.HV_KVM_SPICE_JPEG_IMG_COMPR, 
  81    constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR, 
  82    constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION, 
  83    constants.HV_KVM_SPICE_USE_TLS, 
  84    ]) 
  85   
  86  # below constants show the format of runtime file 
  87  # the nics are in second possition, while the disks in 4th (last) 
  88  # moreover disk entries are stored as a list of in tuples 
  89  # (L{objects.Disk}, link_name, uri) 
  90  _KVM_NICS_RUNTIME_INDEX = 1 
  91  _KVM_DISKS_RUNTIME_INDEX = 3 
  92  _DEVICE_RUNTIME_INDEX = { 
  93    constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX, 
  94    constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX 
  95    } 
  96  _FIND_RUNTIME_ENTRY = { 
  97    constants.HOTPLUG_TARGET_NIC: 
  98      lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid], 
  99    constants.HOTPLUG_TARGET_DISK: 
 100      lambda disk, kvm_disks: [(d, l, u) for (d, l, u) in kvm_disks 
 101                               if d.uuid == disk.uuid] 
 102    } 
 103  _RUNTIME_DEVICE = { 
 104    constants.HOTPLUG_TARGET_NIC: lambda d: d, 
 105    constants.HOTPLUG_TARGET_DISK: lambda (d, e, _): d 
 106    } 
 107  _RUNTIME_ENTRY = { 
 108    constants.HOTPLUG_TARGET_NIC: lambda d, e: d, 
 109    constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e[0], e[1]) 
 110    } 
 111   
 112  _MIGRATION_CAPS_DELIM = ":" 
113 114 115 -def _with_qmp(fn):
116 """Wrapper used on hotplug related methods""" 117 def wrapper(self, instance, *args, **kwargs): 118 """Create a QmpConnection and run the wrapped method""" 119 if not getattr(self, "qmp", None): 120 filename = self._InstanceQmpMonitor(instance.name)# pylint: disable=W0212 121 self.qmp = QmpConnection(filename) 122 return fn(self, instance, *args, **kwargs)
123 return wrapper 124
125 126 -def _GetDriveURI(disk, link, uri):
127 """Helper function to get the drive uri to be used in --drive kvm option 128 129 Invoked during startup and disk hot-add. In latter case and if no userspace 130 access mode is used it will be overriden with /dev/fdset/<fdset-id> (see 131 HotAddDisk() and AddFd() of QmpConnection). 132 133 @type disk: L{objects.Disk} 134 @param disk: A disk configuration object 135 @type link: string 136 @param link: The device link as returned by _SymlinkBlockDev() 137 @type uri: string 138 @param uri: The drive uri as returned by _CalculateDeviceURI() 139 140 @return: The drive uri to use in kvm option 141 142 """ 143 access_mode = disk.params.get(constants.LDP_ACCESS, 144 constants.DISK_KERNELSPACE) 145 # If uri is available, use it during startup/hot-add 146 if (uri and access_mode == constants.DISK_USERSPACE): 147 drive_uri = uri 148 # Otherwise use the link previously created 149 else: 150 drive_uri = link 151 152 return drive_uri
153
154 155 -def _GenerateDeviceKVMId(dev_type, dev):
156 """Helper function to generate a unique device name used by KVM 157 158 QEMU monitor commands use names to identify devices. Here we use their pci 159 slot and a part of their UUID to name them. dev.pci might be None for old 160 devices in the cluster. 161 162 @type dev_type: sting 163 @param dev_type: device type of param dev 164 @type dev: L{objects.Disk} or L{objects.NIC} 165 @param dev: the device object for which we generate a kvm name 166 @raise errors.HotplugError: in case a device has no pci slot (old devices) 167 168 """ 169 170 if not dev.pci: 171 raise errors.HotplugError("Hotplug is not supported for %s with UUID %s" % 172 (dev_type, dev.uuid)) 173 174 return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
175
176 177 -def _GetExistingDeviceInfo(dev_type, device, runtime):
178 """Helper function to get an existing device inside the runtime file 179 180 Used when an instance is running. Load kvm runtime file and search 181 for a device based on its type and uuid. 182 183 @type dev_type: sting 184 @param dev_type: device type of param dev 185 @type device: L{objects.Disk} or L{objects.NIC} 186 @param device: the device object for which we generate a kvm name 187 @type runtime: tuple (cmd, nics, hvparams, disks) 188 @param runtime: the runtime data to search for the device 189 @raise errors.HotplugError: in case the requested device does not 190 exist (e.g. device has been added without --hotplug option) or 191 device info has not pci slot (e.g. old devices in the cluster) 192 193 """ 194 index = _DEVICE_RUNTIME_INDEX[dev_type] 195 found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index]) 196 if not found: 197 raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" % 198 (dev_type, device.uuid)) 199 200 return found[0]
201
202 203 -def _UpgradeSerializedRuntime(serialized_runtime):
204 """Upgrade runtime data 205 206 Remove any deprecated fields or change the format of the data. 207 The runtime files are not upgraded when Ganeti is upgraded, so the required 208 modification have to be performed here. 209 210 @type serialized_runtime: string 211 @param serialized_runtime: raw text data read from actual runtime file 212 @return: (cmd, nic dicts, hvparams, bdev dicts) 213 @rtype: tuple 214 215 """ 216 loaded_runtime = serializer.Load(serialized_runtime) 217 kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3] 218 if len(loaded_runtime) >= 4: 219 serialized_disks = loaded_runtime[3] 220 else: 221 serialized_disks = [] 222 223 for nic in serialized_nics: 224 # Add a dummy uuid slot if an pre-2.8 NIC is found 225 if "uuid" not in nic: 226 nic["uuid"] = utils.NewUUID() 227 228 return kvm_cmd, serialized_nics, hvparams, serialized_disks
229
230 231 -def _AnalyzeSerializedRuntime(serialized_runtime):
232 """Return runtime entries for a serialized runtime file 233 234 @type serialized_runtime: string 235 @param serialized_runtime: raw text data read from actual runtime file 236 @return: (cmd, nics, hvparams, bdevs) 237 @rtype: tuple 238 239 """ 240 kvm_cmd, serialized_nics, hvparams, serialized_disks = \ 241 _UpgradeSerializedRuntime(serialized_runtime) 242 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics] 243 kvm_disks = [(objects.Disk.FromDict(sdisk), link, uri) 244 for sdisk, link, uri in serialized_disks] 245 246 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
247
248 249 -class HeadRequest(urllib2.Request):
250 - def get_method(self):
251 return "HEAD"
252
253 254 -def _CheckUrl(url):
255 """Check if a given URL exists on the server 256 257 """ 258 try: 259 urllib2.urlopen(HeadRequest(url)) 260 return True 261 except urllib2.URLError: 262 return False
263
264 265 -class KVMHypervisor(hv_base.BaseHypervisor):
266 """KVM hypervisor interface 267 268 """ 269 CAN_MIGRATE = True 270 271 _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor" 272 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids 273 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids 274 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets 275 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data 276 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations 277 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps 278 # KVM instances with chroot enabled are started in empty chroot directories. 279 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories 280 # After an instance is stopped, its chroot directory is removed. 281 # If the chroot directory is not empty, it can't be removed. 282 # A non-empty chroot directory indicates a possible security incident. 283 # To support forensics, the non-empty chroot directory is quarantined in 284 # a separate directory, called 'chroot-quarantine'. 285 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine" 286 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR, 287 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR] 288 289 PARAMETERS = { 290 constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK, 291 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK, 292 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK, 293 constants.HV_ROOT_PATH: hv_base.NO_CHECK, 294 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK, 295 constants.HV_ACPI: hv_base.NO_CHECK, 296 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK, 297 constants.HV_SERIAL_SPEED: hv_base.NO_CHECK, 298 constants.HV_VNC_BIND_ADDRESS: hv_base.NO_CHECK, # will be checked later 299 constants.HV_VNC_TLS: hv_base.NO_CHECK, 300 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK, 301 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK, 302 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK, 303 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later 304 constants.HV_KVM_SPICE_IP_VERSION: 305 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or 306 x in constants.VALID_IP_VERSIONS), 307 "The SPICE IP version should be 4 or 6", 308 None, None), 309 constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK, 310 constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR: 311 hv_base.ParamInSet( 312 False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS), 313 constants.HV_KVM_SPICE_JPEG_IMG_COMPR: 314 hv_base.ParamInSet( 315 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS), 316 constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR: 317 hv_base.ParamInSet( 318 False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS), 319 constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION: 320 hv_base.ParamInSet( 321 False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS), 322 constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK, 323 constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK, 324 constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK, 325 constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK, 326 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 327 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK, 328 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK, 329 constants.HV_BOOT_ORDER: 330 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES), 331 constants.HV_NIC_TYPE: 332 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES), 333 constants.HV_DISK_TYPE: 334 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES), 335 constants.HV_KVM_CDROM_DISK_TYPE: 336 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES), 337 constants.HV_USB_MOUSE: 338 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES), 339 constants.HV_KEYMAP: hv_base.NO_CHECK, 340 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 341 constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK, 342 constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK, 343 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 344 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK, 345 constants.HV_DISK_CACHE: 346 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES), 347 constants.HV_KVM_DISK_AIO: 348 hv_base.ParamInSet(False, constants.HT_KVM_VALID_AIO_TYPES), 349 constants.HV_SECURITY_MODEL: 350 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES), 351 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK, 352 constants.HV_KVM_FLAG: 353 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES), 354 constants.HV_VHOST_NET: hv_base.NO_CHECK, 355 constants.HV_VIRTIO_NET_QUEUES: hv_base.OPT_VIRTIO_NET_QUEUES_CHECK, 356 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK, 357 constants.HV_KVM_USER_SHUTDOWN: hv_base.NO_CHECK, 358 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK, 359 constants.HV_REBOOT_BEHAVIOR: 360 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS), 361 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK, 362 constants.HV_CPU_TYPE: hv_base.NO_CHECK, 363 constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK, 364 constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK, 365 constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK, 366 constants.HV_SOUNDHW: hv_base.NO_CHECK, 367 constants.HV_USB_DEVICES: hv_base.NO_CHECK, 368 constants.HV_VGA: hv_base.NO_CHECK, 369 constants.HV_KVM_EXTRA: hv_base.NO_CHECK, 370 constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK, 371 constants.HV_KVM_MIGRATION_CAPS: hv_base.NO_CHECK, 372 constants.HV_VNET_HDR: hv_base.NO_CHECK, 373 } 374 375 _VIRTIO = "virtio" 376 _VIRTIO_NET_PCI = "virtio-net-pci" 377 _VIRTIO_BLK_PCI = "virtio-blk-pci" 378 379 _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)", 380 re.M | re.I) 381 _MIGRATION_PROGRESS_RE = \ 382 re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n" 383 r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n" 384 r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I) 385 386 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5 387 _MIGRATION_INFO_RETRY_DELAY = 2 388 389 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b") 390 391 _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I) 392 _CPU_INFO_CMD = "info cpus" 393 _CONT_CMD = "cont" 394 395 _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M) 396 _CHECK_MACHINE_VERSION_RE = \ 397 staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M)) 398 399 _QMP_RE = re.compile(r"^-qmp\s", re.M) 400 _SPICE_RE = re.compile(r"^-spice\s", re.M) 401 _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M) 402 _VIRTIO_NET_QUEUES_RE = re.compile(r"^-net\s.*,fds=x:y:...:z", re.M) 403 _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M) 404 _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M) 405 _NETDEV_RE = re.compile(r"^-netdev\s", re.M) 406 _DISPLAY_RE = re.compile(r"^-display\s", re.M) 407 _MACHINE_RE = re.compile(r"^-machine\s", re.M) 408 _VIRTIO_NET_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M) 409 _VIRTIO_BLK_RE = re.compile(r"^name \"%s\"" % _VIRTIO_BLK_PCI, re.M) 410 # match -drive.*boot=on|off on different lines, but in between accept only 411 # dashes not preceeded by a new line (which would mean another option 412 # different than -drive is starting) 413 _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S) 414 _UUID_RE = re.compile(r"^-uuid\s", re.M) 415 416 _INFO_VERSION_RE = \ 417 re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M) 418 _INFO_VERSION_CMD = "info version" 419 420 # Slot 0 for Host bridge, Slot 1 for ISA bridge, Slot 2 for VGA controller 421 _DEFAULT_PCI_RESERVATIONS = "11100000000000000000000000000000" 422 _SOUNDHW_WITH_PCI_SLOT = ["ac97", "es1370", "hda"] 423 424 ANCILLARY_FILES = [ 425 _KVM_NETWORK_SCRIPT, 426 ] 427 ANCILLARY_FILES_OPT = [ 428 _KVM_NETWORK_SCRIPT, 429 ] 430 431 # Supported kvm options to get output from 432 _KVMOPT_HELP = "help" 433 _KVMOPT_MLIST = "mlist" 434 _KVMOPT_DEVICELIST = "devicelist" 435 436 # Command to execute to get the output from kvm, and whether to 437 # accept the output even on failure. 438 _KVMOPTS_CMDS = { 439 _KVMOPT_HELP: (["--help"], False), 440 _KVMOPT_MLIST: (["-M", "?"], False), 441 _KVMOPT_DEVICELIST: (["-device", "?"], True), 442 } 443
444 - def __init__(self):
445 hv_base.BaseHypervisor.__init__(self) 446 # Let's make sure the directories we need exist, even if the RUN_DIR lives 447 # in a tmpfs filesystem or has been otherwise wiped out. 448 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS] 449 utils.EnsureDirs(dirs) 450 self.qmp = None
451 452 @staticmethod
453 - def VersionsSafeForMigration(src, target):
454 """Predict if migration is safe between those versions 455 456 """ 457 # Actually, it is not that easy. However, with the kvm machine_version 458 # feature, migration suceeds in most cases. So we try not to block 459 # legitimate migrations. 460 return True
461 462 @classmethod
463 - def _InstancePidFile(cls, instance_name):
464 """Returns the instance pidfile. 465 466 """ 467 return utils.PathJoin(cls._PIDS_DIR, instance_name)
468 469 @classmethod
470 - def _InstanceUidFile(cls, instance_name):
471 """Returns the instance uidfile. 472 473 """ 474 return utils.PathJoin(cls._UIDS_DIR, instance_name)
475 476 @classmethod
477 - def _InstancePidInfo(cls, pid):
478 """Check pid file for instance information. 479 480 Check that a pid file is associated with an instance, and retrieve 481 information from its command line. 482 483 @type pid: string or int 484 @param pid: process id of the instance to check 485 @rtype: tuple 486 @return: (instance_name, memory, vcpus) 487 @raise errors.HypervisorError: when an instance cannot be found 488 489 """ 490 alive = utils.IsProcessAlive(pid) 491 if not alive: 492 raise errors.HypervisorError("Cannot get info for pid %s" % pid) 493 494 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline") 495 try: 496 cmdline = utils.ReadFile(cmdline_file) 497 except EnvironmentError, err: 498 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" % 499 (pid, err)) 500 501 instance = None 502 memory = 0 503 vcpus = 0 504 505 arg_list = cmdline.split("\x00") 506 while arg_list: 507 arg = arg_list.pop(0) 508 if arg == "-name": 509 instance = arg_list.pop(0) 510 elif arg == "-m": 511 memory = int(arg_list.pop(0)) 512 elif arg == "-smp": 513 vcpus = int(arg_list.pop(0).split(",")[0]) 514 515 if instance is None: 516 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm" 517 " instance" % pid) 518 519 return (instance, memory, vcpus)
520 521 @classmethod
522 - def _InstancePidAlive(cls, instance_name):
523 """Returns the instance pidfile, pid, and liveness. 524 525 @type instance_name: string 526 @param instance_name: instance name 527 @rtype: tuple 528 @return: (pid file name, pid, liveness) 529 530 """ 531 pidfile = cls._InstancePidFile(instance_name) 532 pid = utils.ReadPidFile(pidfile) 533 534 alive = False 535 try: 536 cmd_instance = cls._InstancePidInfo(pid)[0] 537 alive = (cmd_instance == instance_name) 538 except errors.HypervisorError: 539 pass 540 541 return (pidfile, pid, alive)
542 543 @classmethod
544 - def _CheckDown(cls, instance_name):
545 """Raises an error unless the given instance is down. 546 547 """ 548 alive = cls._InstancePidAlive(instance_name)[2] 549 if alive: 550 raise errors.HypervisorError("Failed to start instance %s: %s" % 551 (instance_name, "already running"))
552 553 @classmethod
554 - def _InstanceMonitor(cls, instance_name):
555 """Returns the instance monitor socket name 556 557 """ 558 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
559 560 @classmethod
561 - def _InstanceSerial(cls, instance_name):
562 """Returns the instance serial socket name 563 564 """ 565 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
566 567 @classmethod
568 - def _InstanceQmpMonitor(cls, instance_name):
569 """Returns the instance serial QMP socket name 570 571 """ 572 return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
573 574 @classmethod
575 - def _InstanceKvmdMonitor(cls, instance_name):
576 """Returns the instance kvm daemon socket name 577 578 """ 579 return utils.PathJoin(cls._CTRL_DIR, "%s.kvmd" % instance_name)
580 581 @classmethod
582 - def _InstanceShutdownMonitor(cls, instance_name):
583 """Returns the instance QMP output filename 584 585 """ 586 return utils.PathJoin(cls._CTRL_DIR, "%s.shutdown" % instance_name)
587 588 @staticmethod
590 """Returns the correct parameters for socat 591 592 If we have a new-enough socat we can use raw mode with an escape character. 593 594 """ 595 if constants.SOCAT_USE_ESCAPE: 596 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE 597 else: 598 return "echo=0,icanon=0"
599 600 @classmethod
601 - def _InstanceKVMRuntime(cls, instance_name):
602 """Returns the instance KVM runtime filename 603 604 """ 605 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
606 607 @classmethod
608 - def _InstanceChrootDir(cls, instance_name):
609 """Returns the name of the KVM chroot dir of the instance 610 611 """ 612 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
613 614 @classmethod
615 - def _InstanceNICDir(cls, instance_name):
616 """Returns the name of the directory holding the tap device files for a 617 given instance. 618 619 """ 620 return utils.PathJoin(cls._NICS_DIR, instance_name)
621 622 @classmethod
623 - def _InstanceNICFile(cls, instance_name, seq):
624 """Returns the name of the file containing the tap device for a given NIC 625 626 """ 627 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
628 629 @classmethod
630 - def _InstanceKeymapFile(cls, instance_name):
631 """Returns the name of the file containing the keymap for a given instance 632 633 """ 634 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
635 636 @classmethod
637 - def _TryReadUidFile(cls, uid_file):
638 """Try to read a uid file 639 640 """ 641 if os.path.exists(uid_file): 642 try: 643 uid = int(utils.ReadOneLineFile(uid_file)) 644 return uid 645 except EnvironmentError: 646 logging.warning("Can't read uid file", exc_info=True) 647 except (TypeError, ValueError): 648 logging.warning("Can't parse uid file contents", exc_info=True) 649 return None
650 651 @classmethod
652 - def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
653 """Removes an instance's rutime sockets/files/dirs. 654 655 """ 656 utils.RemoveFile(pidfile) 657 utils.RemoveFile(cls._InstanceMonitor(instance_name)) 658 utils.RemoveFile(cls._InstanceSerial(instance_name)) 659 utils.RemoveFile(cls._InstanceQmpMonitor(instance_name)) 660 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name)) 661 utils.RemoveFile(cls._InstanceKeymapFile(instance_name)) 662 uid_file = cls._InstanceUidFile(instance_name) 663 uid = cls._TryReadUidFile(uid_file) 664 utils.RemoveFile(uid_file) 665 if uid is not None: 666 uidpool.ReleaseUid(uid) 667 try: 668 shutil.rmtree(cls._InstanceNICDir(instance_name)) 669 except OSError, err: 670 if err.errno != errno.ENOENT: 671 raise 672 try: 673 chroot_dir = cls._InstanceChrootDir(instance_name) 674 utils.RemoveDir(chroot_dir) 675 except OSError, err: 676 if err.errno == errno.ENOTEMPTY: 677 # The chroot directory is expected to be empty, but it isn't. 678 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR, 679 prefix="%s-%s-" % 680 (instance_name, 681 utils.TimestampForFilename())) 682 logging.warning("The chroot directory of instance %s can not be" 683 " removed as it is not empty. Moving it to the" 684 " quarantine instead. Please investigate the" 685 " contents (%s) and clean up manually", 686 instance_name, new_chroot_dir) 687 utils.RenameFile(chroot_dir, new_chroot_dir) 688 else: 689 raise
690 691 @staticmethod
692 - def _ConfigureNIC(instance, seq, nic, tap):
693 """Run the network configuration script for a specified NIC 694 695 See L{hv_base.ConfigureNIC}. 696 697 @param instance: instance we're acting on 698 @type instance: instance object 699 @param seq: nic sequence number 700 @type seq: int 701 @param nic: nic we're acting on 702 @type nic: nic object 703 @param tap: the host's tap interface this NIC corresponds to 704 @type tap: str 705 706 """ 707 hv_base.ConfigureNIC([pathutils.KVM_IFUP, tap], instance, seq, nic, tap)
708 709 @classmethod
710 - def _SetProcessAffinity(cls, process_id, cpus):
711 """Sets the affinity of a process to the given CPUs. 712 713 @type process_id: int 714 @type cpus: list of int 715 @param cpus: The list of CPUs the process ID may use. 716 717 """ 718 if psutil is None: 719 raise errors.HypervisorError("psutil Python package not" 720 " found; cannot use CPU pinning under KVM") 721 722 target_process = psutil.Process(process_id) 723 if cpus == constants.CPU_PINNING_OFF: 724 target_process.set_cpu_affinity(range(psutil.cpu_count())) 725 else: 726 target_process.set_cpu_affinity(cpus)
727 728 @classmethod
729 - def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
730 """Change CPU affinity for running VM according to given CPU mask. 731 732 @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3" 733 @type cpu_mask: string 734 @param process_id: process ID of KVM process. Used to pin entire VM 735 to physical CPUs. 736 @type process_id: int 737 @param thread_dict: map of virtual CPUs to KVM thread IDs 738 @type thread_dict: dict int:int 739 740 """ 741 # Convert the string CPU mask to a list of list of int's 742 cpu_list = utils.ParseMultiCpuMask(cpu_mask) 743 744 if len(cpu_list) == 1: 745 all_cpu_mapping = cpu_list[0] 746 if all_cpu_mapping == constants.CPU_PINNING_OFF: 747 # If CPU pinning has 1 entry that's "all", then do nothing 748 pass 749 else: 750 # If CPU pinning has one non-all entry, map the entire VM to 751 # one set of physical CPUs 752 cls._SetProcessAffinity(process_id, all_cpu_mapping) 753 else: 754 # The number of vCPUs mapped should match the number of vCPUs 755 # reported by KVM. This was already verified earlier, so 756 # here only as a sanity check. 757 assert len(thread_dict) == len(cpu_list) 758 759 # For each vCPU, map it to the proper list of physical CPUs 760 for i, vcpu in enumerate(cpu_list): 761 cls._SetProcessAffinity(thread_dict[i], vcpu)
762
763 - def _GetVcpuThreadIds(self, instance_name):
764 """Get a mapping of vCPU no. to thread IDs for the instance 765 766 @type instance_name: string 767 @param instance_name: instance in question 768 @rtype: dictionary of int:int 769 @return: a dictionary mapping vCPU numbers to thread IDs 770 771 """ 772 result = {} 773 output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD) 774 for line in output.stdout.splitlines(): 775 match = self._CPU_INFO_RE.search(line) 776 if not match: 777 continue 778 grp = map(int, match.groups()) 779 result[grp[0]] = grp[1] 780 781 return result
782
783 - def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
784 """Complete CPU pinning. 785 786 @type instance_name: string 787 @param instance_name: name of instance 788 @type cpu_mask: string 789 @param cpu_mask: CPU pinning mask as entered by user 790 791 """ 792 # Get KVM process ID, to be used if need to pin entire VM 793 _, pid, _ = self._InstancePidAlive(instance_name) 794 # Get vCPU thread IDs, to be used if need to pin vCPUs separately 795 thread_dict = self._GetVcpuThreadIds(instance_name) 796 # Run CPU pinning, based on configured mask 797 self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
798
799 - def ListInstances(self, hvparams=None):
800 """Get the list of running instances. 801 802 We can do this by listing our live instances directory and 803 checking whether the associated kvm process is still alive. 804 805 """ 806 result = [] 807 for name in os.listdir(self._PIDS_DIR): 808 if self._InstancePidAlive(name)[2]: 809 result.append(name) 810 return result
811 812 @classmethod
813 - def _IsUserShutdown(cls, instance_name):
814 return os.path.exists(cls._InstanceShutdownMonitor(instance_name))
815 816 @classmethod
817 - def _ClearUserShutdown(cls, instance_name):
818 utils.RemoveFile(cls._InstanceShutdownMonitor(instance_name))
819
820 - def GetInstanceInfo(self, instance_name, hvparams=None):
821 """Get instance properties. 822 823 @type instance_name: string 824 @param instance_name: the instance name 825 @type hvparams: dict of strings 826 @param hvparams: hypervisor parameters to be used with this instance 827 @rtype: tuple of strings 828 @return: (name, id, memory, vcpus, stat, times) 829 830 """ 831 _, pid, alive = self._InstancePidAlive(instance_name) 832 if not alive: 833 if self._IsUserShutdown(instance_name): 834 return (instance_name, -1, 0, 0, hv_base.HvInstanceState.SHUTDOWN, 0) 835 else: 836 return None 837 838 _, memory, vcpus = self._InstancePidInfo(pid) 839 istat = hv_base.HvInstanceState.RUNNING 840 times = 0 841 842 try: 843 qmp = QmpConnection(self._InstanceQmpMonitor(instance_name)) 844 qmp.connect() 845 vcpus = len(qmp.Execute("query-cpus")) 846 # Will fail if ballooning is not enabled, but we can then just resort to 847 # the value above. 848 mem_bytes = qmp.Execute("query-balloon")[qmp.ACTUAL_KEY] 849 memory = mem_bytes / 1048576 850 except errors.HypervisorError: 851 pass 852 853 return (instance_name, pid, memory, vcpus, istat, times)
854
855 - def GetAllInstancesInfo(self, hvparams=None):
856 """Get properties of all instances. 857 858 @type hvparams: dict of strings 859 @param hvparams: hypervisor parameters 860 @return: list of tuples (name, id, memory, vcpus, stat, times) 861 862 """ 863 data = [] 864 for name in os.listdir(self._PIDS_DIR): 865 try: 866 info = self.GetInstanceInfo(name) 867 except errors.HypervisorError: 868 # Ignore exceptions due to instances being shut down 869 continue 870 if info: 871 data.append(info) 872 return data
873
874 - def _GenerateKVMBlockDevicesOptions(self, instance, up_hvp, kvm_disks, 875 kvmhelp, devlist):
876 """Generate KVM options regarding instance's block devices. 877 878 @type instance: L{objects.Instance} 879 @param instance: the instance object 880 @type up_hvp: dict 881 @param up_hvp: the instance's runtime hypervisor parameters 882 @type kvm_disks: list of tuples 883 @param kvm_disks: list of tuples [(disk, link_name, uri)..] 884 @type kvmhelp: string 885 @param kvmhelp: output of kvm --help 886 @type devlist: string 887 @param devlist: output of kvm -device ? 888 @rtype: list 889 @return: list of command line options eventually used by kvm executable 890 891 """ 892 kernel_path = up_hvp[constants.HV_KERNEL_PATH] 893 if kernel_path: 894 boot_disk = False 895 else: 896 boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK 897 898 # whether this is an older KVM version that uses the boot=on flag 899 # on devices 900 needs_boot_flag = self._BOOT_RE.search(kvmhelp) 901 902 dev_opts = [] 903 device_driver = None 904 disk_type = up_hvp[constants.HV_DISK_TYPE] 905 if disk_type == constants.HT_DISK_PARAVIRTUAL: 906 if_val = ",if=%s" % self._VIRTIO 907 try: 908 if self._VIRTIO_BLK_RE.search(devlist): 909 if_val = ",if=none" 910 # will be passed in -device option as driver 911 device_driver = self._VIRTIO_BLK_PCI 912 except errors.HypervisorError, _: 913 pass 914 else: 915 if_val = ",if=%s" % disk_type 916 # AIO mode 917 aio_mode = up_hvp[constants.HV_KVM_DISK_AIO] 918 if aio_mode == constants.HT_KVM_AIO_NATIVE: 919 aio_val = ",aio=%s" % aio_mode 920 else: 921 aio_val = "" 922 # Cache mode 923 disk_cache = up_hvp[constants.HV_DISK_CACHE] 924 if instance.disk_template in constants.DTS_EXT_MIRROR: 925 if disk_cache != "none": 926 # TODO: make this a hard error, instead of a silent overwrite 927 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'" 928 " to prevent shared storage corruption on migration", 929 disk_cache) 930 cache_val = ",cache=none" 931 elif disk_cache != constants.HT_CACHE_DEFAULT: 932 cache_val = ",cache=%s" % disk_cache 933 else: 934 cache_val = "" 935 for cfdev, link_name, uri in kvm_disks: 936 if cfdev.mode != constants.DISK_RDWR: 937 raise errors.HypervisorError("Instance has read-only disks which" 938 " are not supported by KVM") 939 # TODO: handle FD_LOOP and FD_BLKTAP (?) 940 boot_val = "" 941 if boot_disk: 942 dev_opts.extend(["-boot", "c"]) 943 boot_disk = False 944 if needs_boot_flag and disk_type != constants.HT_DISK_IDE: 945 boot_val = ",boot=on" 946 947 drive_uri = _GetDriveURI(cfdev, link_name, uri) 948 949 drive_val = "file=%s,format=raw%s%s%s%s" % \ 950 (drive_uri, if_val, boot_val, cache_val, aio_val) 951 952 if device_driver: 953 # kvm_disks are the 4th entry of runtime file that did not exist in 954 # the past. That means that cfdev should always have pci slot and 955 # _GenerateDeviceKVMId() will not raise a exception. 956 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK, cfdev) 957 drive_val += (",id=%s" % kvm_devid) 958 drive_val += (",bus=0,unit=%d" % cfdev.pci) 959 dev_val = ("%s,drive=%s,id=%s" % 960 (device_driver, kvm_devid, kvm_devid)) 961 dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci) 962 dev_opts.extend(["-device", dev_val]) 963 964 dev_opts.extend(["-drive", drive_val]) 965 966 return dev_opts
967 968 @staticmethod
969 - def _CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image, cdrom_boot, 970 needs_boot_flag):
971 """Extends L{kvm_cmd} with the '-drive' option for a cdrom, and 972 optionally the '-boot' option. 973 974 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide -boot d 975 976 Example: -drive file=cdrom.iso,media=cdrom,format=raw,if=ide,boot=on 977 978 Example: -drive file=http://hostname.com/cdrom.iso,media=cdrom 979 980 @type kvm_cmd: string 981 @param kvm_cmd: KVM command line 982 983 @type cdrom_disk_type: 984 @param cdrom_disk_type: 985 986 @type cdrom_image: 987 @param cdrom_image: 988 989 @type cdrom_boot: 990 @param cdrom_boot: 991 992 @type needs_boot_flag: 993 @param needs_boot_flag: 994 995 """ 996 # Check that the ISO image is accessible 997 # See https://bugs.launchpad.net/qemu/+bug/597575 998 if utils.IsUrl(cdrom_image) and not _CheckUrl(cdrom_image): 999 raise errors.HypervisorError("Cdrom ISO image '%s' is not accessible" % 1000 cdrom_image) 1001 1002 # set cdrom 'media' and 'format', if needed 1003 if utils.IsUrl(cdrom_image): 1004 options = ",media=cdrom" 1005 else: 1006 options = ",media=cdrom,format=raw" 1007 1008 # set cdrom 'if' type 1009 if cdrom_boot: 1010 if_val = ",if=" + constants.HT_DISK_IDE 1011 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL: 1012 if_val = ",if=virtio" 1013 else: 1014 if_val = ",if=" + cdrom_disk_type 1015 1016 # set boot flag, if needed 1017 boot_val = "" 1018 if cdrom_boot: 1019 kvm_cmd.extend(["-boot", "d"]) 1020 1021 # whether this is an older KVM version that requires the 'boot=on' flag 1022 # on devices 1023 if needs_boot_flag: 1024 boot_val = ",boot=on" 1025 1026 # build '-drive' option 1027 drive_val = "file=%s%s%s%s" % (cdrom_image, options, if_val, boot_val) 1028 kvm_cmd.extend(["-drive", drive_val])
1029
1030 - def _GenerateKVMRuntime(self, instance, block_devices, startup_paused, 1031 kvmhelp):
1032 """Generate KVM information to start an instance. 1033 1034 @type kvmhelp: string 1035 @param kvmhelp: output of kvm --help 1036 @attention: this function must not have any side-effects; for 1037 example, it must not write to the filesystem, or read values 1038 from the current system the are expected to differ between 1039 nodes, since it is only run once at instance startup; 1040 actions/kvm arguments that can vary between systems should be 1041 done in L{_ExecuteKVMRuntime} 1042 1043 """ 1044 # pylint: disable=R0912,R0914,R0915 1045 hvp = instance.hvparams 1046 self.ValidateParameters(hvp) 1047 1048 pidfile = self._InstancePidFile(instance.name) 1049 kvm = hvp[constants.HV_KVM_PATH] 1050 kvm_cmd = [kvm] 1051 # used just by the vnc server, if enabled 1052 kvm_cmd.extend(["-name", instance.name]) 1053 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]]) 1054 1055 smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]] 1056 if hvp[constants.HV_CPU_CORES]: 1057 smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES]) 1058 if hvp[constants.HV_CPU_THREADS]: 1059 smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS]) 1060 if hvp[constants.HV_CPU_SOCKETS]: 1061 smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS]) 1062 1063 kvm_cmd.extend(["-smp", ",".join(smp_list)]) 1064 1065 kvm_cmd.extend(["-pidfile", pidfile]) 1066 1067 pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS) 1068 1069 # As requested by music lovers 1070 if hvp[constants.HV_SOUNDHW]: 1071 soundhw = hvp[constants.HV_SOUNDHW] 1072 # For some reason only few sound devices require a PCI slot 1073 # while the Audio controller *must* be in slot 3. 1074 # That's why we bridge this option early in command line 1075 if soundhw in self._SOUNDHW_WITH_PCI_SLOT: 1076 _ = utils.GetFreeSlot(pci_reservations, reserve=True) 1077 kvm_cmd.extend(["-soundhw", soundhw]) 1078 1079 if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI: 1080 # The SCSI controller requires another PCI slot. 1081 _ = utils.GetFreeSlot(pci_reservations, reserve=True) 1082 1083 # Add id to ballon and place to the first available slot (3 or 4) 1084 addr = utils.GetFreeSlot(pci_reservations, reserve=True) 1085 pci_info = ",bus=pci.0,addr=%s" % hex(addr) 1086 kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info]) 1087 kvm_cmd.extend(["-daemonize"]) 1088 if not instance.hvparams[constants.HV_ACPI]: 1089 kvm_cmd.extend(["-no-acpi"]) 1090 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \ 1091 constants.INSTANCE_REBOOT_EXIT: 1092 kvm_cmd.extend(["-no-reboot"]) 1093 1094 mversion = hvp[constants.HV_KVM_MACHINE_VERSION] 1095 if not mversion: 1096 mversion = self._GetDefaultMachineVersion(kvm) 1097 if self._MACHINE_RE.search(kvmhelp): 1098 # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as 1099 # extra hypervisor parameters. We should also investigate whether and how 1100 # shadow_mem should be considered for the resource model. 1101 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED): 1102 specprop = ",accel=kvm" 1103 else: 1104 specprop = "" 1105 machinespec = "%s%s" % (mversion, specprop) 1106 kvm_cmd.extend(["-machine", machinespec]) 1107 else: 1108 kvm_cmd.extend(["-M", mversion]) 1109 if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and 1110 self._ENABLE_KVM_RE.search(kvmhelp)): 1111 kvm_cmd.extend(["-enable-kvm"]) 1112 elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and 1113 self._DISABLE_KVM_RE.search(kvmhelp)): 1114 kvm_cmd.extend(["-disable-kvm"]) 1115 1116 kernel_path = hvp[constants.HV_KERNEL_PATH] 1117 if kernel_path: 1118 boot_cdrom = boot_floppy = boot_network = False 1119 else: 1120 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM 1121 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY 1122 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK 1123 1124 if startup_paused: 1125 kvm_cmd.extend([_KVM_START_PAUSED_FLAG]) 1126 1127 if boot_network: 1128 kvm_cmd.extend(["-boot", "n"]) 1129 1130 disk_type = hvp[constants.HV_DISK_TYPE] 1131 1132 # Now we can specify a different device type for CDROM devices. 1133 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE] 1134 if not cdrom_disk_type: 1135 cdrom_disk_type = disk_type 1136 1137 cdrom_image1 = hvp[constants.HV_CDROM_IMAGE_PATH] 1138 if cdrom_image1: 1139 needs_boot_flag = self._BOOT_RE.search(kvmhelp) 1140 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image1, boot_cdrom, 1141 needs_boot_flag) 1142 1143 cdrom_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH] 1144 if cdrom_image2: 1145 self._CdromOption(kvm_cmd, cdrom_disk_type, cdrom_image2, False, False) 1146 1147 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH] 1148 if floppy_image: 1149 options = ",format=raw,media=disk" 1150 if boot_floppy: 1151 kvm_cmd.extend(["-boot", "a"]) 1152 options = "%s,boot=on" % options 1153 if_val = ",if=floppy" 1154 options = "%s%s" % (options, if_val) 1155 drive_val = "file=%s%s" % (floppy_image, options) 1156 kvm_cmd.extend(["-drive", drive_val]) 1157 1158 if kernel_path: 1159 kvm_cmd.extend(["-kernel", kernel_path]) 1160 initrd_path = hvp[constants.HV_INITRD_PATH] 1161 if initrd_path: 1162 kvm_cmd.extend(["-initrd", initrd_path]) 1163 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH], 1164 hvp[constants.HV_KERNEL_ARGS]] 1165 if hvp[constants.HV_SERIAL_CONSOLE]: 1166 serial_speed = hvp[constants.HV_SERIAL_SPEED] 1167 root_append.append("console=ttyS0,%s" % serial_speed) 1168 kvm_cmd.extend(["-append", " ".join(root_append)]) 1169 1170 mem_path = hvp[constants.HV_MEM_PATH] 1171 if mem_path: 1172 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"]) 1173 1174 monitor_dev = ("unix:%s,server,nowait" % 1175 self._InstanceMonitor(instance.name)) 1176 kvm_cmd.extend(["-monitor", monitor_dev]) 1177 if hvp[constants.HV_SERIAL_CONSOLE]: 1178 serial_dev = ("unix:%s,server,nowait" % 1179 self._InstanceSerial(instance.name)) 1180 kvm_cmd.extend(["-serial", serial_dev]) 1181 else: 1182 kvm_cmd.extend(["-serial", "none"]) 1183 1184 mouse_type = hvp[constants.HV_USB_MOUSE] 1185 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS] 1186 spice_bind = hvp[constants.HV_KVM_SPICE_BIND] 1187 spice_ip_version = None 1188 1189 kvm_cmd.extend(["-usb"]) 1190 1191 if mouse_type: 1192 kvm_cmd.extend(["-usbdevice", mouse_type]) 1193 elif vnc_bind_address: 1194 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET]) 1195 1196 if vnc_bind_address: 1197 if netutils.IsValidInterface(vnc_bind_address): 1198 if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address) 1199 if_ip4_addresses = if_addresses[constants.IP4_VERSION] 1200 if len(if_ip4_addresses) < 1: 1201 logging.error("Could not determine IPv4 address of interface %s", 1202 vnc_bind_address) 1203 else: 1204 vnc_bind_address = if_ip4_addresses[0] 1205 if netutils.IP4Address.IsValid(vnc_bind_address): 1206 if instance.network_port > constants.VNC_BASE_PORT: 1207 display = instance.network_port - constants.VNC_BASE_PORT 1208 if vnc_bind_address == constants.IP4_ADDRESS_ANY: 1209 vnc_arg = ":%d" % (display) 1210 else: 1211 vnc_arg = "%s:%d" % (vnc_bind_address, display) 1212 else: 1213 logging.error("Network port is not a valid VNC display (%d < %d)," 1214 " not starting VNC", 1215 instance.network_port, constants.VNC_BASE_PORT) 1216 vnc_arg = "none" 1217 1218 # Only allow tls and other option when not binding to a file, for now. 1219 # kvm/qemu gets confused otherwise about the filename to use. 1220 vnc_append = "" 1221 if hvp[constants.HV_VNC_TLS]: 1222 vnc_append = "%s,tls" % vnc_append 1223 if hvp[constants.HV_VNC_X509_VERIFY]: 1224 vnc_append = "%s,x509verify=%s" % (vnc_append, 1225 hvp[constants.HV_VNC_X509]) 1226 elif hvp[constants.HV_VNC_X509]: 1227 vnc_append = "%s,x509=%s" % (vnc_append, 1228 hvp[constants.HV_VNC_X509]) 1229 if hvp[constants.HV_VNC_PASSWORD_FILE]: 1230 vnc_append = "%s,password" % vnc_append 1231 1232 vnc_arg = "%s%s" % (vnc_arg, vnc_append) 1233 1234 else: 1235 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name) 1236 1237 kvm_cmd.extend(["-vnc", vnc_arg]) 1238 elif spice_bind: 1239 # FIXME: this is wrong here; the iface ip address differs 1240 # between systems, so it should be done in _ExecuteKVMRuntime 1241 if netutils.IsValidInterface(spice_bind): 1242 # The user specified a network interface, we have to figure out the IP 1243 # address. 1244 addresses = netutils.GetInterfaceIpAddresses(spice_bind) 1245 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION] 1246 1247 # if the user specified an IP version and the interface does not 1248 # have that kind of IP addresses, throw an exception 1249 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED: 1250 if not addresses[spice_ip_version]: 1251 raise errors.HypervisorError("SPICE: Unable to get an IPv%s address" 1252 " for %s" % (spice_ip_version, 1253 spice_bind)) 1254 1255 # the user did not specify an IP version, we have to figure it out 1256 elif (addresses[constants.IP4_VERSION] and 1257 addresses[constants.IP6_VERSION]): 1258 # we have both ipv4 and ipv6, let's use the cluster default IP 1259 # version 1260 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily() 1261 spice_ip_version = \ 1262 netutils.IPAddress.GetVersionFromAddressFamily(cluster_family) 1263 elif addresses[constants.IP4_VERSION]: 1264 spice_ip_version = constants.IP4_VERSION 1265 elif addresses[constants.IP6_VERSION]: 1266 spice_ip_version = constants.IP6_VERSION 1267 else: 1268 raise errors.HypervisorError("SPICE: Unable to get an IP address" 1269 " for %s" % (spice_bind)) 1270 1271 spice_address = addresses[spice_ip_version][0] 1272 1273 else: 1274 # spice_bind is known to be a valid IP address, because 1275 # ValidateParameters checked it. 1276 spice_address = spice_bind 1277 1278 spice_arg = "addr=%s" % spice_address 1279 if hvp[constants.HV_KVM_SPICE_USE_TLS]: 1280 spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" % 1281 (spice_arg, instance.network_port, 1282 pathutils.SPICE_CACERT_FILE)) 1283 spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" % 1284 (spice_arg, pathutils.SPICE_CERT_FILE, 1285 pathutils.SPICE_CERT_FILE)) 1286 tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS] 1287 if tls_ciphers: 1288 spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers) 1289 else: 1290 spice_arg = "%s,port=%s" % (spice_arg, instance.network_port) 1291 1292 if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]: 1293 spice_arg = "%s,disable-ticketing" % spice_arg 1294 1295 if spice_ip_version: 1296 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version) 1297 1298 # Image compression options 1299 img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR] 1300 img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR] 1301 img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR] 1302 if img_lossless: 1303 spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless) 1304 if img_jpeg: 1305 spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg) 1306 if img_zlib_glz: 1307 spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz) 1308 1309 # Video stream detection 1310 video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION] 1311 if video_streaming: 1312 spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming) 1313 1314 # Audio compression, by default in qemu-kvm it is on 1315 if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]: 1316 spice_arg = "%s,playback-compression=off" % spice_arg 1317 if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]: 1318 spice_arg = "%s,agent-mouse=off" % spice_arg 1319 else: 1320 # Enable the spice agent communication channel between the host and the 1321 # agent. 1322 addr = utils.GetFreeSlot(pci_reservations, reserve=True) 1323 pci_info = ",bus=pci.0,addr=%s" % hex(addr) 1324 kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info]) 1325 kvm_cmd.extend([ 1326 "-device", 1327 "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0", 1328 ]) 1329 kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"]) 1330 1331 logging.info("KVM: SPICE will listen on port %s", instance.network_port) 1332 kvm_cmd.extend(["-spice", spice_arg]) 1333 1334 else: 1335 # From qemu 1.4 -nographic is incompatible with -daemonize. The new way 1336 # also works in earlier versions though (tested with 1.1 and 1.3) 1337 if self._DISPLAY_RE.search(kvmhelp): 1338 kvm_cmd.extend(["-display", "none"]) 1339 else: 1340 kvm_cmd.extend(["-nographic"]) 1341 1342 if hvp[constants.HV_USE_LOCALTIME]: 1343 kvm_cmd.extend(["-localtime"]) 1344 1345 if hvp[constants.HV_KVM_USE_CHROOT]: 1346 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)]) 1347 1348 # Add qemu-KVM -cpu param 1349 if hvp[constants.HV_CPU_TYPE]: 1350 kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]]) 1351 1352 # Pass a -vga option if requested, or if spice is used, for backwards 1353 # compatibility. 1354 if hvp[constants.HV_VGA]: 1355 kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]]) 1356 elif spice_bind: 1357 kvm_cmd.extend(["-vga", "qxl"]) 1358 1359 # Various types of usb devices, comma separated 1360 if hvp[constants.HV_USB_DEVICES]: 1361 for dev in hvp[constants.HV_USB_DEVICES].split(","): 1362 kvm_cmd.extend(["-usbdevice", dev]) 1363 1364 # Set system UUID to instance UUID 1365 if self._UUID_RE.search(kvmhelp): 1366 kvm_cmd.extend(["-uuid", instance.uuid]) 1367 1368 if hvp[constants.HV_KVM_EXTRA]: 1369 kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" ")) 1370 1371 kvm_disks = [] 1372 for disk, link_name, uri in block_devices: 1373 disk.pci = utils.GetFreeSlot(pci_reservations, disk.pci, True) 1374 kvm_disks.append((disk, link_name, uri)) 1375 1376 kvm_nics = [] 1377 for nic in instance.nics: 1378 nic.pci = utils.GetFreeSlot(pci_reservations, nic.pci, True) 1379 kvm_nics.append(nic) 1380 1381 hvparams = hvp 1382 1383 return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1384
1385 - def _WriteKVMRuntime(self, instance_name, data):
1386 """Write an instance's KVM runtime 1387 1388 """ 1389 try: 1390 utils.WriteFile(self._InstanceKVMRuntime(instance_name), 1391 data=data) 1392 except EnvironmentError, err: 1393 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1394
1395 - def _ReadKVMRuntime(self, instance_name):
1396 """Read an instance's KVM runtime 1397 1398 """ 1399 try: 1400 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name)) 1401 except EnvironmentError, err: 1402 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err) 1403 return file_content
1404
1405 - def _SaveKVMRuntime(self, instance, kvm_runtime):
1406 """Save an instance's KVM runtime 1407 1408 """ 1409 kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime 1410 1411 serialized_nics = [nic.ToDict() for nic in kvm_nics] 1412 serialized_disks = [(blk.ToDict(), link, uri) 1413 for blk, link, uri in kvm_disks] 1414 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams, 1415 serialized_disks)) 1416 1417 self._WriteKVMRuntime(instance.name, serialized_form)
1418
1419 - def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1420 """Load an instance's KVM runtime 1421 1422 """ 1423 if not serialized_runtime: 1424 serialized_runtime = self._ReadKVMRuntime(instance.name) 1425 1426 return _AnalyzeSerializedRuntime(serialized_runtime)
1427
1428 - def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1429 """Run the KVM cmd and check for errors 1430 1431 @type name: string 1432 @param name: instance name 1433 @type kvm_cmd: list of strings 1434 @param kvm_cmd: runcmd input for kvm 1435 @type tap_fds: list of int 1436 @param tap_fds: fds of tap devices opened by Ganeti 1437 1438 """ 1439 try: 1440 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds) 1441 finally: 1442 for fd in tap_fds: 1443 utils_wrapper.CloseFdNoError(fd) 1444 1445 if result.failed: 1446 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" % 1447 (name, result.fail_reason, result.output)) 1448 if not self._InstancePidAlive(name)[2]: 1449 raise errors.HypervisorError("Failed to start instance %s" % name)
1450 1451 @staticmethod
1452 - def _GenerateKvmTapName(nic):
1453 """Generate a TAP network interface name for a NIC. 1454 1455 See L{hv_base.GenerateTapName}. 1456 1457 For the case of the empty string, see L{OpenTap} 1458 1459 @type nic: ganeti.objects.NIC 1460 @param nic: NIC object for the name should be generated 1461 1462 @rtype: string 1463 @return: TAP network interface name, or the empty string if the 1464 NIC is not used in instance communication 1465 1466 """ 1467 if nic.name is None or not \ 1468 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX): 1469 return "" 1470 1471 return hv_base.GenerateTapName()
1472
1473 - def _GetNetworkDeviceFeatures(self, up_hvp, devlist, kvmhelp):
1474 """Get network device options to properly enable supported features. 1475 1476 Return a dict of supported and enabled tap features with nic_model along 1477 with the extra strings to be appended to the --netdev and --device options. 1478 This function is called before opening a new tap device. 1479 1480 Currently the features_dict includes the following attributes: 1481 - vhost (boolean) 1482 - vnet_hdr (boolean) 1483 - mq (boolean, int) 1484 1485 @rtype: (dict, str, str) tuple 1486 @return: The supported features, 1487 the string to be appended to the --netdev option, 1488 the string to be appended to the --device option 1489 1490 """ 1491 nic_type = up_hvp[constants.HV_NIC_TYPE] 1492 nic_extra_str = "" 1493 tap_extra_str = "" 1494 features = { 1495 "vhost": False, 1496 "vnet_hdr": False, 1497 "mq": (False, 1) 1498 } 1499 update_features = {} 1500 if nic_type == constants.HT_NIC_PARAVIRTUAL: 1501 nic_model = self._VIRTIO 1502 try: 1503 if self._VIRTIO_NET_RE.search(devlist): 1504 nic_model = self._VIRTIO_NET_PCI 1505 update_features["vnet_hdr"] = up_hvp[constants.HV_VNET_HDR] 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 update_features["vhost"] = True 1515 tap_extra_str = ",vhost=on" 1516 else: 1517 raise errors.HypervisorError("vhost_net is configured" 1518 " but it is not available") 1519 virtio_net_queues = up_hvp.get(constants.HV_VIRTIO_NET_QUEUES, 1) 1520 if virtio_net_queues > 1: 1521 # Check for multiqueue virtio-net support. 1522 if self._VIRTIO_NET_QUEUES_RE.search(kvmhelp): 1523 # As advised at http://www.linux-kvm.org/page/Multiqueue formula 1524 # for calculating vector size is: vectors=2*N+1 where N is the 1525 # number of queues (HV_VIRTIO_NET_QUEUES). 1526 nic_extra_str = ",mq=on,vectors=%d" % (2 * virtio_net_queues + 1) 1527 update_features["mq"] = (True, virtio_net_queues) 1528 else: 1529 raise errors.HypervisorError("virtio_net_queues is configured" 1530 " but it is not available") 1531 else: 1532 nic_model = nic_type 1533 1534 update_features["driver"] = nic_model 1535 features.update(update_features) 1536 1537 return features, tap_extra_str, nic_extra_str
1538 1539 # too many local variables 1540 # pylint: disable=R0914
1541 - def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1542 """Execute a KVM cmd, after completing it with some last minute data. 1543 1544 @type incoming: tuple of strings 1545 @param incoming: (target_host_ip, port) 1546 @type kvmhelp: string 1547 @param kvmhelp: output of kvm --help 1548 1549 """ 1550 # Small _ExecuteKVMRuntime hv parameters programming howto: 1551 # - conf_hvp contains the parameters as configured on ganeti. they might 1552 # have changed since the instance started; only use them if the change 1553 # won't affect the inside of the instance (which hasn't been rebooted). 1554 # - up_hvp contains the parameters as they were when the instance was 1555 # started, plus any new parameter which has been added between ganeti 1556 # versions: it is paramount that those default to a value which won't 1557 # affect the inside of the instance as well. 1558 conf_hvp = instance.hvparams 1559 name = instance.name 1560 self._CheckDown(name) 1561 1562 self._ClearUserShutdown(instance.name) 1563 self._StartKvmd(instance.hvparams) 1564 1565 temp_files = [] 1566 1567 kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime 1568 # the first element of kvm_cmd is always the path to the kvm binary 1569 kvm_path = kvm_cmd[0] 1570 up_hvp = objects.FillDict(conf_hvp, up_hvp) 1571 1572 # We know it's safe to run as a different user upon migration, so we'll use 1573 # the latest conf, from conf_hvp. 1574 security_model = conf_hvp[constants.HV_SECURITY_MODEL] 1575 if security_model == constants.HT_SM_USER: 1576 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]]) 1577 1578 keymap = conf_hvp[constants.HV_KEYMAP] 1579 if keymap: 1580 keymap_path = self._InstanceKeymapFile(name) 1581 # If a keymap file is specified, KVM won't use its internal defaults. By 1582 # first including the "en-us" layout, an error on loading the actual 1583 # layout (e.g. because it can't be found) won't lead to a non-functional 1584 # keyboard. A keyboard with incorrect keys is still better than none. 1585 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap) 1586 kvm_cmd.extend(["-k", keymap_path]) 1587 1588 # We have reasons to believe changing something like the nic driver/type 1589 # upon migration won't exactly fly with the instance kernel, so for nic 1590 # related parameters we'll use up_hvp 1591 tapfds = [] 1592 taps = [] 1593 devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST) 1594 if not kvm_nics: 1595 kvm_cmd.extend(["-net", "none"]) 1596 else: 1597 features, tap_extra, nic_extra = \ 1598 self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp) 1599 nic_model = features["driver"] 1600 kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp) 1601 for nic_seq, nic in enumerate(kvm_nics): 1602 tapname, nic_tapfds, nic_vhostfds = \ 1603 OpenTap(features=features, name=self._GenerateKvmTapName(nic)) 1604 1605 tapfds.extend(nic_tapfds) 1606 tapfds.extend(nic_vhostfds) 1607 taps.append(tapname) 1608 tapfd = "%s%s" % ("fds=" if len(nic_tapfds) > 1 else "fd=", 1609 ":".join(str(fd) for fd in nic_tapfds)) 1610 1611 if nic_vhostfds: 1612 vhostfd = "%s%s" % (",vhostfds=" 1613 if len(nic_vhostfds) > 1 else ",vhostfd=", 1614 ":".join(str(fd) for fd in nic_vhostfds)) 1615 else: 1616 vhostfd = "" 1617 1618 if kvm_supports_netdev: 1619 nic_val = "%s,mac=%s" % (nic_model, nic.mac) 1620 try: 1621 # kvm_nics already exist in old runtime files and thus there might 1622 # be some entries without pci slot (therefore try: except:) 1623 kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic) 1624 netdev = kvm_devid 1625 nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci))) 1626 except errors.HotplugError: 1627 netdev = "netdev%d" % nic_seq 1628 nic_val += (",netdev=%s%s" % (netdev, nic_extra)) 1629 tap_val = ("type=tap,id=%s,%s%s%s" % 1630 (netdev, tapfd, vhostfd, tap_extra)) 1631 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val]) 1632 else: 1633 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq, 1634 nic.mac, nic_model) 1635 tap_val = "tap,vlan=%s,%s" % (nic_seq, tapfd) 1636 kvm_cmd.extend(["-net", tap_val, "-net", nic_val]) 1637 1638 if incoming: 1639 target, port = incoming 1640 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)]) 1641 1642 # Changing the vnc password doesn't bother the guest that much. At most it 1643 # will surprise people who connect to it. Whether positively or negatively 1644 # it's debatable. 1645 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE] 1646 vnc_pwd = None 1647 if vnc_pwd_file: 1648 try: 1649 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True) 1650 except EnvironmentError, err: 1651 raise errors.HypervisorError("Failed to open VNC password file %s: %s" 1652 % (vnc_pwd_file, err)) 1653 1654 if conf_hvp[constants.HV_KVM_USE_CHROOT]: 1655 utils.EnsureDirs([(self._InstanceChrootDir(name), 1656 constants.SECURE_DIR_MODE)]) 1657 1658 # Automatically enable QMP if version is >= 0.14 1659 if self._QMP_RE.search(kvmhelp): 1660 logging.debug("Enabling QMP") 1661 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" % 1662 self._InstanceQmpMonitor(instance.name)]) 1663 # Add a second monitor for kvmd 1664 kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" % 1665 self._InstanceKvmdMonitor(instance.name)]) 1666 1667 # Configure the network now for starting instances and bridged/OVS 1668 # interfaces, during FinalizeMigration for incoming instances' routed 1669 # interfaces. 1670 for nic_seq, nic in enumerate(kvm_nics): 1671 if (incoming and 1672 nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED): 1673 continue 1674 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq]) 1675 1676 bdev_opts = self._GenerateKVMBlockDevicesOptions(instance, 1677 up_hvp, 1678 kvm_disks, 1679 kvmhelp, 1680 devlist) 1681 kvm_cmd.extend(bdev_opts) 1682 # CPU affinity requires kvm to start paused, so we set this flag if the 1683 # instance is not already paused and if we are not going to accept a 1684 # migrating instance. In the latter case, pausing is not needed. 1685 start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming 1686 if start_kvm_paused: 1687 kvm_cmd.extend([_KVM_START_PAUSED_FLAG]) 1688 1689 # Note: CPU pinning is using up_hvp since changes take effect 1690 # during instance startup anyway, and to avoid problems when soft 1691 # rebooting the instance. 1692 cpu_pinning = False 1693 if up_hvp.get(constants.HV_CPU_MASK, None): 1694 cpu_pinning = True 1695 1696 if security_model == constants.HT_SM_POOL: 1697 ss = ssconf.SimpleStore() 1698 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n") 1699 all_uids = set(uidpool.ExpandUidPool(uid_pool)) 1700 uid = uidpool.RequestUnusedUid(all_uids) 1701 try: 1702 username = pwd.getpwuid(uid.GetUid()).pw_name 1703 kvm_cmd.extend(["-runas", username]) 1704 self._RunKVMCmd(name, kvm_cmd, tapfds) 1705 except: 1706 uidpool.ReleaseUid(uid) 1707 raise 1708 else: 1709 uid.Unlock() 1710 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr()) 1711 else: 1712 self._RunKVMCmd(name, kvm_cmd, tapfds) 1713 1714 utils.EnsureDirs([(self._InstanceNICDir(instance.name), 1715 constants.RUN_DIRS_MODE)]) 1716 for nic_seq, tap in enumerate(taps): 1717 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq), 1718 data=tap) 1719 1720 if vnc_pwd: 1721 change_cmd = "change vnc password %s" % vnc_pwd 1722 self._CallMonitorCommand(instance.name, change_cmd) 1723 1724 # Setting SPICE password. We are not vulnerable to malicious passwordless 1725 # connection attempts because SPICE by default does not allow connections 1726 # if neither a password nor the "disable_ticketing" options are specified. 1727 # As soon as we send the password via QMP, that password is a valid ticket 1728 # for connection. 1729 spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE] 1730 if spice_password_file: 1731 spice_pwd = "" 1732 try: 1733 spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True) 1734 except EnvironmentError, err: 1735 raise errors.HypervisorError("Failed to open SPICE password file %s: %s" 1736 % (spice_password_file, err)) 1737 1738 qmp = QmpConnection(self._InstanceQmpMonitor(instance.name)) 1739 qmp.connect() 1740 arguments = { 1741 "protocol": "spice", 1742 "password": spice_pwd, 1743 } 1744 qmp.Execute("set_password", arguments) 1745 1746 for filename in temp_files: 1747 utils.RemoveFile(filename) 1748 1749 # If requested, set CPU affinity and resume instance execution 1750 if cpu_pinning: 1751 self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK]) 1752 1753 start_memory = self._InstanceStartupMemory(instance) 1754 if start_memory < instance.beparams[constants.BE_MAXMEM]: 1755 self.BalloonInstanceMemory(instance, start_memory) 1756 1757 if start_kvm_paused: 1758 # To control CPU pinning, ballooning, and vnc/spice passwords 1759 # the VM was started in a frozen state. If freezing was not 1760 # explicitly requested resume the vm status. 1761 self._CallMonitorCommand(instance.name, self._CONT_CMD)
1762 1763 @staticmethod
1764 - def _StartKvmd(hvparams):
1765 """Ensure that the Kvm daemon is running. 1766 1767 @type hvparams: dict of strings 1768 @param hvparams: hypervisor parameters 1769 1770 """ 1771 if hvparams is None \ 1772 or not hvparams[constants.HV_KVM_USER_SHUTDOWN] \ 1773 or utils.IsDaemonAlive(constants.KVMD): 1774 return 1775 1776 result = utils.RunCmd([pathutils.DAEMON_UTIL, "start", constants.KVMD]) 1777 1778 if result.failed: 1779 raise errors.HypervisorError("Failed to start KVM daemon")
1780
1781 - def StartInstance(self, instance, block_devices, startup_paused):
1782 """Start an instance. 1783 1784 """ 1785 self._CheckDown(instance.name) 1786 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 1787 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 1788 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, 1789 startup_paused, kvmhelp) 1790 self._SaveKVMRuntime(instance, kvm_runtime) 1791 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
1792 1793 @classmethod
1794 - def _CallMonitorCommand(cls, instance_name, command, timeout=None):
1795 """Invoke a command on the instance monitor. 1796 1797 """ 1798 if timeout is not None: 1799 timeout_cmd = "timeout %s" % (timeout, ) 1800 else: 1801 timeout_cmd = "" 1802 1803 # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum 1804 # version. The monitor protocol is designed for human consumption, whereas 1805 # QMP is made for programmatic usage. In the worst case QMP can also 1806 # execute monitor commands. As it is, all calls to socat take at least 1807 # 500ms and likely more: socat can't detect the end of the reply and waits 1808 # for 500ms of no data received before exiting (500 ms is the default for 1809 # the "-t" parameter). 1810 socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" % 1811 (utils.ShellQuote(command), 1812 timeout_cmd, 1813 constants.SOCAT_PATH, 1814 utils.ShellQuote(cls._InstanceMonitor(instance_name)))) 1815 result = utils.RunCmd(socat) 1816 if result.failed: 1817 msg = ("Failed to send command '%s' to instance '%s', reason '%s'," 1818 " output: %s" % 1819 (command, instance_name, result.fail_reason, result.output)) 1820 raise errors.HypervisorError(msg) 1821 1822 return result
1823 1824 @_with_qmp
1825 - def VerifyHotplugSupport(self, instance, action, dev_type):
1826 """Verifies that hotplug is supported. 1827 1828 @raise errors.HypervisorError: in one of the previous cases 1829 1830 """ 1831 if dev_type == constants.HOTPLUG_TARGET_DISK: 1832 if action == constants.HOTPLUG_ACTION_ADD: 1833 self.qmp.CheckDiskHotAddSupport() 1834 if dev_type == constants.HOTPLUG_TARGET_NIC: 1835 if action == constants.HOTPLUG_ACTION_ADD: 1836 self.qmp.CheckNicHotAddSupport()
1837
1838 - def HotplugSupported(self, instance):
1839 """Checks if hotplug is generally supported. 1840 1841 Hotplug is *not* supported in case of: 1842 - qemu versions < 1.7 (where all qmp related commands are supported) 1843 - for stopped instances 1844 1845 @raise errors.HypervisorError: in one of the previous cases 1846 1847 """ 1848 try: 1849 output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD) 1850 except errors.HypervisorError: 1851 raise errors.HotplugError("Instance is probably down") 1852 1853 match = self._INFO_VERSION_RE.search(output.stdout) 1854 if not match: 1855 raise errors.HotplugError("Cannot parse qemu version via monitor") 1856 1857 #TODO: delegate more fine-grained checks to VerifyHotplugSupport 1858 v_major, v_min, _, _ = match.groups() 1859 if (int(v_major), int(v_min)) < (1, 7): 1860 raise errors.HotplugError("Hotplug not supported for qemu versions < 1.7")
1861 1862 @_with_qmp
1863 - def _VerifyHotplugCommand(self, _instance, 1864 device, kvm_devid, should_exist):
1865 """Checks if a previous hotplug command has succeeded. 1866 1867 Depending on the should_exist value, verifies that an entry identified by 1868 the PCI slot and device ID is present or not. 1869 1870 @raise errors.HypervisorError: if result is not the expected one 1871 1872 """ 1873 for i in range(5): 1874 found = self.qmp.HasPCIDevice(device, kvm_devid) 1875 logging.info("Verifying hotplug command (retry %s): %s", i, found) 1876 if found and should_exist: 1877 break 1878 if not found and not should_exist: 1879 break 1880 time.sleep(1) 1881 1882 if found and not should_exist: 1883 msg = "Device %s should have been removed but is still there" % kvm_devid 1884 raise errors.HypervisorError(msg) 1885 1886 if not found and should_exist: 1887 msg = "Device %s should have been added but is missing" % kvm_devid 1888 raise errors.HypervisorError(msg) 1889 1890 logging.info("Device %s has been correctly hot-plugged", kvm_devid)
1891 1892 @_with_qmp
1893 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
1894 """ Helper method to hot-add a new device 1895 1896 It gets free pci slot generates the device name and invokes the 1897 device specific method. 1898 1899 """ 1900 # in case of hot-mod this is given 1901 if device.pci is None: 1902 device.pci = self.qmp.GetFreePCISlot() 1903 kvm_devid = _GenerateDeviceKVMId(dev_type, device) 1904 runtime = self._LoadKVMRuntime(instance) 1905 if dev_type == constants.HOTPLUG_TARGET_DISK: 1906 uri = _GetDriveURI(device, extra[0], extra[1]) 1907 self.qmp.HotAddDisk(device, kvm_devid, uri) 1908 elif dev_type == constants.HOTPLUG_TARGET_NIC: 1909 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 1910 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 1911 devlist = self._GetKVMOutput(kvmpath, self._KVMOPT_DEVICELIST) 1912 up_hvp = runtime[2] 1913 features, _, _ = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp) 1914 (tap, tapfds, vhostfds) = OpenTap(features=features) 1915 self._ConfigureNIC(instance, seq, device, tap) 1916 self.qmp.HotAddNic(device, kvm_devid, tapfds, vhostfds, features) 1917 utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap) 1918 1919 self._VerifyHotplugCommand(instance, device, kvm_devid, True) 1920 # update relevant entries in runtime file 1921 index = _DEVICE_RUNTIME_INDEX[dev_type] 1922 entry = _RUNTIME_ENTRY[dev_type](device, extra) 1923 runtime[index].append(entry) 1924 self._SaveKVMRuntime(instance, runtime)
1925 1926 @_with_qmp
1927 - def HotDelDevice(self, instance, dev_type, device, _, seq):
1928 """ Helper method for hot-del device 1929 1930 It gets device info from runtime file, generates the device name and 1931 invokes the device specific method. 1932 1933 """ 1934 runtime = self._LoadKVMRuntime(instance) 1935 entry = _GetExistingDeviceInfo(dev_type, device, runtime) 1936 kvm_device = _RUNTIME_DEVICE[dev_type](entry) 1937 kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device) 1938 if dev_type == constants.HOTPLUG_TARGET_DISK: 1939 self.qmp.HotDelDisk(kvm_devid) 1940 # drive_del is not implemented yet in qmp 1941 command = "drive_del %s\n" % kvm_devid 1942 self._CallMonitorCommand(instance.name, command) 1943 elif dev_type == constants.HOTPLUG_TARGET_NIC: 1944 self.qmp.HotDelNic(kvm_devid) 1945 utils.RemoveFile(self._InstanceNICFile(instance.name, seq)) 1946 self._VerifyHotplugCommand(instance, kvm_device, kvm_devid, False) 1947 index = _DEVICE_RUNTIME_INDEX[dev_type] 1948 runtime[index].remove(entry) 1949 self._SaveKVMRuntime(instance, runtime) 1950 1951 return kvm_device.pci
1952
1953 - def HotModDevice(self, instance, dev_type, device, _, seq):
1954 """ Helper method for hot-mod device 1955 1956 It gets device info from runtime file, generates the device name and 1957 invokes the device specific method. Currently only NICs support hot-mod 1958 1959 """ 1960 if dev_type == constants.HOTPLUG_TARGET_NIC: 1961 # putting it back in the same pci slot 1962 device.pci = self.HotDelDevice(instance, dev_type, device, _, seq) 1963 self.HotAddDevice(instance, dev_type, device, _, seq)
1964 1965 @classmethod
1966 - def _ParseKVMVersion(cls, text):
1967 """Parse the KVM version from the --help output. 1968 1969 @type text: string 1970 @param text: output of kvm --help 1971 @return: (version, v_maj, v_min, v_rev) 1972 @raise errors.HypervisorError: when the KVM version cannot be retrieved 1973 1974 """ 1975 match = cls._VERSION_RE.search(text.splitlines()[0]) 1976 if not match: 1977 raise errors.HypervisorError("Unable to get KVM version") 1978 1979 v_all = match.group(0) 1980 v_maj = int(match.group(1)) 1981 v_min = int(match.group(2)) 1982 if match.group(4): 1983 v_rev = int(match.group(4)) 1984 else: 1985 v_rev = 0 1986 return (v_all, v_maj, v_min, v_rev)
1987 1988 @classmethod
1989 - def _GetKVMOutput(cls, kvm_path, option):
1990 """Return the output of a kvm invocation 1991 1992 @type kvm_path: string 1993 @param kvm_path: path to the kvm executable 1994 @type option: a key of _KVMOPTS_CMDS 1995 @param option: kvm option to fetch the output from 1996 @return: output a supported kvm invocation 1997 @raise errors.HypervisorError: when the KVM help output cannot be retrieved 1998 1999 """ 2000 assert option in cls._KVMOPTS_CMDS, "Invalid output option" 2001 2002 optlist, can_fail = cls._KVMOPTS_CMDS[option] 2003 2004 result = utils.RunCmd([kvm_path] + optlist) 2005 if result.failed and not can_fail: 2006 raise errors.HypervisorError("Unable to get KVM %s output" % 2007 " ".join(optlist)) 2008 return result.output
2009 2010 @classmethod
2011 - def _GetKVMVersion(cls, kvm_path):
2012 """Return the installed KVM version. 2013 2014 @return: (version, v_maj, v_min, v_rev) 2015 @raise errors.HypervisorError: when the KVM version cannot be retrieved 2016 2017 """ 2018 return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2019 2020 @classmethod
2021 - def _GetDefaultMachineVersion(cls, kvm_path):
2022 """Return the default hardware revision (e.g. pc-1.1) 2023 2024 """ 2025 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST) 2026 match = cls._DEFAULT_MACHINE_VERSION_RE.search(output) 2027 if match: 2028 return match.group(1) 2029 else: 2030 return "pc"
2031 2032 @classmethod
2033 - def _StopInstance(cls, instance, force=False, name=None, timeout=None):
2034 """Stop an instance. 2035 2036 """ 2037 assert(timeout is None or force is not None) 2038 2039 if name is not None and not force: 2040 raise errors.HypervisorError("Cannot shutdown cleanly by name only") 2041 if name is None: 2042 name = instance.name 2043 acpi = instance.hvparams[constants.HV_ACPI] 2044 else: 2045 acpi = False 2046 _, pid, alive = cls._InstancePidAlive(name) 2047 if pid > 0 and alive: 2048 if force or not acpi: 2049 utils.KillProcess(pid) 2050 else: 2051 cls._CallMonitorCommand(name, "system_powerdown", timeout) 2052 cls._ClearUserShutdown(instance.name)
2053
2054 - def StopInstance(self, instance, force=False, retry=False, name=None, 2055 timeout=None):
2056 """Stop an instance. 2057 2058 """ 2059 self._StopInstance(instance, force, name=name, timeout=timeout)
2060
2061 - def CleanupInstance(self, instance_name):
2062 """Cleanup after a stopped instance 2063 2064 """ 2065 pidfile, pid, alive = self._InstancePidAlive(instance_name) 2066 if pid > 0 and alive: 2067 raise errors.HypervisorError("Cannot cleanup a live instance") 2068 self._RemoveInstanceRuntimeFiles(pidfile, instance_name) 2069 self._ClearUserShutdown(instance_name)
2070
2071 - def RebootInstance(self, instance):
2072 """Reboot an instance. 2073 2074 """ 2075 # For some reason if we do a 'send-key ctrl-alt-delete' to the control 2076 # socket the instance will stop, but now power up again. So we'll resort 2077 # to shutdown and restart. 2078 _, _, alive = self._InstancePidAlive(instance.name) 2079 if not alive: 2080 raise errors.HypervisorError("Failed to reboot instance %s:" 2081 " not running" % instance.name) 2082 # StopInstance will delete the saved KVM runtime so: 2083 # ...first load it... 2084 kvm_runtime = self._LoadKVMRuntime(instance) 2085 # ...now we can safely call StopInstance... 2086 if not self.StopInstance(instance): 2087 self.StopInstance(instance, force=True) 2088 # ...and finally we can save it again, and execute it... 2089 self._SaveKVMRuntime(instance, kvm_runtime) 2090 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 2091 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 2092 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2093
2094 - def MigrationInfo(self, instance):
2095 """Get instance information to perform a migration. 2096 2097 @type instance: L{objects.Instance} 2098 @param instance: instance to be migrated 2099 @rtype: string 2100 @return: content of the KVM runtime file 2101 2102 """ 2103 return self._ReadKVMRuntime(instance.name)
2104
2105 - def AcceptInstance(self, instance, info, target):
2106 """Prepare to accept an instance. 2107 2108 @type instance: L{objects.Instance} 2109 @param instance: instance to be accepted 2110 @type info: string 2111 @param info: content of the KVM runtime file on the source node 2112 @type target: string 2113 @param target: target host (usually ip), on this node 2114 2115 """ 2116 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info) 2117 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT]) 2118 kvmpath = instance.hvparams[constants.HV_KVM_PATH] 2119 kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) 2120 self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp, 2121 incoming=incoming_address)
2122
2123 - def FinalizeMigrationDst(self, instance, info, success):
2124 """Finalize the instance migration on the target node. 2125 2126 Stop the incoming mode KVM. 2127 2128 @type instance: L{objects.Instance} 2129 @param instance: instance whose migration is being finalized 2130 2131 """ 2132 if success: 2133 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info) 2134 kvm_nics = kvm_runtime[1] 2135 2136 for nic_seq, nic in enumerate(kvm_nics): 2137 if nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_ROUTED: 2138 # Bridged/OVS interfaces have already been configured 2139 continue 2140 try: 2141 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq)) 2142 except EnvironmentError, err: 2143 logging.warning("Failed to find host interface for %s NIC #%d: %s", 2144 instance.name, nic_seq, str(err)) 2145 continue 2146 try: 2147 self._ConfigureNIC(instance, nic_seq, nic, tap) 2148 except errors.HypervisorError, err: 2149 logging.warning(str(err)) 2150 2151 self._WriteKVMRuntime(instance.name, info) 2152 else: 2153 self.StopInstance(instance, force=True)
2154
2155 - def MigrateInstance(self, cluster_name, instance, target, live):
2156 """Migrate an instance to a target node. 2157 2158 The migration will not be attempted if the instance is not 2159 currently running. 2160 2161 @type cluster_name: string 2162 @param cluster_name: name of the cluster 2163 @type instance: L{objects.Instance} 2164 @param instance: the instance to be migrated 2165 @type target: string 2166 @param target: ip address of the target node 2167 @type live: boolean 2168 @param live: perform a live migration 2169 2170 """ 2171 instance_name = instance.name 2172 port = instance.hvparams[constants.HV_MIGRATION_PORT] 2173 _, _, alive = self._InstancePidAlive(instance_name) 2174 if not alive: 2175 raise errors.HypervisorError("Instance not running, cannot migrate") 2176 2177 if not live: 2178 self._CallMonitorCommand(instance_name, "stop") 2179 2180 migrate_command = ("migrate_set_speed %dm" % 2181 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH]) 2182 self._CallMonitorCommand(instance_name, migrate_command) 2183 2184 migrate_command = ("migrate_set_downtime %dms" % 2185 instance.hvparams[constants.HV_MIGRATION_DOWNTIME]) 2186 self._CallMonitorCommand(instance_name, migrate_command) 2187 2188 migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS] 2189 if migration_caps: 2190 for c in migration_caps.split(_MIGRATION_CAPS_DELIM): 2191 migrate_command = ("migrate_set_capability %s on" % c) 2192 self._CallMonitorCommand(instance_name, migrate_command) 2193 2194 migrate_command = "migrate -d tcp:%s:%s" % (target, port) 2195 self._CallMonitorCommand(instance_name, migrate_command)
2196
2197 - def FinalizeMigrationSource(self, instance, success, live):
2198 """Finalize the instance migration on the source node. 2199 2200 @type instance: L{objects.Instance} 2201 @param instance: the instance that was migrated 2202 @type success: bool 2203 @param success: whether the migration succeeded or not 2204 @type live: bool 2205 @param live: whether the user requested a live migration or not 2206 2207 """ 2208 if success: 2209 pidfile, pid, _ = self._InstancePidAlive(instance.name) 2210 utils.KillProcess(pid) 2211 self._RemoveInstanceRuntimeFiles(pidfile, instance.name) 2212 elif live: 2213 self._CallMonitorCommand(instance.name, self._CONT_CMD) 2214 self._ClearUserShutdown(instance.name)
2215
2216 - def GetMigrationStatus(self, instance):
2217 """Get the migration status 2218 2219 @type instance: L{objects.Instance} 2220 @param instance: the instance that is being migrated 2221 @rtype: L{objects.MigrationStatus} 2222 @return: the status of the current migration (one of 2223 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional 2224 progress info that can be retrieved from the hypervisor 2225 2226 """ 2227 info_command = "info migrate" 2228 for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS): 2229 result = self._CallMonitorCommand(instance.name, info_command) 2230 match = self._MIGRATION_STATUS_RE.search(result.stdout) 2231 if not match: 2232 if not result.stdout: 2233 logging.info("KVM: empty 'info migrate' result") 2234 else: 2235 logging.warning("KVM: unknown 'info migrate' result: %s", 2236 result.stdout) 2237 else: 2238 status = match.group(1) 2239 if status in constants.HV_KVM_MIGRATION_VALID_STATUSES: 2240 migration_status = objects.MigrationStatus(status=status) 2241 match = self._MIGRATION_PROGRESS_RE.search(result.stdout) 2242 if match: 2243 migration_status.transferred_ram = match.group("transferred") 2244 migration_status.total_ram = match.group("total") 2245 2246 return migration_status 2247 2248 logging.warning("KVM: unknown migration status '%s'", status) 2249 2250 time.sleep(self._MIGRATION_INFO_RETRY_DELAY) 2251 2252 return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2253
2254 - def BalloonInstanceMemory(self, instance, mem):
2255 """Balloon an instance memory to a certain value. 2256 2257 @type instance: L{objects.Instance} 2258 @param instance: instance to be accepted 2259 @type mem: int 2260 @param mem: actual memory size to use for instance runtime 2261 2262 """ 2263 self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2264
2265 - def GetNodeInfo(self, hvparams=None):
2266 """Return information about the node. 2267 2268 @type hvparams: dict of strings 2269 @param hvparams: hypervisor parameters, not used in this class 2270 2271 @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus 2272 the following keys: 2273 - hv_version: the hypervisor version in the form (major, minor, 2274 revision) 2275 2276 """ 2277 result = self.GetLinuxNodeInfo() 2278 kvmpath = constants.KVM_PATH 2279 if hvparams is not None: 2280 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH) 2281 _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath) 2282 result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev) 2283 return result
2284 2285 @classmethod
2286 - def GetInstanceConsole(cls, instance, primary_node, node_group, 2287 hvparams, beparams):
2288 """Return a command for connecting to the console of an instance. 2289 2290 """ 2291 if hvparams[constants.HV_SERIAL_CONSOLE]: 2292 cmd = [pathutils.KVM_CONSOLE_WRAPPER, 2293 constants.SOCAT_PATH, utils.ShellQuote(instance.name), 2294 utils.ShellQuote(cls._InstanceMonitor(instance.name)), 2295 "STDIO,%s" % cls._SocatUnixConsoleParams(), 2296 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)] 2297 ndparams = node_group.FillND(primary_node) 2298 return objects.InstanceConsole(instance=instance.name, 2299 kind=constants.CONS_SSH, 2300 host=primary_node.name, 2301 port=ndparams.get(constants.ND_SSH_PORT), 2302 user=constants.SSH_CONSOLE_USER, 2303 command=cmd) 2304 2305 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS] 2306 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT: 2307 display = instance.network_port - constants.VNC_BASE_PORT 2308 return objects.InstanceConsole(instance=instance.name, 2309 kind=constants.CONS_VNC, 2310 host=vnc_bind_address, 2311 port=instance.network_port, 2312 display=display) 2313 2314 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 2315 if spice_bind: 2316 return objects.InstanceConsole(instance=instance.name, 2317 kind=constants.CONS_SPICE, 2318 host=spice_bind, 2319 port=instance.network_port) 2320 2321 return objects.InstanceConsole(instance=instance.name, 2322 kind=constants.CONS_MESSAGE, 2323 message=("No serial shell for instance %s" % 2324 instance.name))
2325
2326 - def Verify(self, hvparams=None):
2327 """Verify the hypervisor. 2328 2329 Check that the required binaries exist. 2330 2331 @type hvparams: dict of strings 2332 @param hvparams: hypervisor parameters to be verified against, not used here 2333 2334 @return: Problem description if something is wrong, C{None} otherwise 2335 2336 """ 2337 msgs = [] 2338 kvmpath = constants.KVM_PATH 2339 if hvparams is not None: 2340 kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH) 2341 if not os.path.exists(kvmpath): 2342 msgs.append("The KVM binary ('%s') does not exist" % kvmpath) 2343 if not os.path.exists(constants.SOCAT_PATH): 2344 msgs.append("The socat binary ('%s') does not exist" % 2345 constants.SOCAT_PATH) 2346 2347 return self._FormatVerifyResults(msgs)
2348 2349 @classmethod
2350 - def CheckParameterSyntax(cls, hvparams):
2351 """Check the given parameters for validity. 2352 2353 @type hvparams: dict of strings 2354 @param hvparams: hypervisor parameters 2355 @raise errors.HypervisorError: when a parameter is not valid 2356 2357 """ 2358 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams) 2359 2360 kernel_path = hvparams[constants.HV_KERNEL_PATH] 2361 if kernel_path: 2362 if not hvparams[constants.HV_ROOT_PATH]: 2363 raise errors.HypervisorError("Need a root partition for the instance," 2364 " if a kernel is defined") 2365 2366 if (hvparams[constants.HV_VNC_X509_VERIFY] and 2367 not hvparams[constants.HV_VNC_X509]): 2368 raise errors.HypervisorError("%s must be defined, if %s is" % 2369 (constants.HV_VNC_X509, 2370 constants.HV_VNC_X509_VERIFY)) 2371 2372 if hvparams[constants.HV_SERIAL_CONSOLE]: 2373 serial_speed = hvparams[constants.HV_SERIAL_SPEED] 2374 valid_speeds = constants.VALID_SERIAL_SPEEDS 2375 if not serial_speed or serial_speed not in valid_speeds: 2376 raise errors.HypervisorError("Invalid serial console speed, must be" 2377 " one of: %s" % 2378 utils.CommaJoin(valid_speeds)) 2379 2380 boot_order = hvparams[constants.HV_BOOT_ORDER] 2381 if (boot_order == constants.HT_BO_CDROM and 2382 not hvparams[constants.HV_CDROM_IMAGE_PATH]): 2383 raise errors.HypervisorError("Cannot boot from cdrom without an" 2384 " ISO path") 2385 2386 security_model = hvparams[constants.HV_SECURITY_MODEL] 2387 if security_model == constants.HT_SM_USER: 2388 if not hvparams[constants.HV_SECURITY_DOMAIN]: 2389 raise errors.HypervisorError("A security domain (user to run kvm as)" 2390 " must be specified") 2391 elif (security_model == constants.HT_SM_NONE or 2392 security_model == constants.HT_SM_POOL): 2393 if hvparams[constants.HV_SECURITY_DOMAIN]: 2394 raise errors.HypervisorError("Cannot have a security domain when the" 2395 " security model is 'none' or 'pool'") 2396 2397 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 2398 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION] 2399 if spice_bind: 2400 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED: 2401 # if an IP version is specified, the spice_bind parameter must be an 2402 # IP of that family 2403 if (netutils.IP4Address.IsValid(spice_bind) and 2404 spice_ip_version != constants.IP4_VERSION): 2405 raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but" 2406 " the specified IP version is %s" % 2407 (spice_bind, spice_ip_version)) 2408 2409 if (netutils.IP6Address.IsValid(spice_bind) and 2410 spice_ip_version != constants.IP6_VERSION): 2411 raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but" 2412 " the specified IP version is %s" % 2413 (spice_bind, spice_ip_version)) 2414 else: 2415 # All the other SPICE parameters depend on spice_bind being set. Raise an 2416 # error if any of them is set without it. 2417 for param in _SPICE_ADDITIONAL_PARAMS: 2418 if hvparams[param]: 2419 raise errors.HypervisorError("SPICE: %s requires %s to be set" % 2420 (param, constants.HV_KVM_SPICE_BIND))
2421 2422 @classmethod
2423 - def ValidateParameters(cls, hvparams):
2424 """Check the given parameters for validity. 2425 2426 @type hvparams: dict of strings 2427 @param hvparams: hypervisor parameters 2428 @raise errors.HypervisorError: when a parameter is not valid 2429 2430 """ 2431 super(KVMHypervisor, cls).ValidateParameters(hvparams) 2432 2433 kvm_path = hvparams[constants.HV_KVM_PATH] 2434 2435 security_model = hvparams[constants.HV_SECURITY_MODEL] 2436 if security_model == constants.HT_SM_USER: 2437 username = hvparams[constants.HV_SECURITY_DOMAIN] 2438 try: 2439 pwd.getpwnam(username) 2440 except KeyError: 2441 raise errors.HypervisorError("Unknown security domain user %s" 2442 % username) 2443 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS] 2444 if vnc_bind_address: 2445 bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address) 2446 is_interface = netutils.IsValidInterface(vnc_bind_address) 2447 is_path = utils.IsNormAbsPath(vnc_bind_address) 2448 if not bound_to_addr and not is_interface and not is_path: 2449 raise errors.HypervisorError("VNC: The %s parameter must be either" 2450 " a valid IP address, an interface name," 2451 " or an absolute path" % 2452 constants.HV_KVM_SPICE_BIND) 2453 2454 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 2455 if spice_bind: 2456 # only one of VNC and SPICE can be used currently. 2457 if hvparams[constants.HV_VNC_BIND_ADDRESS]: 2458 raise errors.HypervisorError("Both SPICE and VNC are configured, but" 2459 " only one of them can be used at a" 2460 " given time") 2461 2462 # check that KVM supports SPICE 2463 kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP) 2464 if not cls._SPICE_RE.search(kvmhelp): 2465 raise errors.HypervisorError("SPICE is configured, but it is not" 2466 " supported according to 'kvm --help'") 2467 2468 # if spice_bind is not an IP address, it must be a valid interface 2469 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or 2470 netutils.IP6Address.IsValid(spice_bind)) 2471 if not bound_to_addr and not netutils.IsValidInterface(spice_bind): 2472 raise errors.HypervisorError("SPICE: The %s parameter must be either" 2473 " a valid IP address or interface name" % 2474 constants.HV_KVM_SPICE_BIND) 2475 2476 machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION] 2477 if machine_version: 2478 output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST) 2479 if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output): 2480 raise errors.HypervisorError("Unsupported machine version: %s" % 2481 machine_version)
2482 2483 @classmethod
2484 - def PowercycleNode(cls, hvparams=None):
2485 """KVM powercycle, just a wrapper over Linux powercycle. 2486 2487 @type hvparams: dict of strings 2488 @param hvparams: hypervisor parameters to be used on this node 2489 2490 """ 2491 cls.LinuxPowercycle()
2492