Add SR-IOV support to nmcli module (#9168)

* Add SR-IOV support to nmcli module (#9168)

* Add SR-IOV support to nmcli module (#9168)

Fixes

* Add SR-IOV support to nmcli module (#9168)

Add test

* Update changelogs/fragments/9168-nmcli-add-sriov-parameter.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/nmcli.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/nmcli.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/nmcli.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Populate sriov options

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
pull/9223/head
Ian Bishop 2024-12-03 05:18:28 +10:00 committed by GitHub
parent ab0959480e
commit 82462e407e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 107 additions and 0 deletions

View File

@ -0,0 +1,2 @@
minor_changes:
- nmcli - add ``sriov`` parameter that enables support for SR-IOV settings (https://github.com/ansible-collections/community.general/pull/9168).

View File

@ -1058,6 +1058,38 @@ options:
You can encode using this Ansible jinja2 expression: V("0s{{ '[YOUR PRE-SHARED KEY]' | ansible.builtin.b64encode }}"). You can encode using this Ansible jinja2 expression: V("0s{{ '[YOUR PRE-SHARED KEY]' | ansible.builtin.b64encode }}").
- This is only used when O(vpn.ipsec-enabled=true). - This is only used when O(vpn.ipsec-enabled=true).
type: str type: str
sriov:
description:
- Allow to configure SR-IOV settings.
- 'An up-to-date list of supported attributes can be found here:
U(https://networkmanager.pages.freedesktop.org/NetworkManager/NetworkManager/settings-sriov.html).'
type: dict
version_added: 10.1.0
suboptions:
autoprobe-drivers:
description:
- Whether to autoprobe virtual functions by a compatible driver.
type: int
eswitch-encap-mode:
description:
- Select the eswitch encapsulation support.
type: int
eswitch-inline-mode:
description:
- Select the eswitch inline-mode of the device.
type: int
eswitch-mode:
description:
- Select the eswitch mode of the device.
type: int
total-vfs:
description: Number of virtual functions to create. Consult your NIC documentation for the maximum number of VFs supported.
type: int
vfs:
description:
- 'Virtual function descriptors in the form: V(INDEX [ATTR=VALUE[ ATTR=VALUE]...]).'
- Multiple VFs can be specified using a comma as separator, for example V(2 mac=00:11:22:33:44:55 spoof-check=true,3 vlans=100).
type: str
''' '''
EXAMPLES = r''' EXAMPLES = r'''
@ -1687,6 +1719,7 @@ class Nmcli(object):
self.wireguard = module.params['wireguard'] self.wireguard = module.params['wireguard']
self.vpn = module.params['vpn'] self.vpn = module.params['vpn']
self.transport_mode = module.params['transport_mode'] self.transport_mode = module.params['transport_mode']
self.sriov = module.params['sriov']
if self.method4: if self.method4:
self.ipv4_method = self.method4 self.ipv4_method = self.method4
@ -1952,6 +1985,13 @@ class Nmcli(object):
'infiniband.transport-mode': self.transport_mode, 'infiniband.transport-mode': self.transport_mode,
}) })
if self.type == 'ethernet':
if self.sriov:
for name, value in self.sriov.items():
options.update({
'sriov.%s' % name: value,
})
# Convert settings values based on the situation. # Convert settings values based on the situation.
for setting, value in options.items(): for setting, value in options.items():
setting_type = self.settings_type(setting) setting_type = self.settings_type(setting)
@ -2607,6 +2647,7 @@ def main():
wireguard=dict(type='dict'), wireguard=dict(type='dict'),
vpn=dict(type='dict'), vpn=dict(type='dict'),
transport_mode=dict(type='str', choices=['datagram', 'connected']), transport_mode=dict(type='str', choices=['datagram', 'connected']),
sriov=dict(type='dict'),
), ),
mutually_exclusive=[['never_default4', 'gw4'], mutually_exclusive=[['never_default4', 'gw4'],
['routes4_extended', 'routes4'], ['routes4_extended', 'routes4'],

View File

@ -357,6 +357,28 @@ ipv6.ignore-auto-dns: no
ipv6.ignore-auto-routes: no ipv6.ignore-auto-routes: no
""" """
TESTCASE_ETHERNET_ADD_SRIOV_VFS = [
{
'type': 'ethernet',
'conn_name': 'non_existent_nw_device',
'ifname': 'ethernet_non_existant',
'sriov': {
'total-vfs': 16,
'vfs': '0 spoof-check=true vlans=100',
},
'state': 'present',
'_ansible_check_mode': False,
}
]
TESTCASE_ETHERNET_ADD_SRIOV_VFS_SHOW_OUTPUT = """\
connection.id: non_existent_nw_device
connection.interface-name: ethernet_non_existant
connection.autoconnect: yes
sriov.total-vfs: 16
sriov.vfs: 0 spoof-check=true vlans=100
"""
TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC = [ TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC = [
{ {
'type': 'ethernet', 'type': 'ethernet',
@ -1806,6 +1828,12 @@ def mocked_ethernet_connection_with_ipv6_static_address_multiple_static_routes_c
)) ))
@pytest.fixture
def mocked_ethernet_connection_with_sriov_vfs_create(mocker):
mocker_set(mocker,
execute_return=(0, TESTCASE_ETHERNET_ADD_SRIOV_VFS_SHOW_OUTPUT, ""))
@pytest.fixture @pytest.fixture
def mocked_ethernet_connection_with_ipv6_static_address_static_route_with_metric_create(mocker): def mocked_ethernet_connection_with_ipv6_static_address_static_route_with_metric_create(mocker):
mocker_set(mocker, mocker_set(mocker,
@ -3312,6 +3340,41 @@ def test_ethernet_connection_static_ipv6_address_multiple_static_routes_with_met
assert results['changed'] assert results['changed']
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_ADD_SRIOV_VFS, indirect=['patch_ansible_module'])
def test_ethernet_connection_sriov_vfs_create(
mocked_ethernet_connection_with_sriov_vfs_create, capfd):
"""
Test : Create ethernet connection with SR-IOV VFs
"""
with pytest.raises(SystemExit):
nmcli.main()
assert nmcli.Nmcli.execute_command.call_count == 1
arg_list = nmcli.Nmcli.execute_command.call_args_list
add_args, add_kw = arg_list[0]
assert add_args[0][0] == '/usr/bin/nmcli'
assert add_args[0][1] == 'con'
assert add_args[0][2] == 'add'
assert add_args[0][3] == 'type'
assert add_args[0][4] == 'ethernet'
assert add_args[0][5] == 'con-name'
assert add_args[0][6] == 'non_existent_nw_device'
add_args_text = list(map(to_text, add_args[0]))
for param in ['connection.interface-name', 'ethernet_non_existant',
'con-name', 'non_existent_nw_device',
'sriov.total-vfs', '16',
'sriov.vfs', '0 spoof-check=true vlans=100']:
assert param in add_args_text
out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert results['changed']
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC, indirect=['patch_ansible_module']) @pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC, indirect=['patch_ansible_module'])
def test_ethernet_connection_static_ipv6_address_static_route_with_metric_create( def test_ethernet_connection_static_ipv6_address_static_route_with_metric_create(
mocked_ethernet_connection_with_ipv6_static_address_static_route_with_metric_create, capfd): mocked_ethernet_connection_with_ipv6_static_address_static_route_with_metric_create, capfd):
@ -4384,6 +4447,7 @@ def test_bond_connection_unchanged(mocked_generic_connection_diff_check, capfd):
wireguard=dict(type='dict'), wireguard=dict(type='dict'),
vpn=dict(type='dict'), vpn=dict(type='dict'),
transport_mode=dict(type='str', choices=['datagram', 'connected']), transport_mode=dict(type='str', choices=['datagram', 'connected']),
sriov=dict(type='dict'),
), ),
mutually_exclusive=[['never_default4', 'gw4'], mutually_exclusive=[['never_default4', 'gw4'],
['routes4_extended', 'routes4'], ['routes4_extended', 'routes4'],