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