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

Source Code for Package ganeti.hypervisor.hv_kvm

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