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