Enable support for hardware offload
Enable support for use of hardware offload via OVS; this requires OpenStack Stein or later in conjunction with the latest HWE kernel for Ubuntu 18.04 LTS. Change-Id: I4ce47b1712e79bfbed9ac708cc521840b3709724
This commit is contained in:
parent
7ba64f9412
commit
ab5de86972
17
config.yaml
17
config.yaml
@ -238,6 +238,23 @@ options:
|
|||||||
NOTE: Changing this value will disrupt networking on all SR-IOV capable
|
NOTE: Changing this value will disrupt networking on all SR-IOV capable
|
||||||
interfaces for blanket configuration or listed interfaces when per-device
|
interfaces for blanket configuration or listed interfaces when per-device
|
||||||
configuration is used.
|
configuration is used.
|
||||||
|
enable-hardware-offload:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: |
|
||||||
|
Enable support for hardware offload of flows from Open vSwitch to supported
|
||||||
|
network adapters. This requires use of OpenStack Stein or later and has
|
||||||
|
only been tested on Mellanox ConnectX 5 adapters.
|
||||||
|
.
|
||||||
|
Enabling this option will make use of the sriov-numvfs option to configure
|
||||||
|
the VF functions of the physical network adapters detected in each unit.
|
||||||
|
.
|
||||||
|
Enabling this option also requires that the firewall-driver option be
|
||||||
|
set to 'openvswitch'; this will allow security groups to be applied to
|
||||||
|
hardware offloaded ports (note that this feature is currently not
|
||||||
|
supported by OVS or the Linux kernel).
|
||||||
|
.
|
||||||
|
This option must not be enabled with either enable-sriov or enable-dpdk.
|
||||||
networking-tools-source:
|
networking-tools-source:
|
||||||
type: string
|
type: string
|
||||||
default: ppa:openstack-charmers/networking-tools
|
default: ppa:openstack-charmers/networking-tools
|
||||||
|
@ -51,6 +51,7 @@ from charmhelpers.core.hookenv import (
|
|||||||
DEBUG,
|
DEBUG,
|
||||||
log,
|
log,
|
||||||
status_set,
|
status_set,
|
||||||
|
ERROR,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.neutron import (
|
from charmhelpers.contrib.openstack.neutron import (
|
||||||
parse_bridge_mappings,
|
parse_bridge_mappings,
|
||||||
@ -288,6 +289,9 @@ def install_packages():
|
|||||||
if use_dpdk():
|
if use_dpdk():
|
||||||
enable_ovs_dpdk()
|
enable_ovs_dpdk()
|
||||||
|
|
||||||
|
if use_hw_offload():
|
||||||
|
enable_hw_offload()
|
||||||
|
|
||||||
# NOTE(tpsilva): if we're using openvswitch driver, we need to explicitly
|
# NOTE(tpsilva): if we're using openvswitch driver, we need to explicitly
|
||||||
# load the nf_conntrack_ipv4/6 module, since it won't be
|
# load the nf_conntrack_ipv4/6 module, since it won't be
|
||||||
# loaded automatically in some cases. LP#1834213
|
# loaded automatically in some cases. LP#1834213
|
||||||
@ -356,6 +360,11 @@ def determine_packages():
|
|||||||
pkgs.append('neutron-plugin-sriov-agent')
|
pkgs.append('neutron-plugin-sriov-agent')
|
||||||
pkgs.append('sriov-netplan-shim')
|
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':
|
if cmp_release >= 'rocky':
|
||||||
pkgs = [p for p in pkgs if not p.startswith('python-')]
|
pkgs = [p for p in pkgs if not p.startswith('python-')]
|
||||||
pkgs.extend(PY3_PACKAGES)
|
pkgs.extend(PY3_PACKAGES)
|
||||||
@ -545,6 +554,19 @@ def enable_ovs_dpdk():
|
|||||||
service_restart('openvswitch-switch')
|
service_restart('openvswitch-switch')
|
||||||
|
|
||||||
|
|
||||||
|
def enable_hw_offload():
|
||||||
|
'''Enable hardware offload support in Open vSwitch'''
|
||||||
|
values_changed = [
|
||||||
|
set_Open_vSwitch_column_value('other_config:hw-offload',
|
||||||
|
'true'),
|
||||||
|
set_Open_vSwitch_column_value('other_config:max-idle',
|
||||||
|
'30000')
|
||||||
|
]
|
||||||
|
if ((values_changed and any(values_changed)) and
|
||||||
|
not is_unit_paused_set()):
|
||||||
|
service_restart('openvswitch-switch')
|
||||||
|
|
||||||
|
|
||||||
def install_tmpfilesd():
|
def install_tmpfilesd():
|
||||||
'''Install systemd-tmpfiles configuration for ovs vhost-user sockets'''
|
'''Install systemd-tmpfiles configuration for ovs vhost-user sockets'''
|
||||||
# NOTE(jamespage): Only do this if libvirt is actually installed
|
# NOTE(jamespage): Only do this if libvirt is actually installed
|
||||||
@ -604,7 +626,14 @@ def configure_ovs():
|
|||||||
else:
|
else:
|
||||||
add_ovsbridge_linuxbridge(br, port)
|
add_ovsbridge_linuxbridge(br, port)
|
||||||
|
|
||||||
if use_dpdk():
|
# NOTE(jamespage):
|
||||||
|
# hw-offload and dpdk are mutually exclusive so log and error
|
||||||
|
# and skip any subsequent DPDK configuration
|
||||||
|
if use_dpdk() and use_hw_offload():
|
||||||
|
log('DPDK and Hardware offload are mutually exclusive, '
|
||||||
|
'please disable enable-dpdk or enable-hardware-offload',
|
||||||
|
level=ERROR)
|
||||||
|
elif use_dpdk():
|
||||||
log('Configuring bridges with DPDK', level=DEBUG)
|
log('Configuring bridges with DPDK', level=DEBUG)
|
||||||
global_mtu = (
|
global_mtu = (
|
||||||
neutron_ovs_context.NeutronAPIContext()()['global_physnet_mtu'])
|
neutron_ovs_context.NeutronAPIContext()()['global_physnet_mtu'])
|
||||||
@ -809,7 +838,11 @@ def configure_sriov():
|
|||||||
yaml.dump(interfaces_yaml, _conf)
|
yaml.dump(interfaces_yaml, _conf)
|
||||||
return numvfs_changed
|
return numvfs_changed
|
||||||
|
|
||||||
if not enable_sriov():
|
# NOTE(jamespage)
|
||||||
|
# Hardware offload makes use of SR-IOV VF representor ports so
|
||||||
|
# makes use of the general SR-IOV configuration support in this
|
||||||
|
# charm
|
||||||
|
if not (enable_sriov() or use_hw_offload()):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Tidy up any prior installation of obsolete sriov startup
|
# Tidy up any prior installation of obsolete sriov startup
|
||||||
@ -821,7 +854,7 @@ def configure_sriov():
|
|||||||
# Trigger remote restart in parent application
|
# Trigger remote restart in parent application
|
||||||
remote_restart('neutron-plugin', 'nova-compute')
|
remote_restart('neutron-plugin', 'nova-compute')
|
||||||
|
|
||||||
if numvfs_changed:
|
if not use_hw_offload() and numvfs_changed:
|
||||||
# Restart of SRIOV agent is required after changes to system runtime
|
# Restart of SRIOV agent is required after changes to system runtime
|
||||||
# VF configuration
|
# VF configuration
|
||||||
cmp_release = CompareOpenStackReleases(
|
cmp_release = CompareOpenStackReleases(
|
||||||
@ -831,6 +864,9 @@ def configure_sriov():
|
|||||||
else:
|
else:
|
||||||
service_restart('neutron-plugin-sriov-agent')
|
service_restart('neutron-plugin-sriov-agent')
|
||||||
|
|
||||||
|
if use_hw_offload() and numvfs_changed:
|
||||||
|
service_restart('mlnx-switchdev-mode')
|
||||||
|
|
||||||
|
|
||||||
def get_shared_secret():
|
def get_shared_secret():
|
||||||
ctxt = neutron_ovs_context.SharedSecretContext()()
|
ctxt = neutron_ovs_context.SharedSecretContext()()
|
||||||
@ -866,6 +902,19 @@ def use_dpdk():
|
|||||||
return (cmp_release >= 'mitaka' and config('enable-dpdk'))
|
return (cmp_release >= 'mitaka' and config('enable-dpdk'))
|
||||||
|
|
||||||
|
|
||||||
|
def use_hw_offload():
|
||||||
|
'''
|
||||||
|
Determine whether OVS hardware offload should be used
|
||||||
|
|
||||||
|
:returns: boolean indicating whether hardware offload should be enabled
|
||||||
|
:rtype: bool
|
||||||
|
'''
|
||||||
|
cmp_release = CompareOpenStackReleases(
|
||||||
|
os_release('neutron-common')
|
||||||
|
)
|
||||||
|
return (cmp_release >= 'stein' and config('enable-hardware-offload'))
|
||||||
|
|
||||||
|
|
||||||
def ovs_has_late_dpdk_init():
|
def ovs_has_late_dpdk_init():
|
||||||
''' OVS 2.6.0 introduces late initialization '''
|
''' OVS 2.6.0 introduces late initialization '''
|
||||||
import apt_pkg
|
import apt_pkg
|
||||||
|
@ -74,6 +74,7 @@ TO_PATCH = [
|
|||||||
'init_is_systemd',
|
'init_is_systemd',
|
||||||
'modprobe',
|
'modprobe',
|
||||||
'is_container',
|
'is_container',
|
||||||
|
'is_unit_paused_set',
|
||||||
]
|
]
|
||||||
|
|
||||||
head_pkg = 'linux-headers-3.15.0-5-generic'
|
head_pkg = 'linux-headers-3.15.0-5-generic'
|
||||||
@ -182,6 +183,27 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
call(self.filter_installed_packages(), fatal=True),
|
call(self.filter_installed_packages(), fatal=True),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_hw_offload')
|
||||||
|
@patch.object(nutils, 'enable_hw_offload')
|
||||||
|
@patch.object(nutils, 'determine_packages')
|
||||||
|
def test_install_packages_hwoffload(self, _determine_packages,
|
||||||
|
_enable_hw_offload,
|
||||||
|
_use_hw_offload):
|
||||||
|
self.os_release.return_value = 'stein'
|
||||||
|
_determine_packages.return_value = 'randompkg'
|
||||||
|
_use_hw_offload.return_value = True
|
||||||
|
self.determine_dkms_package.return_value = \
|
||||||
|
['openvswitch-datapath-dkms']
|
||||||
|
self.headers_package.return_value = 'linux-headers-foobar'
|
||||||
|
nutils.install_packages()
|
||||||
|
self.apt_update.assert_called_with()
|
||||||
|
self.apt_install.assert_has_calls([
|
||||||
|
call(['linux-headers-foobar',
|
||||||
|
'openvswitch-datapath-dkms'], fatal=True),
|
||||||
|
call(self.filter_installed_packages(), fatal=True),
|
||||||
|
])
|
||||||
|
_enable_hw_offload.assert_called_once_with()
|
||||||
|
|
||||||
@patch.object(nutils, 'use_l3ha')
|
@patch.object(nutils, 'use_l3ha')
|
||||||
@patch.object(nutils, 'use_dvr')
|
@patch.object(nutils, 'use_dvr')
|
||||||
@patch.object(charmhelpers.contrib.openstack.neutron, 'os_release')
|
@patch.object(charmhelpers.contrib.openstack.neutron, 'os_release')
|
||||||
@ -394,6 +416,33 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
]
|
]
|
||||||
self.assertEqual(pkg_list, expect)
|
self.assertEqual(pkg_list, expect)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_hw_offload')
|
||||||
|
@patch.object(nutils, 'use_l3ha')
|
||||||
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.neutron, 'os_release')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.neutron, 'headers_package')
|
||||||
|
def test_determine_pkgs_hardware_offload(self, _head_pkgs, _os_rel,
|
||||||
|
_use_dvr, _use_l3ha,
|
||||||
|
_use_hw_offload):
|
||||||
|
self.test_config.set('enable-local-dhcp-and-metadata', False)
|
||||||
|
self.test_config.set('enable-hardware-offload', True)
|
||||||
|
_use_hw_offload.return_value = True
|
||||||
|
_use_dvr.return_value = False
|
||||||
|
_use_l3ha.return_value = False
|
||||||
|
_os_rel.return_value = 'stein'
|
||||||
|
self.os_release.return_value = 'stein'
|
||||||
|
_head_pkgs.return_value = head_pkg
|
||||||
|
pkg_list = nutils.determine_packages()
|
||||||
|
expect = [
|
||||||
|
head_pkg,
|
||||||
|
'neutron-openvswitch-agent',
|
||||||
|
'mlnx-switchdev-mode',
|
||||||
|
'sriov-netplan-shim',
|
||||||
|
'python3-neutron',
|
||||||
|
'python3-zmq',
|
||||||
|
]
|
||||||
|
self.assertEqual(pkg_list, expect)
|
||||||
|
|
||||||
@patch.object(nutils, 'use_dvr')
|
@patch.object(nutils, 'use_dvr')
|
||||||
def test_register_configs(self, _use_dvr):
|
def test_register_configs(self, _use_dvr):
|
||||||
class _mock_OSConfigRenderer():
|
class _mock_OSConfigRenderer():
|
||||||
@ -764,6 +813,7 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
2, _late_init), 1500)],
|
2, _late_init), 1500)],
|
||||||
any_order=True)
|
any_order=True)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_hw_offload', return_value=False)
|
||||||
@patch.object(neutron_ovs_context, 'NeutronAPIContext')
|
@patch.object(neutron_ovs_context, 'NeutronAPIContext')
|
||||||
@patch.object(neutron_ovs_context, 'resolve_dpdk_bonds')
|
@patch.object(neutron_ovs_context, 'resolve_dpdk_bonds')
|
||||||
@patch.object(neutron_ovs_context, 'resolve_dpdk_bridges')
|
@patch.object(neutron_ovs_context, 'resolve_dpdk_bridges')
|
||||||
@ -772,7 +822,8 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
def test_configure_ovs_dpdk(self, mock_config, _use_dvr,
|
def test_configure_ovs_dpdk(self, mock_config, _use_dvr,
|
||||||
_resolve_dpdk_bridges,
|
_resolve_dpdk_bridges,
|
||||||
_resolve_dpdk_bonds,
|
_resolve_dpdk_bonds,
|
||||||
_NeutronAPIContext):
|
_NeutronAPIContext,
|
||||||
|
_use_hw_offload):
|
||||||
_NeutronAPIContext.return_value = DummyContext(
|
_NeutronAPIContext.return_value = DummyContext(
|
||||||
return_value={'global_physnet_mtu': 1500})
|
return_value={'global_physnet_mtu': 1500})
|
||||||
return self._run_configure_ovs_dpdk(mock_config, _use_dvr,
|
return self._run_configure_ovs_dpdk(mock_config, _use_dvr,
|
||||||
@ -781,6 +832,7 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
_late_init=False,
|
_late_init=False,
|
||||||
_test_bonds=False)
|
_test_bonds=False)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_hw_offload', return_value=False)
|
||||||
@patch.object(neutron_ovs_context, 'NeutronAPIContext')
|
@patch.object(neutron_ovs_context, 'NeutronAPIContext')
|
||||||
@patch.object(neutron_ovs_context, 'resolve_dpdk_bonds')
|
@patch.object(neutron_ovs_context, 'resolve_dpdk_bonds')
|
||||||
@patch.object(neutron_ovs_context, 'resolve_dpdk_bridges')
|
@patch.object(neutron_ovs_context, 'resolve_dpdk_bridges')
|
||||||
@ -789,7 +841,8 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
def test_configure_ovs_dpdk_late_init(self, mock_config, _use_dvr,
|
def test_configure_ovs_dpdk_late_init(self, mock_config, _use_dvr,
|
||||||
_resolve_dpdk_bridges,
|
_resolve_dpdk_bridges,
|
||||||
_resolve_dpdk_bonds,
|
_resolve_dpdk_bonds,
|
||||||
_NeutronAPIContext):
|
_NeutronAPIContext,
|
||||||
|
_use_hw_offload):
|
||||||
_NeutronAPIContext.return_value = DummyContext(
|
_NeutronAPIContext.return_value = DummyContext(
|
||||||
return_value={'global_physnet_mtu': 1500})
|
return_value={'global_physnet_mtu': 1500})
|
||||||
return self._run_configure_ovs_dpdk(mock_config, _use_dvr,
|
return self._run_configure_ovs_dpdk(mock_config, _use_dvr,
|
||||||
@ -798,6 +851,7 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
_late_init=True,
|
_late_init=True,
|
||||||
_test_bonds=False)
|
_test_bonds=False)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_hw_offload', return_value=False)
|
||||||
@patch.object(neutron_ovs_context, 'NeutronAPIContext')
|
@patch.object(neutron_ovs_context, 'NeutronAPIContext')
|
||||||
@patch.object(neutron_ovs_context, 'resolve_dpdk_bonds')
|
@patch.object(neutron_ovs_context, 'resolve_dpdk_bonds')
|
||||||
@patch.object(neutron_ovs_context, 'resolve_dpdk_bridges')
|
@patch.object(neutron_ovs_context, 'resolve_dpdk_bridges')
|
||||||
@ -806,7 +860,8 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
def test_configure_ovs_dpdk_late_init_bonds(self, mock_config, _use_dvr,
|
def test_configure_ovs_dpdk_late_init_bonds(self, mock_config, _use_dvr,
|
||||||
_resolve_dpdk_bridges,
|
_resolve_dpdk_bridges,
|
||||||
_resolve_dpdk_bonds,
|
_resolve_dpdk_bonds,
|
||||||
_NeutronAPIContext):
|
_NeutronAPIContext,
|
||||||
|
_use_hw_offload):
|
||||||
_NeutronAPIContext.return_value = DummyContext(
|
_NeutronAPIContext.return_value = DummyContext(
|
||||||
return_value={'global_physnet_mtu': 1500})
|
return_value={'global_physnet_mtu': 1500})
|
||||||
return self._run_configure_ovs_dpdk(mock_config, _use_dvr,
|
return self._run_configure_ovs_dpdk(mock_config, _use_dvr,
|
||||||
@ -1170,6 +1225,54 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
_kv().get.return_value = True
|
_kv().get.return_value = True
|
||||||
self.assertEquals(nutils.use_fqdn_hint(), True)
|
self.assertEquals(nutils.use_fqdn_hint(), True)
|
||||||
|
|
||||||
|
def test_use_hw_offload_rocky(self):
|
||||||
|
self.os_release.return_value = 'rocky'
|
||||||
|
self.test_config.set('enable-hardware-offload', True)
|
||||||
|
self.assertFalse(nutils.use_hw_offload())
|
||||||
|
|
||||||
|
def test_use_hw_offload_stein(self):
|
||||||
|
self.os_release.return_value = 'stein'
|
||||||
|
self.test_config.set('enable-hardware-offload', True)
|
||||||
|
self.assertTrue(nutils.use_hw_offload())
|
||||||
|
|
||||||
|
def test_use_hw_offload_disabled(self):
|
||||||
|
self.os_release.return_value = 'stein'
|
||||||
|
self.test_config.set('enable-hardware-offload', False)
|
||||||
|
self.assertFalse(nutils.use_hw_offload())
|
||||||
|
|
||||||
|
@patch.object(nutils, 'set_Open_vSwitch_column_value')
|
||||||
|
def test_enable_hw_offload(self, _ovs_set):
|
||||||
|
_ovs_set.return_value = True
|
||||||
|
self.is_unit_paused_set.return_value = False
|
||||||
|
nutils.enable_hw_offload()
|
||||||
|
_ovs_set.assert_has_calls([
|
||||||
|
call('other_config:hw-offload', 'true'),
|
||||||
|
call('other_config:max-idle', '30000'),
|
||||||
|
])
|
||||||
|
self.service_restart.assert_called_once_with('openvswitch-switch')
|
||||||
|
|
||||||
|
@patch.object(nutils, 'set_Open_vSwitch_column_value')
|
||||||
|
def test_enable_hw_offload_unit_paused(self, _ovs_set):
|
||||||
|
_ovs_set.return_value = True
|
||||||
|
self.is_unit_paused_set.return_value = True
|
||||||
|
nutils.enable_hw_offload()
|
||||||
|
_ovs_set.assert_has_calls([
|
||||||
|
call('other_config:hw-offload', 'true'),
|
||||||
|
call('other_config:max-idle', '30000'),
|
||||||
|
])
|
||||||
|
self.service_restart.assert_not_called()
|
||||||
|
|
||||||
|
@patch.object(nutils, 'set_Open_vSwitch_column_value')
|
||||||
|
def test_enable_hw_offload_no_changes(self, _ovs_set):
|
||||||
|
_ovs_set.return_value = False
|
||||||
|
self.is_unit_paused_set.return_value = False
|
||||||
|
nutils.enable_hw_offload()
|
||||||
|
_ovs_set.assert_has_calls([
|
||||||
|
call('other_config:hw-offload', 'true'),
|
||||||
|
call('other_config:max-idle', '30000'),
|
||||||
|
])
|
||||||
|
self.service_restart.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
class TestDPDKBridgeBondMap(CharmTestCase):
|
class TestDPDKBridgeBondMap(CharmTestCase):
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user