Commit 2615646c authored by Dimitris Aragiorgis's avatar Dimitris Aragiorgis Committed by Thomas Thrainer
Browse files

Helper methods for PCI slots and device ids



Device naming:
QEMU monitor expects devices to be uniquely named. Device ids derive
from the following function:
kvm_devid = <device_type>-<part of uuid>-pci-<pci_slot>
Device ids must be reproduce-able when we want to remove them.
For that reason we store the pci slot inside the runtime file and
in case we want to remove a device we obtain its pci slot by
parsing the corresponding runtime enrty and matching the device
by its uuid.

Finding the PCI slot:
For newly added devices Hypervisor parses existing PCI allocations
(via _GetFreePCISlot() and eventually ``info pci`` monitor
command) and decides the PCI slot to plug in the device. During
instance startup hypervisor invokes _UpdatePCISlots() for every
device of the instance.  Initial PCI reservations derive from KVM
default setup, that allocates 4 slots for devices other than disks
and NICs.
Signed-off-by: default avatarDimitris Aragiorgis <dimara@grnet.gr>
Signed-off-by: default avatarThomas Thrainer <thomasth@google.com>
Reviewed-by: default avatarThomas Thrainer <thomasth@google.com>
parent 0fe22ad2
......@@ -37,6 +37,7 @@ import shutil
import socket
import stat
import StringIO
from bitarray import bitarray
try:
import affinity # pylint: disable=F0401
except ImportError:
......@@ -79,6 +80,10 @@ _SPICE_ADDITIONAL_PARAMS = frozenset([
constants.HV_KVM_SPICE_USE_TLS,
])
# Constant bitarray that reflects to a free pci slot
# Use it with bitarray.search()
_AVAILABLE_PCI_SLOT = bitarray("0")
# below constants show the format of runtime file
# the nics are in second possition, while the disks in 4th (last)
# moreover disk entries are stored in tupples of L{objects.Disk}, dev_path
......@@ -105,6 +110,53 @@ _RUNTIME_ENTRY = {
}
def _GenerateDeviceKVMId(dev_type, dev):
"""Helper function to generate a unique device name used by KVM
QEMU monitor commands use names to identify devices. Here we use their pci
slot and a part of their UUID to name them. dev.pci might be None for old
devices in the cluster.
@type dev_type: sting
@param dev_type: device type of param dev
@type dev: L{objects.Disk} or L{objects.NIC}
@param dev: the device object for which we generate a kvm name
@raise errors.HotplugError: in case a device has no pci slot (old devices)
"""
if not dev.pci:
raise errors.HotplugError("Hotplug is not supported for %s with UUID %s" %
(dev_type, dev.uuid))
return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
def _UpdatePCISlots(dev, pci_reservations):
"""Update pci configuration for a stopped instance
If dev has a pci slot then reserve it, else find first available
in pci_reservations bitarray. It acts on the same objects passed
as params so there is no need to return anything.
@type dev: L{objects.Disk} or L{objects.NIC}
@param dev: the device object for which we update its pci slot
@type pci_reservations: bitarray
@param pci_reservations: existing pci reservations for an instance
@raise errors.HotplugError: in case an instance has all its slot occupied
"""
if dev.pci:
free = dev.pci
else: # pylint: disable=E1103
[free] = pci_reservations.search(_AVAILABLE_PCI_SLOT, 1)
if not free:
raise errors.HypervisorError("All PCI slots occupied")
dev.pci = int(free)
pci_reservations[free] = True
def _GetExistingDeviceInfo(dev_type, device, runtime):
"""Helper function to get an existing device inside the runtime file
......@@ -658,6 +710,14 @@ class KVMHypervisor(hv_base.BaseHypervisor):
_BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
_UUID_RE = re.compile(r"^-uuid\s", re.M)
_INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
_INFO_PCI_CMD = "info pci"
_INFO_VERSION_RE = \
re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
_INFO_VERSION_CMD = "info version"
_DEFAULT_PCI_RESERVATIONS = "11110000000000000000000000000000"
ANCILLARY_FILES = [
_KVM_NETWORK_SCRIPT,
]
......@@ -1853,6 +1913,25 @@ class KVMHypervisor(hv_base.BaseHypervisor):
return result
def _GetFreePCISlot(self, instance, dev):
"""Get the first available pci slot of a runnung instance.
"""
slots = bitarray(32)
slots.setall(False) # pylint: disable=E1101
output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
for line in output.stdout.splitlines():
match = self._INFO_PCI_RE.search(line)
if match:
slot = int(match.group(1))
slots[slot] = True
[free] = slots.search(_AVAILABLE_PCI_SLOT, 1) # pylint: disable=E1101
if not free:
raise errors.HypervisorError("All PCI slots occupied")
dev.pci = int(free)
@classmethod
def _ParseKVMVersion(cls, text):
"""Parse the KVM version from the --help output.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment