diff --git a/hooks/install b/hooks/install deleted file mode 120000 index 55aa8e52..00000000 --- a/hooks/install +++ /dev/null @@ -1 +0,0 @@ -neutron_ovs_hooks.py \ No newline at end of file diff --git a/hooks/install b/hooks/install new file mode 100755 index 00000000..c6aa01b9 --- /dev/null +++ b/hooks/install @@ -0,0 +1,27 @@ +#!/bin/bash -e +# Wrapper to ensure that python dependencies are installed before we get into +# the python part of the hook execution. + +check_and_install() { + pkg="${1}" + if ! dpkg -s ${pkg} 2>&1 > /dev/null; then + apt-get -y install ${pkg} + fi +} + +# NOTE: If any of enable-dpdk, enable-sriov and enable-hardware-offload config +# options is set to `True` then package sriov-netplan-shim from +# ppa:openstack-charmers/networking-tools must be installed. +USE_DPDK=`config-get enable-dpdk` +USE_SRIOV=`config-get enable-sriov` +USE_HW_OFFLOAD=`config-get enable-hardware-offload` +NETWORKING_TOOLS_PPA=`config-get networking-tools-source` + +if [[ $NETWORKING_TOOLS_PPA ]] && \ + [[ $USE_DPDK == "True" || $USE_SRIOV == "True" || $USE_HW_OFFLOAD == "True" ]]; then + apt-add-repository --yes $NETWORKING_TOOLS_PPA + apt-get update + check_and_install sriov-netplan-shim +fi + +exec ./hooks/install.real diff --git a/hooks/install.real b/hooks/install.real new file mode 120000 index 00000000..55aa8e52 --- /dev/null +++ b/hooks/install.real @@ -0,0 +1 @@ +neutron_ovs_hooks.py \ No newline at end of file diff --git a/hooks/neutron_ovs_context.py b/hooks/neutron_ovs_context.py index 2120bcc9..dd8d387b 100644 --- a/hooks/neutron_ovs_context.py +++ b/hooks/neutron_ovs_context.py @@ -12,11 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import collections -import glob import os import uuid -from pci import PCINetDevices from charmhelpers.core.hookenv import ( config, log, @@ -42,13 +39,11 @@ from charmhelpers.contrib.network.ip import ( from charmhelpers.contrib.openstack.context import ( OSContextGenerator, NeutronAPIContext, - parse_data_port_mappings ) from charmhelpers.contrib.openstack.utils import ( os_release, CompareOpenStackReleases, ) -from charmhelpers.core.unitdata import kv IPTABLES_HYBRID = 'iptables_hybrid' OPENVSWITCH = 'openvswitch' @@ -368,182 +363,6 @@ class L3AgentContext(OSContextGenerator): return ctxt -def resolve_dpdk_bridges(): - ''' - Resolve local PCI devices from configured mac addresses - using the data-port configuration option - - @return: OrderDict indexed by PCI device address. - ''' - ports = config('data-port') - devices = PCINetDevices() - resolved_devices = collections.OrderedDict() - db = kv() - if ports: - # NOTE: ordered dict of format {[mac]: bridge} - portmap = parse_data_port_mappings(ports) - for mac, bridge in portmap.items(): - pcidev = devices.get_device_from_mac(mac) - if pcidev: - # NOTE: store mac->pci allocation as post binding - # to dpdk, it disappears from PCIDevices. - db.set(mac, pcidev.pci_address) - db.flush() - - pci_address = db.get(mac) - if pci_address: - resolved_devices[pci_address] = bridge - - return resolved_devices - - -def resolve_dpdk_bonds(): - ''' - Resolve local PCI devices from configured mac addresses - using the dpdk-bond-mappings configuration option - - @return: OrderDict indexed by PCI device address. - ''' - bonds = config('dpdk-bond-mappings') - devices = PCINetDevices() - resolved_devices = collections.OrderedDict() - db = kv() - if bonds: - # NOTE: ordered dict of format {[mac]: bond} - bondmap = parse_data_port_mappings(bonds) - for mac, bond in bondmap.items(): - pcidev = devices.get_device_from_mac(mac) - if pcidev: - # NOTE: store mac->pci allocation as post binding - # to dpdk, it disappears from PCIDevices. - db.set(mac, pcidev.pci_address) - db.flush() - - pci_address = db.get(mac) - if pci_address: - resolved_devices[pci_address] = bond - - return resolved_devices - - -def parse_cpu_list(cpulist): - ''' - Parses a linux cpulist for a numa node - - @return list of cores - ''' - cores = [] - ranges = cpulist.split(',') - for cpu_range in ranges: - if "-" in cpu_range: - cpu_min_max = cpu_range.split('-') - cores += range(int(cpu_min_max[0]), - int(cpu_min_max[1]) + 1) - else: - cores.append(int(cpu_range)) - return cores - - -def numa_node_cores(): - '''Dict of numa node -> cpu core mapping''' - nodes = {} - node_regex = '/sys/devices/system/node/node*' - for node in glob.glob(node_regex): - index = node.lstrip('/sys/devices/system/node/node') - with open(os.path.join(node, 'cpulist')) as cpulist: - nodes[index] = parse_cpu_list(cpulist.read().strip()) - return nodes - - -class DPDKDeviceContext(OSContextGenerator): - - def __call__(self): - driver = config('dpdk-driver') - if driver is None: - return {} - # Resolve PCI devices for both directly used devices (_bridges) - # and devices for use in dpdk bonds (_bonds) - pci_devices = resolve_dpdk_bridges() - pci_devices.update(resolve_dpdk_bonds()) - return {'devices': pci_devices, - 'driver': driver} - - -class OVSDPDKDeviceContext(OSContextGenerator): - - def cpu_mask(self): - ''' - Hex formatted CPU mask based on using the first - config:dpdk-socket-cores cores of each NUMA node - in the unit. - ''' - num_cores = config('dpdk-socket-cores') - mask = 0 - for cores in numa_node_cores().values(): - for core in cores[:num_cores]: - mask = mask | 1 << core - return format(mask, '#04x') - - def socket_memory(self): - ''' - Formatted list of socket memory configuration for dpdk using - config:dpdk-socket-memory per NUMA node. - ''' - sm_size = config('dpdk-socket-memory') - node_regex = '/sys/devices/system/node/node*' - mem_list = [str(sm_size) for _ in glob.glob(node_regex)] - if mem_list: - return ','.join(mem_list) - else: - return str(sm_size) - - def devices(self): - '''List of PCI devices for use by DPDK''' - pci_devices = resolve_dpdk_bridges() - pci_devices.update(resolve_dpdk_bonds()) - return pci_devices - - def _formatted_whitelist(self, flag): - '''Flag formatted list of devices to whitelist - - :param flag: flag format to use - :type flag: str - :rtype: str - ''' - whitelist = [] - for device in self.devices(): - whitelist.append(flag.format(device=device)) - return ' '.join(whitelist) - - def device_whitelist(self): - ''' - Formatted list of devices to whitelist for dpdk - using the old style '-w' flag - - :rtype: str - ''' - return self._formatted_whitelist('-w {device}') - - def pci_whitelist(self): - ''' - Formatted list of devices to whitelist for dpdk - using the new style '--pci-whitelist' flag - - :rtype: str - ''' - return self._formatted_whitelist('--pci-whitelist {device}') - - def __call__(self): - ctxt = {} - whitelist = self.device_whitelist() - if whitelist: - ctxt['dpdk_enabled'] = config('enable-dpdk') - ctxt['device_whitelist'] = self.device_whitelist() - ctxt['socket_memory'] = self.socket_memory() - ctxt['cpu_mask'] = self.cpu_mask() - return ctxt - - SHARED_SECRET = "/etc/neutron/secret.txt" diff --git a/hooks/neutron_ovs_utils.py b/hooks/neutron_ovs_utils.py index c0a9208c..ab37fe4a 100644 --- a/hooks/neutron_ovs_utils.py +++ b/hooks/neutron_ovs_utils.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import hashlib import json import os from itertools import chain @@ -38,6 +37,7 @@ import neutron_ovs_context from charmhelpers.contrib.network.ovs import ( add_bridge, add_bridge_port, + add_bridge_bond, is_linuxbridge_interface, add_ovsbridge_linuxbridge, full_restart, @@ -50,6 +50,7 @@ from charmhelpers.core.hookenv import ( log, status_set, ERROR, + WARNING, ) from charmhelpers.contrib.openstack.neutron import ( parse_bridge_mappings, @@ -57,11 +58,15 @@ from charmhelpers.contrib.openstack.neutron import ( headers_package, ) from charmhelpers.contrib.openstack.context import ( - ExternalPortContext, DataPortContext, - WorkerConfigContext, - parse_data_port_mappings, DHCPAgentContext, + DPDKDeviceContext, + ExternalPortContext, + OVSDPDKDeviceContext, + WorkerConfigContext, + BondConfig, + BridgePortInterfaceMap, + parse_data_port_mappings, validate_ovs_use_veth, ) from charmhelpers.core.host import ( @@ -174,17 +179,6 @@ BASE_RESOURCE_MAP = OrderedDict([ 'services': ['neutron-openvswitch-agent'], 'contexts': [neutron_ovs_context.OVSPluginContext()], }), - (OVS_DEFAULT, { - 'services': ['openvswitch-switch'], - 'contexts': [neutron_ovs_context.OVSDPDKDeviceContext(), - neutron_ovs_context.OVSPluginContext(), - neutron_ovs_context.RemoteRestartContext( - ['neutron-plugin', 'neutron-control'])], - }), - (DPDK_INTERFACES, { - 'services': ['dpdk', 'openvswitch-switch'], - 'contexts': [neutron_ovs_context.DPDKDeviceContext()], - }), (PHY_NIC_MTU_CONF, { 'services': ['os-charm-phy-nic-mtu'], 'contexts': [context.PhyNICMTUContext()], @@ -222,6 +216,18 @@ DVR_RESOURCE_MAP = OrderedDict([ 'contexts': [context.ExternalPortContext()], }), ]) +DPDK_RESOURCE_MAP = OrderedDict([ + (OVS_DEFAULT, { + 'services': ['openvswitch-switch'], + 'contexts': [DPDKDeviceContext(), + neutron_ovs_context.RemoteRestartContext( + ['neutron-plugin', 'neutron-control'])], + }), + (DPDK_INTERFACES, { + 'services': ['dpdk', 'openvswitch-switch'], + 'contexts': [DPDKDeviceContext()], + }), +]) SRIOV_RESOURCE_MAP = OrderedDict([ (NEUTRON_SRIOV_AGENT_CONF, { 'services': ['neutron-sriov-agent'], @@ -247,7 +253,7 @@ def install_packages(): # The default PPA published packages back to Xenial, which covers # all target series for this charm. if config('networking-tools-source') and \ - (enable_sriov() or use_hw_offload()): + (use_dpdk() or enable_sriov() or use_hw_offload()): add_source(config('networking-tools-source')) apt_update() # NOTE(jamespage): ensure early install of dkms related @@ -333,12 +339,9 @@ def determine_packages(): pkgs.append('neutron-sriov-agent') else: pkgs.append('neutron-plugin-sriov-agent') - pkgs.append('sriov-netplan-shim') if use_hw_offload(): pkgs.append('mlnx-switchdev-mode') - if 'sriov-netplan-shim' not in pkgs: - pkgs.append('sriov-netplan-shim') if cmp_release >= 'rocky': pkgs = [p for p in pkgs if not p.startswith('python-')] @@ -367,10 +370,11 @@ def register_configs(release=None): def resource_map(): - ''' - Dynamically generate a map of resources that will be managed for a single - hook execution. - ''' + """Get map of resources that will be managed for a single hook execution. + + :returns: map of resources + :rtype: OrderedDict[str,Dict[str,List[str]]] + """ drop_config = [] resource_map = deepcopy(BASE_RESOURCE_MAP) if use_dvr(): @@ -395,10 +399,12 @@ def resource_map(): resource_map[NEUTRON_CONF]['services'].append( 'neutron-openvswitch-agent' ) - if not use_dpdk(): - drop_config.append(DPDK_INTERFACES) + if use_dpdk(): + resource_map.update(DPDK_RESOURCE_MAP) + if ovs_has_late_dpdk_init(): + drop_config.append(OVS_DEFAULT) else: - drop_config.extend([OVS_CONF, DPDK_INTERFACES]) + drop_config.append(OVS_CONF) if enable_sriov(): sriov_agent_name = 'neutron-sriov-agent' @@ -525,7 +531,7 @@ def enable_ovs_dpdk(): subprocess.check_call(UPDATE_ALTERNATIVES + [OVS_DPDK_BIN]) values_changed = [] if ovs_has_late_dpdk_init(): - dpdk_context = neutron_ovs_context.OVSDPDKDeviceContext() + dpdk_context = OVSDPDKDeviceContext() other_config = OrderedDict([ ('dpdk-lcore-mask', dpdk_context.cpu_mask()), ('dpdk-socket-mem', dpdk_context.socket_memory()), @@ -593,9 +599,15 @@ def configure_ovs(): status_set('maintenance', 'Configuring ovs') if not service_running('openvswitch-switch'): full_restart() - datapath_type = determine_datapath_type() - add_bridge(INT_BRIDGE, datapath_type) - add_bridge(EXT_BRIDGE, datapath_type) + + # all bridges use the same datapath_type + brdata = { + 'datapath-type': determine_datapath_type(), + } + + add_bridge(INT_BRIDGE, brdata=brdata) + add_bridge(EXT_BRIDGE, brdata=brdata) + ext_port_ctx = None if use_dvr(): ext_port_ctx = ExternalPortContext()() @@ -605,6 +617,7 @@ def configure_ovs(): modern_ovs = ovs_has_late_dpdk_init() bridgemaps = None + portmaps = None if not use_dpdk(): # NOTE(jamespage): # Its possible to support both hardware offloaded 'direct' ports @@ -614,7 +627,7 @@ def configure_ovs(): portmaps = DataPortContext()() bridgemaps = parse_bridge_mappings(config('bridge-mappings')) for br in bridgemaps.values(): - add_bridge(br, datapath_type) + add_bridge(br, brdata=brdata) if not portmaps: continue @@ -634,81 +647,78 @@ def configure_ovs(): level=ERROR) elif use_dpdk(): log('Configuring bridges with DPDK', level=DEBUG) - global_mtu = ( - neutron_ovs_context.NeutronAPIContext()()['global_physnet_mtu']) - # NOTE: when in dpdk mode, add based on pci bus order - # with type 'dpdk' - bridgemaps = neutron_ovs_context.resolve_dpdk_bridges() - log('bridgemaps: {}'.format(bridgemaps), level=DEBUG) - device_index = 0 - for pci_address, br in bridgemaps.items(): - log('Adding DPDK bridge: {}:{}'.format(br, datapath_type), + + # TODO(sahid): We should also take into account the + # "physical-network-mtus" in case different MTUs are + # configured based on physical networks. + global_mtu = (neutron_ovs_context.NeutronAPIContext() + ()['global_physnet_mtu']) + + dpdk_context = OVSDPDKDeviceContext() + devices = dpdk_context.devices() + + portmaps = parse_data_port_mappings(config('data-port')) + bridgemaps = parse_bridge_mappings(config('bridge-mappings')) + + bridge_port_interface_map = BridgePortInterfaceMap() + bond_config = BondConfig() + + for br, port_iface_map in bridge_port_interface_map.items(): + log('Adding DPDK bridge: {}:{}'.format(br, brdata), level=DEBUG) - add_bridge(br, datapath_type) + add_bridge(br, brdata=brdata) if modern_ovs: - portname = 'dpdk-{}'.format( - hashlib.sha1(pci_address.encode('UTF-8')).hexdigest()[:7] - ) - else: - portname = 'dpdk{}'.format(device_index) - - log('Adding DPDK port: {}:{}:{}'.format(br, portname, - pci_address), - level=DEBUG) - dpdk_add_bridge_port(br, portname, - pci_address) - # TODO(sahid): We should also take into account the - # "physical-network-mtus" in case different MTUs are - # configured based on physical networks. - dpdk_set_mtu_request(portname, global_mtu) - device_index += 1 - - if modern_ovs: - log('Configuring bridges with modern_ovs/DPDK', - level=DEBUG) - bondmaps = neutron_ovs_context.resolve_dpdk_bonds() - log('bondmaps: {}'.format(bondmaps), level=DEBUG) - bridge_bond_map = DPDKBridgeBondMap() - portmap = parse_data_port_mappings(config('data-port')) - log('portmap: {}'.format(portmap), level=DEBUG) - for pci_address, bond in bondmaps.items(): - if bond in portmap: - log('Adding DPDK bridge: {}:{}'.format(portmap[bond], - datapath_type), - level=DEBUG) - add_bridge(portmap[bond], datapath_type) - portname = 'dpdk-{}'.format( - hashlib.sha1(pci_address.encode('UTF-8')) - .hexdigest()[:7] - ) - bridge_bond_map.add_port(portmap[bond], bond, - portname, pci_address) - - log('bridge_bond_map: {}'.format(bridge_bond_map), - level=DEBUG) - bond_configs = DPDKBondsConfig() - for br, bonds in bridge_bond_map.items(): - for bond, port_map in bonds.items(): - log('Adding DPDK bond: {}:{}:{}'.format(br, bond, - port_map), - level=DEBUG) - dpdk_add_bridge_bond(br, bond, port_map) - dpdk_set_interfaces_mtu( - global_mtu, - port_map.keys()) - log('Configuring DPDK bond: {}:{}'.format( - bond, - bond_configs.get_bond_config(bond)), - level=DEBUG) - dpdk_set_bond_config( - bond, - bond_configs.get_bond_config(bond) - ) + for port in port_iface_map.keys(): + ifdatamap = bridge_port_interface_map.get_ifdatamap( + br, port) + # NOTE: DPDK bonds are referenced by name and can be found + # in the data-port config, regular DPDK ports are + # referenced by MAC addresses and their names should + # never be found in data-port + if port in portmaps.keys(): + log('Adding DPDK bond: {}({}) to bridge: {}'.format( + port, list(ifdatamap.keys()), br), level=DEBUG) + add_bridge_bond( + br, port, list(ifdatamap.keys()), + portdata=bond_config.get_ovs_portdata(port), + ifdatamap=ifdatamap) + else: + log('Adding DPDK port: {} to bridge: {}'.format( + port, br), level=DEBUG) + ifdata = ifdatamap[port] + add_bridge_port(br, port, ifdata=ifdata, + linkup=False, promisc=None) + if not modern_ovs: + # port enumeration in legacy OVS-DPDK must follow alphabetic order + # of the PCI addresses + dev_idx = 0 + for pci, mac in sorted(devices.items()): + # if mac.entity is a bridge, then the port can be added + # directly, otherwise it is a bond (supported only in + # modern_ovs) or misconfiguration + if mac.entity in bridgemaps.values(): + ifdata = { + 'type': 'dpdk', + 'mtu-request': global_mtu + } + ifname = 'dpdk{}'.format(dev_idx) + log('Adding DPDK port {}:{} to bridge {}'.format( + ifname, ifdata, mac.entity), level=DEBUG) + add_bridge_port( + mac.entity, ifname, ifdata=ifdata, linkup=False, + promisc=None) + else: + log('DPDK device {} skipped, {} is not a bridge'.format( + pci, mac.entity), level=WARNING) + dev_idx += 1 target = config('ipfix-target') bridges = [INT_BRIDGE, EXT_BRIDGE] if bridgemaps: bridges.extend(bridgemaps.values()) + elif portmaps: + bridges.extend( + [bridge_mac.entity for bridge_mac in portmaps.values()]) if target: for bridge in bridges: @@ -769,7 +779,7 @@ def determine_datapath_type(): def use_dpdk(): '''Determine whether DPDK should be used''' cmp_release = CompareOpenStackReleases( - os_release('neutron-common', base='icehouse')) + os_release('neutron-common', base='icehouse', reset_cache=True)) return (cmp_release >= 'mitaka' and config('enable-dpdk')) @@ -825,72 +835,6 @@ class SRIOVContext_adapter(object): return {'sriov_device': self._sriov_device} -# TODO: update into charm-helpers to add port_type parameter -def dpdk_add_bridge_port(name, port, pci_address=None): - ''' Add a port to the named openvswitch bridge ''' - # log('Adding port {} to bridge {}'.format(port, name)) - if ovs_has_late_dpdk_init(): - cmd = ["ovs-vsctl", - "add-port", name, port, "--", - "set", "Interface", port, - "type=dpdk", - "options:dpdk-devargs={}".format(pci_address)] - else: - cmd = ["ovs-vsctl", "--", - "--may-exist", "add-port", name, port, - "--", "set", "Interface", port, "type=dpdk"] - subprocess.check_call(cmd) - - -def dpdk_add_bridge_bond(bridge_name, bond_name, port_map): - ''' Add ports to a bond attached to the named openvswitch bridge ''' - if not ovs_has_late_dpdk_init(): - raise Exception("Bonds are not supported for OVS pre-2.6.0") - - cmd = ["ovs-vsctl", "--may-exist", - "add-bond", bridge_name, bond_name] - for portname in port_map.keys(): - cmd.append(portname) - for portname, pci_address in port_map.items(): - cmd.extend(["--", "set", "Interface", portname, - "type=dpdk", - "options:dpdk-devargs={}".format(pci_address)]) - subprocess.check_call(cmd) - - -def dpdk_set_bond_config(bond_name, config): - if not ovs_has_late_dpdk_init(): - raise Exception("Bonds are not supported for OVS pre-2.6.0") - - cmd = ["ovs-vsctl", - "--", "set", "port", bond_name, - "bond_mode={}".format(config['mode']), - "--", "set", "port", bond_name, - "lacp={}".format(config['lacp']), - "--", "set", "port", bond_name, - "other_config:lacp-time=={}".format(config['lacp-time']), - ] - subprocess.check_call(cmd) - - -def dpdk_set_mtu_request(port, mtu): - cmd = ["ovs-vsctl", "set", "Interface", port, - "mtu_request={}".format(mtu)] - subprocess.check_call(cmd) - - -def dpdk_set_interfaces_mtu(mtu, ports): - """Set MTU on dpdk ports. - - :param mtu: Name of unit to match - :type mtu: str - :param ports: List of ports - :type ports: [] - """ - for port in ports: - dpdk_set_mtu_request(port, mtu) - - def enable_nova_metadata(): return not is_container() and (use_dvr() or enable_local_dhcp()) @@ -982,91 +926,3 @@ def _pause_resume_helper(f, configs, exclude_services=None): f(assess_status_func(configs, exclude_services), services=services(exclude_services), ports=None) - - -class DPDKBridgeBondMap(): - - def __init__(self): - self.map = {} - - def add_port(self, bridge, bond, portname, pci_address): - if bridge not in self.map: - self.map[bridge] = {} - if bond not in self.map[bridge]: - self.map[bridge][bond] = {} - self.map[bridge][bond][portname] = pci_address - - def items(self): - return list(self.map.items()) - - -class DPDKBondsConfig(): - ''' - A class to parse dpdk-bond-config into a dictionary and - provide a convenient config get interface. - ''' - - DEFAUL_LACP_CONFIG = { - 'mode': 'balance-tcp', - 'lacp': 'active', - 'lacp-time': 'fast' - } - ALL_BONDS = 'ALL_BONDS' - - BOND_MODES = ['active-backup', 'balance-slb', 'balance-tcp'] - BOND_LACP = ['active', 'passive', 'off'] - BOND_LACP_TIME = ['fast', 'slow'] - - def __init__(self): - - self.lacp_config = { - self.ALL_BONDS: deepcopy(self.DEFAUL_LACP_CONFIG) - } - - lacp_config = config('dpdk-bond-config') - if lacp_config: - lacp_config_map = lacp_config.split() - for entry in lacp_config_map: - bond, entry = self._partition_entry(entry) - if not bond: - bond = self.ALL_BONDS - - mode, entry = self._partition_entry(entry) - if not mode: - mode = self.DEFAUL_LACP_CONFIG['mode'] - assert mode in self.BOND_MODES, \ - "Bond mode {} is invalid".format(mode) - - lacp, entry = self._partition_entry(entry) - if not lacp: - lacp = self.DEFAUL_LACP_CONFIG['lacp'] - assert lacp in self.BOND_LACP, \ - "Bond lacp {} is invalid".format(lacp) - - lacp_time, entry = self._partition_entry(entry) - if not lacp_time: - lacp_time = self.DEFAUL_LACP_CONFIG['lacp-time'] - assert lacp_time in self.BOND_LACP_TIME, \ - "Bond lacp-time {} is invalid".format(lacp_time) - - self.lacp_config[bond] = { - 'mode': mode, - 'lacp': lacp, - 'lacp-time': lacp_time - } - - def _partition_entry(self, entry): - t = entry.partition(":") - return t[0], t[2] - - def get_bond_config(self, bond): - ''' - Get the LACP configuration for a bond - - :param bond: the bond name - :return: a dictionary with the configuration of the - ''' - if bond not in self.lacp_config: - return self.lacp_config[self.ALL_BONDS] - - return self.lacp_config[bond] diff --git a/unit_tests/test_neutron_ovs_context.py b/unit_tests/test_neutron_ovs_context.py index 942d6cc3..29c95d18 100644 --- a/unit_tests/test_neutron_ovs_context.py +++ b/unit_tests/test_neutron_ovs_context.py @@ -14,10 +14,9 @@ from test_utils import CharmTestCase from test_utils import patch_open -from mock import patch, Mock +from mock import patch import neutron_ovs_context as context import charmhelpers -import copy _LSB_RELEASE_XENIAL = { 'DISTRIB_CODENAME': 'xenial', @@ -32,8 +31,6 @@ TO_PATCH = [ 'unit_get', 'get_host_ip', 'network_get_primary_address', - 'glob', - 'PCINetDevices', 'relation_ids', 'relation_get', 'related_units', @@ -627,194 +624,6 @@ class SharedSecretContext(CharmTestCase): self.assertEqual(context.SharedSecretContext()(), {}) -class MockPCIDevice(object): - '''Simple wrapper to mock pci.PCINetDevice class''' - def __init__(self, address): - self.pci_address = address - - -TEST_CPULIST_1 = "0-3" -TEST_CPULIST_2 = "0-7,16-23" -TEST_CPULIST_3 = "0,4,8,12,16,20,24" -DPDK_DATA_PORTS = ( - "br-phynet3:fe:16:41:df:23:fe " - "br-phynet1:fe:16:41:df:23:fd " - "br-phynet2:fe:f2:d0:45:dc:66" -) -BOND_MAPPINGS = ( - "bond0:fe:16:41:df:23:fe " - "bond0:fe:16:41:df:23:fd " - "bond1:fe:f2:d0:45:dc:66" -) -PCI_DEVICE_MAP = { - 'fe:16:41:df:23:fd': MockPCIDevice('0000:00:1c.0'), - 'fe:16:41:df:23:fe': MockPCIDevice('0000:00:1d.0'), -} - - -class TestDPDKUtils(CharmTestCase): - - def setUp(self): - super(TestDPDKUtils, self).setUp(context, TO_PATCH) - self.config.side_effect = self.test_config.get - - def test_parse_cpu_list(self): - self.assertEqual(context.parse_cpu_list(TEST_CPULIST_1), - [0, 1, 2, 3]) - self.assertEqual(context.parse_cpu_list(TEST_CPULIST_2), - [0, 1, 2, 3, 4, 5, 6, 7, - 16, 17, 18, 19, 20, 21, 22, 23]) - self.assertEqual(context.parse_cpu_list(TEST_CPULIST_3), - [0, 4, 8, 12, 16, 20, 24]) - - @patch.object(context, 'parse_cpu_list', wraps=context.parse_cpu_list) - def test_numa_node_cores(self, _parse_cpu_list): - self.glob.glob.return_value = [ - '/sys/devices/system/node/node0' - ] - with patch_open() as (_, mock_file): - mock_file.read.return_value = TEST_CPULIST_1 - self.assertEqual(context.numa_node_cores(), - {'0': [0, 1, 2, 3]}) - self.glob.glob.assert_called_with('/sys/devices/system/node/node*') - _parse_cpu_list.assert_called_with(TEST_CPULIST_1) - - def test_resolve_dpdk_bridges(self): - self.test_config.set('data-port', DPDK_DATA_PORTS) - _pci_devices = Mock() - _pci_devices.get_device_from_mac.side_effect = PCI_DEVICE_MAP.get - self.PCINetDevices.return_value = _pci_devices - self.assertEqual(context.resolve_dpdk_bridges(), - {'0000:00:1c.0': 'br-phynet1', - '0000:00:1d.0': 'br-phynet3'}) - - def test_resolve_dpdk_bonds(self): - self.test_config.set('dpdk-bond-mappings', BOND_MAPPINGS) - _pci_devices = Mock() - _pci_devices.get_device_from_mac.side_effect = PCI_DEVICE_MAP.get - self.PCINetDevices.return_value = _pci_devices - self.assertEqual(context.resolve_dpdk_bonds(), - {'0000:00:1c.0': 'bond0', - '0000:00:1d.0': 'bond0'}) - - -DPDK_PATCH = [ - 'parse_cpu_list', - 'numa_node_cores', - 'resolve_dpdk_bridges', - 'resolve_dpdk_bonds', - 'glob', -] - -NUMA_CORES_SINGLE = { - '0': [0, 1, 2, 3] -} - -NUMA_CORES_MULTI = { - '0': [0, 1, 2, 3], - '1': [4, 5, 6, 7] -} - - -class TestOVSDPDKDeviceContext(CharmTestCase): - - def setUp(self): - super(TestOVSDPDKDeviceContext, self).setUp(context, - TO_PATCH + DPDK_PATCH) - self.config.side_effect = self.test_config.get - self.test_context = context.OVSDPDKDeviceContext() - self.test_config.set('enable-dpdk', True) - - def test_device_whitelist(self): - '''Test device whitelist generation''' - self.resolve_dpdk_bridges.return_value = { - '0000:00:1c.0': 'br-data', - '0000:00:1d.0': 'br-data', - } - self.assertEqual(self.test_context.device_whitelist(), - '-w 0000:00:1c.0 -w 0000:00:1d.0') - - def test_socket_memory(self): - '''Test socket memory configuration''' - self.glob.glob.return_value = ['a'] - self.assertEqual(self.test_context.socket_memory(), - '1024') - - self.glob.glob.return_value = ['a', 'b'] - self.assertEqual(self.test_context.socket_memory(), - '1024,1024') - - self.test_config.set('dpdk-socket-memory', 2048) - self.assertEqual(self.test_context.socket_memory(), - '2048,2048') - - def test_cpu_mask(self): - '''Test generation of hex CPU masks''' - self.numa_node_cores.return_value = NUMA_CORES_SINGLE - self.assertEqual(self.test_context.cpu_mask(), '0x01') - - self.numa_node_cores.return_value = NUMA_CORES_MULTI - self.assertEqual(self.test_context.cpu_mask(), '0x11') - - self.test_config.set('dpdk-socket-cores', 2) - self.assertEqual(self.test_context.cpu_mask(), '0x33') - - def test_context_no_devices(self): - '''Ensure that DPDK is disable when no devices detected''' - self.resolve_dpdk_bridges.return_value = {} - self.assertEqual(self.test_context(), {}) - - def test_context_devices(self): - '''Ensure DPDK is enabled when devices are detected''' - self.resolve_dpdk_bridges.return_value = { - '0000:00:1c.0': 'br-data', - '0000:00:1d.0': 'br-data', - } - self.resolve_dpdk_bonds.return_value = {} - self.numa_node_cores.return_value = NUMA_CORES_SINGLE - self.glob.glob.return_value = ['a'] - self.assertEqual(self.test_context(), { - 'cpu_mask': '0x01', - 'device_whitelist': '-w 0000:00:1c.0 -w 0000:00:1d.0', - 'dpdk_enabled': True, - 'socket_memory': '1024' - }) - - -class TestDPDKDeviceContext(CharmTestCase): - - _dpdk_bridges = { - '0000:00:1c.0': 'br-data', - '0000:00:1d.0': 'br-physnet1', - } - _dpdk_bonds = { - '0000:00:1c.1': 'dpdk-bond0', - '0000:00:1d.1': 'dpdk-bond0', - } - - def setUp(self): - super(TestDPDKDeviceContext, self).setUp(context, - TO_PATCH + DPDK_PATCH) - self.config.side_effect = self.test_config.get - self.test_context = context.DPDKDeviceContext() - self.resolve_dpdk_bridges.return_value = self._dpdk_bridges - self.resolve_dpdk_bonds.return_value = self._dpdk_bonds - - def test_context(self): - self.test_config.set('dpdk-driver', 'uio_pci_generic') - devices = copy.deepcopy(self._dpdk_bridges) - devices.update(self._dpdk_bonds) - self.assertEqual(self.test_context(), { - 'devices': devices, - 'driver': 'uio_pci_generic' - }) - self.config.assert_called_with('dpdk-driver') - - def test_context_none_driver(self): - self.assertEqual(self.test_context(), {}) - self.config.assert_called_with('dpdk-driver') - - class TestRemoteRestartContext(CharmTestCase): def setUp(self): diff --git a/unit_tests/test_neutron_ovs_utils.py b/unit_tests/test_neutron_ovs_utils.py index 17c1ae6a..d636b88c 100644 --- a/unit_tests/test_neutron_ovs_utils.py +++ b/unit_tests/test_neutron_ovs_utils.py @@ -19,6 +19,8 @@ from mock import MagicMock, patch, call from collections import OrderedDict import charmhelpers.contrib.openstack.templating as templating +from charmhelpers.contrib.openstack.context import EntityMac + templating.OSConfigRenderer = MagicMock() import neutron_ovs_utils as nutils @@ -34,13 +36,9 @@ import charmhelpers.core.hookenv as hookenv TO_PATCH = [ 'add_bridge', 'add_bridge_port', + 'add_bridge_bond', 'add_ovsbridge_linuxbridge', 'is_linuxbridge_interface', - 'dpdk_add_bridge_port', - 'dpdk_add_bridge_bond', - 'dpdk_set_bond_config', - 'dpdk_set_mtu_request', - 'dpdk_set_interfaces_mtu', 'add_source', 'apt_install', 'apt_update', @@ -381,7 +379,6 @@ class TestNeutronOVSUtils(CharmTestCase): head_pkg, 'neutron-openvswitch-agent', 'neutron-sriov-agent', - 'sriov-netplan-shim', ] self.assertEqual(pkg_list, expect) @@ -407,7 +404,6 @@ class TestNeutronOVSUtils(CharmTestCase): head_pkg, 'neutron-openvswitch-agent', 'mlnx-switchdev-mode', - 'sriov-netplan-shim', 'python3-neutron', 'python3-zmq', ] @@ -431,7 +427,7 @@ class TestNeutronOVSUtils(CharmTestCase): _regconfs = nutils.register_configs() confs = ['/etc/neutron/neutron.conf', '/etc/neutron/plugins/ml2/ml2_conf.ini', - '/etc/default/openvswitch-switch', + # '/etc/default/openvswitch-switch', '/etc/init/os-charm-phy-nic-mtu.conf'] self.assertEqual(_regconfs.configs, confs) @@ -453,7 +449,7 @@ class TestNeutronOVSUtils(CharmTestCase): _regconfs = nutils.register_configs() confs = ['/etc/neutron/neutron.conf', '/etc/neutron/plugins/ml2/openvswitch_agent.ini', - '/etc/default/openvswitch-switch', + # '/etc/default/openvswitch-switch', '/etc/init/os-charm-phy-nic-mtu.conf'] self.assertEqual(_regconfs.configs, confs) @@ -587,7 +583,7 @@ class TestNeutronOVSUtils(CharmTestCase): for item in _restart_map: self.assertTrue(item in _restart_map) self.assertTrue(expect[item] == _restart_map[item]) - self.assertEqual(len(_restart_map.keys()), 3) + self.assertEqual(len(_restart_map.keys()), 2) @patch('charmhelpers.contrib.openstack.context.list_nics', return_value=['eth0']) @@ -605,10 +601,11 @@ class TestNeutronOVSUtils(CharmTestCase): # assumed) self.test_config.set('data-port', 'eth0') nutils.configure_ovs() + expected_brdata = {'datapath-type': 'system'} self.add_bridge.assert_has_calls([ - call('br-int', 'system'), - call('br-ex', 'system'), - call('br-data', 'system') + call('br-int', brdata=expected_brdata), + call('br-ex', brdata=expected_brdata), + call('br-data', brdata=expected_brdata) ]) self.assertTrue(self.add_bridge_port.called) @@ -618,9 +615,9 @@ class TestNeutronOVSUtils(CharmTestCase): self.add_bridge_port.reset_mock() nutils.configure_ovs() self.add_bridge.assert_has_calls([ - call('br-int', 'system'), - call('br-ex', 'system'), - call('br-data', 'system') + call('br-int', brdata=expected_brdata), + call('br-ex', brdata=expected_brdata), + call('br-data', brdata=expected_brdata) ]) # Not called since we have a bogus bridge in data-ports self.assertFalse(self.add_bridge_port.called) @@ -678,15 +675,19 @@ class TestNeutronOVSUtils(CharmTestCase): self.ExternalPortContext.return_value = \ DummyContext(return_value={'ext_port': 'eth0'}) nutils.configure_ovs() + expected_brdata = {'datapath-type': 'system'} self.add_bridge.assert_has_calls([ - call('br-int', 'system'), - call('br-ex', 'system'), - call('br-data', 'system') + call('br-int', brdata=expected_brdata), + call('br-ex', brdata=expected_brdata), + call('br-data', brdata=expected_brdata) ]) self.add_bridge_port.assert_called_with('br-ex', 'eth0') def _run_configure_ovs_dpdk(self, mock_config, _use_dvr, - _resolve_dpdk_bridges, _resolve_dpdk_bonds, + _OVSDPDKDeviceContext, + _BridgePortInterfaceMap, + _parse_data_port_mappings, + _parse_bridge_mappings, _late_init, _test_bonds, _ovs_vhostuser_client=False): def _resolve_port_name(pci_address, device_index, late_init): @@ -696,153 +697,221 @@ class TestNeutronOVSUtils(CharmTestCase): ) else: return 'dpdk{}'.format(device_index) + + pci = ['0000:00:1c.1', '0000:00:1c.2', '0000:00:1c.3'] + mac = ['00:00:00:00:00:01', '00:00:00:00:00:02', '00:00:00:00:00:03'] + br = ['br-phynet1', 'br-phynet2', 'br-phynet3'] + + map_mock = MagicMock() + ovs_dpdk_mock = MagicMock() if _test_bonds: - _resolve_dpdk_bridges.return_value = OrderedDict() - _resolve_dpdk_bonds.return_value = OrderedDict([ - ('0000:001c.01', 'bond0'), - ('0000:001c.02', 'bond1'), - ('0000:001c.03', 'bond2'), - ]) - self.parse_data_port_mappings.return_value = OrderedDict([ - ('bond0', 'br-phynet1'), - ('bond1', 'br-phynet2'), - ('bond2', 'br-phynet3'), - ]) + _parse_data_port_mappings.return_value = { + 'bond0': br[0], 'bond1': br[1], 'bond2': br[2]} + ovs_dpdk_mock.devices.return_value = OrderedDict([ + (pci[0], EntityMac('bond0', mac[0])), + (pci[1], EntityMac('bond1', mac[1])), + (pci[2], EntityMac('bond2', mac[2]))]) + map_mock.items.return_value = [ + (br[0], {'bond0': {_resolve_port_name(pci[0], 0, _late_init): + {'type': 'dpdk', 'pci-address': pci[0]}}}), + (br[1], {'bond1': {_resolve_port_name(pci[1], 1, _late_init): + {'type': 'dpdk', 'pci-address': pci[1]}}}), + (br[2], {'bond2': {_resolve_port_name(pci[2], 2, _late_init): + {'type': 'dpdk', 'pci-address': pci[2]}}})] + map_mock.get_ifdatamap.side_effect = [ + {_resolve_port_name(pci[0], 0, _late_init): { + 'type': 'dpdk', 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[0]}}}, + {_resolve_port_name(pci[1], 1, _late_init): { + 'type': 'dpdk', 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[1]}}}, + {_resolve_port_name(pci[2], 2, _late_init): { + 'type': 'dpdk', 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[2]}}}] else: - _resolve_dpdk_bridges.return_value = OrderedDict([ - ('0000:001c.01', 'br-phynet1'), - ('0000:001c.02', 'br-phynet2'), - ('0000:001c.03', 'br-phynet3'), - ]) - _resolve_dpdk_bonds.return_value = OrderedDict() + _parse_data_port_mappings.return_value = { + mac[0]: br[0], mac[1]: br[1], mac[2]: br[2]} + ovs_dpdk_mock.devices.return_value = OrderedDict([ + (pci[0], EntityMac(br[0], mac[0])), + (pci[1], EntityMac(br[1], mac[1])), + (pci[2], EntityMac(br[2], mac[2]))]) + map_mock.items.return_value = [ + (br[0], {_resolve_port_name(pci[0], 0, _late_init): + {_resolve_port_name(pci[0], 0, _late_init): + {'type': 'dpdk', 'pci-address': pci[0]}}}), + (br[1], {_resolve_port_name(pci[1], 1, _late_init): + {_resolve_port_name(pci[1], 1, _late_init): + {'type': 'dpdk', 'pci-address': pci[1]}}}), + (br[2], {_resolve_port_name(pci[2], 2, _late_init): + {_resolve_port_name(pci[2], 2, _late_init): + {'type': 'dpdk', 'pci-address': pci[2]}}})] + map_mock.get_ifdatamap.side_effect = [ + {_resolve_port_name(pci[0], 0, _late_init): + {'type': 'dpdk', 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[0]}}}, + {_resolve_port_name(pci[1], 1, _late_init): + {'type': 'dpdk', 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[1]}}}, + {_resolve_port_name(pci[2], 2, _late_init): + {'type': 'dpdk', 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[2]}}}] + + _OVSDPDKDeviceContext.return_value = ovs_dpdk_mock + _BridgePortInterfaceMap.return_value = map_mock + _parse_bridge_mappings.return_value = { + 'phynet1': br[0], 'phynet2': br[1], 'phynet3': br[2]} _use_dvr.return_value = True - self.use_dpdk.return_value = True - self.ovs_has_late_dpdk_init.return_value = _late_init - self.ovs_vhostuser_client.return_value = _ovs_vhostuser_client mock_config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get self.test_config.set('enable-dpdk', True) + self.use_dpdk.return_value = True + self.ovs_has_late_dpdk_init.return_value = _late_init + self.ovs_vhostuser_client.return_value = _ovs_vhostuser_client nutils.configure_ovs() + expetected_brdata = {'datapath-type': 'netdev'} self.add_bridge.assert_has_calls([ - call('br-int', 'netdev'), - call('br-ex', 'netdev'), - call('br-phynet1', 'netdev'), - call('br-phynet2', 'netdev'), - call('br-phynet3', 'netdev')], + call('br-int', brdata=expetected_brdata), + call('br-ex', brdata=expetected_brdata), + call(br[0], brdata=expetected_brdata), + call(br[1], brdata=expetected_brdata), + call(br[2], brdata=expetected_brdata)], any_order=True ) if _test_bonds: - self.dpdk_add_bridge_bond.assert_has_calls([ - call('br-phynet1', 'bond0', - {_resolve_port_name('0000:001c.01', - 0, _late_init): '0000:001c.01'}), - call('br-phynet2', 'bond1', - {_resolve_port_name('0000:001c.02', - 1, _late_init): '0000:001c.02'}), - call('br-phynet3', 'bond2', - {_resolve_port_name('0000:001c.03', - 2, _late_init): '0000:001c.03'})], - any_order=True - ) - self.dpdk_set_bond_config.assert_has_calls([ - call('bond0', - {'mode': 'balance-tcp', - 'lacp': 'active', - 'lacp-time': 'fast'}), - call('bond1', - {'mode': 'balance-tcp', - 'lacp': 'active', - 'lacp-time': 'fast'}), - call('bond2', - {'mode': 'balance-tcp', - 'lacp': 'active', - 'lacp-time': 'fast'})], - any_order=True - ) - self.dpdk_set_interfaces_mtu.assert_has_calls([ - call(1500, {'dpdk-ac48d24': None}.keys()), - call(1500, {'dpdk-82c1c9e': None}.keys()), - call(1500, {'dpdk-aebdb4d': None}.keys())], + self.add_bridge_bond.assert_has_calls( + [call(br[0], 'bond0', + [_resolve_port_name(pci[0], 0, _late_init)], + portdata={'bond_mode': 'balance-tcp', + 'lacp': 'active', + 'other_config': {'lacp-time': 'fast'}}, + ifdatamap={ + _resolve_port_name(pci[0], 0, _late_init): { + 'type': 'dpdk', + 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[0]}}}), + call(br[1], 'bond1', + [_resolve_port_name(pci[1], 1, _late_init)], + portdata={'bond_mode': 'balance-tcp', + 'lacp': 'active', + 'other_config': {'lacp-time': 'fast'}}, + ifdatamap={ + _resolve_port_name(pci[1], 1, _late_init):{ + 'type': 'dpdk', + 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[1]}}}), + call(br[2], 'bond2', + [_resolve_port_name(pci[2], 2, _late_init)], + portdata={'bond_mode': 'balance-tcp', + 'lacp': 'active', + 'other_config': {'lacp-time': 'fast'}}, + ifdatamap={ + _resolve_port_name(pci[2], 2, _late_init): { + 'type': 'dpdk', + 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[2]}}})], any_order=True) else: - self.dpdk_add_bridge_port.assert_has_calls([ - call('br-phynet1', - _resolve_port_name('0000:001c.01', - 0, _late_init), - '0000:001c.01'), - call('br-phynet2', - _resolve_port_name('0000:001c.02', - 1, _late_init), - '0000:001c.02'), - call('br-phynet3', - _resolve_port_name('0000:001c.03', - 2, _late_init), - '0000:001c.03')], - any_order=True - ) - self.dpdk_set_mtu_request.assert_has_calls([ - call(_resolve_port_name('0000:001c.01', - 0, _late_init), 1500), - call(_resolve_port_name('0000:001c.02', - 1, _late_init), 1500), - call(_resolve_port_name('0000:001c.03', - 2, _late_init), 1500)], - any_order=True) + if _late_init: + self.add_bridge_port.assert_has_calls([ + call(br[0], _resolve_port_name(pci[0], 0, _late_init), + ifdata={'type': 'dpdk', + 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[0]}}, + linkup=False, promisc=None), + call(br[1], _resolve_port_name(pci[1], 1, _late_init), + ifdata={'type': 'dpdk', + 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[1]}}, + linkup=False, promisc=None), + call(br[2], _resolve_port_name(pci[2], 2, _late_init), + ifdata={'type': 'dpdk', + 'mtu-request': 1500, + 'options': {'dpdk-devargs': pci[2]}}, + linkup=False, promisc=None)], any_order=True) + else: + self.add_bridge_port.assert_has_calls([ + call(br[0], _resolve_port_name(pci[0], 0, _late_init), + ifdata={'type': 'dpdk', 'mtu-request': 1500}, + linkup=False, promisc=None), + call(br[1], _resolve_port_name(pci[1], 1, _late_init), + ifdata={'type': 'dpdk', 'mtu-request': 1500}, + linkup=False, promisc=None), + call(br[2], _resolve_port_name(pci[2], 2, _late_init), + ifdata={'type': 'dpdk', 'mtu-request': 1500}, + linkup=False, promisc=None)], any_order=True) @patch.object(nutils, 'use_hw_offload', return_value=False) + @patch.object(nutils, 'parse_bridge_mappings') + @patch.object(nutils, 'parse_data_port_mappings') @patch.object(neutron_ovs_context, 'NeutronAPIContext') - @patch.object(neutron_ovs_context, 'resolve_dpdk_bonds') - @patch.object(neutron_ovs_context, 'resolve_dpdk_bridges') + @patch.object(nutils, 'BridgePortInterfaceMap') + @patch.object(nutils, 'OVSDPDKDeviceContext') @patch.object(nutils, 'use_dvr') @patch('charmhelpers.contrib.openstack.context.config') def test_configure_ovs_dpdk(self, mock_config, _use_dvr, - _resolve_dpdk_bridges, - _resolve_dpdk_bonds, + _OVSDPDKDeviceContext, + _BridgePortInterfaceMap, _NeutronAPIContext, + _parse_data_port_mappings, + _parse_bridge_mappings, _use_hw_offload): _NeutronAPIContext.return_value = DummyContext( return_value={'global_physnet_mtu': 1500}) return self._run_configure_ovs_dpdk(mock_config, _use_dvr, - _resolve_dpdk_bridges, - _resolve_dpdk_bonds, + _OVSDPDKDeviceContext, + _BridgePortInterfaceMap, + _parse_data_port_mappings, + _parse_bridge_mappings, _late_init=False, _test_bonds=False) @patch.object(nutils, 'use_hw_offload', return_value=False) + @patch.object(nutils, 'parse_bridge_mappings') + @patch.object(nutils, 'parse_data_port_mappings') @patch.object(neutron_ovs_context, 'NeutronAPIContext') - @patch.object(neutron_ovs_context, 'resolve_dpdk_bonds') - @patch.object(neutron_ovs_context, 'resolve_dpdk_bridges') + @patch.object(nutils, 'BridgePortInterfaceMap') + @patch.object(nutils, 'OVSDPDKDeviceContext') @patch.object(nutils, 'use_dvr') @patch('charmhelpers.contrib.openstack.context.config') def test_configure_ovs_dpdk_late_init(self, mock_config, _use_dvr, - _resolve_dpdk_bridges, - _resolve_dpdk_bonds, + _OVSDPDKDeviceContext, + _BridgePortInterfaceMap, _NeutronAPIContext, + _parse_data_port_mappings, + _parse_bridge_mappings, _use_hw_offload): _NeutronAPIContext.return_value = DummyContext( return_value={'global_physnet_mtu': 1500}) return self._run_configure_ovs_dpdk(mock_config, _use_dvr, - _resolve_dpdk_bridges, - _resolve_dpdk_bonds, + _OVSDPDKDeviceContext, + _BridgePortInterfaceMap, + _parse_data_port_mappings, + _parse_bridge_mappings, _late_init=True, _test_bonds=False) @patch.object(nutils, 'use_hw_offload', return_value=False) + @patch.object(nutils, 'parse_bridge_mappings') + @patch.object(nutils, 'parse_data_port_mappings') @patch.object(neutron_ovs_context, 'NeutronAPIContext') - @patch.object(neutron_ovs_context, 'resolve_dpdk_bonds') - @patch.object(neutron_ovs_context, 'resolve_dpdk_bridges') + @patch.object(nutils, 'BridgePortInterfaceMap') + @patch.object(nutils, 'OVSDPDKDeviceContext') @patch.object(nutils, 'use_dvr') @patch('charmhelpers.contrib.openstack.context.config') def test_configure_ovs_dpdk_late_init_bonds(self, mock_config, _use_dvr, - _resolve_dpdk_bridges, - _resolve_dpdk_bonds, + _OVSDPDKDeviceContext, + _BridgePortInterfaceMap, _NeutronAPIContext, + _parse_data_port_mappings, + _parse_bridge_mappings, _use_hw_offload): _NeutronAPIContext.return_value = DummyContext( return_value={'global_physnet_mtu': 1500}) return self._run_configure_ovs_dpdk(mock_config, _use_dvr, - _resolve_dpdk_bridges, - _resolve_dpdk_bonds, + _OVSDPDKDeviceContext, + _BridgePortInterfaceMap, + _parse_data_port_mappings, + _parse_bridge_mappings, _late_init=True, _test_bonds=True) @@ -947,7 +1016,7 @@ class TestNeutronOVSUtils(CharmTestCase): @patch.object(nutils, 'is_unit_paused_set') @patch.object(nutils.subprocess, 'check_call') - @patch.object(neutron_ovs_context, 'OVSDPDKDeviceContext') + @patch.object(nutils, 'OVSDPDKDeviceContext') @patch.object(nutils, 'set_Open_vSwitch_column_value') def test_enable_ovs_dpdk(self, _set_Open_vSwitch_column_value, @@ -980,7 +1049,7 @@ class TestNeutronOVSUtils(CharmTestCase): @patch.object(nutils, 'is_unit_paused_set') @patch.object(nutils.subprocess, 'check_call') - @patch.object(neutron_ovs_context, 'OVSDPDKDeviceContext') + @patch.object(nutils, 'OVSDPDKDeviceContext') @patch.object(nutils, 'set_Open_vSwitch_column_value') def test_enable_ovs_dpdk_vhostuser_client( self, @@ -1101,76 +1170,3 @@ class TestNeutronOVSUtils(CharmTestCase): call('other_config:max-idle', '30000'), ]) self.service_restart.assert_not_called() - - -class TestDPDKBridgeBondMap(CharmTestCase): - - def setUp(self): - super(TestDPDKBridgeBondMap, self).setUp(nutils, - TO_PATCH) - self.config.side_effect = self.test_config.get - - def test_add_port(self): - ctx = nutils.DPDKBridgeBondMap() - ctx.add_port("br1", "bond1", "port1", "00:00:00:00:00:01") - ctx.add_port("br1", "bond1", "port2", "00:00:00:00:00:02") - ctx.add_port("br1", "bond2", "port3", "00:00:00:00:00:03") - ctx.add_port("br1", "bond2", "port4", "00:00:00:00:00:04") - - expected = [( - 'br1', { - 'bond1': { - 'port1': '00:00:00:00:00:01', - 'port2': '00:00:00:00:00:02' - }, - 'bond2': { - 'port3': '00:00:00:00:00:03', - 'port4': '00:00:00:00:00:04', - }, - }, - )] - - self.assertEqual(ctx.items(), expected) - - -class TestDPDKBondsConfig(CharmTestCase): - - def setUp(self): - super(TestDPDKBondsConfig, self).setUp(nutils, TO_PATCH) - self.config.side_effect = self.test_config.get - - def test_get_bond_config(self): - self.test_config.set('dpdk-bond-config', - ':active-backup bond1:balance-slb:off') - bonds_config = nutils.DPDKBondsConfig() - - self.assertEqual(bonds_config.get_bond_config('bond0'), - {'mode': 'active-backup', - 'lacp': 'active', - 'lacp-time': 'fast' - }) - self.assertEqual(bonds_config.get_bond_config('bond1'), - {'mode': 'balance-slb', - 'lacp': 'off', - 'lacp-time': 'fast' - }) - - -class TestMTURequest(CharmTestCase): - - def setUp(self): - super(TestMTURequest, self).setUp(nutils, []) - - @patch.object(nutils, 'subprocess') - def test_dpdk_set_mtu_request(self, mock_subprocess): - nutils.dpdk_set_mtu_request("dpdk1", 9000) - mock_subprocess.check_call.assert_called_once_with( - ['ovs-vsctl', 'set', 'Interface', 'dpdk1', 'mtu_request=9000']) - - @patch.object(nutils, 'dpdk_set_mtu_request') - def test_dpdk_set_interfaces_mtu(self, mock_dpdk_set_mtu_request): - nutils.dpdk_set_interfaces_mtu('1234', ['nic1', 'nic2']) - expected_calls = [ - call('nic1', '1234'), - call('nic2', '1234')] - mock_dpdk_set_mtu_request.assert_has_calls(expected_calls)