From 50c2f3a97d6cc1b0a1c54c4b0d7fe5d9319f0295 Mon Sep 17 00:00:00 2001 From: Alex Groshev <38885591+haddystuff@users.noreply.github.com> Date: Fri, 19 Nov 2021 09:07:35 +0300 Subject: [PATCH] change ip4 type to list of str (#3738) * change ip4 type to list of str * Add several tests and change documentation * Update changelogs/fragments/1088-nmcli_add_multiple_addresses_support.yml Co-authored-by: Andrew Pantuso Co-authored-by: Andrew Pantuso --- ...8-nmcli_add_multiple_addresses_support.yml | 4 + plugins/modules/net_tools/nmcli.py | 25 +++- .../plugins/modules/net_tools/test_nmcli.py | 129 ++++++++++++++++++ 3 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 changelogs/fragments/1088-nmcli_add_multiple_addresses_support.yml diff --git a/changelogs/fragments/1088-nmcli_add_multiple_addresses_support.yml b/changelogs/fragments/1088-nmcli_add_multiple_addresses_support.yml new file mode 100644 index 0000000000..b78f7790f8 --- /dev/null +++ b/changelogs/fragments/1088-nmcli_add_multiple_addresses_support.yml @@ -0,0 +1,4 @@ +--- +minor_changes: + - nmcli - add multiple addresses support for ``ip4`` parameter + (https://github.com/ansible-collections/community.general/issues/1088, https://github.com/ansible-collections/community.general/pull/3738). \ No newline at end of file diff --git a/plugins/modules/net_tools/nmcli.py b/plugins/modules/net_tools/nmcli.py index c1b824be59..fffd921427 100644 --- a/plugins/modules/net_tools/nmcli.py +++ b/plugins/modules/net_tools/nmcli.py @@ -69,10 +69,11 @@ options: type: str ip4: description: - - The IPv4 address to this interface. + - List of IPv4 addresses to this interface. - Use the format C(192.0.2.24/24). - If defined and I(method4) is not specified, automatically set C(ipv4.method) to C(manual). - type: str + type: list + elements: str gw4: description: - The IPv4 gateway for this interface. @@ -822,7 +823,9 @@ EXAMPLES = r''' # nmcli_ethernet: # - conn_name: em1 # ifname: em1 -# ip4: '{{ tenant_ip }}' +# ip4: +# - '{{ tenant_ip }}' +# - '{{ second_tenant_ip }}' # gw4: '{{ tenant_gw }}' # - conn_name: em2 # ifname: em2 @@ -844,6 +847,7 @@ EXAMPLES = r''' # storage_ip: "192.0.2.91/23" # external_ip: "198.51.100.23/21" # tenant_ip: "203.0.113.77/23" +# second_tenant_ip: "204.0.113.77/23" # ``` @@ -997,6 +1001,16 @@ EXAMPLES = r''' type: ethernet state: present + - name: Add second ip4 address + community.general.nmcli: + conn_name: my-eth1 + ifname: eth1 + type: ethernet + ip4: + - 192.0.2.100/24 + - 192.0.3.100/24 + state: present + - name: Add VxLan community.general.nmcli: type: vxlan @@ -1468,7 +1482,8 @@ class Nmcli(object): 'ipv6.ignore-auto-routes', '802-11-wireless.hidden'): return bool - elif setting in ('ipv4.dns', + elif setting in ('ipv4.addresses', + 'ipv4.dns', 'ipv4.dns-search', 'ipv4.routes', 'ipv4.routing-rules', @@ -1758,7 +1773,7 @@ def main(): 'wifi', 'gsm', ]), - ip4=dict(type='str'), + ip4=dict(type='list', elements='str'), gw4=dict(type='str'), gw4_ignore_auto=dict(type='bool', default=False), routes4=dict(type='list', elements='str'), diff --git a/tests/unit/plugins/modules/net_tools/test_nmcli.py b/tests/unit/plugins/modules/net_tools/test_nmcli.py index 9c136aa5da..694d42db82 100644 --- a/tests/unit/plugins/modules/net_tools/test_nmcli.py +++ b/tests/unit/plugins/modules/net_tools/test_nmcli.py @@ -563,6 +563,37 @@ ipv6.ignore-auto-dns: no ipv6.ignore-auto-routes: no """ +TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip4': ['10.10.10.10/24', '10.10.20.10/24'], + 'gw4': '10.10.10.1', + 'dns4': ['1.1.1.1', '8.8.8.8'], + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +ipv4.method: manual +ipv4.addresses: 10.10.10.10/24,10.10.20.10/24 +ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv4.dns: 1.1.1.1,8.8.8.8 +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + TESTCASE_WIRELESS = [ { 'type': 'wifi', @@ -920,6 +951,24 @@ def mocked_ethernet_connection_static_unchanged(mocker): execute_return=(0, TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT, "")) +@pytest.fixture +def mocked_ethernet_connection_static_multiple_ip4_addresses_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_ethernet_connection_static_modify(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + @pytest.fixture def mocked_ethernet_connection_dhcp_to_static(mocker): mocker_set(mocker, @@ -2456,3 +2505,83 @@ def test_gsm_connection_unchanged(mocked_gsm_connection_unchanged, capfd): results = json.loads(out) assert not results.get('failed') assert not results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES, indirect=['patch_ansible_module']) +def test_create_ethernet_with_mulitple_ip4_addresses_static(mocked_generic_connection_create, capfd): + """ + Test : Create ethernet connection with static IP configuration + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 2 + 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', + 'ipv4.addresses', '10.10.10.10/24,10.10.20.10/24', + 'ipv4.gateway', '10.10.10.1', + 'ipv4.dns', '1.1.1.1,8.8.8.8']: + assert param in add_args_text + + up_args, up_kw = arg_list[1] + assert up_args[0][0] == '/usr/bin/nmcli' + assert up_args[0][1] == 'con' + assert up_args[0][2] == 'up' + assert up_args[0][3] == 'non_existent_nw_device' + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_with_mulitple_ip4_addresses_unchanged(mocked_ethernet_connection_static_multiple_ip4_addresses_unchanged, capfd): + """ + Test : Ethernet connection with static IP configuration 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_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES, indirect=['patch_ansible_module']) +def test_add_second_ip4_address_to_ethernet_connection(mocked_ethernet_connection_static_modify, capfd): + """ + Test : Modify ethernet connection from DHCP to static + """ + 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] == 'non_existent_nw_device' + + for param in ['ipv4.addresses', '10.10.10.10/24,10.10.20.10/24']: + assert param in args[0] + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed']