nmcli: added new module option 'slave_type' to allow create non-ethernet slave connections (#6108)

* nmcli: added new module option 'slave_type' to allow create non-ethernet slave connections

* argument specs updated

* documentation updated

* examples updated

* added warning message when using type='bridge-slave'

* remove trailing whitespace

* Added warnings about rewrite 'slave-type' property when using type one of 'bond-slave', 'bridge-slave', 'team-slave'.
Added module fails when user sets contradicting values of 'slave-type' for types 'bond-slave', 'bridge-slave', 'team-slave'.
Returned back checking for types that can be a slave to assign 'master' and 'slave-type' properties.

* Extending list of slave-conn-types

* Update plugins/modules/nmcli.py

Version updated

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* Update plugins/modules/nmcli.py

Updated documentation for `slave_type`

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* Updated argspec's 'required_by' for 'master' property.

* Fixed mistake in property  naming in module argspec.

* changelog fragment and module docs updated

* Validation of 'master', 'slave_type' options improved. (rebased)

* Validation of 'master' and 'slave_type' separated to special method.

* Wrote 6 tests for slave_type option behaviour

* Removed erroneously added property 'hairpin'

* Update version_added for 'slave_type'

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* Update changelogs/fragments/473-nmcli-slave-type-implemented.yml

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

* Update plugins/modules/nmcli.py

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

* Let master be without slave_type

---------

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
Sam Potekhin 2023-05-09 00:44:30 +07:00 committed by GitHub
parent 9f3c86a589
commit c949f3a834
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 389 additions and 8 deletions

View file

@ -4031,6 +4031,7 @@ def test_bond_connection_unchanged(mocked_generic_connection_diff_check, capfd):
state=dict(type='str', required=True, choices=['absent', 'present']),
conn_name=dict(type='str', required=True),
master=dict(type='str'),
slave_type=dict(type=str, choices=['bond', 'bridge', 'team']),
ifname=dict(type='str'),
type=dict(type='str',
choices=[
@ -4258,3 +4259,294 @@ def test_macvlan_mod(mocked_generic_connection_modify, capfd):
results = json.loads(out)
assert not results.get('failed')
assert results['changed']
TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION = [
{
'type': 'ethernet',
'conn_name': 'fake_conn',
'ifname': 'fake_eth0',
'state': 'present',
'slave_type': 'bridge',
'master': 'fake_br0',
'_ansible_check_mode': False,
}
]
TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION_SHOW_OUTPUT = """\
connection.id: fake_conn
connection.type: 802-3-ethernet
connection.interface-name: fake_eth0
connection.autoconnect: yes
connection.master: --
connection.slave-type: --
802-3-ethernet.mtu: auto
"""
TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION_UNCHANGED_SHOW_OUTPUT = """\
connection.id: fake_conn
connection.type: 802-3-ethernet
connection.interface-name: fake_eth0
connection.autoconnect: yes
connection.master: fake_br0
connection.slave-type: bridge
802-3-ethernet.mtu: auto
"""
@pytest.fixture
def mocked_slave_type_bridge_create(mocker):
mocker_set(mocker,
execute_return=None,
execute_side_effect=(
(0, TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION_SHOW_OUTPUT, ""),
(0, "", ""),
))
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION, indirect=['patch_ansible_module'])
def test_create_slave_type_bridge(mocked_slave_type_bridge_create, capfd):
"""
Test : slave for bridge created
"""
with pytest.raises(SystemExit):
nmcli.main()
assert nmcli.Nmcli.execute_command.call_count == 1
arg_list = nmcli.Nmcli.execute_command.call_args_list
args, kwargs = arg_list[0]
assert args[0][0] == '/usr/bin/nmcli'
assert args[0][1] == 'con'
assert args[0][2] == 'add'
assert args[0][3] == 'type'
assert args[0][4] == 'ethernet'
assert args[0][5] == 'con-name'
assert args[0][6] == 'fake_conn'
con_master_index = args[0].index('connection.master')
slave_type_index = args[0].index('connection.slave-type')
assert args[0][con_master_index + 1] == 'fake_br0'
assert args[0][slave_type_index + 1] == 'bridge'
out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert results['changed']
@pytest.fixture
def mocked_create_slave_type_bridge_unchanged(mocker):
mocker_set(mocker,
connection_exists=True,
execute_return=(0, TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION_UNCHANGED_SHOW_OUTPUT, ""))
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION, indirect=['patch_ansible_module'])
def test_slave_type_bridge_unchanged(mocked_create_slave_type_bridge_unchanged, capfd):
"""
Test : Existent slave for bridge unchanged
"""
with pytest.raises(SystemExit):
nmcli.main()
out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert not results['changed']
TESTCASE_SLAVE_TYPE_BOND_CONNECTION = [
{
'type': 'ethernet',
'conn_name': 'fake_conn',
'ifname': 'fake_eth0',
'state': 'present',
'slave_type': 'bond',
'master': 'fake_bond0',
'_ansible_check_mode': False,
}
]
TESTCASE_SLAVE_TYPE_BOND_CONNECTION_SHOW_OUTPUT = """\
connection.id: fake_conn
connection.type: 802-3-ethernet
connection.interface-name: fake_eth0
connection.autoconnect: yes
connection.master: --
connection.slave-type: --
802-3-ethernet.mtu: auto
"""
TESTCASE_SLAVE_TYPE_BOND_CONNECTION_UNCHANGED_SHOW_OUTPUT = """\
connection.id: fake_conn
connection.type: 802-3-ethernet
connection.interface-name: fake_eth0
connection.autoconnect: yes
connection.master: fake_bond0
connection.slave-type: bond
802-3-ethernet.mtu: auto
"""
@pytest.fixture
def mocked_slave_type_bond_create(mocker):
mocker_set(mocker,
execute_return=None,
execute_side_effect=(
(0, TESTCASE_SLAVE_TYPE_BOND_CONNECTION_SHOW_OUTPUT, ""),
(0, "", ""),
))
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_BOND_CONNECTION, indirect=['patch_ansible_module'])
def test_create_slave_type_bond(mocked_slave_type_bond_create, capfd):
"""
Test : slave for bond created
"""
with pytest.raises(SystemExit):
nmcli.main()
assert nmcli.Nmcli.execute_command.call_count == 1
arg_list = nmcli.Nmcli.execute_command.call_args_list
args, kwargs = arg_list[0]
assert args[0][0] == '/usr/bin/nmcli'
assert args[0][1] == 'con'
assert args[0][2] == 'add'
assert args[0][3] == 'type'
assert args[0][4] == 'ethernet'
assert args[0][5] == 'con-name'
assert args[0][6] == 'fake_conn'
con_master_index = args[0].index('connection.master')
slave_type_index = args[0].index('connection.slave-type')
assert args[0][con_master_index + 1] == 'fake_bond0'
assert args[0][slave_type_index + 1] == 'bond'
out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert results['changed']
@pytest.fixture
def mocked_create_slave_type_bond_unchanged(mocker):
mocker_set(mocker,
connection_exists=True,
execute_return=(0, TESTCASE_SLAVE_TYPE_BOND_CONNECTION_UNCHANGED_SHOW_OUTPUT, ""))
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_BOND_CONNECTION, indirect=['patch_ansible_module'])
def test_slave_type_bond_unchanged(mocked_create_slave_type_bond_unchanged, capfd):
"""
Test : Existent slave for bridge unchanged
"""
with pytest.raises(SystemExit):
nmcli.main()
out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert not results['changed']
TESTCASE_SLAVE_TYPE_TEAM_CONNECTION = [
{
'type': 'ethernet',
'conn_name': 'fake_conn',
'ifname': 'fake_eth0',
'state': 'present',
'slave_type': 'team',
'master': 'fake_team0',
'_ansible_check_mode': False,
}
]
TESTCASE_SLAVE_TYPE_TEAM_CONNECTION_SHOW_OUTPUT = """\
connection.id: fake_conn
connection.type: 802-3-ethernet
connection.interface-name: fake_eth0
connection.autoconnect: yes
connection.master: --
connection.slave-type: --
802-3-ethernet.mtu: auto
"""
TESTCASE_SLAVE_TYPE_TEAM_CONNECTION_UNCHANGED_SHOW_OUTPUT = """\
connection.id: fake_conn
connection.type: 802-3-ethernet
connection.interface-name: fake_eth0
connection.autoconnect: yes
connection.master: fake_team0
connection.slave-type: team
802-3-ethernet.mtu: auto
"""
@pytest.fixture
def mocked_slave_type_team_create(mocker):
mocker_set(mocker,
execute_return=None,
execute_side_effect=(
(0, TESTCASE_SLAVE_TYPE_TEAM_CONNECTION_SHOW_OUTPUT, ""),
(0, "", ""),
))
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_TEAM_CONNECTION, indirect=['patch_ansible_module'])
def test_create_slave_type_team(mocked_slave_type_team_create, capfd):
"""
Test : slave for bond created
"""
with pytest.raises(SystemExit):
nmcli.main()
assert nmcli.Nmcli.execute_command.call_count == 1
arg_list = nmcli.Nmcli.execute_command.call_args_list
args, kwargs = arg_list[0]
assert args[0][0] == '/usr/bin/nmcli'
assert args[0][1] == 'con'
assert args[0][2] == 'add'
assert args[0][3] == 'type'
assert args[0][4] == 'ethernet'
assert args[0][5] == 'con-name'
assert args[0][6] == 'fake_conn'
con_master_index = args[0].index('connection.master')
slave_type_index = args[0].index('connection.slave-type')
assert args[0][con_master_index + 1] == 'fake_team0'
assert args[0][slave_type_index + 1] == 'team'
out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert results['changed']
@pytest.fixture
def mocked_create_slave_type_team_unchanged(mocker):
mocker_set(mocker,
connection_exists=True,
execute_return=(0, TESTCASE_SLAVE_TYPE_TEAM_CONNECTION_UNCHANGED_SHOW_OUTPUT, ""))
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_TEAM_CONNECTION, indirect=['patch_ansible_module'])
def test_slave_type_team_unchanged(mocked_create_slave_type_team_unchanged, capfd):
"""
Test : Existent slave for bridge unchanged
"""
with pytest.raises(SystemExit):
nmcli.main()
out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert not results['changed']