diff --git a/README.md b/README.md index 842b851..ccd2a93 100644 --- a/README.md +++ b/README.md @@ -540,6 +540,17 @@ The IP configuration supports the following options: The default gateway for IPv4 (`gateway4`) or IPv6 (`gateway6`) packets. +- `ipv4_ignore_auto_dns` and `ipv6_ignore_auto_dns` + + If enabled, the automatically configured name servers and search domains (via + DHCPv4, DHCPv6, modem etc) for IPv4 or IPv6 are ignored, only the name servers and + search domains specified in `dns` and `dns_search` properties are used. The + settings are distinguished by the address families. The variables are not supported + by initscripts provider. + + If the variables are not specified, the role will use the default behavior of nm + provider. + - `route_metric4` and `route_metric6` For `NetworkManager`, `route_metric4` and `route_metric6` corresponds to the diff --git a/examples/ignore_auto_dns.yml b/examples/ignore_auto_dns.yml new file mode 100644 index 0000000..75dd4a2 --- /dev/null +++ b/examples/ignore_auto_dns.yml @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- name: Test ipv4_ignore_auto_dns and ipv6_ignore_auto_dns settings + hosts: all + tasks: + - name: Test configuring ipv4_ignore_auto_dns and ipv6_ignore_auto_dns + settings for an interface + block: + - name: Configure an interface with ipv4_ignore_auto_dns disabled and + ipv6_ignore_auto_dns enabled + import_role: + name: linux-system-roles.network + vars: + network_connections: + - name: eth0 + state: up + type: ethernet + autoconnect: false + ip: + ipv4_ignore_auto_dns: false + ipv6_ignore_auto_dns: true + auto6: true + address: + - 198.51.100.3/24 diff --git a/library/network_connections.py b/library/network_connections.py index 1f9f6eb..2ee151c 100644 --- a/library/network_connections.py +++ b/library/network_connections.py @@ -1173,6 +1173,13 @@ class NMUtil: s_ip6.set_property(NM.SETTING_IP_CONFIG_NEVER_DEFAULT, True) s_ip4.set_property(NM.SETTING_IP_CONFIG_NEVER_DEFAULT, True) + s_ip4.set_property( + NM.SETTING_IP_CONFIG_IGNORE_AUTO_DNS, bool(ip["ipv4_ignore_auto_dns"]) + ) + s_ip6.set_property( + NM.SETTING_IP_CONFIG_IGNORE_AUTO_DNS, bool(ip["ipv6_ignore_auto_dns"]) + ) + for nameserver in ip["dns"]: if nameserver["family"] == socket.AF_INET6: s_ip6.add_dns(nameserver["address"]) diff --git a/module_utils/network_lsr/argument_validator.py b/module_utils/network_lsr/argument_validator.py index 019b90e..0919110 100644 --- a/module_utils/network_lsr/argument_validator.py +++ b/module_utils/network_lsr/argument_validator.py @@ -867,6 +867,8 @@ class ArgValidator_DictIP(ArgValidatorDict): "route_metric4", val_min=-1, val_max=UINT32_MAX, default_value=None ), ArgValidatorBool("auto6", default_value=None), + ArgValidatorBool("ipv4_ignore_auto_dns", default_value=None), + ArgValidatorBool("ipv6_ignore_auto_dns", default_value=None), ArgValidatorBool("ipv6_disabled", default_value=None), ArgValidatorIP("gateway6", family=socket.AF_INET6), ArgValidatorNum( @@ -918,6 +920,8 @@ class ArgValidator_DictIP(ArgValidatorDict): "gateway4": None, "route_metric4": None, "auto6": True, + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "ipv6_disabled": False, "gateway6": None, "route_metric6": None, @@ -2461,6 +2465,19 @@ class ArgValidator_ListConnections(ArgValidatorList): "if you need to use initscripts.", ) + # initscripts does not support ip.ipv4_ignore_auto_dns or + # ip.ipv6_ignore_auto_dns, so raise errors when network + # provider is initscripts + if ( + connection["ip"]["ipv4_ignore_auto_dns"] is not None + or connection["ip"]["ipv6_ignore_auto_dns"] is not None + ): + if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS: + raise ValidationError.from_connection( + idx, + "ip.ipv4_ignore_auto_dns or ip.ipv6_ignore_auto_dns is not " + "supported by initscripts.", + ) # initscripts does not support ip.dns_options, so raise errors when network # provider is initscripts if connection["ip"]["dns_options"]: diff --git a/tests/ensure_provider_tests.py b/tests/ensure_provider_tests.py index c043764..c798cd1 100755 --- a/tests/ensure_provider_tests.py +++ b/tests/ensure_provider_tests.py @@ -76,6 +76,7 @@ ibution_major_version | int < 9", EXTRA_RUN_CONDITION: "ansible_distribution != 'RedHat' or\n ansible_distr\ ibution_major_version | int < 9", }, + "playbooks/tests_ignore_auto_dns.yml": {}, "playbooks/tests_bond_options.yml": {}, "playbooks/tests_eth_dns_support.yml": {}, "playbooks/tests_dummy.yml": {}, # wokeignore:rule=dummy diff --git a/tests/playbooks/tests_ignore_auto_dns.yml b/tests/playbooks/tests_ignore_auto_dns.yml new file mode 100644 index 0000000..9007162 --- /dev/null +++ b/tests/playbooks/tests_ignore_auto_dns.yml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- name: Test ipv4_ignore_auto_dns and ipv6_ignore_auto_dns settings + hosts: all + vars: + type: veth + interface: ethtest0 + + tasks: + - name: Include the task 'show_interfaces.yml' + include_tasks: tasks/show_interfaces.yml + - name: Include the task 'manage_test_interface.yml' + include_tasks: tasks/manage_test_interface.yml + vars: + state: present + - name: Include the task 'assert_device_present.yml' + include_tasks: tasks/assert_device_present.yml + + - name: Test configuring ipv4_ignore_auto_dns and ipv6_ignore_auto_dns + settings for an interface + block: + - name: Configure an interface with ipv4_ignore_auto_dns disabled and + ipv6_ignore_auto_dns enabled + import_role: + name: linux-system-roles.network + vars: + network_connections: + - name: "{{ interface }}" + interface_name: "{{ interface }}" + state: up + type: ethernet + autoconnect: false + ip: + ipv4_ignore_auto_dns: false + ipv6_ignore_auto_dns: true + auto6: true + address: + - 198.51.100.3/24 + - name: Get the nmcli ipv4.ignore-auto-dns setting + command: nmcli -f ipv4.ignore-auto-dns c show {{ interface }} + register: ipv4_ignore_auto_dns + changed_when: false + + - name: Get the nmcli ipv6.ignore-auto-dns setting + command: nmcli -f ipv6.ignore-auto-dns c show {{ interface }} + register: ipv6_ignore_auto_dns + changed_when: false + + - name: Assert that the setting ipv4.ignore-auto-dns is 'no' + assert: + that: + - "'no' in ipv4_ignore_auto_dns.stdout" + msg: "the setting ipv4.ignore-auto-dns is 'yes'" + + - name: Assert that the setting ipv6.ignore-auto-dns is 'yes' + assert: + that: + - "'yes' in ipv6_ignore_auto_dns.stdout" + msg: "the setting ipv6.ignore-auto-dns is 'no'" + + always: + - name: Remove test configuration + tags: + - "tests::cleanup" + block: + - name: Bring down test devices and profiles + include_role: + name: linux-system-roles.network + vars: + network_connections: + - name: "{{ interface }}" + persistent_state: absent + state: down + - name: Delete interface - '{{ interface }}' + include_tasks: tasks/delete_interface.yml + - name: Include the task 'assert_device_absent.yml' + include_tasks: tasks/assert_device_absent.yml + - name: Include the task 'assert_profile_absent.yml' + include_tasks: tasks/assert_profile_absent.yml + vars: + profile: "{{ interface }}" +... diff --git a/tests/tests_ignore_auto_dns_nm.yml b/tests/tests_ignore_auto_dns_nm.yml new file mode 100644 index 0000000..7a1f799 --- /dev/null +++ b/tests/tests_ignore_auto_dns_nm.yml @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: BSD-3-Clause +# This file was generated by ensure_provider_tests.py +--- +# set network provider and gather facts +- hosts: all + # yamllint disable rule:line-length + name: Run playbook 'playbooks/tests_ignore_auto_dns.yml' with nm as provider + tasks: + - name: Include the task 'el_repo_setup.yml' + include_tasks: tasks/el_repo_setup.yml + - name: Set network provider to 'nm' + set_fact: + network_provider: nm + tags: + - always + + +# The test requires or should run with NetworkManager, therefore it cannot run +# on RHEL/CentOS 6 +- name: Import the playbook 'playbooks/tests_ignore_auto_dns.yml' + import_playbook: playbooks/tests_ignore_auto_dns.yml + when: + - ansible_distribution_major_version != '6' diff --git a/tests/unit/test_network_connections.py b/tests/unit/test_network_connections.py index e53e33d..3aac891 100644 --- a/tests/unit/test_network_connections.py +++ b/tests/unit/test_network_connections.py @@ -189,6 +189,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -586,6 +588,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -645,6 +649,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -698,6 +704,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -801,6 +809,8 @@ class TestValidator(Python26CompatTestCase): "address": "192.168.174.5", } ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -870,6 +880,8 @@ class TestValidator(Python26CompatTestCase): "address": "192.168.174.5", } ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -942,6 +954,8 @@ class TestValidator(Python26CompatTestCase): "prefix": 32, }, ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1040,6 +1054,8 @@ class TestValidator(Python26CompatTestCase): "address": "192.168.177.5", }, ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1105,6 +1121,8 @@ class TestValidator(Python26CompatTestCase): "address": "a:b:c::6", }, ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1193,6 +1211,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dns": [], "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": True, "route_append_only": False, "rule_append_only": False, @@ -1272,6 +1292,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dns": [], "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": False, "route_append_only": False, "rule_append_only": False, @@ -1371,6 +1393,8 @@ class TestValidator(Python26CompatTestCase): "address": "192.168.177.5", }, ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1437,6 +1461,8 @@ class TestValidator(Python26CompatTestCase): "address": "a:b:c::6", }, ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1522,6 +1548,8 @@ class TestValidator(Python26CompatTestCase): "address": "192.168.122.3", } ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1582,6 +1610,8 @@ class TestValidator(Python26CompatTestCase): "address": "192.168.244.1", } ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1642,6 +1672,8 @@ class TestValidator(Python26CompatTestCase): "address": "192.168.245.7", } ], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1733,6 +1765,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": "bridge2", "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "auto6": False, "dhcp4": False, @@ -1778,6 +1812,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": "eth1", "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "auto6": True, "dhcp4": True, @@ -1886,6 +1922,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dns": [], "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -1967,6 +2005,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dns": [], "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -2019,6 +2059,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": None, "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -2077,6 +2119,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -2151,6 +2195,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": "6643-controller", "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "auto6": True, "ipv6_disabled": False, @@ -2196,6 +2242,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": "6643", "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "auto6": True, "dhcp4_send_hostname": None, @@ -2257,6 +2305,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": None, "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "auto6": True, "dhcp4": True, @@ -2335,6 +2385,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": None, "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "auto6": True, "dhcp4": True, @@ -2419,6 +2471,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": "ib0", "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "auto6": True, "dhcp4": True, @@ -2463,6 +2517,8 @@ class TestValidator(Python26CompatTestCase): "interface_name": None, "ip": { "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "auto6": True, "dhcp4": True, @@ -2578,6 +2634,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -2680,6 +2738,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": True, "rule_append_only": False, @@ -2831,6 +2891,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": True, "rule_append_only": False, @@ -2983,6 +3045,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -3065,6 +3129,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -3147,6 +3213,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -3227,6 +3295,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -3297,6 +3367,8 @@ class TestValidator(Python26CompatTestCase): "ipv6_disabled": False, "dhcp4": True, "address": [], + "ipv4_ignore_auto_dns": None, + "ipv6_ignore_auto_dns": None, "auto_gateway": None, "route_append_only": False, "rule_append_only": False, @@ -4445,6 +4517,27 @@ class TestValidator(Python26CompatTestCase): # wokeignore:rule=slave self.assertTrue("slave_type" not in connection) + def test_validate_ignore_auto_dns(self): + """ + Test and validate a connection profile with ipv4_ignore_auto_dns and + ipv6_ignore_auto_dns enabled. + """ + validator = network_lsr.argument_validator.ArgValidator_ListConnections() + test_ignore_auto_dns = [ + { + "name": "ignore_auto_dns", + "type": "ethernet", + "ip": { + "auto6": True, + "dhcp4": True, + "ipv4_ignore_auto_dns": True, + "ipv6_ignore_auto_dns": True, + }, + } + ] + + validator.validate(test_ignore_auto_dns) + @my_test_skipIf(nmutil is None, "no support for NM (libnm via pygobject)") class TestNM(unittest.TestCase):