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:
parent
34b35db625
commit
ad7f870c0d
@ -1 +0,0 @@
|
||||
neutron_ovs_hooks.py
|
27
hooks/install
Executable file
27
hooks/install
Executable 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
1
hooks/install.real
Symbolic link
@ -0,0 +1 @@
|
||||
neutron_ovs_hooks.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"
|
||||
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||