libvirt: Add typing information

As with the 'nova.virt.hardware' module, add typing information here now
so that we can use it during development of later features. This
requires some minor tweaks of code that mypy found confusing.

Part of blueprint use-pcpu-and-vcpu-in-one-instance

Change-Id: Icc7b3d250bb9dd3d162731959185d9e962727247
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2020-03-16 13:00:15 +00:00
parent 26c1567a16
commit 6a71981e47
2 changed files with 64 additions and 56 deletions

View File

@ -1,2 +1,3 @@
nova/virt/hardware.py nova/virt/hardware.py
nova/virt/libvirt/__init__.py nova/virt/libvirt/__init__.py
nova/virt/libvirt/driver.py

View File

@ -41,6 +41,7 @@ import random
import shutil import shutil
import tempfile import tempfile
import time import time
import typing as ty
import uuid import uuid
from castellan import key_manager from castellan import key_manager
@ -76,6 +77,7 @@ from nova.api.metadata import base as instance_metadata
from nova.api.metadata import password from nova.api.metadata import password
from nova import block_device from nova import block_device
from nova.compute import power_state from nova.compute import power_state
from nova.compute import provider_tree
from nova.compute import task_states from nova.compute import task_states
from nova.compute import utils as compute_utils from nova.compute import utils as compute_utils
from nova.compute import vm_states from nova.compute import vm_states
@ -127,7 +129,7 @@ from nova.virt.libvirt.volume import remotefs
from nova.virt import netutils from nova.virt import netutils
from nova.volume import cinder from nova.volume import cinder
libvirt = None libvirt: ty.Any = None
uefi_logged = False uefi_logged = False
@ -389,7 +391,7 @@ class LibvirtDriver(driver.ComputeDriver):
} }
self._sysinfo_serial_func = sysinfo_serial_funcs.get( self._sysinfo_serial_func = sysinfo_serial_funcs.get(
CONF.libvirt.sysinfo_serial) CONF.libvirt.sysinfo_serial, lambda: None)
self.job_tracker = instancejobtracker.InstanceJobTracker() self.job_tracker = instancejobtracker.InstanceJobTracker()
self._remotefs = remotefs.RemoteFilesystem() self._remotefs = remotefs.RemoteFilesystem()
@ -406,7 +408,7 @@ class LibvirtDriver(driver.ComputeDriver):
# every time update_provider_tree() is called. # every time update_provider_tree() is called.
# NOTE(sbauza): We only want a read-only cache, this attribute is not # NOTE(sbauza): We only want a read-only cache, this attribute is not
# intended to be updatable directly # intended to be updatable directly
self.provider_tree = None self.provider_tree: provider_tree.ProviderTree = None
# driver traits will not change during the runtime of the agent # driver traits will not change during the runtime of the agent
# so calcuate them once and save them # so calcuate them once and save them
@ -449,10 +451,12 @@ class LibvirtDriver(driver.ComputeDriver):
MIN_LIBVIRT_PMEM_SUPPORT)}) MIN_LIBVIRT_PMEM_SUPPORT)})
# vpmem keyed by name {name: objects.LibvirtVPMEMDevice,...} # vpmem keyed by name {name: objects.LibvirtVPMEMDevice,...}
vpmems_by_name = {} vpmems_by_name: ty.Dict[str, 'objects.LibvirtVPMEMDevice'] = {}
# vpmem list keyed by resource class # vpmem list keyed by resource class
# {'RC_0': [objects.LibvirtVPMEMDevice, ...], 'RC_1': [...]} # {'RC_0': [objects.LibvirtVPMEMDevice, ...], 'RC_1': [...]}
vpmems_by_rc = collections.defaultdict(list) vpmems_by_rc: ty.Dict[str, ty.List['objects.LibvirtVPMEMDevice']] = (
collections.defaultdict(list)
)
vpmems_host = self._get_vpmems_on_host() vpmems_host = self._get_vpmems_on_host()
for ns_conf in vpmem_conf: for ns_conf in vpmem_conf:
@ -1054,10 +1058,10 @@ class LibvirtDriver(driver.ComputeDriver):
@staticmethod @staticmethod
def _live_migration_uri(dest): def _live_migration_uri(dest):
uris = { uris = {
'kvm': 'qemu+%s://%s/system', 'kvm': 'qemu+%(scheme)s://%(dest)s/system',
'qemu': 'qemu+%s://%s/system', 'qemu': 'qemu+%(scheme)s://%(dest)s/system',
'xen': 'xenmigr://%s/system', 'xen': 'xenmigr://%(dest)s/system',
'parallels': 'parallels+tcp://%s/system', 'parallels': 'parallels+tcp://%(dest)s/system',
} }
dest = oslo_netutils.escape_ipv6(dest) dest = oslo_netutils.escape_ipv6(dest)
@ -1071,11 +1075,11 @@ class LibvirtDriver(driver.ComputeDriver):
if uri is None: if uri is None:
raise exception.LiveMigrationURINotAvailable(virt_type=virt_type) raise exception.LiveMigrationURINotAvailable(virt_type=virt_type)
str_format = (dest,) str_format = {
if virt_type in ('kvm', 'qemu'): 'dest': dest,
scheme = CONF.libvirt.live_migration_scheme or 'tcp' 'scheme': CONF.libvirt.live_migration_scheme or 'tcp',
str_format = (scheme, dest) }
return uris.get(virt_type) % str_format return uri % str_format
@staticmethod @staticmethod
def _migrate_uri(dest): def _migrate_uri(dest):
@ -1462,9 +1466,9 @@ class LibvirtDriver(driver.ComputeDriver):
def _detach_encrypted_volumes(self, instance, block_device_info): def _detach_encrypted_volumes(self, instance, block_device_info):
"""Detaches encrypted volumes attached to instance.""" """Detaches encrypted volumes attached to instance."""
disks = self._get_instance_disk_info(instance, block_device_info) disks = self._get_instance_disk_info(instance, block_device_info)
encrypted_volumes = filter(dmcrypt.is_encrypted, for path in [
[disk['path'] for disk in disks]) d['path'] for d in disks if dmcrypt.is_encrypted(d['path'])
for path in encrypted_volumes: ]:
dmcrypt.delete_volume(path) dmcrypt.delete_volume(path)
def _get_serial_ports_from_guest(self, guest, mode=None): def _get_serial_ports_from_guest(self, guest, mode=None):
@ -2977,17 +2981,12 @@ class LibvirtDriver(driver.ComputeDriver):
raise exception.InstanceNotRunning(instance_id=instance.uuid) raise exception.InstanceNotRunning(instance_id=instance.uuid)
# Find dev name # Find dev name
my_dev = None
active_disk = None
xml = guest.get_xml_desc() xml = guest.get_xml_desc()
xml_doc = etree.fromstring(xml) xml_doc = etree.fromstring(xml)
device_info = vconfig.LibvirtConfigGuest() device_info = vconfig.LibvirtConfigGuest()
device_info.parse_dom(xml_doc) device_info.parse_dom(xml_doc)
active_disk_object = None
for guest_disk in device_info.devices: for guest_disk in device_info.devices:
if (guest_disk.root_name != 'disk'): if (guest_disk.root_name != 'disk'):
continue continue
@ -2995,17 +2994,21 @@ class LibvirtDriver(driver.ComputeDriver):
if (guest_disk.target_dev is None or guest_disk.serial is None): if (guest_disk.target_dev is None or guest_disk.serial is None):
continue continue
if (
guest_disk.source_path is None and
guest_disk.source_protocol is None
):
continue
if guest_disk.serial == volume_id: if guest_disk.serial == volume_id:
my_dev = guest_disk.target_dev my_dev = guest_disk.target_dev
active_disk = guest_disk.source_path
active_protocol = guest_disk.source_protocol active_protocol = guest_disk.source_protocol
active_disk_object = guest_disk active_disk_object = guest_disk
break break
else:
if my_dev is None or (active_disk is None and active_protocol is None):
LOG.debug('Domain XML: %s', xml, instance=instance) LOG.debug('Domain XML: %s', xml, instance=instance)
msg = (_('Disk with id: %s not found attached to instance.') msg = (_("Disk with id '%s' not found attached to instance.")
% volume_id) % volume_id)
raise exception.InternalError(msg) raise exception.InternalError(msg)
@ -3881,15 +3884,13 @@ class LibvirtDriver(driver.ComputeDriver):
target_partition = None target_partition = None
# Handles the key injection. # Handles the key injection.
key = None
if CONF.libvirt.inject_key and instance.get('key_data'): if CONF.libvirt.inject_key and instance.get('key_data'):
key = str(instance.key_data) key = str(instance.key_data)
else:
key = None
# Handles the admin password injection. # Handles the admin password injection.
if not CONF.libvirt.inject_password: admin_pass = None
admin_pass = None if CONF.libvirt.inject_password:
else:
admin_pass = injection_info.admin_pass admin_pass = injection_info.admin_pass
# Handles the network injection. # Handles the network injection.
@ -4874,7 +4875,7 @@ class LibvirtDriver(driver.ComputeDriver):
return idmaps return idmaps
def _get_guest_idmaps(self): def _get_guest_idmaps(self):
id_maps = [] id_maps: ty.List[vconfig.LibvirtConfigGuestIDMap] = []
if CONF.libvirt.virt_type == 'lxc' and CONF.libvirt.uid_maps: if CONF.libvirt.virt_type == 'lxc' and CONF.libvirt.uid_maps:
uid_maps = self._create_idmaps(vconfig.LibvirtConfigGuestUIDMap, uid_maps = self._create_idmaps(vconfig.LibvirtConfigGuestUIDMap,
CONF.libvirt.uid_maps) CONF.libvirt.uid_maps)
@ -6500,7 +6501,7 @@ class LibvirtDriver(driver.ComputeDriver):
events = [] events = []
pause = bool(events) pause = bool(events)
guest = None guest: libvirt_guest.Guest = None
try: try:
with self.virtapi.wait_for_instance_event( with self.virtapi.wait_for_instance_event(
instance, events, deadline=timeout, instance, events, deadline=timeout,
@ -6763,7 +6764,7 @@ class LibvirtDriver(driver.ComputeDriver):
mdev device handles for that GPU mdev device handles for that GPU
""" """
counts_per_parent = collections.defaultdict(int) counts_per_parent: ty.Dict[str, int] = collections.defaultdict(int)
mediated_devices = self._get_mediated_devices(types=enabled_vgpu_types) mediated_devices = self._get_mediated_devices(types=enabled_vgpu_types)
for mdev in mediated_devices: for mdev in mediated_devices:
parent_vgpu_type = self._get_vgpu_type_per_pgpu(mdev['parent']) parent_vgpu_type = self._get_vgpu_type_per_pgpu(mdev['parent'])
@ -6785,7 +6786,7 @@ class LibvirtDriver(driver.ComputeDriver):
""" """
mdev_capable_devices = self._get_mdev_capable_devices( mdev_capable_devices = self._get_mdev_capable_devices(
types=enabled_vgpu_types) types=enabled_vgpu_types)
counts_per_dev = collections.defaultdict(int) counts_per_dev: ty.Dict[str, int] = collections.defaultdict(int)
for dev in mdev_capable_devices: for dev in mdev_capable_devices:
# dev_id is the libvirt name for the PCI device, # dev_id is the libvirt name for the PCI device,
# eg. pci_0000_84_00_0 which matches a PCI address of 0000:84:00.0 # eg. pci_0000_84_00_0 which matches a PCI address of 0000:84:00.0
@ -7384,7 +7385,9 @@ class LibvirtDriver(driver.ComputeDriver):
return cell.get(page_size, 0) return cell.get(page_size, 0)
def _get_physnet_numa_affinity(): def _get_physnet_numa_affinity():
affinities = {cell.id: set() for cell in topology.cells} affinities: ty.Dict[int, ty.Set[str]] = {
cell.id: set() for cell in topology.cells
}
for physnet in CONF.neutron.physnets: for physnet in CONF.neutron.physnets:
# This will error out if the group is not registered, which is # This will error out if the group is not registered, which is
# exactly what we want as that would be a bug # exactly what we want as that would be a bug
@ -7429,11 +7432,12 @@ class LibvirtDriver(driver.ComputeDriver):
cpuset = cpus & available_shared_cpus cpuset = cpus & available_shared_cpus
pcpuset = cpus & available_dedicated_cpus pcpuset = cpus & available_dedicated_cpus
siblings = sorted(map(set, # de-duplicate and sort the list of CPU sibling sets
set(tuple(cpu.siblings) siblings = sorted(
if cpu.siblings else () set(x) for x in set(
for cpu in cell.cpus) tuple(cpu.siblings) or () for cpu in cell.cpus
)) )
)
cpus &= available_shared_cpus | available_dedicated_cpus cpus &= available_shared_cpus | available_dedicated_cpus
siblings = [sib & cpus for sib in siblings] siblings = [sib & cpus for sib in siblings]
@ -7593,7 +7597,9 @@ class LibvirtDriver(driver.ComputeDriver):
# otherwise. # otherwise.
inv = provider_tree.data(nodename).inventory inv = provider_tree.data(nodename).inventory
ratios = self._get_allocation_ratios(inv) ratios = self._get_allocation_ratios(inv)
resources = collections.defaultdict(set) resources: ty.Dict[str, ty.Set['objects.Resource']] = (
collections.defaultdict(set)
)
result = { result = {
orc.MEMORY_MB: { orc.MEMORY_MB: {
'total': memory_mb, 'total': memory_mb,
@ -7733,11 +7739,11 @@ class LibvirtDriver(driver.ComputeDriver):
return db_const.MAX_INT return db_const.MAX_INT
@property @property
def static_traits(self): def static_traits(self) -> ty.Dict[str, bool]:
if self._static_traits is not None: if self._static_traits is not None:
return self._static_traits return self._static_traits
traits = {} traits: ty.Dict[str, bool] = {}
traits.update(self._get_cpu_traits()) traits.update(self._get_cpu_traits())
traits.update(self._get_storage_bus_traits()) traits.update(self._get_storage_bus_traits())
traits.update(self._get_video_model_traits()) traits.update(self._get_video_model_traits())
@ -7890,7 +7896,7 @@ class LibvirtDriver(driver.ComputeDriver):
:return: dict, keyed by PGPU device ID, to count of VGPUs on that :return: dict, keyed by PGPU device ID, to count of VGPUs on that
device device
""" """
vgpu_count_per_pgpu = collections.defaultdict(int) vgpu_count_per_pgpu: ty.Dict[str, int] = collections.defaultdict(int)
for mdev_uuid in mdev_uuids: for mdev_uuid in mdev_uuids:
# libvirt name is like mdev_00ead764_fdc0_46b6_8db9_2963f5c815b4 # libvirt name is like mdev_00ead764_fdc0_46b6_8db9_2963f5c815b4
dev_name = libvirt_utils.mdev_uuid2name(mdev_uuid) dev_name = libvirt_utils.mdev_uuid2name(mdev_uuid)
@ -8393,7 +8399,7 @@ class LibvirtDriver(driver.ComputeDriver):
return migrate_data return migrate_data
def _get_resources(self, instance, prefix=None): def _get_resources(self, instance, prefix=None):
resources = [] resources: 'objects.ResourceList' = []
if prefix: if prefix:
migr_context = instance.migration_context migr_context = instance.migration_context
attr_name = prefix + 'resources' attr_name = prefix + 'resources'
@ -9089,7 +9095,8 @@ class LibvirtDriver(driver.ComputeDriver):
recover_method, block_migration, recover_method, block_migration,
migrate_data, finish_event, migrate_data, finish_event,
disk_paths): disk_paths):
on_migration_failure = deque()
on_migration_failure: ty.Deque[str] = deque()
data_gb = self._live_migration_data_gb(instance, disk_paths) data_gb = self._live_migration_data_gb(instance, disk_paths)
downtime_steps = list(libvirt_migrate.downtime_steps(data_gb)) downtime_steps = list(libvirt_migrate.downtime_steps(data_gb))
migration = migrate_data.migration migration = migrate_data.migration
@ -10401,7 +10408,7 @@ class LibvirtDriver(driver.ComputeDriver):
@staticmethod @staticmethod
def _get_io_devices(xml_doc): def _get_io_devices(xml_doc):
"""get the list of io devices from the xml document.""" """get the list of io devices from the xml document."""
result = {"volumes": [], "ifaces": []} result: ty.Dict[str, ty.List[str]] = {"volumes": [], "ifaces": []}
try: try:
doc = etree.fromstring(xml_doc) doc = etree.fromstring(xml_doc)
except Exception: except Exception:
@ -10851,7 +10858,7 @@ class LibvirtDriver(driver.ComputeDriver):
nova.privsep.fs.FS_FORMAT_EXT4, nova.privsep.fs.FS_FORMAT_EXT4,
nova.privsep.fs.FS_FORMAT_XFS] nova.privsep.fs.FS_FORMAT_XFS]
def _get_vif_model_traits(self): def _get_vif_model_traits(self) -> ty.Dict[str, bool]:
"""Get vif model traits based on the currently enabled virt_type. """Get vif model traits based on the currently enabled virt_type.
Not all traits generated by this function may be valid and the result Not all traits generated by this function may be valid and the result
@ -10871,7 +10878,7 @@ class LibvirtDriver(driver.ComputeDriver):
in supported_models for model in all_models in supported_models for model in all_models
} }
def _get_storage_bus_traits(self): def _get_storage_bus_traits(self) -> ty.Dict[str, bool]:
"""Get storage bus traits based on the currently enabled virt_type. """Get storage bus traits based on the currently enabled virt_type.
For QEMU and KVM this function uses the information returned by the For QEMU and KVM this function uses the information returned by the
@ -10889,7 +10896,7 @@ class LibvirtDriver(driver.ComputeDriver):
if CONF.libvirt.virt_type in ('qemu', 'kvm'): if CONF.libvirt.virt_type in ('qemu', 'kvm'):
dom_caps = self._host.get_domain_capabilities() dom_caps = self._host.get_domain_capabilities()
supported_buses = set() supported_buses: ty.Set[str] = set()
for arch_type in dom_caps: for arch_type in dom_caps:
for machine_type in dom_caps[arch_type]: for machine_type in dom_caps[arch_type]:
supported_buses.update( supported_buses.update(
@ -10906,7 +10913,7 @@ class LibvirtDriver(driver.ComputeDriver):
supported_buses for bus in all_buses supported_buses for bus in all_buses
} }
def _get_video_model_traits(self): def _get_video_model_traits(self) -> ty.Dict[str, bool]:
"""Get video model traits from libvirt. """Get video model traits from libvirt.
Not all traits generated by this function may be valid and the result Not all traits generated by this function may be valid and the result
@ -10917,7 +10924,7 @@ class LibvirtDriver(driver.ComputeDriver):
all_models = fields.VideoModel.ALL all_models = fields.VideoModel.ALL
dom_caps = self._host.get_domain_capabilities() dom_caps = self._host.get_domain_capabilities()
supported_models = set() supported_models: ty.Set[str] = set()
for arch_type in dom_caps: for arch_type in dom_caps:
for machine_type in dom_caps[arch_type]: for machine_type in dom_caps[arch_type]:
supported_models.update( supported_models.update(
@ -10930,7 +10937,7 @@ class LibvirtDriver(driver.ComputeDriver):
in supported_models for model in all_models in supported_models for model in all_models
} }
def _get_cpu_traits(self): def _get_cpu_traits(self) -> ty.Dict[str, bool]:
"""Get CPU-related traits to be set and unset on the host's resource """Get CPU-related traits to be set and unset on the host's resource
provider. provider.
@ -10942,7 +10949,7 @@ class LibvirtDriver(driver.ComputeDriver):
return traits return traits
def _get_cpu_feature_traits(self): def _get_cpu_feature_traits(self) -> ty.Dict[str, bool]:
"""Get CPU traits of VMs based on guest CPU model config. """Get CPU traits of VMs based on guest CPU model config.
1. If mode is 'host-model' or 'host-passthrough', use host's 1. If mode is 'host-model' or 'host-passthrough', use host's
@ -10981,7 +10988,7 @@ class LibvirtDriver(driver.ComputeDriver):
feature_names = [f.name for f in cpu.features] feature_names = [f.name for f in cpu.features]
return feature_names return feature_names
features = set() features: ty.Set[str] = set()
# Choose a default CPU model when cpu_mode is not specified # Choose a default CPU model when cpu_mode is not specified
if cpu.mode is None: if cpu.mode is None:
caps.host.cpu.model = libvirt_utils.get_cpu_model_from_arch( caps.host.cpu.model = libvirt_utils.get_cpu_model_from_arch(