Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for loopback type in nmcli module #7515

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/6572-nmcli-add-support-loopback-type.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- nmcli - add support for new connection type ``loopback`` (https://github.com/ansible-collections/community.general/issues/6572).
5 changes: 4 additions & 1 deletion plugins/modules/nmcli.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@
- Type V(generic) is added in Ansible 2.5.
- Type V(infiniband) is added in community.general 2.0.0.
- Type V(gsm) is added in community.general 3.7.0.
- Type V(loopback) is added in community.general 8.1.0.
- Type V(macvlan) is added in community.general 6.6.0.
- Type V(wireguard) is added in community.general 4.3.0.
- Type V(vpn) is added in community.general 5.1.0.
- Using V(bond-slave), V(bridge-slave), or V(team-slave) implies V(ethernet) connection type with corresponding O(slave_type) option.
- If you want to control non-ethernet connection attached to V(bond), V(bridge), or V(team) consider using O(slave_type) option.
haddystuff marked this conversation as resolved.
Show resolved Hide resolved
type: str
choices: [ bond, bond-slave, bridge, bridge-slave, dummy, ethernet, generic, gre, infiniband, ipip, macvlan, sit, team, team-slave, vlan, vxlan,
wifi, gsm, wireguard, vpn ]
wifi, gsm, wireguard, vpn, loopback ]
mode:
description:
- This is the type of device or network connection that you wish to create for a bond or bridge.
Expand Down Expand Up @@ -1922,6 +1923,7 @@ def ip_conn_type(self):
'macvlan',
'wireguard',
'vpn',
'loopback',
)

@property
Expand Down Expand Up @@ -2400,6 +2402,7 @@ def main():
'macvlan',
'wireguard',
'vpn',
'loopback',
]),
ip4=dict(type='list', elements='str'),
gw4=dict(type='str'),
Expand Down
133 changes: 133 additions & 0 deletions tests/unit/plugins/modules/test_nmcli.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@
'state': 'absent',
'_ansible_check_mode': True,
},
{
'type': 'loopback',
'conn_name': 'non_existent_nw_device',
'state': 'absent',
'_ansible_check_mode': True,
},
]

TESTCASE_GENERIC = [
Expand Down Expand Up @@ -944,6 +950,28 @@
}
]

TESTCASE_LOOPBACK = [
{
'type': 'loopback',
'conn_name': 'lo',
'ifname': 'lo',
'ip4': '127.0.0.1/8',
'state': 'present',
'_ansible_check_mode': False,
}
]

TESTCASE_LOOPBACK_MODIFY = [
{
'type': 'loopback',
'conn_name': 'lo',
'ifname': 'lo',
'ip4': ['127.0.0.1/8', '127.0.0.2/8'],
'state': 'present',
'_ansible_check_mode': False,
}
]

TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT = """\
connection.id: non_existent_nw_device
connection.interface-name: ethernet_non_existant
Expand All @@ -962,6 +990,21 @@
ipv6.ignore-auto-routes: no
"""

TESTCASE_LOOPBACK_SHOW_OUTPUT = """\
connection.id: lo
connection.interface-name: lo
connection.autoconnect: yes
ipv4.method: manual
ipv4.addresses: 127.0.0.1/8
ipv4.ignore-auto-dns: no
ipv4.ignore-auto-routes: no
ipv4.never-default: no
ipv4.may-fail: yes
ipv6.method: manual
ipv6.ignore-auto-dns: no
ipv6.ignore-auto-routes: no
"""

TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES = [
{
'type': 'ethernet',
Expand Down Expand Up @@ -1945,6 +1988,24 @@ def mocked_generic_connection_diff_check(mocker):
execute_return=(0, TESTCASE_GENERIC_SHOW_OUTPUT, ""))


@pytest.fixture
def mocked_loopback_connection_unchanged(mocker):
mocker_set(mocker,
connection_exists=True,
execute_return=(0, TESTCASE_LOOPBACK_SHOW_OUTPUT, ""))


@pytest.fixture
def mocked_loopback_connection_modify(mocker):
mocker_set(mocker,
connection_exists=True,
execute_return=None,
execute_side_effect=(
(0, TESTCASE_LOOPBACK_SHOW_OUTPUT, ""),
(0, "", ""),
))


@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BOND, indirect=['patch_ansible_module'])
def test_bond_connection_create(mocked_generic_connection_create, capfd):
"""
Expand Down Expand Up @@ -4711,3 +4772,75 @@ def test_slave_type_team_unchanged(mocked_create_slave_type_team_unchanged, capf
results = json.loads(out)
assert not results.get('failed')
assert not results['changed']


@pytest.mark.parametrize('patch_ansible_module', TESTCASE_LOOPBACK, indirect=['patch_ansible_module'])
def test_create_loopback(mocked_generic_connection_create, capfd):
"""
Test : Create loopback connection
"""

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] == 'loopback'
assert add_args[0][5] == 'con-name'
assert add_args[0][6] == 'lo'

add_args_text = list(map(to_text, add_args[0]))
for param in ['connection.interface-name', 'lo',
'ipv4.addresses', '127.0.0.1/8']:
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_LOOPBACK, indirect=['patch_ansible_module'])
def test_unchanged_loopback(mocked_loopback_connection_unchanged, capfd):
"""
Test : loopback connection unchanged
"""
with pytest.raises(SystemExit):
nmcli.main()

out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert not results['changed']


@pytest.mark.parametrize('patch_ansible_module', TESTCASE_LOOPBACK_MODIFY, indirect=['patch_ansible_module'])
def test_add_second_ip4_address_to_loopback_connection(mocked_loopback_connection_modify, capfd):
"""
Test : Modify loopback connection
"""
with pytest.raises(SystemExit):
nmcli.main()

assert nmcli.Nmcli.execute_command.call_count == 2
arg_list = nmcli.Nmcli.execute_command.call_args_list
args, kwargs = arg_list[1]

assert args[0][0] == '/usr/bin/nmcli'
assert args[0][1] == 'con'
assert args[0][2] == 'modify'
assert args[0][3] == 'lo'

for param in ['ipv4.addresses', '127.0.0.1/8,127.0.0.2/8']:
assert param in args[0]

out, err = capfd.readouterr()
results = json.loads(out)
assert not results.get('failed')
assert results['changed']