1
0
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:
Yu Watanabe 2019-04-02 06:10:36 +09:00 committed by GitHub
commit dd09a9ec0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 35 deletions

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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"

View File

@ -0,0 +1,5 @@
[Match]
Name=test1
[Network]
IPv6AcceptRA=no

View File

@ -0,0 +1,5 @@
[Match]
Name=bridge99
[Network]
IPv6AcceptRA=no

View File

@ -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()