diff --git a/hooks/neutron_ovs_context.py b/hooks/neutron_ovs_context.py index 1ce34fdd..0b686d05 100644 --- a/hooks/neutron_ovs_context.py +++ b/hooks/neutron_ovs_context.py @@ -410,14 +410,42 @@ class OVSDPDKDeviceContext(OSContextGenerator): else: return str(sm_size) - def device_whitelist(self): - '''Formatted list of devices to whitelist for dpdk''' - _flag = '-w {device}' + 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 resolve_dpdk_bridges(): - whitelist.append(_flag.format(device=device)) + 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() diff --git a/hooks/neutron_ovs_utils.py b/hooks/neutron_ovs_utils.py index 66655149..639ed1de 100644 --- a/hooks/neutron_ovs_utils.py +++ b/hooks/neutron_ovs_utils.py @@ -473,12 +473,20 @@ def enable_ovs_dpdk(): if ovs_has_late_dpdk_init(): dpdk_context = neutron_ovs_context.OVSDPDKDeviceContext() other_config = OrderedDict([ - ('pmd-cpu-mask', dpdk_context.cpu_mask()), + ('dpdk-lcore-mask', dpdk_context.cpu_mask()), ('dpdk-socket-mem', dpdk_context.socket_memory()), - ('dpdk-extra', - '--vhost-owner libvirt-qemu:kvm --vhost-perm 0660'), ('dpdk-init', 'true'), ]) + if not ovs_vhostuser_client(): + other_config['dpdk-extra'] = ( + '--vhost-owner libvirt-qemu:kvm --vhost-perm 0660 ' + + dpdk_context.pci_whitelist() + ) + else: + other_config['dpdk-extra'] = ( + dpdk_context.pci_whitelist() + ) + other_config['dpdk-init'] = 'true' for column, value in other_config.items(): values_changed.append( set_Open_vSwitch_column_value( @@ -738,6 +746,17 @@ def ovs_has_late_dpdk_init(): return apt_pkg.version_compare(ovs_version, '2.6.0') >= 0 +def ovs_vhostuser_client(): + ''' + Determine whether OVS will act as a client on the vhostuser socket + + @returns boolean indicating whether OVS will act as a client + ''' + import apt_pkg + ovs_version = get_upstream_version("openvswitch-switch") + return apt_pkg.version_compare(ovs_version, '2.9.0') >= 0 + + def enable_sriov(): '''Determine whether SR-IOV is enabled and supported''' cmp_release = CompareOpenStackReleases( diff --git a/unit_tests/test_neutron_ovs_context.py b/unit_tests/test_neutron_ovs_context.py index 16e59ec6..1a5a9aea 100644 --- a/unit_tests/test_neutron_ovs_context.py +++ b/unit_tests/test_neutron_ovs_context.py @@ -623,10 +623,10 @@ class TestOVSDPDKDeviceContext(CharmTestCase): def test_device_whitelist(self): '''Test device whitelist generation''' - self.resolve_dpdk_bridges.return_value = [ - '0000:00:1c.0', - '0000:00:1d.0' - ] + 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') @@ -657,15 +657,16 @@ class TestOVSDPDKDeviceContext(CharmTestCase): def test_context_no_devices(self): '''Ensure that DPDK is disable when no devices detected''' - self.resolve_dpdk_bridges.return_value = [] + 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', - '0000:00:1d.0' - ] + 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(), { diff --git a/unit_tests/test_neutron_ovs_utils.py b/unit_tests/test_neutron_ovs_utils.py index f9386cb5..e35af959 100644 --- a/unit_tests/test_neutron_ovs_utils.py +++ b/unit_tests/test_neutron_ovs_utils.py @@ -62,6 +62,7 @@ TO_PATCH = [ 'enable_ipfix', 'disable_ipfix', 'ovs_has_late_dpdk_init', + 'ovs_vhostuser_client', 'parse_data_port_mappings', 'user_exists', 'group_exists', @@ -104,6 +105,7 @@ class TestNeutronOVSUtils(CharmTestCase): self.config.side_effect = self.test_config.get self.use_dpdk.return_value = False self.ovs_has_late_dpdk_init.return_value = False + self.ovs_vhostuser_client.return_value = False def tearDown(self): # Reset cached cache @@ -604,7 +606,8 @@ class TestNeutronOVSUtils(CharmTestCase): def _run_configure_ovs_dpdk(self, mock_config, _use_dvr, _resolve_dpdk_bridges, _resolve_dpdk_bonds, - _late_init, _test_bonds): + _late_init, _test_bonds, + _ovs_vhostuser_client=False): def _resolve_port_name(pci_address, device_index, late_init): if late_init: return 'dpdk-{}'.format( @@ -634,6 +637,7 @@ class TestNeutronOVSUtils(CharmTestCase): _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) @@ -971,6 +975,72 @@ class TestNeutronOVSUtils(CharmTestCase): ['systemd-tmpfiles', '--create'] ) + @patch.object(nutils, 'is_unit_paused_set') + @patch.object(nutils.subprocess, 'check_call') + @patch.object(neutron_ovs_context, 'OVSDPDKDeviceContext') + @patch.object(nutils, 'set_Open_vSwitch_column_value') + def test_enable_ovs_dpdk(self, + _set_Open_vSwitch_column_value, + _OVSDPDKDeviceContext, + _check_call, + _is_unit_paused_set): + mock_context = MagicMock() + mock_context.cpu_mask.return_value = '0x03' + mock_context.socket_memory.return_value = '4096,4096' + mock_context.pci_whitelist.return_value = \ + '--pci-whitelist 00:0300:01' + _OVSDPDKDeviceContext.return_value = mock_context + _set_Open_vSwitch_column_value.return_value = True + self.ovs_has_late_dpdk_init.return_value = True + self.ovs_vhostuser_client.return_value = False + _is_unit_paused_set.return_value = False + nutils.enable_ovs_dpdk() + _set_Open_vSwitch_column_value.assert_has_calls([ + call('other_config:dpdk-lcore-mask', '0x03'), + call('other_config:dpdk-socket-mem', '4096,4096'), + call('other_config:dpdk-init', 'true'), + call('other_config:dpdk-extra', + '--vhost-owner libvirt-qemu:kvm --vhost-perm 0660 ' + '--pci-whitelist 00:0300:01') + ]) + _check_call.assert_called_once_with( + nutils.UPDATE_ALTERNATIVES + [nutils.OVS_DPDK_BIN] + ) + self.service_restart.assert_called_with('openvswitch-switch') + + @patch.object(nutils, 'is_unit_paused_set') + @patch.object(nutils.subprocess, 'check_call') + @patch.object(neutron_ovs_context, 'OVSDPDKDeviceContext') + @patch.object(nutils, 'set_Open_vSwitch_column_value') + def test_enable_ovs_dpdk_vhostuser_client( + self, + _set_Open_vSwitch_column_value, + _OVSDPDKDeviceContext, + _check_call, + _is_unit_paused_set): + mock_context = MagicMock() + mock_context.cpu_mask.return_value = '0x03' + mock_context.socket_memory.return_value = '4096,4096' + mock_context.pci_whitelist.return_value = \ + '--pci-whitelist 00:0300:01' + _OVSDPDKDeviceContext.return_value = mock_context + _set_Open_vSwitch_column_value.return_value = True + self.ovs_has_late_dpdk_init.return_value = True + self.ovs_vhostuser_client.return_value = True + _is_unit_paused_set.return_value = False + nutils.enable_ovs_dpdk() + _set_Open_vSwitch_column_value.assert_has_calls([ + call('other_config:dpdk-lcore-mask', '0x03'), + call('other_config:dpdk-socket-mem', '4096,4096'), + call('other_config:dpdk-init', 'true'), + call('other_config:dpdk-extra', + '--pci-whitelist 00:0300:01') + ]) + _check_call.assert_called_once_with( + nutils.UPDATE_ALTERNATIVES + [nutils.OVS_DPDK_BIN] + ) + self.service_restart.assert_called_with('openvswitch-switch') + class TestDPDKBridgeBondMap(CharmTestCase):