diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c index 43ac10704b..f481149d81 100644 --- a/src/network/wait-online/manager.c +++ b/src/network/wait-online/manager.c @@ -61,9 +61,11 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange assert(l); /* This returns the following: - * -EAGAIN: not processed by udev or networkd - * 0: operstate is not enough - * 1: online */ + * -EAGAIN : not processed by udev + * -EBUSY : being processed by networkd + * -EADDRNOTAVAIL: requested conditions (operstate and/or addresses) are not satisfied + * false : unmanaged + * true : online */ if (!l->state || streq(l->state, "pending")) /* If no state string exists, networkd (and possibly also udevd) has not detected the @@ -79,13 +81,13 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange /* If the link is in unmanaged state, then ignore the interface unless the interface is * specified in '--interface/-i' option. */ if (!link_in_command_line_interfaces(l, m)) { - log_link_debug(l, "link is not managed by networkd (yet?)."); - return 0; + log_link_debug(l, "link is not managed by networkd."); + return false; } } else if (!streq(l->state, "configured")) /* If the link is in non-configured state, return negative value here. */ - return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN), + return log_link_debug_errno(l, SYNTHETIC_ERRNO(EBUSY), "link is being processed by networkd: setup state is %s.", l->state); @@ -97,84 +99,75 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s.max = m->required_operstate.max >= 0 ? m->required_operstate.max : l->required_operstate.max; - if (l->operational_state < s.min || l->operational_state > s.max) { - log_link_debug(l, "Operational state '%s' is not in range ['%s':'%s']", - link_operstate_to_string(l->operational_state), - link_operstate_to_string(s.min), link_operstate_to_string(s.max)); - return 0; - } + if (l->operational_state < s.min || l->operational_state > s.max) + return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL), + "Operational state '%s' is not in range ['%s':'%s']", + link_operstate_to_string(l->operational_state), + link_operstate_to_string(s.min), link_operstate_to_string(s.max)); required_family = m->required_family > 0 ? m->required_family : l->required_family; needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4; needs_ipv6 = required_family & ADDRESS_FAMILY_IPV6; if (s.min < LINK_OPERSTATE_ROUTABLE) { - if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED) { - log_link_debug(l, "No routable or link-local IPv4 address is configured."); - return 0; - } + if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED) + return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL), + "No routable or link-local IPv4 address is configured."); - if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_DEGRADED) { - log_link_debug(l, "No routable or link-local IPv6 address is configured."); - return 0; - } + if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_DEGRADED) + return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL), + "No routable or link-local IPv6 address is configured."); } else { - if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_ROUTABLE) { - log_link_debug(l, "No routable IPv4 address is configured."); - return 0; - } + if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_ROUTABLE) + return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL), + "No routable IPv4 address is configured."); - if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_ROUTABLE) { - log_link_debug(l, "No routable IPv6 address is configured."); - return 0; - } + if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_ROUTABLE) + return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL), + "No routable IPv6 address is configured."); } log_link_debug(l, "link is configured by networkd and online."); - return 1; + return true; } bool manager_configured(Manager *m) { - bool one_ready = false; - const char *ifname; Link *l; int r; if (!hashmap_isempty(m->command_line_interfaces_by_name)) { LinkOperationalStateRange *range; + const char *ifname; /* wait for all the links given on the command line to appear */ HASHMAP_FOREACH_KEY(range, ifname, m->command_line_interfaces_by_name) { l = hashmap_get(m->links_by_name, ifname); - if (!l && range->min == LINK_OPERSTATE_MISSING) { - one_ready = true; - continue; - } - if (!l) { - log_debug("still waiting for %s", ifname); - if (!m->any) - return false; + if (range->min == LINK_OPERSTATE_MISSING) { + if (m->any) + return true; + } else { + log_debug("still waiting for %s", ifname); + if (!m->any) + return false; + } continue; } - if (manager_link_is_online(m, l, *range) <= 0) { - if (!m->any) - return false; - continue; - } - - one_ready = true; + r = manager_link_is_online(m, l, *range); + if (r <= 0 && !m->any) + return false; + if (r > 0 && m->any) + return true; } - /* all interfaces given by the command line are online, or - * one of the specified interfaces is online. */ - return one_ready; + /* With '--any' : no interface is ready + * Without '--any': all interfaces are ready */ + return !m->any; } - /* wait for all links networkd manages to be in admin state 'configured' - * and at least one link to gain a carrier */ + /* wait for all links networkd manages */ HASHMAP_FOREACH(l, m->links_by_index) { if (manager_ignore_link(m, l)) { log_link_debug(l, "link is ignored"); @@ -184,15 +177,15 @@ bool manager_configured(Manager *m) { r = manager_link_is_online(m, l, (LinkOperationalStateRange) { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID }); - if (r < 0 && !m->any) + if (r < 0 && !m->any) /* Unlike the above loop, unmanaged interfaces are ignored here. */ return false; - if (r > 0) - /* we wait for at least one link to be ready, - * regardless of who manages it */ - one_ready = true; + if (r > 0 && m->any) + return true; } - return one_ready; + /* With '--any' : no interface is ready + * Without '--any': all interfaces are ready or unmanaged */ + return !m->any; } static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index dec1d085b9..12c7a1391e 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -854,7 +854,7 @@ class Utilities(): This returns if the links reached the requested operstate/setup_state; otherwise it raises CalledProcessError or fails test assertion. """ - args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate] + args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate] + [f'--ignore={link}' for link in protected_links] if bool_any: args += ['--any'] if ipv4: @@ -1136,6 +1136,36 @@ class NetworkdMatchTests(unittest.TestCase, Utilities): output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98-2', env=env) self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output) + def test_match_udev_property(self): + copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network') + start_networkd() + self.wait_online(['dummy98:routable']) + + output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env) + print(output) + self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property') + +class WaitOnlineTests(unittest.TestCase, Utilities): + + def setUp(self): + setup_common() + + def tearDown(self): + tear_down_common() + + def test_wait_online_all_unmanaged(self): + start_networkd() + self.wait_online([]) + + def test_wait_online_any(self): + copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network') + start_networkd() + + self.wait_online(['bridge99', 'test1:degraded'], bool_any=True) + + self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring') + self.wait_operstate('test1', 'degraded') + class NetworkdNetDevTests(unittest.TestCase, Utilities): def setUp(self): @@ -1154,24 +1184,6 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '00:50:56:c0:00:28') - def test_match_udev_property(self): - copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network') - start_networkd() - self.wait_online(['dummy98:routable']) - - output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env) - print(output) - self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property') - - def test_wait_online_any(self): - copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network') - start_networkd() - - self.wait_online(['bridge99', 'test1:degraded'], bool_any=True) - - self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring') - self.wait_operstate('test1', 'degraded') - @expectedFailureIfModuleIsNotAvailable('bareudp') def test_bareudp(self): copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')