feat: Support wait_ip property

Add support for the `wait_ip` property, the system will consider
connection activated only when specific IP stack is configured.
This enables flexibility in scenarios such as
IPv6-only networks, where the overall network configuration can still
succeed when IPv4 configuration fails but IPv6 completes successfully.

The `wait_ip` can be configured with the following possible values:
  * "any": System will consider interface activated when any IP stack is
           configured.
  * "ipv4": System will wait IPv4 been configured.
  * "ipv6": System will wait IPv6 been configured.
  * "ipv4+ipv6": System will wait both IPv4 and IPv6 been configured.

Resolves: https://issues.redhat.com/browse/RHEL-63026

Signed-off-by: Wen Liang <wenliang@redhat.com>
This commit is contained in:
Wen Liang 2024-10-15 21:03:57 -04:00 committed by liangwen12year
parent 38a61f76e9
commit d92baacf1f
8 changed files with 128 additions and 0 deletions

View file

@ -591,6 +591,16 @@ The IP configuration supports the following options:
The default gateway for IPv4 (`gateway4`) or IPv6 (`gateway6`) packets. The default gateway for IPv4 (`gateway4`) or IPv6 (`gateway6`) packets.
- `wait_ip`
The property controls whether the system should wait for a specific IP stack to be
configured before considering the connection activated. It can be set to "any",
"ipv4","ipv6," or "ipv4+ipv6". When set to "any," the system considers the connection
activated when any IP stack is configured. "ipv4" ensures the system waits for IPv4
configuration, while "ipv6" ensures the system waits for IPv6 configuration. The
"ipv4+ipv6" option requires both IPv4 and IPv6 to be configured before the connection
is considered activated.
- `ipv4_ignore_auto_dns` and `ipv6_ignore_auto_dns` - `ipv4_ignore_auto_dns` and `ipv6_ignore_auto_dns`
If enabled, the automatically configured name servers and search domains (via If enabled, the automatically configured name servers and search domains (via

View file

@ -0,0 +1,18 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Configuring wait_ip on ethernet connection
hosts: all
vars:
network_connections:
- name: eth0
state: up
type: ethernet
interface_name: eth0
ip:
address:
- 192.0.2.42/30
- 2001:db8::23/64
wait_ip: any
roles:
- linux-system-roles.network

View file

@ -1205,6 +1205,22 @@ class NMUtil:
) )
if ip["gateway6"] is not None: if ip["gateway6"] is not None:
s_ip6.set_property(NM.SETTING_IP_CONFIG_GATEWAY, ip["gateway6"]) s_ip6.set_property(NM.SETTING_IP_CONFIG_GATEWAY, ip["gateway6"])
# Mapping of wait_ip values to the may-fail settings for IPv4 and IPv6
may_fail_mapping = {
"any": (True, True),
"ipv4": (False, True),
"ipv6": (True, False),
"ipv4+ipv6": (False, False),
}
may_fail_ipv4, may_fail_ipv6 = may_fail_mapping.get(
ip["wait_ip"], (True, True)
)
s_ip4.set_property(NM.SETTING_IP_CONFIG_MAY_FAIL, may_fail_ipv4)
s_ip6.set_property(NM.SETTING_IP_CONFIG_MAY_FAIL, may_fail_ipv6)
if ip["route_metric6"] is not None and ip["route_metric6"] >= 0: if ip["route_metric6"] is not None and ip["route_metric6"] >= 0:
s_ip6.set_property( s_ip6.set_property(
NM.SETTING_IP_CONFIG_ROUTE_METRIC, ip["route_metric6"] NM.SETTING_IP_CONFIG_ROUTE_METRIC, ip["route_metric6"]

View file

@ -907,6 +907,11 @@ class ArgValidator_DictIP(ArgValidatorDict):
ArgValidatorBool("auto6", default_value=None), ArgValidatorBool("auto6", default_value=None),
ArgValidatorBool("ipv4_ignore_auto_dns", default_value=None), ArgValidatorBool("ipv4_ignore_auto_dns", default_value=None),
ArgValidatorBool("ipv6_ignore_auto_dns", default_value=None), ArgValidatorBool("ipv6_ignore_auto_dns", default_value=None),
ArgValidatorStr(
"wait_ip",
enum_values=["any", "ipv4", "ipv6", "ipv4+ipv6"],
default_value="any",
),
ArgValidatorBool("ipv6_disabled", default_value=None), ArgValidatorBool("ipv6_disabled", default_value=None),
ArgValidatorIP("gateway6", family=socket.AF_INET6), ArgValidatorIP("gateway6", family=socket.AF_INET6),
ArgValidatorNum( ArgValidatorNum(
@ -960,6 +965,7 @@ class ArgValidator_DictIP(ArgValidatorDict):
"auto6": True, "auto6": True,
"ipv4_ignore_auto_dns": None, "ipv4_ignore_auto_dns": None,
"ipv6_ignore_auto_dns": None, "ipv6_ignore_auto_dns": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"gateway6": None, "gateway6": None,
"route_metric6": None, "route_metric6": None,
@ -2522,6 +2528,14 @@ class ArgValidator_ListConnections(ArgValidatorList):
"ip.ipv4_ignore_auto_dns or ip.ipv6_ignore_auto_dns is not " "ip.ipv4_ignore_auto_dns or ip.ipv6_ignore_auto_dns is not "
"supported by initscripts.", "supported by initscripts.",
) )
# initscripts does not support ip.wait_ip,
# so raise errors when network provider is initscripts
if connection["ip"]["wait_ip"] != "any":
if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS:
raise ValidationError.from_connection(
idx,
"ip.wait_ip is not supported by initscripts.",
)
# initscripts does not support ip.dns_options, so raise errors when network # initscripts does not support ip.dns_options, so raise errors when network
# provider is initscripts # provider is initscripts
if connection["ip"]["dns_options"]: if connection["ip"]["dns_options"]:

View file

@ -5,6 +5,7 @@
vars: vars:
autocon_retries: 2 autocon_retries: 2
interface: dummy0 interface: dummy0
wait_ip: ipv4+ipv6
profile: "{{ interface }}" profile: "{{ interface }}"
lsr_fail_debug: lsr_fail_debug:
- __network_connections_result - __network_connections_result
@ -32,6 +33,7 @@
- tasks/assert_profile_present.yml - tasks/assert_profile_present.yml
- tasks/assert_device_present.yml - tasks/assert_device_present.yml
- tasks/assert_autoconnect_retries.yml - tasks/assert_autoconnect_retries.yml
- tasks/assert_may_fail.yml
lsr_cleanup: lsr_cleanup:
- tasks/cleanup_profile+device.yml - tasks/cleanup_profile+device.yml
- tasks/check_network_dns.yml - tasks/check_network_dns.yml

View file

@ -0,0 +1,30 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Get ipv4.may-fail
command: >
nmcli -f ipv4.may-fail connection show {{ profile }}
register: may_fail4
ignore_errors: true
changed_when: false
- name: Get ipv6.may-fail
command: >
nmcli -f ipv6.may-fail connection show {{ profile }}
register: may_fail6
ignore_errors: true
changed_when: false
- name: "Assert that ipv4.may-fail is configured as specified"
assert:
that:
- may_fail4.stdout.split(":")[1] | trim
== "no"
msg: "ipv4.may-fail is configured as
{{ may_fail4.stdout.split(':')[1] | trim }}
but wait_ip is specified as {{ wait_ip }}"
- name: "Assert that ipv6.may-fail is configured as specified"
assert:
that:
- may_fail6.stdout.split(":")[1] | trim
== "no"
msg: "ipv6.may-fail is configured as
{{ may_fail6.stdout.split(':')[1] | trim }}
but wait_ip is specified as {{ wait_ip }}"

View file

@ -12,6 +12,8 @@
ip: ip:
address: address:
- "192.0.2.42/30" - "192.0.2.42/30"
- "2001:db8::23/64"
wait_ip: "{{ wait_ip }}"
- name: Show result - name: Show result
debug: debug:
var: __network_connections_result var: __network_connections_result

View file

@ -185,6 +185,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -601,6 +602,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -663,6 +665,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -719,6 +722,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -820,6 +824,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
"dns": [], "dns": [],
@ -892,6 +897,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
"dns": [{"address": "192.168.174.1", "family": socket.AF_INET}], "dns": [{"address": "192.168.174.1", "family": socket.AF_INET}],
@ -956,6 +962,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": "2001:db8::1", "gateway6": "2001:db8::1",
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": False, "auto6": False,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -1093,6 +1100,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"dns": [], "dns": [],
}, },
"mac": "52:54:00:44:9f:ba", "mac": "52:54:00:44:9f:ba",
@ -1131,6 +1139,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"auto6": False, "auto6": False,
"ipv6_disabled": False, "ipv6_disabled": False,
"dns": [], "dns": [],
@ -1235,6 +1244,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
"dns": [], "dns": [],
@ -1317,6 +1327,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
"dns": [], "dns": [],
@ -1438,6 +1449,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"dns": [], "dns": [],
}, },
@ -1477,6 +1489,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"auto6": False, "auto6": False,
"dns": [], "dns": [],
@ -1598,6 +1611,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"dns": [], "dns": [],
}, },
"mac": "33:24:10:24:2f:b9", "mac": "33:24:10:24:2f:b9",
@ -1636,6 +1650,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"auto6": False, "auto6": False,
"dns": [], "dns": [],
"address": [ "address": [
@ -1700,6 +1715,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"auto6": False, "auto6": False,
"dns": [], "dns": [],
@ -1818,6 +1834,7 @@ class TestValidator(Python26CompatTestCase):
"dns_search": [], "dns_search": [],
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"route": [], "route": [],
"route_append_only": False, "route_append_only": False,
@ -1866,6 +1883,7 @@ class TestValidator(Python26CompatTestCase):
"dns_search": [], "dns_search": [],
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"route": [], "route": [],
"route_append_only": False, "route_append_only": False,
@ -1961,6 +1979,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
"dns": [], "dns": [],
@ -2045,6 +2064,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
"dns": [], "dns": [],
@ -2117,6 +2137,7 @@ class TestValidator(Python26CompatTestCase):
"dhcp4_send_hostname": None, "dhcp4_send_hostname": None,
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"route_metric6": None, "route_metric6": None,
"dns": [], "dns": [],
@ -2160,6 +2181,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -2255,6 +2277,7 @@ class TestValidator(Python26CompatTestCase):
"dns_search": [], "dns_search": [],
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"route": [], "route": [],
"route_append_only": False, "route_append_only": False,
"route_metric4": None, "route_metric4": None,
@ -2302,6 +2325,7 @@ class TestValidator(Python26CompatTestCase):
"dns_search": [], "dns_search": [],
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"route": [], "route": [],
"route_append_only": False, "route_append_only": False,
@ -2366,6 +2390,7 @@ class TestValidator(Python26CompatTestCase):
"dns_search": [], "dns_search": [],
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"route": [], "route": [],
"route_append_only": False, "route_append_only": False,
@ -2447,6 +2472,7 @@ class TestValidator(Python26CompatTestCase):
"dns_search": [], "dns_search": [],
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"route": [], "route": [],
"route_append_only": False, "route_append_only": False,
@ -2534,6 +2560,7 @@ class TestValidator(Python26CompatTestCase):
"dns_search": [], "dns_search": [],
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"route": [], "route": [],
"route_append_only": False, "route_append_only": False,
@ -2581,6 +2608,7 @@ class TestValidator(Python26CompatTestCase):
"dns_search": [], "dns_search": [],
"gateway4": None, "gateway4": None,
"gateway6": None, "gateway6": None,
"wait_ip": "any",
"ipv6_disabled": False, "ipv6_disabled": False,
"route": [], "route": [],
"route_append_only": False, "route_append_only": False,
@ -2682,6 +2710,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -2791,6 +2820,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -2951,6 +2981,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -3112,6 +3143,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -3197,6 +3229,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -3282,6 +3315,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -3365,6 +3399,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,
@ -3438,6 +3473,7 @@ class TestValidator(Python26CompatTestCase):
"ip": { "ip": {
"gateway6": None, "gateway6": None,
"gateway4": None, "gateway4": None,
"wait_ip": "any",
"route_metric4": None, "route_metric4": None,
"auto6": True, "auto6": True,
"ipv6_disabled": False, "ipv6_disabled": False,