From 491196937db86dd2fab67d43f63cbee20e0c88a1 Mon Sep 17 00:00:00 2001 From: Andrew Pantuso Date: Tue, 30 Nov 2021 00:07:32 -0500 Subject: [PATCH] Fixing ip address without mask bug (#3784) (#3810) * change ip6 type to list of str and fix problem with setting addresses without netmask * change ip6 type to list of str and fix problem with setting addresses without netmask * Add changelog fragment * add suggestions * fix no mask using bug * Make change independed from feature branch (cherry picked from commit aae3ae1a8e2a31f55c563eddbe41484a0dd3fa72) Co-authored-by: Alex Groshev <38885591+haddystuff@users.noreply.github.com> --- ...768-nmcli_fix_changed_when_no_mask_set.yml | 4 +++ plugins/modules/net_tools/nmcli.py | 24 +++++++++++--- .../plugins/modules/net_tools/test_nmcli.py | 32 ++++++++++++++++++- 3 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 changelogs/fragments/3768-nmcli_fix_changed_when_no_mask_set.yml diff --git a/changelogs/fragments/3768-nmcli_fix_changed_when_no_mask_set.yml b/changelogs/fragments/3768-nmcli_fix_changed_when_no_mask_set.yml new file mode 100644 index 0000000000..0ea7298ba1 --- /dev/null +++ b/changelogs/fragments/3768-nmcli_fix_changed_when_no_mask_set.yml @@ -0,0 +1,4 @@ +--- +bugfixes: + - nmcli - fix returning "changed" when no mask set for IPv4 or IPv6 addresses on task rerun + (https://github.com/ansible-collections/community.general/issues/3768). \ No newline at end of file diff --git a/plugins/modules/net_tools/nmcli.py b/plugins/modules/net_tools/nmcli.py index c1b824be59..a1e9da9fd6 100644 --- a/plugins/modules/net_tools/nmcli.py +++ b/plugins/modules/net_tools/nmcli.py @@ -70,7 +70,7 @@ options: ip4: description: - The IPv4 address to this interface. - - Use the format C(192.0.2.24/24). + - Use the format C(192.0.2.24/24) or C(192.0.2.24). - If defined and I(method4) is not specified, automatically set C(ipv4.method) to C(manual). type: str gw4: @@ -143,7 +143,7 @@ options: ip6: description: - The IPv6 address to this interface. - - Use the format C(abbe::cafe). + - Use the format C(abbe::cafe/128 or abbe::cafe). - If defined and I(method6) is not specified, automatically set C(ipv6.method) to C(manual). type: str gw6: @@ -1241,7 +1241,7 @@ class Nmcli(object): # IP address options. if self.ip_conn_type and not self.master: options.update({ - 'ipv4.addresses': self.ip4, + 'ipv4.addresses': self.enforce_ipv4_cidr_notation(self.ip4), 'ipv4.dhcp-client-id': self.dhcp_client_id, 'ipv4.dns': self.dns4, 'ipv4.dns-search': self.dns4_search, @@ -1254,7 +1254,7 @@ class Nmcli(object): 'ipv4.never-default': self.never_default4, 'ipv4.method': self.ipv4_method, 'ipv4.may-fail': self.may_fail4, - 'ipv6.addresses': self.ip6, + 'ipv6.addresses': self.enforce_ipv6_cidr_notation(self.ip6), 'ipv6.dns': self.dns6, 'ipv6.dns-search': self.dns6_search, 'ipv6.ignore-auto-dns': self.dns6_ignore_auto, @@ -1444,6 +1444,22 @@ class Nmcli(object): 'sit', ) + @staticmethod + def enforce_ipv4_cidr_notation(ip4_address): + if ip4_address is None or '/' in ip4_address: + return ip4_address + + return ip4_address + '/32' + + @staticmethod + def enforce_ipv6_cidr_notation(ip6_address): + if ip6_address is None: + return None + elif '/' in ip6_address: + return ip6_address + else: + return ip6_address + '/128' + @staticmethod def bool_to_string(boolean): if boolean: diff --git a/tests/unit/plugins/modules/net_tools/test_nmcli.py b/tests/unit/plugins/modules/net_tools/test_nmcli.py index 9c136aa5da..c732c1e61d 100644 --- a/tests/unit/plugins/modules/net_tools/test_nmcli.py +++ b/tests/unit/plugins/modules/net_tools/test_nmcli.py @@ -563,6 +563,26 @@ ipv6.ignore-auto-dns: no ipv6.ignore-auto-routes: no """ +TESTCASE_ETHERNET_STATIC_IP6_ADDRESS_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +ipv6.method: manual +ipv6.addresses: 2001:db8::cafe/128 +ipv6.gateway: 2001:db8::cafa +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ipv6.never-default: no +ipv6.may-fail: yes +ipv6.dns: 2001:4860:4860::8888,2001:4860:4860::8844 +ipv4.method: disabled +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +""" + TESTCASE_WIRELESS = [ { 'type': 'wifi', @@ -668,7 +688,6 @@ 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 ipv6.method: manual @@ -920,6 +939,17 @@ def mocked_ethernet_connection_static_unchanged(mocker): execute_return=(0, TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT, "")) +@pytest.fixture +def mocked_ethernet_connection_with_ipv6_address_static_modify(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_STATIC_IP6_ADDRESS_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + @pytest.fixture def mocked_ethernet_connection_dhcp_to_static(mocker): mocker_set(mocker,