Use OVS charmhelpers to set up DPDK ports and bridges

Replace deprecated code that was used for setting up DPDK ports and
bonds with calls to charmhelpers functions.
Pass DPDK configuration using ifdata and portdata dicts instead of
making direct ovs-vsctl calls.
Move installation of sriov-netplan-shim to the bash wrapper. This
resolves problems with non-working imports of sriov-netplan-shim in
charmhelpers.

Update unit tests to reflect that change.

Signed-off-by: Przemysław Lal <przemyslaw.lal@canonical.com>
Change-Id: Ica6f3ea66136bca6c77a5fb55ad7ef5d95aa1f6a
This commit is contained in:
Przemysław Lal 2021-03-24 11:20:09 +01:00
parent 34b35db625
commit ad7f870c0d
6 changed files with 327 additions and 820 deletions

View File

@ -1 +0,0 @@
neutron_ovs_hooks.py

27
hooks/install Executable file
View File

@ -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

1
hooks/install.real Symbolic link
View File

@ -0,0 +1 @@
neutron_ovs_hooks.py

View File

@ -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"

View File

@ -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),
level=DEBUG)
add_bridge(br, datapath_type)
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
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, brdata=brdata)
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]

View File

@ -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):

View File

@ -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',
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',
'lacp-time': 'fast'}),
call('bond1',
{'mode': 'balance-tcp',
'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',
'lacp-time': 'fast'}),
call('bond2',
{'mode': 'balance-tcp',
'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',
'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())],
'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)