From e8a3ff4881cc49ae4ca658da176455ffa7e4675c Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Thu, 26 Jun 2025 14:57:59 +0200 Subject: [PATCH] feat: support this role in container builds Feature: Support running the timesync role during container builds. Reason: This is particularly useful for building bootc derivative OSes. Result: These flags enable running the bootc container scenarios in CI, which ensures that the role works in buildah build environment. This allows us to officially support this role for image mode builds. Signed-off-by: Rich Megginson --- .../workflows/qemu-kvm-integration-tests.yml | 9 - defaults/main.yml | 7 +- library/network_connections.py | 532 +++++++++++++++++- meta/main.yml | 1 + tasks/main.yml | 17 +- tasks/set_facts.yml | 21 + tests/ansible.cfg | 2 +- tests/collection-requirements.yml | 3 + tests/playbooks/tests_802_1x.yml | 64 +-- tests/playbooks/tests_auto_gateway.yml | 232 +++----- tests/playbooks/tests_bond.yml | 101 +++- tests/playbooks/tests_bond_cloned_mac.yml | 112 ++-- tests/playbooks/tests_bond_options.yml | 71 ++- .../tests_bond_port_match_by_mac.yml | 39 +- tests/playbooks/tests_bridge.yml | 79 ++- tests/playbooks/tests_eth_dns_support.yml | 280 ++++----- tests/playbooks/tests_ethernet.yml | 179 +++--- tests/playbooks/tests_ethtool_coalesce.yml | 236 ++++---- tests/playbooks/tests_ethtool_features.yml | 306 +++++----- tests/playbooks/tests_ethtool_ring.yml | 289 ++++------ tests/playbooks/tests_ignore_auto_dns.yml | 61 +- tests/playbooks/tests_ipv6_disabled.yml | 11 +- tests/playbooks/tests_ipv6_dns_search.yml | 28 +- tests/playbooks/tests_network_state.yml | 3 + tests/playbooks/tests_route_device.yml | 236 ++++---- tests/tasks/activate_profile.yml | 2 +- tests/tasks/assert_IPv4_present.yml | 2 +- tests/tasks/assert_bond_options.yml | 9 +- tests/tasks/assert_command_output.yml | 78 +++ tests/tasks/assert_connection_settings.yml | 127 +++++ .../assert_controller_device_present.yml | 2 +- tests/tasks/assert_device_absent.yml | 4 +- tests/tasks/assert_device_present.yml | 6 +- tests/tasks/assert_dhcp_device_present.yml | 4 +- tests/tasks/assert_nm_connection_status.yml | 21 + tests/tasks/check_network_dns.yml | 5 +- tests/tasks/cleanup_profile+device.yml | 8 +- ...cleanup_vlan_and_parent_profile+device.yml | 4 +- tests/tasks/create_bond_port_match_by_mac.yml | 6 +- tests/tasks/create_bond_profile.yml | 49 -- .../tasks/create_bond_profile_reconfigure.yml | 36 -- tests/tasks/create_bridge_profile.yml | 2 +- .../create_bridge_profile_no_autoconnect.yml | 2 +- tests/tasks/create_dummy_profile.yml | 2 +- tests/tasks/create_mac_address_match.yml | 8 +- tests/tasks/create_team_profile.yml | 2 +- .../create_test_interfaces_with_dhcp.yml | 14 + ...reate_wireless_profile_restart_network.yml | 2 +- tests/tasks/delete_interface.yml | 9 +- tests/tasks/down_profile.yml | 1 + tests/tasks/find+remove_profile.yml | 12 +- tests/tasks/get_interface_stat.yml | 4 +- tests/tasks/get_profile_stat.yml | 9 +- tests/tasks/handle_assert_task.yml | 25 + tests/tasks/handle_cleanup_task.yml | 15 + tests/tasks/handle_setup_task.yml | 23 + tests/tasks/handle_test_task.yml | 21 + tests/tasks/manage_test_interface.yml | 78 ++- tests/tasks/remove+down_profile.yml | 2 +- tests/tasks/remove_package.yml | 1 + tests/tasks/run_test.yml | 68 ++- tests/tasks/setup_802_1x_server.yml | 19 + tests/tasks/show_interfaces.yml | 2 + tests/tasks/test_802.1x_capath.yml | 2 +- 64 files changed, 2293 insertions(+), 1312 deletions(-) create mode 100644 tests/collection-requirements.yml create mode 100644 tests/tasks/assert_command_output.yml create mode 100644 tests/tasks/assert_connection_settings.yml create mode 100644 tests/tasks/assert_nm_connection_status.yml delete mode 100644 tests/tasks/create_bond_profile.yml delete mode 100644 tests/tasks/create_bond_profile_reconfigure.yml create mode 100644 tests/tasks/handle_assert_task.yml create mode 100644 tests/tasks/handle_cleanup_task.yml create mode 100644 tests/tasks/handle_setup_task.yml create mode 100644 tests/tasks/handle_test_task.yml diff --git a/.github/workflows/qemu-kvm-integration-tests.yml b/.github/workflows/qemu-kvm-integration-tests.yml index ad4f26314..12b69b597 100644 --- a/.github/workflows/qemu-kvm-integration-tests.yml +++ b/.github/workflows/qemu-kvm-integration-tests.yml @@ -8,8 +8,6 @@ on: # yamllint disable-line rule:truthy types: - checks_requested push: - branches: - - main workflow_dispatch: permissions: @@ -35,13 +33,6 @@ jobs: # container - { image: "centos-9", env: "container-ansible-core-2.16" } - { image: "centos-9-bootc", env: "container-ansible-core-2.16" } - # broken on non-running dbus - # - { image: "centos-10", env: "container-ansible-core-2.17" } - - { image: "centos-10-bootc", env: "container-ansible-core-2.17" } - - { image: "fedora-41", env: "container-ansible-core-2.17" } - - { image: "fedora-42", env: "container-ansible-core-2.17" } - - { image: "fedora-41-bootc", env: "container-ansible-core-2.17" } - - { image: "fedora-42-bootc", env: "container-ansible-core-2.17" } env: TOX_ARGS: "--skip-tags tests::infiniband,tests::nvme,tests::scsi" diff --git a/defaults/main.yml b/defaults/main.yml index 370c84bc8..717488f71 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -31,8 +31,10 @@ network_provider_os_default: "{{ ansible_distribution_major_version is version('7', '<') else 'nm' }}" # If NetworkManager.service is running, assume that 'nm' is currently in-use, -# otherwise initscripts +# otherwise initscripts. However, in non-booted environments we don't have +# ansible_facts.services, so use the above OS default. __network_provider_current: "{{ + network_provider_os_default if ansible_facts.services is not defined else 'nm' if 'NetworkManager.service' in ansible_facts.services and ansible_facts.services['NetworkManager.service']['state'] == 'running' else 'initscripts' }}" @@ -136,6 +138,9 @@ __network_provider_setup: nm: service_name: "{{ __network_service_name_default_nm }}" packages: "{{ __network_packages_default_nm }}" + nm_offline: + service_name: "{{ __network_service_name_default_nm }}" + packages: "{{ __network_packages_default_nm }}" initscripts: service_name: "{{ __network_service_name_default_initscripts }}" packages: "{{ __network_packages_default_initscripts }}" diff --git a/library/network_connections.py b/library/network_connections.py index 574481e28..e86232910 100644 --- a/library/network_connections.py +++ b/library/network_connections.py @@ -71,6 +71,7 @@ import time import traceback import logging +import stat # pylint: disable=import-error, no-name-in-module from ansible.module_utils.basic import AnsibleModule @@ -1712,7 +1713,7 @@ def __init__(self): self._check_mode = None @property - def ifcfg_header(self): + def managed_file_header(self): return None def log( @@ -1728,7 +1729,7 @@ def log( ): raise NotImplementedError() - def run_command(self, argv, encoding=None): + def run_command(self, argv, encoding=None, check_rc=False): raise NotImplementedError() def _check_mode_changed(self, old_check_mode, new_check_mode, connections): @@ -1780,11 +1781,11 @@ def __init__(self): self.module = module @property - def ifcfg_header(self): + def managed_file_header(self): return self.module.params["__header"] - def run_command(self, argv, encoding=None): - return self.module.run_command(argv, encoding=encoding) + def run_command(self, argv, encoding=None, check_rc=False): + return self.module.run_command(argv, encoding=encoding, check_rc=check_rc) def _run_results_push(self, n_connections): c = [] @@ -1935,8 +1936,8 @@ def __init__( self._is_changed_modified_system = False self._debug_flags = debug_flags - def run_command(self, argv, encoding=None): - return self.run_env.run_command(argv, encoding=encoding) + def run_command(self, argv, encoding=None, check_rc=False): + return self.run_env.run_command(argv, encoding=encoding, check_rc=check_rc) @property def is_changed_modified_system(self): @@ -2030,6 +2031,8 @@ def log(self, idx, severity, msg, warn_traceback=False, force_fail=False): def create(provider, **kwargs): if provider == "nm": return Cmd_nm(**kwargs) + if provider == "nm_offline": + return Cmd_nm_offline(**kwargs) elif provider == "initscripts": return Cmd_initscripts(**kwargs) raise MyError("unsupported provider %s" % (provider)) @@ -2639,6 +2642,519 @@ def run_action_down(self, idx): ############################################################################### +class Cmd_nm_offline(Cmd): + def __init__(self, **kwargs): + Cmd.__init__(self, **kwargs) + self.validate_one_type = ArgValidator_ListConnections.VALIDATE_ONE_MODE_NM + self._checkpoint = None + + def profile_path(self, name): + return os.path.join( + "/etc/NetworkManager/system-connections", name + ".nmconnection" + ) + + def run_prepare(self): + # we can't check any hardware or runtime status, we can just trust the input + pass + + # mirror Cmd_nm.connection_create() + def connection_create(self, connections, idx): + connection = connections[idx] + # global/type independent arguments + if "nm.uuid" not in connection: + connection["nm.uuid"] = Util.create_uuid() + if "nm.exists" not in connection: + connection["nm.exists"] = True + argv = [ + "nmcli", + "--offline", + "connection", + "add", + "ifname", + connection["interface_name"], + "con-name", + connection["name"], + "type", + connection["type"], + "autoconnect", + "yes" if connection["autoconnect"] else "no", + "connection.autoconnect-retries", + str(connection["autoconnect_retries"]), + "connection.uuid", + connection["nm.uuid"], + ] + + if connection["cloned_mac"] != "default": + argv.extend(["cloned-mac", connection["cloned_mac"]]) + + # composite devices + if connection["controller"] is not None: + if connection["port_type"] is None: + self.log_error( + idx, + "connection.port-type must be specified when connection.controller is set", + ) + # must use uuid instead of name for controller here + controller_uuid = ArgUtil.connection_find_controller_uuid( + connection["controller"], connections + ) + + argv.extend( + [ + "connection.controller", + controller_uuid, + "connection.port-type", + connection["port_type"], + ] + ) + + # skip IP and other config for composites + return argv + + c_ip = connection["ip"] + ip4_addrs = [] + ip6_addrs = [] + for address in c_ip["address"]: + addr = "{0}/{1}".format(address["address"], address["prefix"]) + if address["family"] == socket.AF_INET: + ip4_addrs.append(addr) + elif address["family"] == socket.AF_INET6: + ip6_addrs.append(addr) + else: + self.log_error(idx, "unknown address family %s" % (address["family"])) + + argv.extend( + [ + "ipv4.method", + "auto" if c_ip["dhcp4"] else "manual" if ip4_addrs else "disabled", + "ipv4.addresses", + ", ".join(ip4_addrs), + "ipv6.method", + ( + "disabled" + if c_ip["ipv6_disabled"] + else ( + "auto" + if c_ip["auto6"] + else ( + "manual" + if ip6_addrs + else + # online backend uses legacy "ignore", DTRT for this new backend + "ignore" + ) + ) + ), # "link-local", + "ipv6.addresses", + ", ".join(ip6_addrs), + ] + ) + + # gateway + if c_ip["gateway4"]: + argv.extend(["ipv4.gateway", c_ip["gateway4"]]) + if c_ip["gateway6"]: + argv.extend(["ipv6.gateway", c_ip["gateway6"]]) + + # dns* + dns4_addrs = [] + dns6_addrs = [] + for dns in c_ip["dns"]: + if dns["family"] == socket.AF_INET: + dns4_addrs.append(dns["address"]) + elif dns["family"] == socket.AF_INET6: + dns6_addrs.append(dns["address"]) + else: + self.log_error(idx, "unknown DNS address family %s" % (dns["family"])) + if dns4_addrs: + argv.extend(["ipv4.dns", ",".join(dns4_addrs)]) + if dns6_addrs: + argv.extend(["ipv6.dns", ",".join(dns6_addrs)]) + + if c_ip["dns_search"]: + # NM only allows to configure ipvN.dns-search when IPvN is enabled + if c_ip["dhcp4"] or ip4_addrs: + argv.extend(["ipv4.dns-search", " ".join(c_ip["dns_search"])]) + if c_ip["auto6"] or ip6_addrs: + argv.extend(["ipv6.dns-search", " ".join(c_ip["dns_search"])]) + + if c_ip["dns_options"]: + argv.extend( + [ + "ipv4.dns-options", + ",".join(c_ip["dns_options"]), + "ipv6.dns-options", + ",".join(c_ip["dns_options"]), + ] + ) + + if c_ip["dns_priority"] != 0: + argv.extend( + [ + "ipv4.dns-priority", + str(c_ip["dns_priority"]), + "ipv6.dns-priority", + str(c_ip["dns_priority"]), + ] + ) + + if c_ip["ipv4_ignore_auto_dns"] is not None: + argv.extend( + [ + "ipv4.ignore-auto-dns", + "yes" if c_ip["ipv4_ignore_auto_dns"] else "no", + ] + ) + if c_ip["ipv6_ignore_auto_dns"] is not None: + argv.extend( + [ + "ipv6.ignore-auto-dns", + "yes" if c_ip["ipv6_ignore_auto_dns"] else "no", + ] + ) + + # route + if c_ip["route_metric4"] is not None: + argv.extend(["ipv4.route-metric", str(c_ip["route_metric4"])]) + if c_ip["route_metric6"] is not None: + argv.extend(["ipv6.route-metric", str(c_ip["route_metric6"])]) + + # routes + if c_ip.get("route"): + ipv4_routes = [] + ipv6_routes = [] + for route in c_ip["route"]: + route_str = route["network"] + if route.get("prefix"): + route_str += "/" + str(route["prefix"]) + if route.get("gateway"): + route_str += " " + route["gateway"] + if route.get("metric"): + route_str += " " + str(route["metric"]) + if route.get("type"): + route_str += " type=" + route["type"] + if route.get("table"): + route_str += " table=" + str(route["table"]) + if route.get("src"): + route_str += " src=" + route["src"] + + if route["family"] == socket.AF_INET: + ipv4_routes.append(route_str) + elif route["family"] == socket.AF_INET6: + ipv6_routes.append(route_str) + + if ipv4_routes: + argv.extend(["ipv4.routes", ",".join(ipv4_routes)]) + if ipv6_routes: + argv.extend(["ipv6.routes", ",".join(ipv6_routes)]) + + # routing rules + if c_ip.get("routing_rule"): + ipv4_rules = [] + ipv6_rules = [] + for rule in c_ip["routing_rule"]: + rule_parts = [] + if rule.get("priority"): + rule_parts.append("priority " + rule["priority"]) + if rule.get("from"): + rule_parts.append("from " + rule["from"]) + if rule.get("to"): + rule_parts.append("to " + rule["to"]) + if rule.get("table"): + rule_parts.append("table " + rule["table"]) + + if rule_parts: + rule_str = " ".join(rule_parts) + if rule["family"] == socket.AF_INET: + ipv4_rules.append(rule_str) + elif rule["family"] == socket.AF_INET6: + ipv6_rules.append(rule_str) + + if ipv4_rules: + argv.extend(["ipv4.routing-rules", " ".join(ipv4_rules)]) + if ipv6_rules: + argv.extend(["ipv6.routing-rules", " ".join(ipv6_rules)]) + + # DHCP options + if c_ip.get("dhcp4_send_hostname") is not None: + argv.extend( + [ + "ipv4.dhcp-send-hostname", + "yes" if c_ip["dhcp4_send_hostname"] else "no", + ] + ) + if c_ip.get("dhcp6_send_hostname") is not None: + argv.extend( + [ + "ipv6.dhcp-send-hostname", + "yes" if c_ip["dhcp6_send_hostname"] else "no", + ] + ) + + # zone + if connection.get("zone"): + argv.extend(["connection.zone", connection["zone"]]) + + # mac address + if connection.get("mac") and connection["mac"] != "default": + if connection["type"] == "wireless": + argv.extend(["802-11-wireless.mac-address", connection["mac"]]) + else: + argv.extend(["802-3-ethernet.mac-address", connection["mac"]]) + + # mtu + if connection.get("mtu"): + if connection["type"] == "wireless": + argv.extend(["802-11-wireless.mtu", str(connection["mtu"])]) + else: + argv.extend(["802-3-ethernet.mtu", str(connection["mtu"])]) + + # ethernet settings + if connection.get("ethernet"): + eth = connection["ethernet"] + if eth.get("autoneg") is not None: + argv.extend( + ["802-3-ethernet.auto-negotiate", "yes" if eth["autoneg"] else "no"] + ) + if eth.get("speed"): + argv.extend(["802-3-ethernet.speed", str(eth["speed"])]) + if eth.get("duplex"): + argv.extend(["802-3-ethernet.duplex", eth["duplex"]]) + + # type-specific configurations + # bond options[mode, miimon, downdelay, updelay, arp_interval, arp_ip_target, arp_validate, balance-slb, primary, + # primary_reselect, fail_over_mac, use_carrier, ad_select, xmit_hash_policy, resend_igmp, lacp_rate, active_slave, ad_actor_sys_prio, + # ad_actor_system, ad_user_port_key, all_slaves_active, arp_all_targets, min_links, num_grat_arp, num_unsol_na, packets_per_slave, + # tlb_dynamic_lb, lp_interval, peer_notif_delay, arp_missed_max, lacp_active, ns_ip6_target]., + if connection["type"] == "bond": + opts = [] + for key, value in connection["bond"].items(): + if value is not None: + if key in ["all_ports_active", "use_carrier", "tlb_dynamic_lb"]: + value = int(value) + if key in ["all_ports_active", "packets_per_port"]: + # wokeignore:rule=slave + key = key.replace("port", "slave") + opts.append("{0}={1}".format(key, str(value))) + if opts: + argv.extend(["bond.options", ",".join(opts)]) + + elif connection["type"] == "bridge": + if connection.get("bridge"): + bridge = connection["bridge"] + if bridge.get("stp") is not None: + argv.extend(["bridge.stp", "yes" if bridge["stp"] else "no"]) + if bridge.get("priority") is not None: + argv.extend(["bridge.priority", str(bridge["priority"])]) + if bridge.get("forward_delay") is not None: + argv.extend(["bridge.forward-delay", str(bridge["forward_delay"])]) + if bridge.get("hello_time") is not None: + argv.extend(["bridge.hello-time", str(bridge["hello_time"])]) + if bridge.get("max_age") is not None: + argv.extend(["bridge.max-age", str(bridge["max_age"])]) + if bridge.get("ageing_time") is not None: + argv.extend(["bridge.ageing-time", str(bridge["ageing_time"])]) + + elif connection["type"] == "vlan": + if connection.get("vlan") and connection["vlan"].get("id") is not None: + argv.extend(["vlan.id", str(connection["vlan"]["id"])]) + if connection.get("parent"): + argv.extend(["vlan.parent", connection["parent"]]) + + elif connection["type"] == "macvlan": + if connection.get("macvlan"): + macvlan = connection["macvlan"] + if macvlan.get("mode"): + argv.extend(["macvlan.mode", macvlan["mode"]]) + if macvlan.get("promiscuous") is not None: + argv.extend( + [ + "macvlan.promiscuous", + "yes" if macvlan["promiscuous"] else "no", + ] + ) + if macvlan.get("tap") is not None: + argv.extend(["macvlan.tap", "yes" if macvlan["tap"] else "no"]) + if connection.get("parent"): + argv.extend(["macvlan.parent", connection["parent"]]) + + elif connection["type"] == "infiniband": + if connection.get("infiniband"): + ib = connection["infiniband"] + if ib.get("transport_mode"): + argv.extend(["infiniband.transport-mode", ib["transport_mode"]]) + if ib.get("p_key") is not None: + argv.extend(["infiniband.p-key", str(ib["p_key"])]) + if connection.get("parent"): + argv.extend(["infiniband.parent", connection["parent"]]) + + elif connection["type"] == "wireless": + if connection.get("wireless"): + wifi = connection["wireless"] + if wifi.get("ssid"): + argv.extend(["802-11-wireless.ssid", wifi["ssid"]]) + if wifi.get("key_mgmt"): + argv.extend(["802-11-wireless-security.key-mgmt", wifi["key_mgmt"]]) + if wifi.get("password") and wifi["key_mgmt"] in ["wpa-psk", "sae"]: + argv.extend(["802-11-wireless-security.psk", wifi["password"]]) + + elif connection["type"] == "team": + if connection.get("team") and connection["team"].get("config"): + # Team config is typically JSON, pass as-is + argv.extend(["team.config", connection["team"]["config"]]) + + # ethtool settings + if connection.get("ethtool"): + ethtool = connection["ethtool"] + + # ethtool features + if ethtool.get("features"): + for feature, value in ethtool["features"].items(): + if value is not None: + feature_name = feature.replace("_", "-") + if value: + value = "on" + else: + value = "off" + argv.extend(["ethtool.feature-" + feature_name, value]) + + # ethtool coalesce settings + if ethtool.get("coalesce"): + for param, value in ethtool["coalesce"].items(): + if value is not None: + param_name = param.replace("_", "-") + argv.extend(["ethtool.coalesce-" + param_name, str(value)]) + + # ethtool ring settings + if ethtool.get("ring"): + for param, value in ethtool["ring"].items(): + if value is not None: + param_name = param.replace("_", "-") + argv.extend(["ethtool.ring-" + param_name, str(value)]) + + # ieee802_1x settings + if connection.get("ieee802_1x"): + ieee = connection["ieee802_1x"] + if ieee.get("eap"): + argv.extend( + [ + "802-1x.eap", + ( + ",".join(ieee["eap"]) + if isinstance(ieee["eap"], list) + else ieee["eap"] + ), + ] + ) + if ieee.get("identity"): + argv.extend(["802-1x.identity", ieee["identity"]]) + if ieee.get("password"): + argv.extend(["802-1x.password", ieee["password"]]) + if ieee.get("ca_cert"): + argv.extend(["802-1x.ca-cert", ieee["ca_cert"]]) + if ieee.get("client_cert"): + argv.extend(["802-1x.client-cert", ieee["client_cert"]]) + if ieee.get("private_key"): + argv.extend(["802-1x.private-key", ieee["private_key"]]) + if ieee.get("private_key_password"): + argv.extend( + ["802-1x.private-key-password", ieee["private_key_password"]] + ) + + # match settings + if connection.get("match"): + match = connection["match"] + if match.get("path"): + match_paths = ( + match["path"] + if isinstance(match["path"], list) + else [match["path"]] + ) + argv.extend(["match.path", ",".join(match_paths)]) + if match.get("driver"): + match_drivers = ( + match["driver"] + if isinstance(match["driver"], list) + else [match["driver"]] + ) + argv.extend(["match.driver", ",".join(match_drivers)]) + if match.get("interface_name"): + match_names = ( + match["interface_name"] + if isinstance(match["interface_name"], list) + else [match["interface_name"]] + ) + argv.extend(["match.interface-name", ",".join(match_names)]) + + return argv + + def run_action_present(self, idx): + connection = self.connections[idx] + + if not connection.get("type"): + # this is mostly test teardown, nobody uses that in container builds + if connection["state"] == "down": + self.log_info(idx, "nm_offline ignoring 'state: down'") + return + + self.log_error(idx, "Connection 'type' not specified") + return + + # DEBUG, drop for final commit + self.log_info( + idx, "XXX nm_offline provider action_present connection: %r" % connection + ) + + # create the profile + (rc, out, err) = self.run_command( + self.connection_create(self.connections, idx), check_rc=True + ) + # Should Not Happen™, but make sure + if not out.strip(): + self.log_error( + idx, "nmcli --offline returned no output (rc %d); err: %s" % (rc, err) + ) + return + + path = self.profile_path(connection["name"]) + if self.check_mode == CheckMode.REAL_RUN: + with open(path, "w") as f: + f.write(self.run_env.managed_file_header) + f.write(out.decode("UTF-8")) + os.chmod(path, stat.S_IRUSR | stat.S_IWUSR) + os.chown(path, 0, 0) + + # nmcli always generates a new UUID, so comparing with existing file is moot + # always treat as changed, good enough for container builds + self.connections_data_set_changed(idx) + + def run_action_absent(self, idx): + connection = self.connections[idx] + # DEBUG, drop for final commit + self.log_info( + idx, "XXX nm_offline provider action_absent connection: %r" % connection + ) + + try: + os.unlink(self.profile_path(connection["name"])) + self.connections_data_set_changed(idx) + except FileNotFoundError: + self.log_debug( + idx, "nm_offline: profile '%s' already absent" % connection["name"] + ) + + def run_action_up(self, idx): + # no runtime ops in offline provider + pass + + def run_action_down(self, idx): + # no runtime ops in offline provider + pass + + +############################################################################### + + class Cmd_initscripts(Cmd): def __init__(self, **kwargs): Cmd.__init__(self, **kwargs) @@ -2763,7 +3279,7 @@ def run_action_present(self, idx): ) new_content = IfcfgUtil.content_from_dict( - ifcfg_all, header=self.run_env.ifcfg_header + ifcfg_all, header=self.run_env.managed_file_header ) if old_content == new_content: diff --git a/meta/main.yml b/meta/main.yml index 5b75bd14d..add6d55c0 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -18,6 +18,7 @@ galaxy_info: - "9" galaxy_tags: - centos + - containerbuild - el6 - el7 - el8 diff --git a/tasks/main.yml b/tasks/main.yml index 070eeef61..bc3462a6a 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -4,6 +4,13 @@ - name: Ensure ansible_facts used by role include_tasks: tasks/set_facts.yml +- name: Switch to offline NM backend if host is not booted + set_fact: + network_provider: "nm_offline" + when: + - network_provider == "nm" + - not __network_is_booted + - name: Print network provider debug: msg: "Using network provider: {{ network_provider }}" @@ -14,7 +21,7 @@ msg: Only the `nm` provider supports using the `network_state` variable when: - network_state != {} - - network_provider == "initscripts" + - network_provider != "nm" - name: Abort applying the network state configuration if the system version of the managed host is below 8 @@ -151,10 +158,10 @@ - name: Enable and start NetworkManager service: name: "{{ network_service_name }}" - state: started + state: "{{ 'started' if __network_is_booted else omit }}" enabled: true when: - - network_provider == "nm" or network_state != {} + - network_provider in ["nm", "nm_offline"] or network_state != {} no_log: true # If any 802.1x connections are used, the wpa_supplicant @@ -162,10 +169,10 @@ - name: Enable and start wpa_supplicant service: name: wpa_supplicant - state: started + state: "{{ 'started' if __network_is_booted else omit }}" enabled: true when: - - network_provider == "nm" + - network_provider in ["nm", "nm_offline"] - __network_wpa_supplicant_required - name: Enable network service diff --git a/tasks/set_facts.yml b/tasks/set_facts.yml index 424a1a5ad..a3376575f 100644 --- a/tasks/set_facts.yml +++ b/tasks/set_facts.yml @@ -19,6 +19,27 @@ set_fact: __network_is_ostree: "{{ __ostree_booted_stat.stat.exists }}" +- name: Determine if system is booted with systemd + when: __network_is_booted is not defined + block: + - name: Run systemctl + # noqa command-instead-of-module + command: systemctl is-system-running + register: __is_system_running + changed_when: false + check_mode: false + failed_when: false + + - name: Require installed systemd + fail: + msg: "Error: This role requires systemd to be installed." + when: '"No such file or directory" in __is_system_running.msg | d("")' + + - name: Set flag to indicate that systemd runtime operations are available + set_fact: + # see https://www.man7.org/linux/man-pages/man1/systemctl.1.html#:~:text=is-system-running%20output + __network_is_booted: "{{ __is_system_running.stdout != 'offline' }}" + - name: Check which services are running service_facts: no_log: true diff --git a/tests/ansible.cfg b/tests/ansible.cfg index 8b8ffd890..e9225efdc 100644 --- a/tests/ansible.cfg +++ b/tests/ansible.cfg @@ -1,2 +1,2 @@ [defaults] -task_timeout=480 +task_timeout=600 diff --git a/tests/collection-requirements.yml b/tests/collection-requirements.yml new file mode 100644 index 000000000..afc836d72 --- /dev/null +++ b/tests/collection-requirements.yml @@ -0,0 +1,3 @@ +--- +collections: + - name: community.general diff --git a/tests/playbooks/tests_802_1x.yml b/tests/playbooks/tests_802_1x.yml index 8f922aa9b..f0a26514b 100644 --- a/tests/playbooks/tests_802_1x.yml +++ b/tests/playbooks/tests_802_1x.yml @@ -5,21 +5,14 @@ vars: interface: 802-1x-test tasks: - - name: "INIT: 802.1x tests" - debug: - msg: "##################################################" - - name: Include the task 'setup_802.1x.yml' - include_tasks: tasks/setup_802.1x.yml - - name: Test configuring 802.1x authentication - block: - - name: "TEST: 802.1x profile with private key password and ca cert" - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network - vars: - network_connections: + - name: Test setting 802.1x authentication + include_tasks: tasks/run_test.yml + vars: + lsr_description: Test 802.1x authentication + lsr_setup: + - tasks/setup_802.1x.yml + lsr_test: + - network_connections: - name: "{{ interface }}" interface_name: veth2 state: up @@ -38,28 +31,25 @@ - none client_cert: /etc/pki/tls/client.pem ca_cert: /etc/pki/tls/cacert.pem - - name: Ensure ping command is present - package: - name: iputils - state: present - use: "{{ (__network_is_ostree | d(false)) | - ternary('ansible.posix.rhel_rpm_ostree', omit) }}" - - name: "TEST: I can ping the EAP server" - command: ping -c1 203.0.113.1 - changed_when: false - - name: Import network role - import_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - persistent_state: absent - state: down - - name: >- - TEST: 802.1x profile with unencrypted private key, - domain suffix match, and system ca certs - debug: - msg: "##################################################" + lsr_assert: + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted }}" + lsr_command: ping -c1 203.0.113.1 + lsr_packages: [iputils] + lsr_cleanup: + - what: tasks/down_profile+delete_interface.yml + condition: "{{ __network_is_booted and not __bootc_validation | default(false) }}" + profile: "{{ interface }}" + lsr_persistent_state: absent + - tasks/check_network_dns.yml + + - name: Stop test if building image or validation is enabled + meta: end_host + when: ansible_connection | d("") == "buildah" or __bootc_validation | default(false) + + - name: >- + Test 802.1x profile with unencrypted private key, domain suffix match, and system ca certs + block: - name: Copy cacert to system truststore copy: src: cacert.pem diff --git a/tests/playbooks/tests_auto_gateway.yml b/tests/playbooks/tests_auto_gateway.yml index 28f595213..c0da0548a 100644 --- a/tests/playbooks/tests_auto_gateway.yml +++ b/tests/playbooks/tests_auto_gateway.yml @@ -6,145 +6,97 @@ type: veth interface: veth0 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 + - name: Test setting auto_gateway to true + include_tasks: tasks/run_test.yml vars: - state: present - - name: Include the task 'assert_device_present.yml' - include_tasks: tasks/assert_device_present.yml - - name: >- - TEST: I can configure an interface with auto_gateway enabled - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network + lsr_description: Test auto_gateway setting to true + lsr_setup: + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + auto_gateway: true + dhcp4: false + auto6: false + address: + - "2001:db8::2/64" + - "203.0.113.2/24" + gateway6: "2001:db8::1" + gateway4: "203.0.113.1" + # change the default route metric to higher value so that it will + # not take precedence over other routes or not ignore other + # routes + route_metric4: 65535 + lsr_assert: + - tasks/assert_device_present.yml + - what: tasks/assert_profile_present.yml + profile: "{{ interface }}" + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted }}" + lsr_command: ip route + lsr_stdout: default via 203.0.113.1 dev {{ interface }} + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted and network_provider == 'nm' }}" + lsr_command: ip -6 route + lsr_stdout: default via 2001:db8::1 dev {{ interface }} + lsr_cleanup: + - what: tasks/down_profile+delete_interface.yml + condition: "{{ __network_is_booted }}" + profile: "{{ interface }}" + lsr_persistent_state: absent + - what: tasks/manage_test_interface.yml + state: absent + - tasks/check_network_dns.yml + + - name: Stop test if building image or validation is enabled + meta: end_host + when: ansible_connection | d("") == "buildah" or __bootc_validation | default(false) + + - name: Test setting auto_gateway to false + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - type: ethernet - state: up - ip: - auto_gateway: true - dhcp4: false - auto6: false - address: - - "2001:db8::2/64" - - "203.0.113.2/24" - gateway6: "2001:db8::1" - gateway4: "203.0.113.1" - # change the default route metric to higher value so that it will - # not take precedence over other routes or not ignore other - # routes - route_metric4: 65535 - - name: Include the task 'assert_device_present.yml' again - include_tasks: tasks/assert_device_present.yml - - name: Include the task 'assert_profile_present.yml' - include_tasks: tasks/assert_profile_present.yml - vars: - profile: "{{ interface }}" - - name: "Show ipv4 routes" - command: "ip route" - register: ipv4_routes - changed_when: false - - name: "Assert default ipv4 route is present" - assert: - that: __test_str in ipv4_routes.stdout - vars: - __test_str: default via 203.0.113.1 dev {{ interface }} - - name: "Get ipv6 routes" - command: "ip -6 route" - register: ipv6_route - changed_when: false - - name: "Assert default ipv6 route is present" - assert: - that: __test_str in ipv6_route.stdout - vars: - __test_str: default via 2001:db8::1 dev {{ interface }} - when: network_provider == "nm" - - name: "TEARDOWN: remove profiles." - debug: - msg: "##################################################" - - name: Import network role to remove interface - import_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - persistent_state: absent - state: down - ignore_errors: true # noqa ignore-errors - - name: Include the task 'manage_test_interface.yml' to remove interface - include_tasks: tasks/manage_test_interface.yml - vars: - state: absent - - name: >- - TEST: I can configure an interface with auto_gateway disabled - debug: - msg: "##################################################" - - name: Include the task 'manage_test_interface.yml' to disable auto_gateway - include_tasks: tasks/manage_test_interface.yml - vars: - state: present - - name: Import network role to disable auto_gateway - import_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - type: ethernet - state: up - ip: - auto_gateway: false - dhcp4: false - auto6: false - address: - - "2001:db8::2/64" - - "203.0.113.2/24" - gateway6: "2001:db8::1" - gateway4: "203.0.113.1" - - name: Include the task 'assert_device_present.yml' - 3 - include_tasks: tasks/assert_device_present.yml - - name: Include the task 'assert_profile_present.yml' again - include_tasks: tasks/assert_profile_present.yml - vars: - profile: "{{ interface }}" - - name: "Show ipv4 routes again" - command: "ip route" - register: ipv4_routes - changed_when: false - - name: "Assert default ipv4 route is absent" - assert: - that: __test_str not in ipv4_routes.stdout - vars: - __test_str: default via 203.0.113.1 dev {{ interface }} - - name: "Get ipv6 routes again" - command: "ip -6 route" - register: ipv6_route - changed_when: false - - name: "Assert default ipv6 route is absent" - assert: - that: __test_str not in ipv6_route.stdout - vars: - __test_str: default via 2001:db8::1 dev {{ interface }} - when: network_provider == "nm" - - name: "TEARDOWN: remove profiles. again" - debug: - msg: "##################################################" - - name: Import network role to remove interface again - import_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - persistent_state: absent - state: down - ignore_errors: true # noqa ignore-errors - - name: Include the task 'manage_test_interface.yml' to remove interface again - include_tasks: tasks/manage_test_interface.yml - vars: - state: absent - - name: Verify network state restored to default - include_tasks: tasks/check_network_dns.yml + lsr_description: Test auto_gateway setting to false + lsr_setup: + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + auto_gateway: false + dhcp4: false + auto6: false + address: + - "2001:db8::2/64" + - "203.0.113.2/24" + gateway6: "2001:db8::1" + gateway4: "203.0.113.1" + lsr_assert: + - tasks/assert_device_present.yml + lsr_assert_when: + - what: tasks/assert_profile_present.yml + profile: "{{ interface }}" + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted }}" + lsr_command: ip route + lsr_not_stdout: default via 203.0.113.1 dev {{ interface }} + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted and network_provider == 'nm' }}" + lsr_command: ip -6 route + lsr_not_stdout: default via 2001:db8::1 dev {{ interface }} + lsr_cleanup: + - what: tasks/down_profile+delete_interface.yml + condition: "{{ __network_is_booted }}" + profile: "{{ interface }}" + lsr_persistent_state: absent + - what: tasks/manage_test_interface.yml + state: absent + - tasks/check_network_dns.yml diff --git a/tests/playbooks/tests_bond.yml b/tests/playbooks/tests_bond.yml index 348207783..17307d601 100644 --- a/tests/playbooks/tests_bond.yml +++ b/tests/playbooks/tests_bond.yml @@ -72,26 +72,85 @@ - "{{ controller_profile }}" - "{{ port1_profile }}" - "{{ port2_profile }}" - - name: "** TEST check polling interval" - command: grep 'Polling Interval' - /proc/net/bonding/{{ controller_device }} - register: result - until: "'110' in result.stdout" - changed_when: false - - name: "** TEST check IPv4" - command: ip -4 a s {{ controller_device }} - register: result - until: "'192.0.2' in result.stdout" - retries: 20 - delay: 2 - changed_when: false - - name: "** TEST check IPv6" - command: ip -6 a s {{ controller_device }} - register: result - until: "'2001' in result.stdout" - retries: 20 - delay: 2 - changed_when: false + + - name: Runtime checks for booted systems + when: __network_is_booted | bool + block: + - name: "** TEST check polling interval" + command: grep 'Polling Interval' + /proc/net/bonding/{{ controller_device }} + register: result + until: "'110' in result.stdout" + changed_when: false + - name: "** TEST check IPv4" + command: ip -4 a s {{ controller_device }} + register: result + until: "'192.0.2' in result.stdout" + retries: 20 + delay: 2 + changed_when: false + - name: "** TEST check IPv6" + command: ip -6 a s {{ controller_device }} + register: result + until: "'2001' in result.stdout" + retries: 20 + delay: 2 + changed_when: false + + # These are primarily interesting for non-booted systems; but let's + # check on booted systems too, to ensure the assertions are valid + - name: NM Config file checks + when: + - network_provider != "initscripts" + - ansible_distribution_major_version | int >= 9 + block: + - name: Read controller config + slurp: + src: "/etc/NetworkManager/system-connections/{{ controller_profile }}.nmconnection" + register: controller_config_b64 + + - name: Check controller config + assert: + that: + # [connection] + - "'id=' + controller_profile in controller_config" + - "'type=bond' in controller_config" + - "'interface-name=' + controller_device in controller_config" + # [bond] + - "'mode=active-backup' in controller_config" + - "'miimon=110' in controller_config" + # [ipv4] + - "'method=auto' in controller_config" + - "'route-metric=65535' in controller_config" + fail_msg: "{{ controller_profile }} is bad: {{ controller_config }}" + vars: + controller_config: "{{ controller_config_b64.content | b64decode }}" + + - name: Read port configs + loop: + - { name: "{{ port1_profile }}", iface_name: "{{ dhcp_interface1 }}" } + - { name: "{{ port2_profile }}", iface_name: "{{ dhcp_interface2 }}" } + slurp: + src: "/etc/NetworkManager/system-connections/{{ item.name }}.nmconnection" + register: port1_config_b64 + + - name: Check port configs + loop: "{{ port1_config_b64.results }}" + vars: + port_config: "{{ item.content | b64decode }}" + assert: + that: + # [connection] + - "'id=' + item.item.name in port_config" + - "'type=ethernet' in port_config" + # that points to UUID of controller, a bit cumbersome to match; but let's assume nmcli DTRT + - "'controller=' in port_config" + - "'interface-name=' + item.item.iface_name in port_config" + - "'port-type=bond' in port_config" + - "'[ipv4]' not in port_config" + - "'[ipv6]' not in port_config" + fail_msg: "{{ item.item.name }} is bad: {{ port_config }}" + always: - name: Clean up the test devices and the connection profiles tags: @@ -116,6 +175,7 @@ command: ip link del {{ controller_device }} failed_when: false changed_when: false + when: __network_is_booted | bool - name: Import the task 'remove_test_interfaces_with_dhcp.yml' import_tasks: tasks/remove_test_interfaces_with_dhcp.yml - name: "Restore the /etc/resolv.conf for initscript" @@ -125,3 +185,4 @@ changed_when: false - name: Verify network state restored to default include_tasks: tasks/check_network_dns.yml + when: __network_is_booted | bool diff --git a/tests/playbooks/tests_bond_cloned_mac.yml b/tests/playbooks/tests_bond_cloned_mac.yml index 511ff0e8d..45d3190f3 100644 --- a/tests/playbooks/tests_bond_cloned_mac.yml +++ b/tests/playbooks/tests_bond_cloned_mac.yml @@ -54,54 +54,80 @@ interface_name: "{{ dhcp_interface2 }}" controller: "{{ controller_profile }}" - - name: Verify nmcli cloned-mac-address entry - command: >- - nmcli -f 802-3-ethernet.cloned-mac-address con show {{ item.name }} - register: cloned_mac_address - ignore_errors: true - changed_when: false - loop: - - {name: "{{ controller_profile }}", mac: "12:23:34:45:56:60"} - - {name: "{{ port1_profile }}", mac: "12:23:34:45:56:61"} - - {name: "{{ port2_profile }}", mac: "--"} + - name: Verify nm provider when: network_provider == 'nm' + block: + - name: Verify nmcli cloned-mac-address entry + command: >- + nmcli -f 802-3-ethernet.cloned-mac-address con show {{ item.name }} + register: cloned_mac_address + ignore_errors: true + changed_when: false + loop: + - {name: "{{ controller_profile }}", mac: "12:23:34:45:56:60"} + - {name: "{{ port1_profile }}", mac: "12:23:34:45:56:61"} + - {name: "{{ port2_profile }}", mac: "--"} - - name: > - Assert that cloned-mac-address addresses are configured correctly - assert: - that: - - item.stdout.endswith(item.item.mac) - msg: cloned-mac-address is configured incorrectly - loop: "{{ cloned_mac_address.results }}" - when: network_provider == 'nm' + - name: > + Assert that cloned-mac-address addresses are configured correctly + assert: + that: + - item.stdout.endswith(item.item.mac) + msg: cloned-mac-address is configured incorrectly + loop: "{{ cloned_mac_address.results }}" - - name: Verify the MAC address in {{ controller_profile }} - command: >- - grep 'MACADDR' - /etc/sysconfig/network-scripts/ifcfg-{{ controller_profile }} - register: mac_address_controller - ignore_errors: true - changed_when: false - when: network_provider == 'initscripts' + # mostly interesting for offline provider, but let's cross-check with + # online NM to ensure the conditions are valid - except that on some + # older versions of NM, the connection files are not created, so we + # skip this test on those versions. + - name: Verify NM config files + when: + - network_provider.startswith('nm') + - ansible_facts['distribution_major_version'] | int >= 9 + block: + - name: Read profile config files + slurp: + src: "/etc/NetworkManager/system-connections/{{ item }}.nmconnection" + register: config_b64 + loop: + - "{{ controller_profile }}" + - "{{ port1_profile }}" + - "{{ port2_profile }}" - - name: Verify the MAC address in {{ port1_profile }} - command: >- - grep 'MACADDR' - /etc/sysconfig/network-scripts/ifcfg-{{ port1_profile }} - register: mac_address_port1 - ignore_errors: true - changed_when: false - when: network_provider == 'initscripts' + - name: Check profile config files + assert: + that: + - "'cloned-mac-address=12:23:34:45:56:60' in config_b64.results[0].content | b64decode" + - "'cloned-mac-address=12:23:34:45:56:61' in config_b64.results[1].content | b64decode" + - "'cloned-mac-adress=' not in config_b64.results[2].content | b64decode" - - name: Assert that MAC addresses are configured correctly for bonding - interface - assert: - that: - - mac_address_controller.stdout is search("12:23:34:45:56:60") - - mac_address_port1.stdout is search("12:23:34:45:56:61") - msg: the MAC addresses are configured incorrectly for bonding - interface + - name: Verify initscripts provider when: network_provider == 'initscripts' + block: + - name: Verify the MAC address in {{ controller_profile }} + command: >- + grep 'MACADDR' + /etc/sysconfig/network-scripts/ifcfg-{{ controller_profile }} + register: mac_address_controller + ignore_errors: true + changed_when: false + + - name: Verify the MAC address in {{ port1_profile }} + command: >- + grep 'MACADDR' + /etc/sysconfig/network-scripts/ifcfg-{{ port1_profile }} + register: mac_address_port1 + ignore_errors: true + changed_when: false + + - name: Assert that MAC addresses are configured correctly for bonding + interface + assert: + that: + - mac_address_controller.stdout is search("12:23:34:45:56:60") + - mac_address_port1.stdout is search("12:23:34:45:56:61") + msg: the MAC addresses are configured incorrectly for bonding + interface always: - name: Clean up the test devices and the connection profiles @@ -127,8 +153,10 @@ command: ip link del {{ controller_device }} failed_when: false changed_when: false + when: __network_is_booted | bool - name: Import the task 'remove_test_interfaces_with_dhcp.yml' import_tasks: tasks/remove_test_interfaces_with_dhcp.yml + when: __network_is_booted | bool - name: Restore the /etc/resolv.conf for initscript command: mv -vf /etc/resolv.conf.bak /etc/resolv.conf when: diff --git a/tests/playbooks/tests_bond_options.yml b/tests/playbooks/tests_bond_options.yml index 73bfc2435..dd86cf97d 100644 --- a/tests/playbooks/tests_bond_options.yml +++ b/tests/playbooks/tests_bond_options.yml @@ -11,6 +11,7 @@ dhcp_interface2: test2 lsr_fail_debug: - __network_connections_result + - __install_status bond_options_to_assert: - {key: 'mode', value: '802.3ad'} - {key: 'ad_actor_sys_prio', value: '65535'} @@ -50,7 +51,44 @@ - tasks/create_test_interfaces_with_dhcp.yml - tasks/assert_dhcp_device_present.yml lsr_test: - - tasks/create_bond_profile.yml + - network_connections: + # Create a bond controller + - name: "{{ controller_profile }}" + state: up + type: bond + interface_name: "{{ controller_device }}" + bond: + mode: 802.3ad + ad_actor_sys_prio: 65535 + ad_actor_system: 00:00:5e:00:53:5d + ad_select: stable + ad_user_port_key: 1023 + all_ports_active: true + downdelay: 0 + lacp_rate: slow + lp_interval: 128 + miimon: 110 + min_links: 0 + num_grat_arp: 64 + primary_reselect: better + resend_igmp: 225 + updelay: 0 + use_carrier: true + xmit_hash_policy: encap2+3 + ip: + route_metric4: 65535 + # add an ethernet to the bond + - name: "{{ port1_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface1 }}" + controller: "{{ controller_profile }}" + # add a second ethernet to the bond + - name: "{{ port2_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface2 }}" + controller: "{{ controller_profile }}" lsr_assert: - tasks/assert_controller_device_present.yml - tasks/assert_bond_port_profile_present.yml @@ -59,6 +97,10 @@ - tasks/cleanup_bond_profile+device.yml - tasks/remove_test_interfaces_with_dhcp.yml + - name: Stop test if building image or validation is enabled + meta: end_host + when: ansible_connection | d("") == "buildah" or __bootc_validation | default(false) + - name: "Reset bond options to assert" set_fact: bond_options_to_assert: @@ -83,7 +125,32 @@ - tasks/create_test_interfaces_with_dhcp.yml - tasks/assert_dhcp_device_present.yml lsr_test: - - tasks/create_bond_profile_reconfigure.yml + - network_connections: + # Create a bond controller + - name: "{{ controller_profile }}" + state: up + type: bond + interface_name: "{{ controller_device }}" + bond: + mode: active-backup + arp_interval: 60 + arp_ip_target: 192.0.2.128 + arp_validate: none + primary: "{{ dhcp_interface1 }}" + ip: + route_metric4: 65535 + # add an ethernet to the bond + - name: "{{ port1_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface1 }}" + controller: "{{ controller_profile }}" + # add a second ethernet to the bond + - name: "{{ port2_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface2 }}" + controller: "{{ controller_profile }}" lsr_assert: - tasks/assert_bond_options.yml lsr_cleanup: diff --git a/tests/playbooks/tests_bond_port_match_by_mac.yml b/tests/playbooks/tests_bond_port_match_by_mac.yml index a66616b24..6d69f7d15 100644 --- a/tests/playbooks/tests_bond_port_match_by_mac.yml +++ b/tests/playbooks/tests_bond_port_match_by_mac.yml @@ -29,7 +29,44 @@ - tasks/create_test_interfaces_with_dhcp.yml - tasks/assert_dhcp_device_present.yml lsr_test: - - tasks/create_bond_profile.yml + - network_connections: + # Create a bond controller + - name: "{{ controller_profile }}" + state: up + type: bond + interface_name: "{{ controller_device }}" + bond: + mode: 802.3ad + ad_actor_sys_prio: 65535 + ad_actor_system: 00:00:5e:00:53:5d + ad_select: stable + ad_user_port_key: 1023 + all_ports_active: true + downdelay: 0 + lacp_rate: slow + lp_interval: 128 + miimon: 110 + min_links: 0 + num_grat_arp: 64 + primary_reselect: better + resend_igmp: 225 + updelay: 0 + use_carrier: true + xmit_hash_policy: encap2+3 + ip: + route_metric4: 65535 + # add an ethernet to the bond + - name: "{{ port1_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface1 }}" + controller: "{{ controller_profile }}" + # add a second ethernet to the bond + - name: "{{ port2_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface2 }}" + controller: "{{ controller_profile }}" - tasks/create_bond_port_match_by_mac.yml lsr_assert: - tasks/assert_controller_device_present.yml diff --git a/tests/playbooks/tests_bridge.yml b/tests/playbooks/tests_bridge.yml index 09f4c8a78..9dbc74253 100644 --- a/tests/playbooks/tests_bridge.yml +++ b/tests/playbooks/tests_bridge.yml @@ -5,51 +5,36 @@ vars: interface: LSR-TST-br31 tasks: - - name: Include the task 'show_interfaces.yml' - include_tasks: tasks/show_interfaces.yml - - - name: Include the task 'assert_device_absent.yml' - include_tasks: tasks/assert_device_absent.yml - - - name: Add test bridge - include_role: - name: linux-system-roles.network + - name: Include the task 'run_test.yml' + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - interface_name: "{{ interface }}" - state: up - type: bridge - ip: - dhcp4: false - auto6: true - - - name: Assert device present - include_tasks: tasks/assert_device_present.yml - - - name: Assert profile present - include_tasks: tasks/assert_profile_present.yml - vars: - profile: "{{ interface }}" - - - name: Include the task 'down_profile+delete_interface.yml' - include_tasks: tasks/down_profile+delete_interface.yml - vars: - profile: "{{ interface }}" - # FIXME: assert profile/device down - - - name: Include the task 'remove_profile.yml' - include_tasks: tasks/remove_profile.yml - vars: - profile: "{{ interface }}" - - - name: Assert profile absent - include_tasks: tasks/assert_profile_absent.yml - vars: - profile: "{{ interface }}" - - - name: Assert device absent - include_tasks: tasks/assert_device_absent.yml - - - name: Verify network state restored to default - include_tasks: tasks/check_network_dns.yml + lsr_description: Test configuring bridges + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/assert_device_absent.yml + condition: "{{ not __bootc_validation | d(false) }}" + lsr_test: + - network_connections: + - name: "{{ interface }}" + interface_name: "{{ interface }}" + state: up + type: bridge + ip: + dhcp4: false + auto6: true + lsr_assert: + - tasks/assert_device_present.yml + - what: tasks/assert_profile_present.yml + profile: "{{ interface }}" + lsr_cleanup: + - what: tasks/down_profile+delete_interface.yml + condition: "{{ __network_is_booted }}" + profile: "{{ interface }}" + lsr_persistent_state: absent + - what: tasks/remove_profile.yml + profile: "{{ interface }}" + - what: tasks/assert_profile_absent.yml + profile: "{{ interface }}" + - what: tasks/assert_device_absent.yml + profile: "{{ interface }}" + - tasks/check_network_dns.yml diff --git a/tests/playbooks/tests_eth_dns_support.yml b/tests/playbooks/tests_eth_dns_support.yml index 5322370b4..338ddfe47 100644 --- a/tests/playbooks/tests_eth_dns_support.yml +++ b/tests/playbooks/tests_eth_dns_support.yml @@ -6,134 +6,154 @@ 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 + - name: Test dns support + include_tasks: tasks/run_test.yml vars: - state: present - - - name: Include the task 'assert_device_present.yml' - include_tasks: tasks/assert_device_present.yml - - - name: Include network role - include_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - interface_name: "{{ interface }}" - state: up - type: ethernet - autoconnect: true - ip: - route_metric4: 100 - dhcp4: false - gateway4: 192.0.2.1 - dns_priority: 9999 - dns: - - 192.0.2.2 - - 198.51.100.5 - - 2001:db8::20 - dns_search: - - example.com - - example.org - dns_options: - - no-aaaa - - rotate - - timeout:1 - - route_metric6: -1 - auto6: false - gateway6: 2001:db8::1 - - address: - - 192.0.2.3/24 - - 198.51.100.3/26 - - 2001:db8::80/7 - - route: - - network: 198.51.100.128 - prefix: 26 - gateway: 198.51.100.1 - metric: 2 - - network: 198.51.100.64 - prefix: 26 - gateway: 198.51.100.6 - metric: 4 - route_append_only: false - rule_append_only: true - - - name: Verify nmcli connection DNS entry for IPv4 - shell: | - set -euxo pipefail - nmcli connection show {{ interface }} | grep ipv4.dns - register: ipv4_dns - ignore_errors: true - changed_when: false - - - name: Verify nmcli connection DNS entry for IPv6 - shell: | - set -euxo pipefail - nmcli connection show {{ interface }} | grep ipv6.dns - register: ipv6_dns - ignore_errors: true - changed_when: false - - - name: "Assert that DNS addresses are configured correctly" - assert: - that: - - "'192.0.2.2' in ipv4_dns.stdout" - - "'198.51.100.5' in ipv4_dns.stdout" - - "'2001:db8::20' in ipv6_dns.stdout" - msg: "DNS addresses are configured incorrectly" - - - name: "Assert that DNS search domains are configured correctly" - assert: - that: - - "'example.com' in ipv4_dns.stdout" - - "'example.org' in ipv4_dns.stdout" - - "'example.com' in ipv6_dns.stdout" - - "'example.org' in ipv6_dns.stdout" - msg: "DNS search domains are configured incorrectly" - - - name: "Assert that DNS options are configured correctly" - assert: - that: - - "'no-aaaa' in ipv4_dns.stdout" - - "'rotate' in ipv4_dns.stdout" - - "'timeout:1' in ipv4_dns.stdout" - - "'no-aaaa' in ipv6_dns.stdout" - - "'rotate' in ipv6_dns.stdout" - - "'timeout:1' in ipv6_dns.stdout" - msg: "DNS options are configured incorrectly" - - - name: "Assert that DNS priority is configured correctly" - assert: - that: - - "'9999' in ipv4_dns.stdout" - - "'9999' in ipv6_dns.stdout" - msg: "DNS priority is configured incorrectly" - - - name: Include the tasks 'down_profile+delete_interface.yml' - include_tasks: tasks/down_profile+delete_interface.yml - vars: - profile: "{{ interface }}" - - # FIXME: assert profile/device down - - name: Include the task 'remove_profile.yml' - include_tasks: tasks/remove_profile.yml - vars: - profile: "{{ interface }}" - - - name: Include the task 'assert_profile_absent.yml' - include_tasks: tasks/assert_profile_absent.yml - vars: - profile: "{{ interface }}" - - - name: Include the task 'assert_device_absent.yml' - include_tasks: tasks/assert_device_absent.yml - - - name: Verify network state restored to default - include_tasks: tasks/check_network_dns.yml + lsr_description: Test dns support + lsr_setup: + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + interface_name: "{{ interface }}" + state: up + type: ethernet + autoconnect: true + ip: + route_metric4: 100 + dhcp4: false + gateway4: 192.0.2.1 + dns_priority: 9999 + dns: + - 192.0.2.2 + - 198.51.100.5 + - 2001:db8::20 + dns_search: + - example.com + - example.org + dns_options: + - no-aaaa + - rotate + - timeout:1 + route_metric6: -1 + auto6: false + gateway6: 2001:db8::1 + address: + - 192.0.2.3/24 + - 198.51.100.3/26 + - 2001:db8::80/7 + route: + - network: 198.51.100.128 + prefix: 26 + gateway: 198.51.100.1 + metric: 2 + - network: 198.51.100.64 + prefix: 26 + gateway: 198.51.100.6 + metric: 4 + route_append_only: false + rule_append_only: true + lsr_assert: + - what: tasks/assert_connection_settings.yml + condition: "{{ network_provider == 'nm' }}" + lsr_connection_name: "{{ interface }}" + lsr_connection_settings: + - section: connection + option: id + value: "{{ interface }}" + - section: connection + option: interface-name + value: "{{ interface }}" + - section: connection + option: type + value: ethernet + nmvalue: 802-3-ethernet + - section: ipv4 + option: route-metric + value: "100" + - section: ipv4 + option: dns + value: 192.0.2.2;198.51.100.5; + nmvalue: 192.0.2.2,198.51.100.5 + - section: ipv4 + option: dns-search + value: example.com;example.org; + nmvalue: example.com,example.org + - section: ipv6 + option: dns + value: 2001:db8::20; + nmvalue: 2001:db8::20 + - section: ipv6 + option: dns-search + value: example.com;example.org; + nmvalue: example.com,example.org + - section: ipv4 + option: dns-options + value: no-aaaa;rotate;timeout:1; + nmvalue: no-aaaa,rotate,timeout:1 + - section: ipv6 + option: dns-options + value: no-aaaa;rotate;timeout:1; + nmvalue: no-aaaa,rotate,timeout:1 + - section: ipv4 + option: dns-priority + value: 9999 + - section: ipv6 + option: dns-priority + value: 9999 + - section: ipv4 + option: method + value: manual + - section: ipv6 + option: method + value: manual + - section: ipv4 + option: address1 + value: 192.0.2.3/24 + nmvalue: false + - section: ipv4 + option: address2 + value: 198.51.100.3/26 + nmvalue: false + - section: ipv4 + option: addresses + nmvalue: 192.0.2.3/24, 198.51.100.3/26 + - section: ipv6 + option: address1 + value: 2001:db8::80/7 + nmvalue: false + - section: ipv6 + option: addresses + nmvalue: 2001:db8::80/7 + - section: ipv4 + option: gateway + value: 192.0.2.1 + - section: ipv6 + option: gateway + value: 2001:db8::1 + - section: ipv4 + option: route1 + value: 198.51.100.128/26,198.51.100.1,2 + nmvalue: false + - section: ipv4 + option: route2 + value: 198.51.100.64/26,198.51.100.6,4 + nmvalue: false + - section: ipv4 + option: routes + nmvalue: 198.51.100.128/26 198.51.100.1 2, 198.51.100.64/26 198.51.100.6 4 + lsr_cleanup: + - what: tasks/down_profile+delete_interface.yml + condition: "{{ __network_is_booted }}" + profile: "{{ interface }}" + lsr_persistent_state: absent + - what: tasks/remove_profile.yml + profile: "{{ interface }}" + - what: tasks/assert_profile_absent.yml + condition: "{{ __network_is_booted }}" + profile: "{{ interface }}" + - what: tasks/assert_device_absent.yml + profile: "{{ interface }}" + - tasks/check_network_dns.yml diff --git a/tests/playbooks/tests_ethernet.yml b/tests/playbooks/tests_ethernet.yml index 4c1e89596..5f88a339f 100644 --- a/tests/playbooks/tests_ethernet.yml +++ b/tests/playbooks/tests_ethernet.yml @@ -10,106 +10,79 @@ debug: msg: Inside ethernet tests - - name: Show network_provider - debug: - var: network_provider - - - 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 static interface up - include_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - interface_name: "{{ interface }}" - state: up - type: ethernet - autoconnect: true - ip: - address: 192.0.2.1/24 - - - name: Include the task 'assert_output_in_stderr_without_warnings.yml' - include_tasks: tasks/assert_output_in_stderr_without_warnings.yml - - - name: Show network_provider after running role - debug: - var: network_provider - - - name: Get NM connection file - slurp: - src: "/etc/NetworkManager/system-connections/{{ interface }}.nmconnection" - register: nm_connection_file - when: - - network_provider == 'nm' - # RHEL up to 8 uses initscripts backend - - ansible_distribution_major_version | int >= 9 - - - name: Assert settings in NM connection file - assert: - that: - - "('interface-name=' + interface) in nm_connection_file.content | b64decode" - - "'type=ethernet' in nm_connection_file.content | b64decode" - - "'address1=192.0.2.1/24' in nm_connection_file.content | b64decode" - - "'method=manual' in nm_connection_file.content | b64decode" - when: - - network_provider == 'nm' - # RHEL up to 8 uses initscripts backend - - ansible_distribution_major_version | int >= 9 - - - name: Get NM connection status - command: "nmcli connection show {{ interface }}" - changed_when: false - register: nm_connection_status - when: network_provider == 'nm' - - - name: Assert NM connection status - assert: - that: - - nm_connection_status.stdout is search("ipv4.addresses:\s+192.0.2.1/24") - when: network_provider == 'nm' - - - name: Get initscripts connection file - slurp: - src: "/etc/sysconfig/network-scripts/ifcfg-{{ interface }}" - register: initscripts_connection_file - when: network_provider == 'initscripts' or ansible_distribution_major_version | int < 9 - - - name: Assert settings in initscripts connection file - assert: - that: - - "'TYPE=Ethernet' in initscripts_connection_file.content | b64decode" - - "'DEVICE={{ interface }}' in initscripts_connection_file.content | b64decode" - - "'IPADDR=192.0.2.1' in initscripts_connection_file.content | b64decode" - - "'PREFIX=24' in initscripts_connection_file.content | b64decode" - when: network_provider == 'initscripts' or ansible_distribution_major_version | int < 9 - - - name: Include the tasks 'down_profile+delete_interface.yml' - include_tasks: tasks/down_profile+delete_interface.yml - vars: - profile: "{{ interface }}" - - # FIXME: assert profile/device down - - name: Include the task 'remove_profile.yml' - include_tasks: tasks/remove_profile.yml - vars: - profile: "{{ interface }}" - - - name: Include the task 'assert_profile_absent.yml' - include_tasks: tasks/assert_profile_absent.yml - vars: - profile: "{{ interface }}" - - name: Include the task 'assert_device_absent.yml' - include_tasks: tasks/assert_device_absent.yml - - - name: Verify network state restored to default - include_tasks: tasks/check_network_dns.yml + - name: Test creating the bridge connection + tags: + - tests::states:create + block: + - name: Include the task 'run_test.yml' + include_tasks: tasks/run_test.yml + vars: + lsr_description: I can create a profile + lsr_setup: + - what: tasks/delete_interface.yml + condition: "{{ not __bootc_validation | default(false) }}" + - what: tasks/assert_device_absent.yml + condition: "{{ not __bootc_validation | default(false) }}" + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + interface_name: "{{ interface }}" + state: up + type: ethernet + autoconnect: true + ip: + address: 192.0.2.1/24 + lsr_assert: + - tasks/assert_output_in_stderr_without_warnings.yml + - tasks/assert_device_present.yml + # Device should be present because of autoconnect: true by + # default for NM (this might be considered a bug) + - what: tasks/assert_connection_settings.yml + condition: "{{ network_provider == 'nm' }}" + lsr_connection_name: "{{ interface }}" + lsr_connection_settings: + - section: connection + option: interface-name + value: "{{ interface }}" + - section: connection + option: type + value: ethernet + nmvalue: 802-3-ethernet + - section: ipv4 + option: address1 + value: 192.0.2.1/24 + nmvalue: false + - section: ipv4 + option: addresses + nmvalue: 192.0.2.1/24 + - section: ipv4 + option: method + value: manual + - what: tasks/assert_connection_settings.yml + condition: "{{ network_provider == 'initscripts' or ansible_distribution_major_version | int < 9 }}" + lsr_connection_name: "{{ interface }}" + lsr_connection_settings: + - TYPE=Ethernet + - DEVICE={{ interface }} + - IPADDR=192.0.2.1 + - PREFIX=24 + - what: tasks/assert_nm_connection_status.yml + condition: "{{ network_provider == 'nm' and __network_is_booted }}" + lsr_connection_name: "{{ interface }}" + lsr_connection_output: 'ipv4.addresses:\s+192.0.2.1/24' + lsr_cleanup: + - what: tasks/down_profile+delete_interface.yml + condition: "{{ __network_is_booted }}" + profile: "{{ interface }}" + lsr_persistent_state: absent + - what: tasks/remove_profile.yml + profile: "{{ interface }}" + - what: tasks/assert_profile_absent.yml + condition: "{{ __network_is_booted }}" + profile: "{{ interface }}" + - what: tasks/assert_device_absent.yml + profile: "{{ interface }}" + - tasks/check_network_dns.yml diff --git a/tests/playbooks/tests_ethtool_coalesce.yml b/tests/playbooks/tests_ethtool_coalesce.yml index d28899117..6ebba69b2 100644 --- a/tests/playbooks/tests_ethtool_coalesce.yml +++ b/tests/playbooks/tests_ethtool_coalesce.yml @@ -12,153 +12,117 @@ tags: - always - - name: "INIT: Ethtool coalesce tests" - debug: - msg: "##################################################" - - 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: Install ethtool (test dependency) - package: - name: ethtool - state: present - use: "{{ (__network_is_ostree | d(false)) | - ternary('ansible.posix.rhel_rpm_ostree', omit) }}" + - name: Include network role vars used by tests + include_role: + name: linux-system-roles.network + tasks_from: set_facts.yml + public: true - - name: Test ethtool coalesce settings + - name: Run tests block: - - name: >- - TEST: I can create a profile without any coalescing option. - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - type: ethernet - state: up - ip: - dhcp4: false - auto6: false - - - name: Get profile's coalescing options - command: nmcli -g ethtool.coalesce-rx-frames c show {{ interface }} - register: no_coalesce_nm - when: - - network_provider == "nm" - changed_when: false - - name: "ASSERT: The profile does not contain coalescing options" - assert: - that: no_coalesce_nm.stdout | length == 0 - when: - - network_provider == "nm" - - name: Get profile's coalescing options - command: - grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} - register: no_coalesce_initscripts - ignore_errors: true + - name: Test creating profile without changing ethtool coalesce settings when: - - network_provider == "initscripts" - changed_when: false - - name: "ASSERT: The profile does not contain coalescing options" - assert: - that: no_coalesce_initscripts.stdout | length == 0 - when: - - network_provider == "initscripts" - - - name: >- - TEST: I can set rx-frames. - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network + - __network_is_booted + - not __bootc_validation | d(false) + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - type: ethernet - state: up - ip: - dhcp4: false - auto6: false - ethtool: - coalesce: - rx_frames: 128 - - name: Get profile's coalescing options - command: nmcli -g ethtool.coalesce-rx-frames c show {{ interface }} - register: with_coalesce_nm - when: - - network_provider == "nm" - changed_when: false - - name: Assert coalesce options set in profile - assert: - that: with_coalesce_nm.stdout == '128' - when: - - network_provider == "nm" - - - name: Get profile's coalescing options - command: - grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} - register: with_coalesce_initscripts - ignore_errors: true - when: - - network_provider == "initscripts" - changed_when: false - - name: Assert coalesce options set in profile - assert: - that: '"rx-frames 128" in with_coalesce_initscripts.stdout' - when: - - network_provider == "initscripts" + lsr_description: Test creating profile without changing ethtool coalesce + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: false + auto6: false + lsr_assert: # NOTE: Cleanup is done in always at very end of file + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'nm' }}" + lsr_command: nmcli -g ethtool.coalesce-rx-frames c show {{ interface | quote }} + lsr_not_stdout: coalesce + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'initscripts' }}" + lsr_command: cat /etc/sysconfig/network-scripts/ifcfg-{{ interface | quote }} + lsr_not_stdout: ETHTOOL + lsr_cleanup: [] - - name: "TEST: I can clear coalescing options" - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network + - name: "TEST: I can set rx-frames." + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - type: ethernet - state: up - ip: - dhcp4: false - auto6: false + lsr_description: Test setting coalesce option + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: false + auto6: false + ethtool: + coalesce: + rx_frames: 128 + lsr_assert: + - what: tasks/assert_connection_settings.yml + condition: "{{ network_provider == 'nm' }}" + lsr_connection_name: "{{ interface }}" + lsr_connection_settings: + - section: ethtool + option: coalesce-rx-frames + value: "128" + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'nm' }}" + lsr_command: nmcli -g ethtool.coalesce-rx-frames c show {{ interface | quote }} + lsr_stdout: "128" + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'initscripts' }}" + lsr_command: grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface | quote }} + lsr_stdout: rx-frames 128 + lsr_cleanup: [] - - name: Get profile's coalescing options - command: nmcli -g ethtool.coalesce-rx-frames c show {{ interface }} - when: - - network_provider == "nm" - register: clear_coalesce_nm - changed_when: false - - name: "ASSERT: The profile does reset coalescing options" - assert: - that: clear_coalesce_nm.stdout | length == 0 + - name: Test clearing ethtool coalesce settings when: - - network_provider == "nm" - - name: Get profile's coalescing options - command: - grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} - register: clear_coalesce_initscripts - ignore_errors: true - when: - - network_provider == "initscripts" - changed_when: false - - name: "ASSERT: The profile does reset coalescing options" - assert: - that: clear_coalesce_initscripts.stdout | length == 0 - when: - - network_provider == "initscripts" + - __network_is_booted + - not __bootc_validation | d(false) + include_tasks: tasks/run_test.yml + vars: + lsr_description: Test creating profile without changing ethtool features + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: false + auto6: false + lsr_assert: # NOTE: Cleanup is done in always at very end of file + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'nm' }}" + lsr_command: nmcli -g ethtool.coalesce-rx-frames c show {{ interface | quote }} + lsr_not_stdout: coalesce + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'initscripts' }}" + lsr_command: cat /etc/sysconfig/network-scripts/ifcfg-{{ interface | quote }} + lsr_not_stdout: ETHTOOL + lsr_cleanup: [] always: - name: Clean up the test device and the connection profile + when: ansible_connection != "buildah" tags: - "tests::cleanup" block: diff --git a/tests/playbooks/tests_ethtool_features.yml b/tests/playbooks/tests_ethtool_features.yml index 3bdc3c4ee..77ee68675 100644 --- a/tests/playbooks/tests_ethtool_features.yml +++ b/tests/playbooks/tests_ethtool_features.yml @@ -12,137 +12,115 @@ tags: - always - - name: "INIT: Ethtool feeatures tests" - debug: - msg: "##################################################" - - 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: Install ethtool (test dependency) - package: - name: ethtool - state: present - use: "{{ (__network_is_ostree | d(false)) | - ternary('ansible.posix.rhel_rpm_ostree', omit) }}" + - name: Include network role vars used by tests + include_role: + name: linux-system-roles.network + tasks_from: set_facts.yml + public: true - - name: Test ethtool features settings + - name: Run tests block: - - name: >- - TEST: I can create a profile without changing the ethtool features. - debug: - msg: "##################################################" - - name: Get current device features - command: "ethtool --show-features {{ interface }}" - register: original_ethtool_features - changed_when: false - - name: Import network role - import_role: - name: linux-system-roles.network + - name: Test creating profile without changing ethtool features + when: + - __network_is_booted + - not __bootc_validation | d(false) + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - state: up - type: ethernet - ip: - dhcp4: "no" - auto6: "no" - - name: Get current device features - command: "ethtool --show-features {{ interface }}" - register: ethtool_features - changed_when: false - - name: "ASSERT: The profile does not change the ethtool features" - assert: - that: - - original_ethtool_features.stdout == ethtool_features.stdout + lsr_description: Test creating profile without changing ethtool features + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + - what: tasks/assert_command_output.yml + lsr_packages: [ethtool] + lsr_command: ethtool --show-features {{ interface | quote }} + lsr_test: + - network_connections: + - name: "{{ interface }}" + state: up + type: ethernet + ip: + dhcp4: "no" + auto6: "no" + lsr_assert: # NOTE: Cleanup is done in always at very end of file + - what: tasks/assert_command_output.yml + lsr_packages: [ethtool] + lsr_command: ethtool --show-features {{ interface | quote }} + lsr_stdout: "{{ __previous_command_output.stdout }}" + lsr_cleanup: [] - - name: >- - TEST: I can disable gro and tx-tcp-segmentation and enable gso. - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network + - name: Set original ethtool features + when: + - __network_is_booted + - not __bootc_validation | d(false) + set_fact: + original_ethtool_features: "{{ __previous_command_output }}" + + - name: "TEST: I can disable gro and tx-tcp-segmentation and enable gso." + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - state: up - type: ethernet - ip: - dhcp4: "no" - auto6: "no" - ethtool: - features: - gro: "no" - gso: "yes" - tx-tcp-segmentation: "no" - - name: Get current device features - command: "ethtool --show-features {{ interface }}" - register: ethtool_features - changed_when: false - - name: Show ethtool_features - debug: - var: ethtool_features.stdout_lines - - name: Assert device features - assert: - that: - - >- - 'generic-receive-offload: off' in - ethtool_features.stdout_lines - - >- - 'generic-segmentation-offload: on' in - ethtool_features.stdout_lines - - >- - 'tx-tcp-segmentation: off' in - ethtool_features.stdout_lines | map('trim') + lsr_description: Test creating profile without changing ethtool features + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted }}" + lsr_packages: [ethtool] + lsr_command: ethtool --show-features {{ interface | quote }} + lsr_test: + - network_connections: + - name: "{{ interface }}" + state: up + type: ethernet + ip: + dhcp4: "no" + auto6: "no" + ethtool: + features: + gro: "no" + gso: "yes" + tx-tcp-segmentation: "no" + lsr_assert: + - what: tasks/assert_connection_settings.yml + condition: "{{ network_provider == 'nm' }}" + lsr_connection_name: "{{ interface }}" + lsr_connection_settings: + - section: ethtool + option: feature-gro + value: "false" + nmvalue: "off" + - section: ethtool + option: feature-gso + value: "true" + nmvalue: "on" + - section: ethtool + option: feature-tx-tcp-segmentation + value: "false" + nmvalue: "off" + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted }}" + lsr_packages: [ethtool] + lsr_command: ethtool --show-features {{ interface | quote }} + lsr_stdout_lines: + - "generic-receive-offload: off" + - "generic-segmentation-offload: on" + - "\ttx-tcp-segmentation: off" # noqa no-tabs + lsr_cleanup: [] - name: >- TEST: I can enable tx_tcp_segmentation (using underscores). - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network + when: + - __network_is_booted + - not __bootc_validation | d(false) + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - state: up - type: ethernet - ip: - dhcp4: "no" - auto6: "no" - ethtool: - features: - tx_tcp_segmentation: "yes" - - name: Get current device features - command: "ethtool --show-features {{ interface }}" - register: ethtool_features - changed_when: false - - name: Show ethtool_features - debug: - var: ethtool_features.stdout_lines - - name: Assert device features - assert: - that: - - >- - 'tx-tcp-segmentation: on' in - ethtool_features.stdout_lines | map('trim') - - - name: I cannot change tx_tcp_segmentation and tx-tcp-segmentation at - the same time. - block: - - name: >- - TEST: Change feature with both underscores and dashes. - debug: - msg: "##################################################" - - name: Configure ethtool features setting - network_connections: - provider: "{{ network_provider | mandatory }}" - connections: + lsr_description: Test enabling tx_tcp_segmentation (using underscores) + lsr_setup: [] + lsr_test: + - network_connections: - name: "{{ interface }}" state: up type: ethernet @@ -151,10 +129,41 @@ auto6: "no" ethtool: features: - tx_tcp_segmentation: "no" - tx-tcp-segmentation: "no" - __header: "# Ansible managed test header" - register: __network_connections_result + tx_tcp_segmentation: "yes" + lsr_assert: + - what: tasks/assert_command_output.yml + lsr_packages: [ethtool] + lsr_command: ethtool --show-features {{ interface | quote }} + lsr_stdout_lines: + - "\ttx-tcp-segmentation: on" # noqa no-tabs + lsr_cleanup: [] + + - name: I cannot change tx_tcp_segmentation and tx-tcp-segmentation at + the same time. + when: + - __network_is_booted + - not __bootc_validation | d(false) + block: + - name: >- + TEST: Change feature with both underscores and dashes. + include_tasks: tasks/run_test.yml + vars: + lsr_description: Test changing feature with both underscores and dashes + lsr_setup: [] + lsr_test: + - network_connections: + - name: "{{ interface }}" + state: up + type: ethernet + ip: + dhcp4: "no" + auto6: "no" + ethtool: + features: + tx_tcp_segmentation: "no" + tx-tcp-segmentation: "no" + lsr_assert: [] + lsr_cleanup: [] rescue: - name: Show network_connections result debug: @@ -168,7 +177,6 @@ fatal error: configuration error: connections[0].ethtool.features: duplicate key 'tx_tcp_segmentation' - always: - name: Check failure debug: @@ -178,32 +186,30 @@ that: __network_connections_result.failed - name: "TEST: I can reset features to their original value." - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - state: up - type: ethernet - ip: - dhcp4: "no" - auto6: "no" - - name: Get current device features - command: "ethtool --show-features {{ interface }}" - register: ethtool_features - changed_when: false - # Resetting the ethtools only works with NetworkManager - - name: "ASSERT: The profile does not change the ethtool features" - assert: - that: - - original_ethtool_features.stdout == ethtool_features.stdout when: - network_provider == 'nm' + - __network_is_booted + - not __bootc_validation | d(false) + include_tasks: tasks/run_test.yml + vars: + lsr_description: Test resetting features to their original value + lsr_setup: [] + lsr_test: + - network_connections: + - name: "{{ interface }}" + state: up + type: ethernet + ip: + dhcp4: "no" + auto6: "no" + lsr_assert: + - what: tasks/assert_command_output.yml + lsr_packages: [ethtool] + lsr_command: ethtool --show-features {{ interface | quote }} + lsr_stdout: "{{ original_ethtool_features.stdout }}" + lsr_cleanup: [] always: - name: Clean up the test device and the connection profile + when: ansible_connection != "buildah" tags: - "tests::cleanup" block: diff --git a/tests/playbooks/tests_ethtool_ring.yml b/tests/playbooks/tests_ethtool_ring.yml index fdcc263de..f06d93cc8 100644 --- a/tests/playbooks/tests_ethtool_ring.yml +++ b/tests/playbooks/tests_ethtool_ring.yml @@ -12,194 +12,129 @@ tags: - always - - name: "INIT: Ethtool ring tests" - debug: - msg: "##################################################" - - 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: Install ethtool (test dependency) - package: - name: ethtool - state: present - use: "{{ (__network_is_ostree | d(false)) | - ternary('ansible.posix.rhel_rpm_ostree', omit) }}" + - name: Include network role vars used by tests + include_role: + name: linux-system-roles.network + tasks_from: set_facts.yml + public: true - - name: Test ethtool ring settings + - name: Run tests block: - - name: >- - TEST: I can create a profile without any ring option. - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network - vars: - network_connections: - - name: "{{ interface }}" - type: ethernet - state: up - ip: - dhcp4: false - auto6: false - - - name: Get profile's ring options - command: nmcli -g ethtool.ring-rx c show {{ interface }} - register: no_ring_nm - when: - - network_provider == "nm" - changed_when: false - - name: "ASSERT: The profile does not contain ring options" - assert: - that: no_ring_nm.stdout | length == 0 + - name: Test creating profile without changing ethtool ring settings when: - - network_provider == "nm" - - name: Get profile's ring options - command: - grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} - register: no_ring_initscripts - ignore_errors: true - when: - - network_provider == "initscripts" - changed_when: false - - name: "ASSERT: The profile does not contain ring options" - assert: - that: no_ring_initscripts.stdout | length == 0 - when: - - network_provider == "initscripts" - - - name: >- - TEST: I can set rx. - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network + - __network_is_booted + - not __bootc_validation | d(false) + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - type: ethernet - state: up - ip: - dhcp4: false - auto6: false - ethtool: - ring: - rx: 128 - rx_jumbo: 128 - rx_mini: 128 - tx: 128 - - - name: Get profile's ethtool.ring-rx options - command: nmcli -g ethtool.ring-rx c show {{ interface }} - register: with_ring_rx - when: - - network_provider == "nm" - changed_when: false - - name: Assert ethtool.ring-rx option set in profile - assert: - that: with_ring_rx.stdout == '128' - when: - - network_provider == "nm" - - name: Get profile's ethtool.ring-rx-jumbo options - command: nmcli -g ethtool.ring-rx-jumbo c show {{ interface }} - register: with_ring_rx_jumbo - when: - - network_provider == "nm" - changed_when: false - - name: Assert ethtool.ring-rx-jumbo option set in profile - assert: - that: with_ring_rx_jumbo.stdout == '128' - when: - - network_provider == "nm" - - name: Get profile's ethtool.ring-rx-mini options - command: nmcli -g ethtool.ring-rx-mini c show {{ interface }} - register: with_ring_rx_mini - when: - - network_provider == "nm" - changed_when: false - - name: Assert ethtool.ring-rx-mini option set in profile - assert: - that: with_ring_rx_mini.stdout == '128' - when: - - network_provider == "nm" - - name: Get profile's ethtool.ring-tx options - command: nmcli -g ethtool.ring-tx c show {{ interface }} - register: with_ring_tx - when: - - network_provider == "nm" - changed_when: false - - name: Assert ethtool.ring-tx option set in profile - assert: - that: with_ring_tx.stdout == '128' - when: - - network_provider == "nm" - - - name: Get profile's ethtool.ring options - command: - grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} - register: with_ring - ignore_errors: true - when: - - network_provider == "initscripts" - changed_when: false - - name: Assert ethtool.ring option set in profile - assert: - that: - - '"rx 128" in with_ring.stdout' - - '"rx-jumbo 128" in with_ring.stdout' - - '"rx-mini 128" in with_ring.stdout' - - '"tx 128" in with_ring.stdout' - when: - - network_provider == "initscripts" + lsr_description: Test creating profile without changing ethtool ring settings + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: false + auto6: false + lsr_assert: # NOTE: Cleanup is done in always at very end of file + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'nm' }}" + lsr_command: nmcli -g ethtool.ring-rx c show {{ interface | quote }} + lsr_not_stdout: ring-rx + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'initscripts' }}" + lsr_command: cat /etc/sysconfig/network-scripts/ifcfg-{{ interface | quote }} + lsr_not_stdout: ETHTOOL + lsr_cleanup: [] - - name: "TEST: I can clear ring options" - debug: - msg: "##################################################" - - name: Import network role - import_role: - name: linux-system-roles.network + - name: "TEST: I can set rx ring settings." + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface }}" - type: ethernet - state: up - ip: - dhcp4: false - auto6: false + lsr_description: Test setting rx ring settings + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: false + auto6: false + ethtool: + ring: + rx: 128 + rx_jumbo: 128 + rx_mini: 128 + tx: 128 + lsr_assert: + - what: tasks/assert_connection_settings.yml + condition: "{{ network_provider == 'nm' }}" + lsr_connection_name: "{{ interface }}" + lsr_connection_settings: + - section: ethtool + option: ring-rx + value: "128" + - section: ethtool + option: ring-rx-jumbo + value: "128" + - section: ethtool + option: ring-rx-mini + value: "128" + - section: ethtool + option: ring-tx + value: "128" + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'initscripts' }}" + lsr_command: grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface | quote }} + lsr_stdout_lines: + - rx 128 + - rx-jumbo 128 + - rx-mini 128 + - tx 128 + lsr_cleanup: [] - - name: Get profile's ring options - command: nmcli -g ethtool.ring-rx c show {{ interface }} - register: clear_ring_nm - when: - - network_provider == "nm" - changed_when: false - - name: "ASSERT: The profile does reset ring options" - assert: - that: clear_ring_nm.stdout | length == 0 - when: - - network_provider == "nm" - - name: Get profile's ring options - command: - grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} - register: clear_ring_initscripts - ignore_errors: true + - name: Test clearing ethtool ring settings when: - - network_provider == "initscripts" - changed_when: false - - name: "ASSERT: The profile does reset ring options" - assert: - that: clear_ring_initscripts.stdout | length == 0 - when: - - network_provider == "initscripts" + - __network_is_booted + - not __bootc_validation | d(false) + include_tasks: tasks/run_test.yml + vars: + lsr_description: Test clearing ethtool ring settings + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + - tasks/assert_device_present.yml + lsr_test: + - network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: false + auto6: false + lsr_assert: # NOTE: Cleanup is done in always at very end of file + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'nm' }}" + lsr_command: nmcli -g ethtool.ring-rx c show {{ interface | quote }} + lsr_not_stdout: ring-rx + - what: tasks/assert_command_output.yml + condition: "{{ network_provider == 'initscripts' }}" + lsr_command: cat /etc/sysconfig/network-scripts/ifcfg-{{ interface | quote }} + lsr_not_stdout: ETHTOOL + lsr_cleanup: [] always: - name: Clean up the test device and the connection profile + when: ansible_connection != "buildah" tags: - "tests::cleanup" block: diff --git a/tests/playbooks/tests_ignore_auto_dns.yml b/tests/playbooks/tests_ignore_auto_dns.yml index f191ce8b3..45d422d6e 100644 --- a/tests/playbooks/tests_ignore_auto_dns.yml +++ b/tests/playbooks/tests_ignore_auto_dns.yml @@ -36,27 +36,52 @@ 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 + # the nmcli and .nmconnection file formats are very different, so we + # need to do online and offline checks in different code paths + - name: Online check + when: __network_is_booted | bool + block: + - 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 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'" + + - name: Offline check + when: not __network_is_booted | bool + block: + # noqa command-instead-of-module + - name: Check that .nmconnection file has no ipv4.ignore-auto-dns setting + command: > + sed -n '/^\[ipv4\]/,/^$/ {/ignore-auto-dns=/p}' /etc/NetworkManager/system-connections/{{ interface }}.nmconnection + changed_when: false + register: ipv4_ignore_auto_dns_file + failed_when: ipv4_ignore_auto_dns_file.stdout | trim != "" - - 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'" + # noqa command-instead-of-module + - name: Check that .nmconnection file has ipv6.ignore-auto-dns setting + command: > + sed -n '/^\[ipv6\]/,/^$/ {/ignore-auto-dns=/p}' /etc/NetworkManager/system-connections/{{ interface }}.nmconnection + changed_when: false + register: ipv6_ignore_auto_dns_file + failed_when: ipv6_ignore_auto_dns_file.stdout | trim != "ignore-auto-dns=true" always: - name: Remove test configuration diff --git a/tests/playbooks/tests_ipv6_disabled.yml b/tests/playbooks/tests_ipv6_disabled.yml index f8aea90f5..7bf2ba506 100644 --- a/tests/playbooks/tests_ipv6_disabled.yml +++ b/tests/playbooks/tests_ipv6_disabled.yml @@ -44,10 +44,13 @@ vars: errmsg: ip.ipv6_disabled is not supported by the running version of NetworkManager - - name: Verify nmcli connection ipv6.method - shell: | - set -euxo pipefail - nmcli connection show {{ interface }} | grep ipv6.method + - name: Verify NM connection ipv6.method + command: >- + {{ + ("nmcli -f ipv6.method connection show " ~ interface) + if __network_is_booted else + ("sed -n '/^\[ipv6\]/,/^$/ {/method=/p}' /etc/NetworkManager/system-connections/" ~ interface ~ ".nmconnection") + }} register: ipv6_method ignore_errors: true changed_when: false diff --git a/tests/playbooks/tests_ipv6_dns_search.yml b/tests/playbooks/tests_ipv6_dns_search.yml index b5828a18e..ae26c36aa 100644 --- a/tests/playbooks/tests_ipv6_dns_search.yml +++ b/tests/playbooks/tests_ipv6_dns_search.yml @@ -21,13 +21,23 @@ - 192.0.2.1/24 state: up - name: Get DNS search entry for IPv4 - command: nmcli -f ipv4.dns-search connection show br-example + command: >- + {{ + "nmcli -f ipv4.dns-search connection show br-example" + if __network_is_booted else + "sed -n '/^\[ipv4\]/,/^$/ {/dns-search=/p}' /etc/NetworkManager/system-connections/br-example.nmconnection" + }} register: ipv4_dns_search ignore_errors: true changed_when: false - name: Get DNS search entry for IPv6 - command: nmcli -f ipv6.dns-search connection show br-example + command: >- + {{ + "nmcli -f ipv6.dns-search connection show br-example" + if __network_is_booted else + "sed -n '/^\[ipv6\]/,/^$/ {/dns-search=/p}' /etc/NetworkManager/system-connections/br-example.nmconnection" + }} register: ipv6_dns_search ignore_errors: true changed_when: false @@ -67,7 +77,12 @@ state: up - name: Get DNS search entry for IPv6 - command: nmcli -f ipv6.dns-search connection show br-example + command: >- + {{ + "nmcli -f ipv6.dns-search connection show br-example" + if __network_is_booted else + "sed -n '/^\[ipv6\]/,/^$/ {/dns-search=/p}' /etc/NetworkManager/system-connections/br-example.nmconnection" + }} register: ipv6_dns_search_static ignore_errors: true changed_when: false @@ -99,7 +114,12 @@ state: up - name: Get DNS search entry for IPv6 - command: nmcli -f ipv6.dns-search connection show br-example + command: >- + {{ + "nmcli -f ipv6.dns-search connection show br-example" + if __network_is_booted else + "sed -n '/^\[ipv6\]/,/^$/ {/dns-search=/p}' /etc/NetworkManager/system-connections/br-example.nmconnection" + }} register: ipv6_dns_search_static_only ignore_errors: true changed_when: false diff --git a/tests/playbooks/tests_network_state.yml b/tests/playbooks/tests_network_state.yml index 5e6260fc0..b51d27aca 100644 --- a/tests/playbooks/tests_network_state.yml +++ b/tests/playbooks/tests_network_state.yml @@ -2,6 +2,9 @@ --- - name: Play for configuring network using network state variable hosts: all + tags: + # this is runtime configuration and checks + - tests::booted vars: type: veth interface0: ethtest0 diff --git a/tests/playbooks/tests_route_device.yml b/tests/playbooks/tests_route_device.yml index 13a4d87e9..c7f5daccc 100644 --- a/tests/playbooks/tests_route_device.yml +++ b/tests/playbooks/tests_route_device.yml @@ -8,111 +8,121 @@ interface1: ethtest1 tasks: - - name: Set type and interface0 - set_fact: - type: "{{ type }}" - interface: "{{ interface0 }}" - - name: Show interfaces - include_tasks: tasks/show_interfaces.yml - - name: Manage test interface - include_tasks: tasks/manage_test_interface.yml - vars: - state: present - - name: Assert device is present - include_tasks: tasks/assert_device_present.yml - - name: Set interface1 - set_fact: - interface: "{{ interface1 }}" - - name: Show interfaces again - include_tasks: tasks/show_interfaces.yml - - name: Manage test interface with second interface - include_tasks: tasks/manage_test_interface.yml - vars: - state: present - - name: Assert device is present with second interface - include_tasks: tasks/assert_device_present.yml - name: Test the route or the warning log when configuring the route with or without the interface name block: - - name: Configure the IP addresses and the route with interface name - specified - import_role: - name: linux-system-roles.network + - name: Test the route or the warning log when configuring the route with + or without the interface name + include_tasks: tasks/run_test.yml vars: - network_connections: - - name: "{{ interface0 }}" - interface_name: "{{ interface0 }}" - state: up - type: ethernet - autoconnect: false - ip: - address: - - 198.51.100.3/24 - - 2001:db8::2/32 - route: - - network: 198.51.10.64 - prefix: 26 - gateway: 198.51.100.6 - metric: 4 - - network: 2001:db6::4 - prefix: 128 - gateway: 2001:db8::1 - metric: 2 - - name: "{{ interface1 }}" - interface_name: "{{ interface1 }}" - state: up - type: ethernet - autoconnect: false - ip: - address: - - 198.51.100.6/24 - - 2001:db8::4/32 - route: - - network: 198.51.12.128 - prefix: 26 - gateway: 198.51.100.1 - metric: 2 - - name: Get the IPv4 routes from the route table main - command: ip -4 route - register: route_table_main_ipv4 - changed_when: false - - - name: Assert that the route table main contains the specified IPv4 - routes - assert: - that: - # When running with nm provider, the route will be configured - # with `proto static` - # In RHEL-6.10 managed host, the attribute in the route is not - # equally spaced - - route_table_main_ipv4.stdout is search("198.51.10.64/26 via - 198.51.100.6 dev ethtest0\s+(proto static )?metric 4") - - route_table_main_ipv4.stdout is search("198.51.12.128/26 via - 198.51.100.1 dev ethtest1\s+(proto static )?metric 2") - msg: "the route table main does not contain the specified IPv4 - route" - - - name: Get the IPv6 routes from the route table main - command: ip -6 route - register: route_table_main_ipv6 - changed_when: false + lsr_description: Test the route or the warning log when configuring the route with + or without the interface name + lsr_setup: + - tasks/show_interfaces.yml + - what: tasks/manage_test_interface.yml + state: present + lsr_interface: "{{ interface0 }}" + - what: tasks/assert_device_present.yml + lsr_interface: "{{ interface0 }}" + - what: tasks/manage_test_interface.yml + state: present + lsr_interface: "{{ interface1 }}" + - what: tasks/assert_device_present.yml + lsr_interface: "{{ interface1 }}" + lsr_test: + - network_connections: + - name: "{{ interface0 }}" + interface_name: "{{ interface0 }}" + state: up + type: ethernet + # Set autoconnect to true for buildah connection, otherwise, + # the test will need to start the connection manually + autoconnect: "{{ ansible_connection | d('') == 'buildah'}}" + ip: + address: + - 198.51.100.3/24 + - 2001:db8::2/32 + route: + - network: 198.51.10.64 + prefix: 26 + gateway: 198.51.100.6 + metric: 4 + - network: 2001:db6::4 + prefix: 128 + gateway: 2001:db8::1 + metric: 2 + - name: "{{ interface1 }}" + interface_name: "{{ interface1 }}" + state: up + type: ethernet + # Set autoconnect to true for buildah connection, otherwise, + # the test will need to start the connection manually + autoconnect: "{{ ansible_connection | d('') == 'buildah'}}" + ip: + address: + - 198.51.100.6/24 + - 2001:db8::4/32 + route: + - network: 198.51.12.128 + prefix: 26 + gateway: 198.51.100.1 + metric: 2 + lsr_assert: + - what: tasks/assert_connection_settings.yml + condition: "{{ network_provider == 'nm' }}" + lsr_connection_name: "{{ interface0 }}" + lsr_connection_settings: + - section: ipv4 + option: route1 + value: 198.51.10.64/26,198.51.100.6,4 + nmvalue: false + ifcfg_route_value: ADDRESS0=198.51.10.64 + - section: ipv4 + option: routes + nmvalue: 198.51.10.64/26 198.51.100.6 4 + - section: ipv6 + option: route1 + value: 2001:db6::4/128,2001:db8::1,2 + nmvalue: false + ifcfg_route6_value: 2001:db6::4/128 via 2001:db8::1 metric 2 + - section: ipv6 + option: routes + nmvalue: 2001:db6::4/128 2001:db8::1 2 + - what: tasks/assert_connection_settings.yml + condition: "{{ network_provider == 'nm' }}" + lsr_connection_name: "{{ interface1 }}" + lsr_connection_settings: + - section: ipv4 + option: route1 + value: 198.51.12.128/26,198.51.100.1,2 + nmvalue: false + - section: ipv4 + option: routes + nmvalue: 198.51.12.128/26 198.51.100.1 2 + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted }}" + lsr_command: ip -4 route + lsr_stdout_regex_list: + - "198.51.10.64/26 via 198.51.100.6 dev ethtest0\\s+(proto static )?metric 4" + - "198.51.12.128/26 via 198.51.100.1 dev ethtest1\\s+(proto static )?metric 2" + - what: tasks/assert_command_output.yml + condition: "{{ __network_is_booted }}" + lsr_command: ip -6 route + lsr_stdout_regex_list: + - "2001:db6::4 via 2001:db8::1 dev ethtest0\\s+(proto static )?metric 2" + lsr_cleanup: [] - - name: Assert that the route table main contains the specified IPv6 - routes - assert: - that: - - route_table_main_ipv6.stdout is search("2001:db6::4 via - 2001:db8::1 dev ethtest0\s+(proto static )?metric 2") - msg: "the route table main does not contain the specified IPv6 - route" - name: Get the interface1 MAC address command: cat /sys/class/net/{{ interface1 }}/address register: interface1_mac changed_when: false + when: __network_is_booted and not __bootc_validation | d(false) + - name: Configure the IP addresses and the route with only the MAC address specified - import_role: + include_role: name: linux-system-roles.network + when: __network_is_booted and not __bootc_validation | d(false) vars: network_connections: - name: "{{ interface1 }}" @@ -133,28 +143,37 @@ the output device is logged for initscripts provider assert: that: - - __network_connections_result.stderr is search("\[003\] - .0, state.None persistent_state.present, - '{{ interface1 }}'. The connection {{ interface1 }} does not - specify an interface name. Therefore, the route to - 198.58.10.64/26 will be configured without the output device - and the kernel will choose it automatically which might result - in an unwanted device being used. To avoid this, specify - `interface_name` in the connection appropriately.") + - __network_connections_result.stderr is search(__stderr_str) msg: The warning about specifying the route without the output device is not logged for initscripts provider - when: network_provider == "initscripts" + when: + - network_provider == "initscripts" + - __network_is_booted + - not __bootc_validation | d(false) + vars: + __stderr_str: >- + \[003\] .0, state.None persistent_state.present, '{{ interface1 }}'. + The connection {{ interface1 }} does not + specify an interface name. Therefore, the route to + 198.58.10.64/26 will be configured without the output device + and the kernel will choose it automatically which might result + in an unwanted device being used. To avoid this, specify + `interface_name` in the connection appropriately. - name: Assert that no warning is logged for nm provider assert: that: - __network_connections_result.stderr is not search("") msg: The warning is logged for nm provider - when: network_provider == "nm" + when: + - network_provider == "nm" + - __network_is_booted + - not __bootc_validation | d(false) always: - name: Remove test configuration tags: - "tests::cleanup" + when: ansible_connection != "buildah" block: - name: Bring down test devices and profiles include_role: @@ -169,15 +188,20 @@ state: down - name: Delete interface1 include_tasks: tasks/delete_interface.yml + vars: + lsr_interface: "{{ interface1 }}" - name: Assert interface1 is absent include_tasks: tasks/assert_device_absent.yml - - name: Set interface0 - set_fact: - interface: "{{ interface0 }}" + vars: + lsr_interface: "{{ interface1 }}" - name: Delete interface0 include_tasks: tasks/delete_interface.yml + vars: + lsr_interface: "{{ interface0 }}" - name: Assert interface0 is absent include_tasks: tasks/assert_device_absent.yml + vars: + lsr_interface: "{{ interface0 }}" - name: Assert interface0 profile and interface1 profile are absent include_tasks: tasks/assert_profile_absent.yml vars: diff --git a/tests/tasks/activate_profile.yml b/tests/tasks/activate_profile.yml index e3e75bdc3..80e377121 100644 --- a/tests/tasks/activate_profile.yml +++ b/tests/tasks/activate_profile.yml @@ -5,6 +5,6 @@ name: linux-system-roles.network vars: network_connections: - - name: "{{ interface }}" + - name: "{{ lsr_interface | d(interface) }}" state: up ... diff --git a/tests/tasks/assert_IPv4_present.yml b/tests/tasks/assert_IPv4_present.yml index 1c62c82ae..79518bb21 100644 --- a/tests/tasks/assert_IPv4_present.yml +++ b/tests/tasks/assert_IPv4_present.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause --- - name: "** TEST check IPv4" - command: ip -4 a s {{ interface | quote }} + command: ip -4 a s {{ lsr_interface | d(interface) | quote }} register: result until: address in result.stdout retries: 20 diff --git a/tests/tasks/assert_bond_options.yml b/tests/tasks/assert_bond_options.yml index 72d1ca73e..d60171e46 100644 --- a/tests/tasks/assert_bond_options.yml +++ b/tests/tasks/assert_bond_options.yml @@ -9,13 +9,18 @@ loop_control: loop_var: bond_opt changed_when: false + when: __network_is_booted | bool + - name: Include the task 'assert_IPv4_present.yml' include_tasks: assert_IPv4_present.yml vars: - interface: "{{ controller_device }}" + lsr_interface: "{{ controller_device }}" address: '192.0.2' + when: __network_is_booted | bool + - name: Include the task 'assert_IPv6_present.yml' include_tasks: assert_IPv6_present.yml vars: - interface: "{{ controller_device }}" + lsr_interface: "{{ controller_device }}" address: '2001' + when: __network_is_booted | bool diff --git a/tests/tasks/assert_command_output.yml b/tests/tasks/assert_command_output.yml new file mode 100644 index 000000000..2892e1863 --- /dev/null +++ b/tests/tasks/assert_command_output.yml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Inputs: +# lsr_command: The command to run with the "command" module +# command can be either a string or a list +# lsr_shell: The command to run with the "shell" module +# lsr_packages: Any packages that need to be installed first +# to provide the command, if any +# lsr_stdout: The string to look for in the stdout of the command +# lsr_stderr: The string to look for in the stderr of the command +# lsr_not_stdout: This string must not be present in the stdout of the command +# lsr_not_stderr: This string must not be present in the stderr of the command +# Output: +# Will raise an error if command fails +# Will raise an error if stdout/stderr do not match given strings +--- +- name: Ensure test packages are present + package: + name: "{{ lsr_packages }}" + state: present + use: "{{ (__network_is_ostree | d(false)) | + ternary('ansible.posix.rhel_rpm_ostree', omit) }}" + when: lsr_packages | d([]) | length > 0 + +- name: Run command and collect output + command: + cmd: "{{ lsr_command if lsr_command is string else omit }}" + argv: "{{ lsr_command if lsr_command is not string else omit }}" + when: lsr_command | d("") | length > 0 + changed_when: false + register: __cmd_output + +# noqa command-instead-of-shell +- name: Run command and collect output with shell + shell: "{{ lsr_shell }}" + when: lsr_shell | d("") | length > 0 + changed_when: false + register: __shell_output + +- name: Set command output + set_fact: + __command_output: "{{ __cmd_output if lsr_command | d('') | length > 0 else __shell_output }}" + +- name: Set previous command output for before/after comparisons + set_fact: + __previous_command_output: "{{ __command_output }}" + +- name: Assert lsr_stdout is in stdout + assert: + that: lsr_stdout in __command_output.stdout + when: lsr_stdout | d("") | length > 0 + +- name: Assert lsr_stdout_lines is in stdout + assert: + that: lsr_stdout_lines is subset(__command_output.stdout_lines) + when: lsr_stdout_lines | d([]) | length > 0 + +# assert that each regex in the list is in the stdout +- name: Assert lsr_stdout_regex_list is in stdout + assert: + that: __command_output.stdout is search(lsr_stdout_regex_list_item) + loop: "{{ lsr_stdout_regex_list | d([]) }}" + loop_control: + loop_var: lsr_stdout_regex_list_item + +- name: Assert lsr_not_stdout is not in stdout + assert: + that: lsr_not_stdout not in __command_output.stdout + when: lsr_not_stdout | d("") | length > 0 + +- name: Assert lsr_stderr is in stderr + assert: + that: lsr_stderr in __command_output.stderr + when: lsr_stderr | d("") | length > 0 + +- name: Assert lsr_not_stderr is not in stderr + assert: + that: lsr_not_stderr not in __command_output.stderr + when: lsr_not_stderr | d("") | length > 0 diff --git a/tests/tasks/assert_connection_settings.yml b/tests/tasks/assert_connection_settings.yml new file mode 100644 index 000000000..aaf304ff8 --- /dev/null +++ b/tests/tasks/assert_connection_settings.yml @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Inputs +# - lsr_connection_name: the connection name +# - network_provider: the network provider +# - lsr_connection_settings: the connection file settings for NM files +--- +- name: Assert NM connection settings + when: + - lsr_connection_name is defined + - lsr_connection_settings | d({}) | length > 0 + - network_provider == "nm" + - ansible_distribution_major_version | int >= 9 + vars: + __nm_connection_file_path: /etc/NetworkManager/system-connections/{{ lsr_connection_name }}.nmconnection + block: + - name: Show connection file + command: cat {{ __nm_connection_file_path | quote }} + changed_when: false + when: ansible_verbosity | int >= 2 + + - name: Stat connection file + stat: + path: "{{ __nm_connection_file_path }}" + register: __nm_connection_file_stat + + - name: Assert settings in NM connection file + community.general.ini_file: + path: "{{ __nm_connection_file_path }}" + section: "{{ __nm_connection_file_item.section }}" + option: "{{ __nm_connection_file_item.option }}" + value: "{{ __nm_connection_file_item.value }}" + state: present + ignore_spaces: true + owner: "{{ __nm_connection_file_stat.stat.pw_name }}" + group: "{{ __nm_connection_file_stat.stat.gr_name }}" + mode: "{{ __nm_connection_file_stat.stat.mode }}" + register: __nm_connection_file + # nm only values do not define value + loop: "{{ lsr_connection_settings | selectattr('value', 'defined') | list }}" + loop_control: + loop_var: __nm_connection_file_item + failed_when: __nm_connection_file is changed + + - name: Get nmcli connection settings + command: nmcli --fields all --terse connection show {{ lsr_connection_name | quote }} + register: __nm_connection_settings + changed_when: false + when: __network_is_booted + + - name: Assert nmcli connection settings + assert: + that: setting in __nm_connection_settings.stdout_lines + loop: "{{ nmitems + items }}" + loop_control: + loop_var: __nm_connection_item + when: __network_is_booted + vars: + nmitems: "{{ lsr_connection_settings | selectattr('nmvalue', 'defined') | selectattr('nmvalue') | list }}" + items: "{{ lsr_connection_settings | rejectattr('nmvalue', 'defined') | list }}" + section: "{{ __nm_connection_item.section }}" + option: "{{ 'addresses' if __nm_connection_item.option == 'address1' else __nm_connection_item.option }}" + value: "{{ __nm_connection_item.nmvalue + if __nm_connection_item.nmvalue is defined and __nm_connection_item.nmvalue + else __nm_connection_item.value }}" + setting: "{{ section ~ '.' ~ option ~ ':' ~ value }}" + +- name: Assert initscripts file settings + when: + - lsr_connection_name is defined + - lsr_connection_settings | d({}) | length > 0 + - network_provider == "initscripts" or ansible_distribution_major_version | int < 9 + vars: + __initscripts_connection_file_path: /etc/sysconfig/network-scripts/ifcfg-{{ lsr_connection_name }} + __initscripts_route_file_path: /etc/sysconfig/network-scripts/route-{{ lsr_connection_name }} + __initscripts_route6_file_path: /etc/sysconfig/network-scripts/route6-{{ lsr_connection_name }} + __check_route: "{{ lsr_connection_settings | selectattr('ifcfg_route_value', 'defined') | selectattr('ifcfg_route_value') | + map(attribute='ifcfg_route_value') | list | length > 0 }}" + __check_route6: "{{ lsr_connection_settings | selectattr('ifcfg_route6_value', 'defined') | selectattr('ifcfg_route6_value') | + map(attribute='ifcfg_route6_value') | list | length > 0 }}" + block: + - name: Show connection file + command: cat {{ __initscripts_connection_file_path | quote }} + changed_when: false + when: ansible_verbosity | int >= 2 + + - name: Slurp connection file + slurp: + src: "{{ __initscripts_connection_file_path }}" + register: __initscripts_connection_file + + - name: Assert settings in initscripts connection file + assert: + that: __initscripts_connection_item in __initscripts_connection_file.content | b64decode + loop: "{{ lsr_connection_settings | selectattr('ifcfg_value', 'defined') | selectattr('ifcfg_value') | + map(attribute='ifcfg_value') | list }}" + loop_control: + loop_var: __initscripts_connection_item + + - name: Slurp route file + when: __check_route + slurp: + src: "{{ __initscripts_route_file_path }}" + register: __initscripts_route_file + + - name: Assert settings in initscripts route file + when: __check_route + assert: + that: __initscripts_route_item in __initscripts_route_file.content | b64decode + loop: "{{ lsr_connection_settings | selectattr('ifcfg_route_value', 'defined') | selectattr('ifcfg_route_value') | + map(attribute='ifcfg_route_value') | list }}" + loop_control: + loop_var: __initscripts_route_item + + - name: Slurp route6 file + when: __check_route6 + slurp: + src: "{{ __initscripts_route6_file_path }}" + register: __initscripts_route6_file + + - name: Assert settings in initscripts route6 file + when: __check_route6 + assert: + that: __initscripts_route6_item in __initscripts_route6_file.content | b64decode + loop: "{{ lsr_connection_settings | selectattr('ifcfg_route6_value', 'defined') | selectattr('ifcfg_route6_value') | + map(attribute='ifcfg_route6_value') | list }}" + loop_control: + loop_var: __initscripts_route6_item diff --git a/tests/tasks/assert_controller_device_present.yml b/tests/tasks/assert_controller_device_present.yml index 7d387cd5a..a5b671d70 100644 --- a/tests/tasks/assert_controller_device_present.yml +++ b/tests/tasks/assert_controller_device_present.yml @@ -3,4 +3,4 @@ - name: Import the task 'assert_device_present.yml' import_tasks: tasks/assert_device_present.yml vars: - interface: "{{ controller_device }}" + lsr_interface: "{{ controller_device }}" diff --git a/tests/tasks/assert_device_absent.yml b/tests/tasks/assert_device_absent.yml index ce4634fe7..1799b06d6 100644 --- a/tests/tasks/assert_device_absent.yml +++ b/tests/tasks/assert_device_absent.yml @@ -2,7 +2,7 @@ --- - name: Include the task 'get_interface_stat.yml' include_tasks: get_interface_stat.yml -- name: "Assert that the interface is absent - '{{ interface }}'" +- name: "Assert that the interface is absent - '{{ lsr_interface | d(interface) }}'" assert: that: not interface_stat.stat.exists - msg: "{{ interface }} exists" + msg: "{{ lsr_interface | d(interface) }} exists" diff --git a/tests/tasks/assert_device_present.yml b/tests/tasks/assert_device_present.yml index 8a2f47cae..29ca2f75b 100644 --- a/tests/tasks/assert_device_present.yml +++ b/tests/tasks/assert_device_present.yml @@ -2,7 +2,9 @@ --- - name: Include the task 'get_interface_stat.yml' include_tasks: get_interface_stat.yml -- name: "Assert that the interface is present - '{{ interface }}'" + when: ansible_connection != 'buildah' +- name: "Assert that the interface is present - '{{ lsr_interface | d(interface) }}'" assert: that: interface_stat.stat.exists - msg: "{{ interface }} does not exist" + msg: "{{ lsr_interface | d(interface) }} does not exist" + when: ansible_connection != 'buildah' diff --git a/tests/tasks/assert_dhcp_device_present.yml b/tests/tasks/assert_dhcp_device_present.yml index 84fb5de6c..909744ba2 100644 --- a/tests/tasks/assert_dhcp_device_present.yml +++ b/tests/tasks/assert_dhcp_device_present.yml @@ -3,8 +3,8 @@ - name: Import the task 'assert_device_present.yml' import_tasks: tasks/assert_device_present.yml vars: - interface: "{{ dhcp_interface1 }}" + lsr_interface: "{{ dhcp_interface1 }}" - name: Import the task 'assert_device_present.yml' import_tasks: tasks/assert_device_present.yml vars: - interface: "{{ dhcp_interface2 }}" + lsr_interface: "{{ dhcp_interface2 }}" diff --git a/tests/tasks/assert_nm_connection_status.yml b/tests/tasks/assert_nm_connection_status.yml new file mode 100644 index 000000000..7fc80e240 --- /dev/null +++ b/tests/tasks/assert_nm_connection_status.yml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Inputs +# - lsr_connection_name: the connection name +# - lsr_connection_output: the output pattern to search for +# - network_provider: the network provider +--- +- name: Assert NM connection status + when: + - lsr_connection_name is defined + - lsr_connection_output is defined + - network_provider == "nm" + - __network_is_booted + block: + - name: Show connection status + command: nmcli connection show {{ lsr_connection_name | quote }} + changed_when: false + register: __lsr_nm_connection_status + + - name: Assert connection status + assert: + that: __lsr_nm_connection_status.stdout is search(lsr_connection_output) diff --git a/tests/tasks/check_network_dns.yml b/tests/tasks/check_network_dns.yml index 0bb3b2428..6f8e39af6 100644 --- a/tests/tasks/check_network_dns.yml +++ b/tests/tasks/check_network_dns.yml @@ -20,6 +20,7 @@ ls -alrtF /etc/resolv.* || : fi changed_when: false + when: __network_is_booted | d(true) | bool - name: Verify DNS and network connectivity shell: | @@ -35,5 +36,7 @@ exit 1 fi done - when: ansible_facts["distribution"] == "CentOS" changed_when: false + when: + - ansible_facts["distribution"] == "CentOS" + - __network_is_booted | d(true) | bool diff --git a/tests/tasks/cleanup_profile+device.yml b/tests/tasks/cleanup_profile+device.yml index 07021e01b..0e8d92bff 100644 --- a/tests/tasks/cleanup_profile+device.yml +++ b/tests/tasks/cleanup_profile+device.yml @@ -2,10 +2,10 @@ --- - name: Cleanup profile and device shell: | - nmcli con delete {{ interface }} - nmcli con load /etc/sysconfig/network-scripts/ifcfg-{{ interface }} - rm -f /etc/sysconfig/network-scripts/ifcfg-{{ interface }} - ip link del {{ interface }} + nmcli con delete {{ lsr_interface | d(interface) }} + nmcli con load /etc/sysconfig/network-scripts/ifcfg-{{ lsr_interface | d(interface) }} + rm -f /etc/sysconfig/network-scripts/ifcfg-{{ lsr_interface | d(interface) }} + ip link del {{ lsr_interface | d(interface) }} ignore_errors: true # noqa ignore-errors changed_when: false ... diff --git a/tests/tasks/cleanup_vlan_and_parent_profile+device.yml b/tests/tasks/cleanup_vlan_and_parent_profile+device.yml index be5bc9043..b0cf4d7d4 100644 --- a/tests/tasks/cleanup_vlan_and_parent_profile+device.yml +++ b/tests/tasks/cleanup_vlan_and_parent_profile+device.yml @@ -19,8 +19,8 @@ persistent_state: absent state: down failed_when: false - - name: Delete the device '{{ interface }}' - command: ip link del {{ interface }} + - name: Delete the device '{{ lsr_interface | d(interface) }}' + command: ip link del {{ lsr_interface | d(interface) }} failed_when: false changed_when: false ... diff --git a/tests/tasks/create_bond_port_match_by_mac.yml b/tests/tasks/create_bond_port_match_by_mac.yml index 5de8133e2..9decd2768 100644 --- a/tests/tasks/create_bond_port_match_by_mac.yml +++ b/tests/tasks/create_bond_port_match_by_mac.yml @@ -2,7 +2,7 @@ --- - name: Retrieve perm_hwaddr using ethtool # wokeignore:rule=slave - command: cat /sys/class/net/{{ interface }}/bonding_slave/perm_hwaddr + command: cat /sys/class/net/{{ lsr_interface | d(interface) }}/bonding_slave/perm_hwaddr register: mac_address_result changed_when: false failed_when: mac_address_result.rc != 0 @@ -11,7 +11,7 @@ mac: "{{ mac_address_result.stdout_lines[-1].split(' ')[-1] }}" - name: Display the retrieved MAC address debug: - msg: "Retrieved MAC address for {{ interface }}: {{ mac }}" + msg: "Retrieved MAC address for {{ lsr_interface | d(interface) }}: {{ mac }}" - name: Test matching the port device based on the perm_hwaddr import_role: name: linux-system-roles.network @@ -20,6 +20,6 @@ - name: "{{ profile }}" state: up type: ethernet - interface_name: "{{ interface }}" + interface_name: "{{ lsr_interface | d(interface) }}" mac: "{{ mac }}" ... diff --git a/tests/tasks/create_bond_profile.yml b/tests/tasks/create_bond_profile.yml deleted file mode 100644 index 3624ec0c2..000000000 --- a/tests/tasks/create_bond_profile.yml +++ /dev/null @@ -1,49 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause ---- -- name: Include network role - include_role: - name: linux-system-roles.network - vars: - network_connections: - # Create a bond controller - - name: "{{ controller_profile }}" - state: up - type: bond - interface_name: "{{ controller_device }}" - bond: - mode: 802.3ad - ad_actor_sys_prio: 65535 - ad_actor_system: 00:00:5e:00:53:5d - ad_select: stable - ad_user_port_key: 1023 - all_ports_active: true - downdelay: 0 - lacp_rate: slow - lp_interval: 128 - miimon: 110 - min_links: 0 - num_grat_arp: 64 - primary_reselect: better - resend_igmp: 225 - updelay: 0 - use_carrier: true - xmit_hash_policy: encap2+3 - ip: - route_metric4: 65535 - - # add an ethernet to the bond - - name: "{{ port1_profile }}" - state: up - type: ethernet - interface_name: "{{ dhcp_interface1 }}" - controller: "{{ controller_profile }}" - # add a second ethernet to the bond - - name: "{{ port2_profile }}" - state: up - type: ethernet - interface_name: "{{ dhcp_interface2 }}" - controller: "{{ controller_profile }}" -- name: Show result - debug: - var: __network_connections_result -... diff --git a/tests/tasks/create_bond_profile_reconfigure.yml b/tests/tasks/create_bond_profile_reconfigure.yml deleted file mode 100644 index 323c2d0ca..000000000 --- a/tests/tasks/create_bond_profile_reconfigure.yml +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause ---- -- name: Reconfigure the bond options - import_role: - name: linux-system-roles.network - vars: - network_connections: - # Create a bond controller - - name: "{{ controller_profile }}" - state: up - type: bond - interface_name: "{{ controller_device }}" - bond: - mode: active-backup - arp_interval: 60 - arp_ip_target: 192.0.2.128 - arp_validate: none - primary: "{{ dhcp_interface1 }}" - ip: - route_metric4: 65535 - # add an ethernet to the bond - - name: "{{ port1_profile }}" - state: up - type: ethernet - interface_name: "{{ dhcp_interface1 }}" - controller: "{{ controller_profile }}" - # add a second ethernet to the bond - - name: "{{ port2_profile }}" - state: up - type: ethernet - interface_name: "{{ dhcp_interface2 }}" - controller: "{{ controller_profile }}" -- name: Show result - debug: - var: __network_connections_result -... diff --git a/tests/tasks/create_bridge_profile.yml b/tests/tasks/create_bridge_profile.yml index 4eb22481b..621f9faab 100644 --- a/tests/tasks/create_bridge_profile.yml +++ b/tests/tasks/create_bridge_profile.yml @@ -5,7 +5,7 @@ name: linux-system-roles.network vars: network_connections: - - name: "{{ interface }}" + - name: "{{ lsr_interface | d(interface) }}" persistent_state: present type: bridge ip: diff --git a/tests/tasks/create_bridge_profile_no_autoconnect.yml b/tests/tasks/create_bridge_profile_no_autoconnect.yml index f93ec5dba..ceb6fd97b 100644 --- a/tests/tasks/create_bridge_profile_no_autoconnect.yml +++ b/tests/tasks/create_bridge_profile_no_autoconnect.yml @@ -5,7 +5,7 @@ name: linux-system-roles.network vars: network_connections: - - name: "{{ interface }}" + - name: "{{ lsr_interface | d(interface) }}" autoconnect: false persistent_state: present type: bridge diff --git a/tests/tasks/create_dummy_profile.yml b/tests/tasks/create_dummy_profile.yml index 1e65ac605..93ee6a0e9 100644 --- a/tests/tasks/create_dummy_profile.yml +++ b/tests/tasks/create_dummy_profile.yml @@ -5,7 +5,7 @@ name: linux-system-roles.network vars: network_connections: - - name: "{{ interface }}" + - name: "{{ lsr_interface | d(interface) }}" autoconnect_retries: "{{ autocon_retries }}" state: up type: dummy diff --git a/tests/tasks/create_mac_address_match.yml b/tests/tasks/create_mac_address_match.yml index 412127bb9..c11053796 100644 --- a/tests/tasks/create_mac_address_match.yml +++ b/tests/tasks/create_mac_address_match.yml @@ -5,12 +5,12 @@ name: linux-system-roles.network vars: network_connections: - - name: "{{ interface }}" + - name: "{{ lsr_interface | d(interface) }}" state: up persistent_state: present autoconnect: true type: ethernet - interface_name: "{{ interface }}" + interface_name: "{{ lsr_interface | d(interface) }}" mac: "{{ mac }}" ip: dhcp4: false @@ -20,7 +20,7 @@ state: up persistent_state: present type: vlan - parent: "{{ interface }}" + parent: "{{ lsr_interface | d(interface) }}" vlan: id: 3732 autoconnect: true @@ -35,7 +35,7 @@ state: up persistent_state: present type: vlan - parent: "{{ interface }}" + parent: "{{ lsr_interface | d(interface) }}" vlan: id: 120 autoconnect: true diff --git a/tests/tasks/create_team_profile.yml b/tests/tasks/create_team_profile.yml index 5d8ea72d8..d4c94683f 100644 --- a/tests/tasks/create_team_profile.yml +++ b/tests/tasks/create_team_profile.yml @@ -6,7 +6,7 @@ vars: network_allow_restart: true network_connections: - - name: "{{ interface }}" + - name: "{{ lsr_interface | d(interface) }}" persistent_state: present type: team ip: diff --git a/tests/tasks/create_test_interfaces_with_dhcp.yml b/tests/tasks/create_test_interfaces_with_dhcp.yml index 28d8eef0d..56b548c8e 100644 --- a/tests/tasks/create_test_interfaces_with_dhcp.yml +++ b/tests/tasks/create_test_interfaces_with_dhcp.yml @@ -143,3 +143,17 @@ --dhcp-host 52:54:00:12:34:56,ignore --dhcp-host 52:54:00:12:34:57,ignore fi changed_when: false + when: ansible_connection != "buildah" + +# When doing bootc validation, have to restart NetworkManager services +# to pick up newly created devices +- name: Restart NetworkManager services for testing + service: + name: "{{ item }}" + state: restarted + loop: + - NetworkManager.service + - NetworkManager-wait-online.service + when: + - network_provider == "nm" + - __bootc_validation | d(false) diff --git a/tests/tasks/create_wireless_profile_restart_network.yml b/tests/tasks/create_wireless_profile_restart_network.yml index 6e8505f6d..661c61e57 100644 --- a/tests/tasks/create_wireless_profile_restart_network.yml +++ b/tests/tasks/create_wireless_profile_restart_network.yml @@ -8,7 +8,7 @@ vars: network_allow_restart: '{{ wifi_restart_network }}' network_connections: - - name: "{{ interface }}" + - name: "{{ lsr_interface | d(interface) }}" type: wireless wireless: ssid: "My WPA2-PSK Network" diff --git a/tests/tasks/delete_interface.yml b/tests/tasks/delete_interface.yml index 228852e7a..47060ce89 100644 --- a/tests/tasks/delete_interface.yml +++ b/tests/tasks/delete_interface.yml @@ -1,7 +1,10 @@ # SPDX-License-Identifier: BSD-3-Clause --- - name: Remove test interface if necessary - command: "ip link del {{ interface }}" - ignore_errors: true # noqa ignore-errors - changed_when: false + command: ip link del {{ lsr_interface | d(interface) | quote }} + changed_when: true + register: __delete_interface_result + failed_when: + - __delete_interface_result.rc != 0 + - __delete_interface_result.stderr is not search("Cannot find device") ... diff --git a/tests/tasks/down_profile.yml b/tests/tasks/down_profile.yml index 25db269e2..f2a55bbc6 100644 --- a/tests/tasks/down_profile.yml +++ b/tests/tasks/down_profile.yml @@ -7,3 +7,4 @@ network_connections: - name: "{{ profile }}" state: down + persistent_state: "{{ lsr_persistent_state | d('present') }}" diff --git a/tests/tasks/find+remove_profile.yml b/tests/tasks/find+remove_profile.yml index fae7a7ac6..574f038af 100644 --- a/tests/tasks/find+remove_profile.yml +++ b/tests/tasks/find+remove_profile.yml @@ -1,15 +1,15 @@ # SPDX-License-Identifier: BSD-3-Clause --- -- name: Get connection profile for '{{ interface }}' +- name: Get connection profile for '{{ lsr_interface | d(interface) }}' shell: executable: /bin/bash cmd: | set -euo pipefail - connection="$(nmcli -g GENERAL.CONNECTION device show {{ interface | quote }})" || : + connection="$(nmcli -g GENERAL.CONNECTION device show {{ lsr_interface | d(interface) | quote }})" || : if [ -z "$connection" ]; then - nmcli device connect {{ interface | quote }} 1>&2 + nmcli device connect {{ lsr_interface | d(interface) | quote }} 1>&2 fi - nmcli -g GENERAL.CONNECTION device show {{ interface | quote }} + nmcli -g GENERAL.CONNECTION device show {{ lsr_interface | d(interface) | quote }} register: connection_name changed_when: false @@ -22,7 +22,7 @@ nmcli device status || : nmcli device show || : nmcli connection show || : - nmcli connection show '{{ interface }}' || : + nmcli connection show '{{ lsr_interface | d(interface) }}' || : ip a echo connection_name: {{ connection_name | to_nice_json | quote }} || : ls -alrtF /etc/sysconfig/network-scripts || : @@ -59,7 +59,7 @@ when: connection_name is failed or connection_name.stdout | length == 0 failed_when: connection_name is failed or connection_name.stdout | length == 0 -- name: Bring down and delete the connection profile for '{{ interface }}' +- name: Bring down and delete the connection profile for '{{ lsr_interface | d(interface) }}' include_role: name: linux-system-roles.network vars: diff --git a/tests/tasks/get_interface_stat.yml b/tests/tasks/get_interface_stat.yml index a8b8e5b91..3ec309ace 100644 --- a/tests/tasks/get_interface_stat.yml +++ b/tests/tasks/get_interface_stat.yml @@ -1,9 +1,9 @@ # SPDX-License-Identifier: BSD-3-Clause --- -- name: "Get stat for interface {{ interface }}" +- name: "Get stat for interface {{ lsr_interface | d(interface) }}" stat: get_attributes: false get_checksum: false get_mime: false - path: "/sys/class/net/{{ interface }}" + path: "/sys/class/net/{{ lsr_interface | d(interface) }}" register: interface_stat diff --git a/tests/tasks/get_profile_stat.yml b/tests/tasks/get_profile_stat.yml index f4f501e18..c609bec10 100644 --- a/tests/tasks/get_profile_stat.yml +++ b/tests/tasks/get_profile_stat.yml @@ -22,8 +22,15 @@ # When certain profile is marked as absent but still up, the `nmcli connection` # still show it with FILENAME starting with /run. Only consider profile exists # when its FILENAME is in /etc folder +# in the non-booted case, we can predict the profile path from nm_offline backend - name: Get NM profile info - shell: nmcli -f NAME,FILENAME connection show |grep {{ profile }} | grep /etc + vars: + nm_profile_cmd: >- + {{ "nmcli -f NAME,FILENAME connection show | grep " ~ profile ~ " | grep /etc" + if __network_is_booted + else "test -s /etc/NetworkManager/system-connections/" ~ profile ~ ".nmconnection" }} + # noqa command-instead-of-shell + shell: "{{ nm_profile_cmd }}" register: nm_profile_exists ignore_errors: true changed_when: false diff --git a/tests/tasks/handle_assert_task.yml b/tests/tasks/handle_assert_task.yml new file mode 100644 index 000000000..d262cd63c --- /dev/null +++ b/tests/tasks/handle_assert_task.yml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- name: Handle assert task file + include_tasks: "{{ lsr_assert_item }}" + when: lsr_assert_item is string + +- name: Handle assert task with condition and variables + include_tasks: "{{ lsr_assert_item['what'] }}" + when: + - lsr_assert_item is mapping + - lsr_assert_item.get("condition", true) + vars: + lsr_connection_name: "{{ lsr_assert_item.get('lsr_connection_name', '') }}" + lsr_connection_settings: "{{ lsr_assert_item.get('lsr_connection_settings', []) }}" + lsr_connection_output: "{{ lsr_assert_item.get('lsr_connection_output', '') }}" + lsr_packages: "{{ lsr_assert_item.get('lsr_packages', []) }}" + lsr_command: "{{ lsr_assert_item.get('lsr_command', '') }}" + lsr_shell: "{{ lsr_assert_item.get('lsr_shell', '') }}" + lsr_stdout: "{{ lsr_assert_item.get('lsr_stdout', '') }}" + lsr_stdout_lines: "{{ lsr_assert_item.get('lsr_stdout_lines', []) }}" + lsr_stdout_regex_list: "{{ lsr_assert_item.get('lsr_stdout_regex_list', []) }}" + lsr_stderr: "{{ lsr_assert_item.get('lsr_stderr', '') }}" + lsr_not_stdout: "{{ lsr_assert_item.get('lsr_not_stdout', '') }}" + lsr_not_stderr: "{{ lsr_assert_item.get('lsr_not_stderr', '') }}" + profile: "{{ lsr_assert_item.get('profile', '') }}" diff --git a/tests/tasks/handle_cleanup_task.yml b/tests/tasks/handle_cleanup_task.yml new file mode 100644 index 000000000..61354c6f1 --- /dev/null +++ b/tests/tasks/handle_cleanup_task.yml @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- name: Handle cleanup task file + include_tasks: "{{ lsr_cleanup_item }}" + when: lsr_cleanup_item is string + +- name: Handle cleanup task with condition and variables + include_tasks: "{{ lsr_cleanup_item['what'] }}" + when: + - lsr_cleanup_item is mapping + - lsr_cleanup_item.get("condition", true) + vars: + profile: "{{ lsr_cleanup_item.get('profile', interface | d('')) }}" + state: "{{ lsr_cleanup_item.get('state', 'present') }}" + lsr_persistent_state: "{{ lsr_cleanup_item.get('lsr_persistent_state', '') }}" diff --git a/tests/tasks/handle_setup_task.yml b/tests/tasks/handle_setup_task.yml new file mode 100644 index 000000000..a41fcff84 --- /dev/null +++ b/tests/tasks/handle_setup_task.yml @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- name: Handle setup task file + include_tasks: "{{ lsr_setup_item }}" + when: lsr_setup_item is string + +- name: Handle setup task with condition and variables + include_tasks: "{{ lsr_setup_item['what'] }}" + when: + - lsr_setup_item is mapping + - lsr_setup_item.get("condition", true) + vars: + state: "{{ lsr_setup_item.get('state', 'present') }}" + lsr_interface: "{{ lsr_setup_item.get('lsr_interface', interface | d('')) }}" + lsr_packages: "{{ lsr_setup_item.get('lsr_packages', []) }}" + lsr_command: "{{ lsr_setup_item.get('lsr_command', '') }}" + lsr_shell: "{{ lsr_setup_item.get('lsr_shell', '') }}" + lsr_stdout: "{{ lsr_setup_item.get('lsr_stdout', '') }}" + lsr_stdout_lines: "{{ lsr_setup_item.get('lsr_stdout_lines', []) }}" + lsr_stdout_regex_list: "{{ lsr_setup_item.get('lsr_stdout_regex_list', []) }}" + lsr_stderr: "{{ lsr_setup_item.get('lsr_stderr', '') }}" + lsr_not_stdout: "{{ lsr_setup_item.get('lsr_not_stdout', '') }}" + lsr_not_stderr: "{{ lsr_setup_item.get('lsr_not_stderr', '') }}" diff --git a/tests/tasks/handle_test_task.yml b/tests/tasks/handle_test_task.yml new file mode 100644 index 000000000..c2d246ba1 --- /dev/null +++ b/tests/tasks/handle_test_task.yml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- name: Handle test task file + include_tasks: "{{ lsr_test_item }}" + when: lsr_test_item is string + +- name: Handle test task with network role + when: + - lsr_test_item is mapping + - lsr_test_item.get("network_connections") is not none + - lsr_test_item.get("condition", true) + block: + - name: Include network role + include_role: + name: linux-system-roles.network + vars: + network_connections: "{{ lsr_test_item['network_connections'] }}" + + - name: Show result + debug: + var: __network_connections_result diff --git a/tests/tasks/manage_test_interface.yml b/tests/tasks/manage_test_interface.yml index fcf7f5281..b397a4c99 100644 --- a/tests/tasks/manage_test_interface.yml +++ b/tests/tasks/manage_test_interface.yml @@ -25,51 +25,73 @@ delay: 10 # veth -- name: Create veth interface {{ interface }} +- name: Create veth interface {{ lsr_interface | d(interface) }} command: "{{ item }}" with_items: - - ip link add {{ interface }} type veth peer name peer{{ interface }} - - ip link set peer{{ interface }} up - - ip link set {{ interface }} up - when: "type == 'veth' and state == 'present' and - interface not in current_interfaces" + - ip link add {{ lsr_interface | d(interface) }} type veth peer name peer{{ lsr_interface | d(interface) }} + - ip link set peer{{ lsr_interface | d(interface) }} up + - ip link set {{ lsr_interface | d(interface) }} up + when: + - ansible_connection != 'buildah' + - type == 'veth' + - state == 'present' + - lsr_interface | d(interface) not in current_interfaces changed_when: false + - name: Set up veth as managed by NetworkManager - command: nmcli d set {{ interface }} managed true + command: nmcli d set {{ lsr_interface | d(interface) }} managed true # The variable for `network_provider` is not exists yet, # just ignore error for initscripts ignore_errors: true # noqa ignore-errors - when: "type == 'veth' and state == 'present'" + when: + - ansible_connection != 'buildah' + - type == 'veth' + - state == 'present' changed_when: false -- name: Delete veth interface {{ interface }} - command: ip link del {{ interface }} type veth - when: "type == 'veth' and state == 'absent' and - interface in current_interfaces" +- name: Delete veth interface {{ lsr_interface | d(interface) }} + command: ip link del {{ lsr_interface | d(interface) }} type veth + when: + - ansible_connection != 'buildah' + - type == 'veth' + - state == 'absent' + - lsr_interface | d(interface) in current_interfaces changed_when: false # dummy -- name: Create dummy interface {{ interface }} - command: ip link add "{{ interface }}" type dummy - when: "type == 'dummy' and state == 'present' and - interface not in current_interfaces" +- name: Create dummy interface {{ lsr_interface | d(interface) }} + command: ip link add "{{ lsr_interface | d(interface) }}" type dummy + when: + - ansible_connection != 'buildah' + - type == 'dummy' + - state == 'present' + - lsr_interface | d(interface) not in current_interfaces changed_when: false -- name: Delete dummy interface {{ interface }} - command: ip link del "{{ interface }}" type dummy - when: "type == 'dummy' and state == 'absent' and - interface in current_interfaces" +- name: Delete dummy interface {{ lsr_interface | d(interface) }} + command: ip link del "{{ lsr_interface | d(interface) }}" type dummy + when: + - ansible_connection != 'buildah' + - type == 'dummy' + - state == 'absent' + - lsr_interface | d(interface) in current_interfaces changed_when: false # tap -- name: Create tap interface {{ interface }} - command: ip tuntap add dev {{ interface }} mode tap - when: "type == 'tap' and state == 'present' - and interface not in current_interfaces" +- name: Create tap interface {{ lsr_interface | d(interface) }} + command: ip tuntap add dev {{ lsr_interface | d(interface) }} mode tap + when: + - ansible_connection != 'buildah' + - type == 'tap' + - state == 'present' + - lsr_interface | d(interface) not in current_interfaces changed_when: false -- name: Delete tap interface {{ interface }} - command: ip tuntap del dev {{ interface }} mode tap - when: "type == 'tap' and state == 'absent' and - interface in current_interfaces" +- name: Delete tap interface {{ lsr_interface | d(interface) }} + command: ip tuntap del dev {{ lsr_interface | d(interface) }} mode tap + when: + - ansible_connection != 'buildah' + - type == 'tap' + - state == 'absent' + - lsr_interface | d(interface) in current_interfaces changed_when: false diff --git a/tests/tasks/remove+down_profile.yml b/tests/tasks/remove+down_profile.yml index 19cbfeea9..322768554 100644 --- a/tests/tasks/remove+down_profile.yml +++ b/tests/tasks/remove+down_profile.yml @@ -5,7 +5,7 @@ name: linux-system-roles.network vars: network_connections: - - name: "{{ interface }}" + - name: "{{ lsr_interface | d(interface) }}" persistent_state: absent state: down ... diff --git a/tests/tasks/remove_package.yml b/tests/tasks/remove_package.yml index 731348a54..429a1eae5 100644 --- a/tests/tasks/remove_package.yml +++ b/tests/tasks/remove_package.yml @@ -4,3 +4,4 @@ package: name: "{{ package }}" state: absent + when: not __network_is_ostree | d(false) diff --git a/tests/tasks/run_test.yml b/tests/tasks/run_test.yml index 61b669b9a..fd07282a1 100644 --- a/tests/tasks/run_test.yml +++ b/tests/tasks/run_test.yml @@ -6,9 +6,16 @@ debug: msg: "########## {{ lsr_description }} ##########" + - name: Include network role vars used by tests + include_role: + name: linux-system-roles.network + tasks_from: set_facts.yml + public: true + when: __network_is_booted is not defined + - name: Show item debug: - var: "{{ item }}" + msg: item {{ item | default("UNDEFINED") | to_nice_json }} loop: - lsr_description - lsr_setup @@ -22,27 +29,50 @@ include_tasks: tasks/show_interfaces.yml - name: Setup - include_tasks: "{{ item }}" + include_tasks: tasks/handle_setup_task.yml loop: "{{ lsr_setup }}" + loop_control: + loop_var: lsr_setup_item tags: - "tests::setup" - name: Test - include_tasks: "{{ item }}" + include_tasks: tasks/handle_test_task.yml loop: "{{ lsr_test }}" + loop_control: + loop_var: lsr_test_item + when: not __bootc_validation | default(false) tags: - "tests::test" - - name: Asserts - include_tasks: "{{ item }}" - loop: "{{ lsr_assert }}" - tags: - - "tests::assert" + # When validation is enabled, we need to load the role variables + # and set the result because we aren't running the role to set + # the network connections - that was already done at build time + - name: Handle case where validation is enabled + when: __bootc_validation | default(false) + block: + - name: Load role variables + include_role: + name: linux-system-roles.network + tasks_from: set_facts.yml + public: true - - name: Conditional asserts - include_tasks: "{{ item['what'] }}" - when: item['condition'] - loop: "{{ lsr_assert_when | default([]) }}" + - name: Set result + set_fact: + __network_connections_result: + failed: false + stderr: "" + stdout: "" + changed: true + stdout_lines: [] + stderr_lines: [] + + # You can use either lsr_assert or lsr_assert_when - but not both + - name: Asserts + include_tasks: tasks/handle_assert_task.yml + loop: "{{ (lsr_assert | default([])) + (lsr_assert_when | default([])) }}" + loop_control: + loop_var: lsr_assert_item - name: "Success in test '{{ lsr_description }}'" debug: @@ -55,7 +85,7 @@ - name: Show item that failed debug: - var: "{{ item }}" + var: "{{ item | d('UNDEFINED') }}" loop: "{{ lsr_fail_debug | default([]) }}" - name: Issue failed message @@ -63,9 +93,19 @@ msg: "!!!!! Failure in test '{{ lsr_description }}' !!!!!" always: + - name: Create QEMU deployment during bootc end-to-end test + delegate_to: localhost + command: "{{ lsr_scriptdir | quote }}/bootc-buildah-qcow.sh {{ ansible_host | quote }}" + changed_when: true + when: ansible_connection == "buildah" + + # skip cleanup if building with buildah - name: Cleanup - include_tasks: "{{ item }}" + include_tasks: tasks/handle_cleanup_task.yml loop: "{{ lsr_cleanup }}" + loop_control: + loop_var: lsr_cleanup_item + when: ansible_connection != "buildah" tags: - "tests::cleanup" ... diff --git a/tests/tasks/setup_802_1x_server.yml b/tests/tasks/setup_802_1x_server.yml index 0041a5a54..605aa3217 100644 --- a/tests/tasks/setup_802_1x_server.yml +++ b/tests/tasks/setup_802_1x_server.yml @@ -3,6 +3,7 @@ - name: Debug debug: msg: facts {{ ansible_facts | to_nice_json }} + when: ansible_verbosity | default(0) >= 3 # This task can be removed once the RHEL-8.5 is not tested anymore - name: Install hostapd via CentOS Stream @@ -66,6 +67,7 @@ # Enable forwarding of EAP 802.1x messages through software bridge "br1". echo 8 > /sys/class/net/br1/bridge/group_fwd_mask changed_when: false + when: __network_is_booted - name: Create hostapd config copy: @@ -98,3 +100,20 @@ - name: Run hostapd in namespace shell: ip netns exec ns1 hostapd -B /etc/hostapd/wired.conf && sleep 5 changed_when: false + when: __network_is_booted + +# Reload test connection - nm tries to start the connection at boot +# but fails because the devices are not created yet - so restart the connection +# here after the devices are created and hostapd is running +- name: Reload test connection + shell: + executable: /bin/bash + cmd: | + set -euo pipefail + exec 1>&2 + nmcli connection down {{ lsr_interface | d(interface) }} || : + nmcli connection up {{ lsr_interface | d(interface) }} + changed_when: false + when: + - network_provider == "nm" + - __bootc_validation | default(false) diff --git a/tests/tasks/show_interfaces.yml b/tests/tasks/show_interfaces.yml index 42874984d..f4f507ede 100644 --- a/tests/tasks/show_interfaces.yml +++ b/tests/tasks/show_interfaces.yml @@ -2,6 +2,8 @@ --- - name: Include the task 'get_current_interfaces.yml' include_tasks: get_current_interfaces.yml + when: ansible_connection != 'buildah' - name: Show current_interfaces debug: msg: "current_interfaces: {{ current_interfaces }}" + when: ansible_connection != 'buildah' diff --git a/tests/tasks/test_802.1x_capath.yml b/tests/tasks/test_802.1x_capath.yml index a8355282c..50e66a460 100644 --- a/tests/tasks/test_802.1x_capath.yml +++ b/tests/tasks/test_802.1x_capath.yml @@ -55,7 +55,7 @@ name: linux-system-roles.network vars: network_connections: - - name: "{{ interface | default('802-1x-test') }}" + - name: "{{ lsr_interface | d(interface | d('802-1x-test')) }}" interface_name: veth2 state: up type: ethernet