mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
Merge pull request #12160 from yuwata/wait-online-allow-configuring
wait-online: add --any option
This commit is contained in:
commit
dd09a9ec0f
@ -35,8 +35,9 @@
|
|||||||
configured. By default, it will wait for all links it is aware of
|
configured. By default, it will wait for all links it is aware of
|
||||||
and which are managed by
|
and which are managed by
|
||||||
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||||
to be fully configured or failed, and for at least one link to
|
to be fully configured or failed, and for at least one link to be online. Here, online means that
|
||||||
gain a carrier.</para>
|
the link's operational state is equal or higher than <literal>degraded</literal>. The threshold
|
||||||
|
can be configured by <option>--operational-state=</option> option.</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
@ -49,13 +50,12 @@
|
|||||||
<term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
|
<term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
|
||||||
<term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
|
<term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
|
||||||
|
|
||||||
<listitem><para>Network interface to wait for before deciding
|
<listitem><para>Network interface to wait for before deciding if the system is online. This
|
||||||
if the system is online. This is useful when a system has
|
is useful when a system has several interfaces which will be configured, but a particular
|
||||||
several interfaces which will be configured, but a particular
|
one is necessary to access some network resources. When used, all other interfaces are ignored.
|
||||||
one is necessary to access some network resources. This option
|
This option may be used more than once to wait for multiple network interfaces. When this
|
||||||
may be used more than once to wait for multiple network
|
option is specified multiple times, then <command>systemd-networkd-wait-online</command> waits
|
||||||
interfaces. When used, all other interfaces are ignored.
|
for all specified interfaces to be online. Optinally, required minimum operational state can be
|
||||||
Optinally, required minimum operational state can be
|
|
||||||
specified after a colon <literal>:</literal>. Please see
|
specified after a colon <literal>:</literal>. Please see
|
||||||
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
for possible operational states. If the operational state is not specified here, then
|
for possible operational states. If the operational state is not specified here, then
|
||||||
@ -81,7 +81,18 @@
|
|||||||
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
for possible operational states. If set, the specified value overrides
|
for possible operational states. If set, the specified value overrides
|
||||||
<varname>RequiredForOnline=</varname> settings in <filename>.network</filename> files.
|
<varname>RequiredForOnline=</varname> settings in <filename>.network</filename> files.
|
||||||
But this does not override operational states specified in <option>--interface</option> option.
|
But this does not override operational states specified in <option>--interface=</option> option.
|
||||||
|
</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--any</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Even if several interfaces are in configuring state,
|
||||||
|
<command>systemd-networkd-wait-online</command> exits with success when at least one interface
|
||||||
|
becomes online. When this option is specified with <option>--interface=</option>, then
|
||||||
|
<command>systemd-networkd-wait-online</command> waits for one of the specified interfaces to be
|
||||||
|
online. This option is useful when some interfaces may not have carrier on boot.
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool manager_all_configured(Manager *m) {
|
bool manager_configured(Manager *m) {
|
||||||
bool one_ready = false;
|
bool one_ready = false;
|
||||||
Iterator i;
|
Iterator i;
|
||||||
const char *ifname;
|
const char *ifname;
|
||||||
@ -67,24 +67,33 @@ bool manager_all_configured(Manager *m) {
|
|||||||
Link *l;
|
Link *l;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* wait for all the links given on the command line to appear */
|
if (!hashmap_isempty(m->interfaces)) {
|
||||||
HASHMAP_FOREACH_KEY(p, ifname, m->interfaces, i) {
|
/* wait for all the links given on the command line to appear */
|
||||||
LinkOperationalState s = PTR_TO_INT(p);
|
HASHMAP_FOREACH_KEY(p, ifname, m->interfaces, i) {
|
||||||
|
LinkOperationalState s = PTR_TO_INT(p);
|
||||||
|
|
||||||
l = hashmap_get(m->links_by_name, ifname);
|
l = hashmap_get(m->links_by_name, ifname);
|
||||||
if (!l) {
|
if (!l) {
|
||||||
log_debug("still waiting for %s", ifname);
|
log_debug("still waiting for %s", ifname);
|
||||||
return false;
|
if (!m->any)
|
||||||
|
return false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manager_link_is_online(m, l, s) <= 0) {
|
||||||
|
if (!m->any)
|
||||||
|
return false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
one_ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manager_link_is_online(m, l, s) <= 0)
|
/* all interfaces given by the command line are online, or
|
||||||
return false;
|
* one of the specified interfaces is online. */
|
||||||
|
return one_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hashmap_isempty(m->interfaces))
|
|
||||||
/* all interfaces given by the command line are online. */
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* wait for all links networkd manages to be in admin state 'configured'
|
/* wait for all links networkd manages to be in admin state 'configured'
|
||||||
* and at least one link to gain a carrier */
|
* and at least one link to gain a carrier */
|
||||||
HASHMAP_FOREACH(l, m->links, i) {
|
HASHMAP_FOREACH(l, m->links, i) {
|
||||||
@ -94,7 +103,7 @@ bool manager_all_configured(Manager *m) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r = manager_link_is_online(m, l, _LINK_OPERSTATE_INVALID);
|
r = manager_link_is_online(m, l, _LINK_OPERSTATE_INVALID);
|
||||||
if (r < 0)
|
if (r < 0 && !m->any)
|
||||||
return false;
|
return false;
|
||||||
if (r > 0)
|
if (r > 0)
|
||||||
/* we wait for at least one link to be ready,
|
/* we wait for at least one link to be ready,
|
||||||
@ -180,7 +189,7 @@ static int on_rtnl_event(sd_netlink *rtnl, sd_netlink_message *mm, void *userdat
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (manager_all_configured(m))
|
if (manager_configured(m))
|
||||||
sd_event_exit(m->event, 0);
|
sd_event_exit(m->event, 0);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -248,7 +257,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
|
|||||||
log_link_warning_errno(l, r, "Failed to update monitor information: %m");
|
log_link_warning_errno(l, r, "Failed to update monitor information: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manager_all_configured(m))
|
if (manager_configured(m))
|
||||||
sd_event_exit(m->event, 0);
|
sd_event_exit(m->event, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -280,7 +289,8 @@ static int manager_network_monitor_listen(Manager *m) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
|
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
|
||||||
LinkOperationalState required_operstate, usec_t timeout) {
|
LinkOperationalState required_operstate,
|
||||||
|
bool any, usec_t timeout) {
|
||||||
_cleanup_(manager_freep) Manager *m = NULL;
|
_cleanup_(manager_freep) Manager *m = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -294,6 +304,7 @@ int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
|
|||||||
.interfaces = interfaces,
|
.interfaces = interfaces,
|
||||||
.ignore = ignore,
|
.ignore = ignore,
|
||||||
.required_operstate = required_operstate,
|
.required_operstate = required_operstate,
|
||||||
|
.any = any,
|
||||||
};
|
};
|
||||||
|
|
||||||
r = sd_event_default(&m->event);
|
r = sd_event_default(&m->event);
|
||||||
|
@ -21,6 +21,7 @@ struct Manager {
|
|||||||
char **ignore;
|
char **ignore;
|
||||||
|
|
||||||
LinkOperationalState required_operstate;
|
LinkOperationalState required_operstate;
|
||||||
|
bool any;
|
||||||
|
|
||||||
sd_netlink *rtnl;
|
sd_netlink *rtnl;
|
||||||
sd_event_source *rtnl_event_source;
|
sd_event_source *rtnl_event_source;
|
||||||
@ -33,8 +34,9 @@ struct Manager {
|
|||||||
|
|
||||||
void manager_free(Manager *m);
|
void manager_free(Manager *m);
|
||||||
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
|
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
|
||||||
LinkOperationalState required_operstate, usec_t timeout);
|
LinkOperationalState required_operstate,
|
||||||
|
bool any, usec_t timeout);
|
||||||
|
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||||
|
|
||||||
bool manager_all_configured(Manager *m);
|
bool manager_configured(Manager *m);
|
||||||
|
@ -19,6 +19,7 @@ static usec_t arg_timeout = 120 * USEC_PER_SEC;
|
|||||||
static Hashmap *arg_interfaces = NULL;
|
static Hashmap *arg_interfaces = NULL;
|
||||||
static char **arg_ignore = NULL;
|
static char **arg_ignore = NULL;
|
||||||
static LinkOperationalState arg_required_operstate = _LINK_OPERSTATE_INVALID;
|
static LinkOperationalState arg_required_operstate = _LINK_OPERSTATE_INVALID;
|
||||||
|
static bool arg_any = false;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_keyp);
|
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_keyp);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
|
||||||
@ -41,6 +42,7 @@ static int help(void) {
|
|||||||
" --ignore=INTERFACE Don't take these interfaces into account\n"
|
" --ignore=INTERFACE Don't take these interfaces into account\n"
|
||||||
" -o --operational-state=OPERSTATE\n"
|
" -o --operational-state=OPERSTATE\n"
|
||||||
" Required operational state\n"
|
" Required operational state\n"
|
||||||
|
" --any Wait until at least one of the interfaces is online\n"
|
||||||
" --timeout=SECS Maximum time to wait for network connectivity\n"
|
" --timeout=SECS Maximum time to wait for network connectivity\n"
|
||||||
"\nSee the %s for details.\n"
|
"\nSee the %s for details.\n"
|
||||||
, program_invocation_short_name
|
, program_invocation_short_name
|
||||||
@ -101,6 +103,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
enum {
|
enum {
|
||||||
ARG_VERSION = 0x100,
|
ARG_VERSION = 0x100,
|
||||||
ARG_IGNORE,
|
ARG_IGNORE,
|
||||||
|
ARG_ANY,
|
||||||
ARG_TIMEOUT,
|
ARG_TIMEOUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,6 +114,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "interface", required_argument, NULL, 'i' },
|
{ "interface", required_argument, NULL, 'i' },
|
||||||
{ "ignore", required_argument, NULL, ARG_IGNORE },
|
{ "ignore", required_argument, NULL, ARG_IGNORE },
|
||||||
{ "operational-state", required_argument, NULL, 'o' },
|
{ "operational-state", required_argument, NULL, 'o' },
|
||||||
|
{ "any", no_argument, NULL, ARG_ANY },
|
||||||
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -158,6 +162,10 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
arg_required_operstate = s;
|
arg_required_operstate = s;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ARG_ANY:
|
||||||
|
arg_any = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case ARG_TIMEOUT:
|
case ARG_TIMEOUT:
|
||||||
r = parse_sec(optarg, &arg_timeout);
|
r = parse_sec(optarg, &arg_timeout);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -192,11 +200,11 @@ static int run(int argc, char *argv[]) {
|
|||||||
|
|
||||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
|
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
|
||||||
|
|
||||||
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_timeout);
|
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_any, arg_timeout);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Could not create manager: %m");
|
return log_error_errno(r, "Could not create manager: %m");
|
||||||
|
|
||||||
if (manager_all_configured(m))
|
if (manager_configured(m))
|
||||||
goto success;
|
goto success;
|
||||||
|
|
||||||
notify_message = notify_start("READY=1\n"
|
notify_message = notify_start("READY=1\n"
|
||||||
|
5
test/test-network/conf/11-dummy.network
Normal file
5
test/test-network/conf/11-dummy.network
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[Match]
|
||||||
|
Name=test1
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
IPv6AcceptRA=no
|
5
test/test-network/conf/25-bridge.network
Normal file
5
test/test-network/conf/25-bridge.network
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[Match]
|
||||||
|
Name=bridge99
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
IPv6AcceptRA=no
|
@ -71,7 +71,6 @@ def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
|
|||||||
return f
|
return f
|
||||||
|
|
||||||
def setUpModule():
|
def setUpModule():
|
||||||
|
|
||||||
os.makedirs(network_unit_file_path, exist_ok=True)
|
os.makedirs(network_unit_file_path, exist_ok=True)
|
||||||
os.makedirs(networkd_ci_path, exist_ok=True)
|
os.makedirs(networkd_ci_path, exist_ok=True)
|
||||||
|
|
||||||
@ -186,8 +185,10 @@ class Utilities():
|
|||||||
if sleep_sec > 0:
|
if sleep_sec > 0:
|
||||||
time.sleep(sleep_sec)
|
time.sleep(sleep_sec)
|
||||||
|
|
||||||
def wait_online(self, links_with_operstate, timeout='20s'):
|
def wait_online(self, links_with_operstate, timeout='20s', bool_any=False):
|
||||||
args = [wait_online_bin, f' --timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
|
args = [wait_online_bin, f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
|
||||||
|
if bool_any:
|
||||||
|
args += ['--any']
|
||||||
subprocess.check_call(args)
|
subprocess.check_call(args)
|
||||||
|
|
||||||
class NetworkdNetDevTests(unittest.TestCase, Utilities):
|
class NetworkdNetDevTests(unittest.TestCase, Utilities):
|
||||||
@ -248,6 +249,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
|
|||||||
units = [
|
units = [
|
||||||
'10-dropin-test.netdev',
|
'10-dropin-test.netdev',
|
||||||
'11-dummy.netdev',
|
'11-dummy.netdev',
|
||||||
|
'11-dummy.network',
|
||||||
'12-dummy.netdev',
|
'12-dummy.netdev',
|
||||||
'15-name-conflict-test.netdev',
|
'15-name-conflict-test.netdev',
|
||||||
'21-macvlan.netdev',
|
'21-macvlan.netdev',
|
||||||
@ -259,6 +261,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
|
|||||||
'25-bond.netdev',
|
'25-bond.netdev',
|
||||||
'25-bond-balanced-tlb.netdev',
|
'25-bond-balanced-tlb.netdev',
|
||||||
'25-bridge.netdev',
|
'25-bridge.netdev',
|
||||||
|
'25-bridge.network',
|
||||||
'25-erspan-tunnel-local-any.netdev',
|
'25-erspan-tunnel-local-any.netdev',
|
||||||
'25-erspan-tunnel.netdev',
|
'25-erspan-tunnel.netdev',
|
||||||
'25-fou-gretap.netdev',
|
'25-fou-gretap.netdev',
|
||||||
@ -367,6 +370,22 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
|
|||||||
else:
|
else:
|
||||||
print('ethtool does not support driver field at least for dummy interfaces, skipping test for Driver field of networkctl.')
|
print('ethtool does not support driver field at least for dummy interfaces, skipping test for Driver field of networkctl.')
|
||||||
|
|
||||||
|
def test_wait_online_any(self):
|
||||||
|
self.copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
|
||||||
|
self.start_networkd(0)
|
||||||
|
|
||||||
|
self.wait_online(['bridge99', 'test1:degraded'], bool_any=True)
|
||||||
|
self.assertTrue(self.link_exits('bridge99'))
|
||||||
|
self.assertTrue(self.link_exits('test1'))
|
||||||
|
|
||||||
|
output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
|
||||||
|
print(output)
|
||||||
|
self.assertRegex(output, 'State: (?:off|no-carrier) \(configuring\)')
|
||||||
|
|
||||||
|
output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
|
||||||
|
print(output)
|
||||||
|
self.assertRegex(output, 'State: degraded \(configured\)')
|
||||||
|
|
||||||
def test_bridge(self):
|
def test_bridge(self):
|
||||||
self.copy_unit_to_networkd_unit_path('25-bridge.netdev')
|
self.copy_unit_to_networkd_unit_path('25-bridge.netdev')
|
||||||
self.start_networkd()
|
self.start_networkd()
|
||||||
|
Loading…
Reference in New Issue
Block a user