Skip to content

Commit

Permalink
Merge pull request #53 from wolsen/bug/1936850
Browse files Browse the repository at this point in the history
Allow additional dpdk runtime libraries to be installed
  • Loading branch information
fnordahl authored Feb 15, 2022
2 parents f0a6c62 + 645e4ae commit 0e971cb
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 0 deletions.
16 changes: 16 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,22 @@ options:
uio_pci_generic
.
Only used when DPDK is enabled.
dpdk-runtime-libraries:
type: string
default:
description: |
Space delimited list of additional DPDK runtime libraries that should
be installed when DPDK is enabled.
.
By default, only the runtime libraries that are recommended with the
dpdk libraries are installed. Environments that need additional libraries
installed should include those library packages. The runtime libraries can
be defined either by the full package name (e.g. librte-pmd-bnx2x20.0)
or by the simple name (e.g. bnx2x). When providing the simple name, a
search is done of the apt-cache for a name matching `librte-*<name>*`
for installation and will install all matching packages that are found.
.
Only used when DPDK is enabled.
enable-hardware-offload:
type: boolean
default: false
Expand Down
50 changes: 50 additions & 0 deletions lib/charms/ovn_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import collections
import ipaddress
import os
import re
import subprocess

import charms.reactive as reactive
Expand Down Expand Up @@ -300,6 +301,53 @@ def enable_openstack(self):
"""
return reactive.is_flag_set('charm.ovn-chassis.enable-openstack')

@property
def additional_dpdk_libraries(self):
"""A list of additional runtime libraries to be installed for dpdk.
:returns: list of additional packages to install
:rtype: List[str]
"""
packages = []
if self.options.enable_dpdk and self.options.dpdk_runtime_libraries:
# dpdk_runtime_libraries is a space delimited list of strings.
# some options are disabled by passing 'None' so filter out a
# specifying of a 'None' value
pkgs = list(filter(lambda x: x and x.lower() != 'none',
self.options.dpdk_runtime_libraries.split()))
# Attempt to be nice and resolve the package names the user has
# provided in order to allow for users to specify 'hinic' etc for
# packages. To do this, we will search for all packages using the
# format of `librte-*<name>* and return all matching packages.
for name in pkgs:
regex = re.compile(r'(^\S.*)', re.MULTILINE)
if name.lower().startswith('librte'):
packages.append(name)
continue

cp = self.run('apt-cache', 'policy',
'librte-*{}*'.format(name))
# The apt-cache search does not return an error code if the
# package could not be found and the return code is 0. The
# stdout will be empty in this case and the regex won't
# produce a match. Log a warning message and use the provided
# package name anyways. This may cause a failure to install,
# but the user should have an idea of why.
results = re.findall(regex, cp.stdout)
if not results:
ch_core.hookenv.log(('Unable to find candidate librte '
'package for {}. Using raw name '
'provided.').format(name),
ch_core.hookenv.WARN)
packages.append(name)
continue

# The regex doesn't remove the trailing ':' so strip it out
# before adding it to the list of packages
packages.extend([p[:-1] for p in results])

return packages

@property
def packages(self):
"""Full list of packages to be installed.
Expand All @@ -310,6 +358,7 @@ def packages(self):
_packages = ['ovn-host']
if self.options.enable_dpdk:
_packages.extend(['openvswitch-switch-dpdk'])
_packages.extend(self.additional_dpdk_libraries)
if self.options.enable_hardware_offload or self.options.enable_sriov:
# The ``sriov-netplan-shim`` package does boot-time
# configuration of Virtual Functions (VFs) in the system.
Expand Down Expand Up @@ -548,6 +597,7 @@ def run(self, *args):
args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True,
universal_newlines=True)
ch_core.hookenv.log(cp, level=ch_core.hookenv.INFO)
return cp

def get_certificate_requests(self):
"""Override default certificate request handler.
Expand Down
184 changes: 184 additions & 0 deletions unit_tests/test_lib_charms_ovn_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import collections
import io
import textwrap
import unittest.mock as mock

import charms_openstack.charm.core as chm_core
Expand Down Expand Up @@ -442,6 +443,188 @@ def test_optional_openstack_metadata_wallaby(self):
'neutron-ovn-metadata-agent'])


class TestDPDKOVNChassisCharmExtraLibs(Helper):

def setUp(self):
self.local_config = {
'enable-hardware-offload': False,
'enable-sriov': False,
'enable-dpdk': True,
'dpdk-bond-mappings': ('dpdk-bond0:a0:36:9f:dd:37:a4 '
'dpdk-bond0:a0:36:9f:dd:3e:9c'),
'bridge-interface-mappings': 'br-ex:eth0 br-data:dpdk-bond0',
'ovn-bridge-mappings': (
'provider:br-ex other:br-data'),
'prefer-chassis-as-gw': False,
'dpdk-runtime-libraries': '',
}
super().setUp(config=self.local_config)

self.run = mock.Mock()
self.patch('charms.ovn_charm.BaseOVNChassisCharm.run',
new_callable=self.run)
self.run.start()
self.called_process = self.run.return_value
self.called_process.returncode = 0

def test_single_match(self):
apt_cache_output = textwrap.dedent(
"""
librte-net-hinic21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/universe amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/universe amd64 Packages
""" # noqa
)
self.called_process.stdout = apt_cache_output

self.local_config['dpdk-runtime-libraries'] = 'hinic'
target = ovn_charm.BaseUssuriOVNChassisCharm()
self.assertEquals(target.additional_dpdk_libraries, [
'librte-net-hinic21'])
self.assertEquals(target.packages, [
'ovn-host', 'openvswitch-switch-dpdk', 'librte-net-hinic21'])

def test_multiple_matches(self):
apt_cache_output = textwrap.dedent(
"""
librte-net-mlx5-21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/main amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/main amd64 Packages
librte-regex-mlx5-21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/universe amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/universe amd64 Packages
librte-common-mlx5-21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/main amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/main amd64 Packages
librte-vdpa-mlx5-21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/universe amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/universe amd64 Packages
""" # noqa
)
self.called_process.stdout = apt_cache_output

self.local_config['dpdk-runtime-libraries'] = 'mlx5'
target = ovn_charm.BaseUssuriOVNChassisCharm()
self.assertEquals(target.additional_dpdk_libraries, [
'librte-net-mlx5-21', 'librte-regex-mlx5-21',
'librte-common-mlx5-21', 'librte-vdpa-mlx5-21'])

def test_specific_package(self):
self.local_config['dpdk-runtime-libraries'] = 'librte-net-mlx5-21'
target = ovn_charm.BaseUssuriOVNChassisCharm()
self.assertEquals(target.additional_dpdk_libraries, [
'librte-net-mlx5-21'])
self.run.assert_not_called()

def test_none_package(self):
self.local_config['dpdk-runtime-libraries'] = 'None'
target = ovn_charm.BaseUssuriOVNChassisCharm()
self.assertEquals(target.additional_dpdk_libraries, [])
self.run.assert_not_called()

def test_multiple_packages(self):
process1 = mock.Mock()
process1.stdout = textwrap.dedent(
"""
librte-net-hinic21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/universe amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/universe amd64 Packages
""" # noqa
)
process2 = mock.Mock()
process2.stdout = textwrap.dedent(
"""
librte-net-mlx5-21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/main amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/main amd64 Packages
librte-regex-mlx5-21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/universe amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/universe amd64 Packages
librte-common-mlx5-21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/main amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/main amd64 Packages
librte-vdpa-mlx5-21:
Installed: 20.11.3-0ubuntu0.21.04.2
Candidate: 20.11.3-0ubuntu0.21.04.2
Version table:
*** 20.11.3-0ubuntu0.21.04.2 500
500 http://us.archive.ubuntu.com/ubuntu hirsute-updates/universe amd64 Packages
100 /var/lib/dpkg/status
20.11.1-1 500
500 http://us.archive.ubuntu.com/ubuntu hirsute/universe amd64 Packages
""" # noqa
)

self.run.side_effect = [process1, process2]

self.local_config['dpdk-runtime-libraries'] = 'hinic mlx'
target = ovn_charm.BaseUssuriOVNChassisCharm()
self.assertEquals(target.additional_dpdk_libraries, [
'librte-net-hinic21', 'librte-net-mlx5-21', 'librte-regex-mlx5-21',
'librte-common-mlx5-21', 'librte-vdpa-mlx5-21'])

def test_package_not_found(self):
# Missing packages don't have output via this command
self.called_process.stdout = ''
self.local_config['dpdk-runtime-libraries'] = 'missing'
target = ovn_charm.BaseUssuriOVNChassisCharm()
self.assertEquals(target.additional_dpdk_libraries, ['missing'])


class TestDPDKOVNChassisCharm(Helper):

def setUp(self):
Expand All @@ -455,6 +638,7 @@ def setUp(self):
'ovn-bridge-mappings': (
'provider:br-ex other:br-data'),
'prefer-chassis-as-gw': False,
'dpdk-runtime-libraries': '',
})

def test__init__(self):
Expand Down

0 comments on commit 0e971cb

Please sign in to comment.