Consider user configuration during the derivation of passthrough whitelist

The user configuration supplied in NovaPCIPassthrough shall be considered
during the derivation of the pci::passthrough parameter for NIC Partitioning.
The VF's specified by user configuration minus the VF's alloted for NIC
Partitioning shall be derrived.

Change-Id: Ic8f506a0eef5c1c8b1cf0d4fca9b2c6d68ef0fc3
(cherry picked from commit e51920623e)
This commit is contained in:
Karthik S 2020-05-21 10:11:28 +00:00 committed by Emilien Macchi
parent 576aa8f410
commit 2c90340530
2 changed files with 221 additions and 29 deletions

View File

@ -20,6 +20,13 @@ import os
import re
import yaml
from oslo_concurrency import processutils
_PASSTHROUGH_WHITELIST_KEY = 'nova::compute::pci::passthrough'
_PCI_DEVICES_PATH = '/sys/bus/pci/devices'
_SYS_CLASS_NET_PATH = '/sys/class/net'
def get_sriov_configs():
configs = []
@ -44,6 +51,21 @@ def get_sriov_nic_partition_pfs(configs):
return pfs
def get_sriov_non_nic_partition_pfs(configs):
all_pfs = []
non_nicp_pfs = []
nicp_pfs = get_sriov_nic_partition_pfs(configs)
for config in configs:
device_type = config.get('device_type', None)
if device_type and 'pf' in device_type:
name = config.get('name', None)
if name and name not in all_pfs:
all_pfs.append(name)
non_nicp_pfs = [x for x in all_pfs if x not in nicp_pfs]
return non_nicp_pfs
def get_pci_device_info_by_ifname(pci_dir, sub_dir):
if not os.path.isdir(os.path.join(pci_dir, sub_dir)):
return None
@ -57,14 +79,14 @@ def get_pci_device_info_by_ifname(pci_dir, sub_dir):
'device')) as product_file:
product = product_file.read().strip()
return (vendor, product)
except IOError as exc:
except IOError:
return None
def get_pci_addresses_by_ifname(pfs, allocated_pci):
pci_addresses = {}
device_info = {}
pci_dir = '/sys/bus/pci/devices'
pci_dir = _PCI_DEVICES_PATH
if os.path.isdir(pci_dir):
for sub_dir in os.listdir(pci_dir):
if sub_dir in allocated_pci:
@ -79,7 +101,8 @@ def get_pci_addresses_by_ifname(pfs, allocated_pci):
else:
pci_addresses[phyfn].append(sub_dir)
if phyfn not in device_info:
dev_info = get_pci_device_info_by_ifname(pci_dir, sub_dir)
dev_info = get_pci_device_info_by_ifname(pci_dir,
sub_dir)
if dev_info:
device_info[phyfn] = dev_info
return (pci_addresses, device_info)
@ -94,32 +117,201 @@ def get_allocated_pci_addresses(configs):
return alloc_pci_info
def get_pci_passthrough_whitelist(pci_addresses, device_info):
pci_passthrough_whitelist = {}
def get_pci_passthrough_whitelist(user_config, pf, pci_addresses,
device_info):
pci_passthrough_list = []
for pf, pci_list in pci_addresses.items():
for pci in pci_list:
pci_passthrough = {}
address = {}
pci_params = re.split('[:.]+', pci)
address['domain'] = '.*'
address['bus'] = pci_params[1]
address['slot'] = pci_params[2]
address['function'] = pci_params[3]
pci_passthrough['address'] = address
pci_passthrough['vendor_id'] = device_info[pf][0]
pci_passthrough['product_id'] = device_info[pf][1]
pci_passthrough_list.append(pci_passthrough)
pci_passthrough_whitelist['nova::compute::pci::passthrough'] = str(json.dumps(pci_passthrough_list))
return pci_passthrough_whitelist
for pci in pci_addresses:
pci_passthrough = {}
address = {}
pci_params = re.split('[:.]+', pci)
address['domain'] = '.*'
address['bus'] = pci_params[1]
address['slot'] = pci_params[2]
address['function'] = pci_params[3]
pci_passthrough['address'] = address
pci_passthrough['vendor_id'] = device_info[pf][0]
pci_passthrough['product_id'] = device_info[pf][1]
if 'trusted' in user_config:
pci_passthrough['trusted'] = user_config['trusted']
pci_passthrough_list.append(pci_passthrough)
return pci_passthrough_list
def user_passthrough_config():
try:
out, err = processutils.execute(
'hiera', '-c', '/etc/puppet/hiera.yaml',
_PASSTHROUGH_WHITELIST_KEY
)
if not err:
return json.loads(out)
except processutils.ProcessExecutionError:
raise
def get_regex_pattern(config_regex, size):
if config_regex == ".*":
regex_pattern = "[0-9a-fA-F]{%d}" % size
else:
regex_pattern = config_regex
return regex_pattern
def get_passthrough_config(user_config, pf, allocated_pci):
sel_addr = []
if 'address' in user_config:
addr_dict = user_config['address']
user_address_pattern = "%s:%s:%s.%s" % (
get_regex_pattern(addr_dict['domain'], 4),
get_regex_pattern(addr_dict['bus'], 2),
get_regex_pattern(addr_dict['slot'], 2),
addr_dict['function'])
else:
user_address_pattern = ("[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:"
"[0-9a-fA-F]{2}.[0-7]")
pci_addresses, dev_info = get_pci_addresses_by_ifname(pf, allocated_pci)
for pci_addr in pci_addresses[pf]:
user_address_regex = re.compile(user_address_pattern)
if user_address_regex.match(pci_addr):
sel_addr.append(pci_addr)
pci_passthrough = get_pci_passthrough_whitelist(
user_config, pf, sel_addr, dev_info)
return pci_passthrough
def get_passthrough_config_by_address(user_config,
system_configs,
allocated_pci):
nic_part_config = []
non_nic_part_config = []
nic_partition_pfs = get_sriov_nic_partition_pfs(system_configs)
non_nic_partition_pfs = get_sriov_non_nic_partition_pfs(system_configs)
for pf in nic_partition_pfs:
passthrough_tmp = get_passthrough_config(
user_config, pf, allocated_pci)
nic_part_config.extend(passthrough_tmp)
if len(nic_part_config) == 0:
return []
for pf in non_nic_partition_pfs:
passthrough_tmp = get_passthrough_config(
user_config, pf, allocated_pci)
non_nic_part_config.extend(passthrough_tmp)
return nic_part_config + non_nic_part_config
def get_passthrough_config_by_product(user_config,
system_configs,
allocated_pci):
nic_part_config = []
non_nic_part_config = []
nic_partition_pfs = get_sriov_nic_partition_pfs(system_configs)
non_nic_partition_pfs = get_sriov_non_nic_partition_pfs(system_configs)
for pf in nic_partition_pfs:
pf_path = _SYS_CLASS_NET_PATH + "/%s/device" % pf
vendor, product = get_pci_device_info_by_ifname(pf_path, 'virtfn0')
if (user_config['product_id'][-4:] == product[-4:] and
user_config['vendor_id'][-4:] == vendor[-4:]):
passthrough_tmp = get_passthrough_config(
user_config, pf, allocated_pci)
nic_part_config.extend(passthrough_tmp)
if len(nic_part_config) == 0:
return []
for pf in non_nic_partition_pfs:
pf_path = _SYS_CLASS_NET_PATH + "/%s/device" % pf
vendor, product = get_pci_device_info_by_ifname(pf_path, 'virtfn0')
if (user_config['product_id'][-4:] == product[-4:] and
user_config['vendor_id'][-4:] == vendor[-4:]):
passthrough_tmp = get_passthrough_config(
user_config, pf, allocated_pci)
non_nic_part_config.extend(passthrough_tmp)
return nic_part_config + non_nic_part_config
def get_pf_name_from_phy_network(physical_network):
try:
out, err = processutils.execute(
'hiera', '-c', '/etc/puppet/hiera.yaml',
'neutron::agents::ml2::sriov::physical_device_mappings')
if not err:
phys_dev_mappings = json.loads(out)
for phy_dev_map in phys_dev_mappings:
net_name, nic_name = phy_dev_map.split(':')
if net_name == physical_network:
return nic_name
return None
except processutils.ProcessExecutionError:
raise
def generate_combined_configuration(user_configs, system_configs):
"""Derived configuration = user_config - system_configs
Identify the user_defined configuration that overlaps with the
NIC Partitioned VFs and remove those VFs from the derived configuration
In case of no overlap, the user defined configuration shall be used
as it is.
:param user_configs: THT param NovaPCIPassthrough
:param system_configs: Derived from sriov-mapping.yaml
"""
non_nic_part_config = []
nic_part_config = []
allocated_pci = get_allocated_pci_addresses(system_configs)
nic_partition_pfs = get_sriov_nic_partition_pfs(system_configs)
for user_config in user_configs:
if ('devname' in user_config and
(user_config['devname'] in nic_partition_pfs)):
passthru_tmp = get_passthrough_config(
user_config, user_config['devname'], allocated_pci)
nic_part_config.extend(passthru_tmp)
elif 'physical_network' in user_config:
pf = get_pf_name_from_phy_network(user_config['physical_network'])
if pf in nic_partition_pfs:
passthru_tmp = get_passthrough_config(
user_config, pf, allocated_pci)
nic_part_config.extend(passthru_tmp)
else:
non_nic_part_config.append(user_config)
elif 'address' in user_config:
passthrough_tmp = get_passthrough_config_by_address(
user_config, system_configs, allocated_pci)
if len(passthrough_tmp) == 0:
non_nic_part_config.append(user_config)
else:
nic_part_config.extend(passthrough_tmp)
elif ('product_id' in user_config and 'vendor_id' in user_config):
passthrough_tmp = get_passthrough_config_by_product(
user_config, system_configs, allocated_pci)
if len(passthrough_tmp) == 0:
non_nic_part_config.append(user_config)
else:
nic_part_config.extend(passthrough_tmp)
else:
non_nic_part_config.append(user_config)
return (non_nic_part_config, nic_part_config)
if __name__ == "__main__":
pci_passthrough = {}
pci_file_path = '/etc/puppet/hieradata/pci_passthrough_whitelist.json'
configs = get_sriov_configs()
nic_partition_pfs = get_sriov_nic_partition_pfs(configs)
allocated_pci = get_allocated_pci_addresses(configs)
pci_addresses, device_info = get_pci_addresses_by_ifname(nic_partition_pfs, allocated_pci)
pci_passthrough = get_pci_passthrough_whitelist(pci_addresses, device_info)
with open(pci_file_path, 'w') as pci_file:
json.dump(pci_passthrough, pci_file)
system_configs = get_sriov_configs()
user_configs = user_passthrough_config()
non_nic_part, nic_part = generate_combined_configuration(
user_configs, system_configs)
if len(nic_part) > 0:
pci_passthrough[_PASSTHROUGH_WHITELIST_KEY] = (non_nic_part +
nic_part)
with open(pci_file_path, 'w') as pci_file:
json.dump(pci_passthrough, pci_file)

View File

@ -231,8 +231,8 @@ outputs:
mode: 0750
- name: derive pci passthrough whitelist
copy:
content: {get_file: ./derive_pci_passthrough_whitelist.py}
dest: '/var/lib/pci_passthrough_whitelist_scripts/derive_pci_passthrough_whitelist.py'
src: /usr/share/openstack-tripleo-heat-templates/deployment/neutron/derive_pci_passthrough_whitelist.py
dest: /var/lib/pci_passthrough_whitelist_scripts/derive_pci_passthrough_whitelist.py
mode: 0700
- name: run derive_pci_passthrough_whitelist.py
command: /var/lib/pci_passthrough_whitelist_scripts/derive_pci_passthrough_whitelist.py