From 61135582e0b2e847e49c96af05e4d101323ce00c Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 18 Jun 2020 16:09:40 -0400 Subject: [PATCH 1/3] network: add ActivationPolicy= configuration parameter This parameter allows configuring the activation policy for an interface, meaning how it manages the interface's administrative state (IFF_UP flag). The policy can be configured to bring the interface either up or down when the interface is (re)configured, to always force the interface either up or down, or to never change the interface administrative state. If the interface is bound with BindCarrier=, its administrative state is controlled by the interface(s) it's bound to, and this parameter is forced to 'bound'. This changes the default behavior of how systemd-networkd sets the IFF_UP flag; previously, it was set up (if not already up) every time the link_joined() function was called. Now, with the default ActivationPolicy= setting of 'up', it will only set the IFF_UP flag once, the first time link_joined() is called, during an interface's configuration; and on the first link_joined() call each time the interface is reconfigured. Fixes: #3031 Fixes: #17437 --- man/systemd.network.xml | 39 ++++++++++++- src/network/networkd-link.c | 58 ++++++++++++++++++- src/network/networkd-link.h | 1 + src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 40 ++++++++++++- src/network/networkd-network.h | 16 +++++ .../fuzz-network-parser/directives.network | 1 + 7 files changed, 148 insertions(+), 8 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 0d9f1dce0dc..6d7d7e94a50 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -249,6 +249,36 @@ if RequiredForOnline=no. + + ActivationPolicy= + + Specifies the policy for systemd-networkd managing the link + administrative state. Specifically, this controls how systemd-networkd + changes the network device's IFF_UP flag, which is sometimes + controlled by system administrators by running e.g., ip set dev eth0 up + or ip set dev eth0 down, and can also be changed with + networkctl up eth0 or networkctl down eth0. + + Takes one of up, always-up, + manual, always-down, down, + or bound. When manual, systemd-networkd + will not change the link's admin state automatically; the system administrator must bring the + interface up or down manually, as desired. When up (the default) or + always-up, or down or always-down, + systemd-networkd will set the link up or down, respectively, + when the interface is (re)configured. When always-up or + always-down, systemd-networkd will set the link up + or down, respectively, any time systemd-networkd detects a change in + the administrative state. When BindCarrier= is also set, this is + automatically set to bound and any other value is ignored. + + The administrative state is not the same as the carrier state, so using + always-up does not mean the link will never lose carrier. The link + carrier depends on both the administrative state as well as the network device's physical + connection. However, to avoid reconfiguration failures, when using always-up, + IgnoreCarrierLoss= is forced to true. + + @@ -592,8 +622,9 @@ IPv6Token=prefixstable:2002:da8:1:: A link name or a list of link names. When set, controls the behavior of the current link. When all links in the list are in an operational down state, the current link is brought - down. When at least one link has carrier, the current interface is brought up. - + down. When at least one link has carrier, the current interface is brought up. + + This forces ActivationPolicy= to be set to bound. @@ -966,6 +997,10 @@ IPv6Token=prefixstable:2002:da8:1:: of the interface even if its carrier is lost. When unset, the value specified with is used. + + When ActivationPolicy= is set to always-up, this + is forced to true. + diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 3fad8f12f82..d1599748818 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1831,17 +1831,38 @@ static int link_joined(Link *link) { assert(link); assert(link->network); - if (!hashmap_isempty(link->bound_to_links)) { + switch (link->network->activation_policy) { + case ACTIVATION_POLICY_BOUND: r = link_handle_bound_to_list(link); if (r < 0) return r; - } else if (!(link->flags & IFF_UP)) { + break; + case ACTIVATION_POLICY_UP: + if (link->activated) + break; + _fallthrough_; + case ACTIVATION_POLICY_ALWAYS_UP: r = link_up(link); if (r < 0) { link_enter_failed(link); return r; } + break; + case ACTIVATION_POLICY_DOWN: + if (link->activated) + break; + _fallthrough_; + case ACTIVATION_POLICY_ALWAYS_DOWN: + r = link_down(link, NULL); + if (r < 0) { + link_enter_failed(link); + return r; + } + break; + default: + break; } + link->activated = true; if (link->network->bridge) { r = link_set_bridge(link); @@ -2254,6 +2275,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for return r; link_set_state(link, LINK_STATE_INITIALIZED); + link->activated = false; link_dirty(link); /* link_configure_duid() returns 0 if it requests product UUID. In that case, @@ -2658,6 +2680,16 @@ int link_carrier_reset(Link *link) { static int link_admin_state_up(Link *link) { int r; + assert(link); + + if (!link->network) + return 0; + + if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) { + log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down"); + return link_down(link, NULL); + } + /* We set the ipv6 mtu after the device mtu, but the kernel resets * ipv6 mtu on NETDEV_UP, so we need to reset it. The check for * ipv6_mtu_set prevents this from trying to set it too early before @@ -2672,6 +2704,21 @@ static int link_admin_state_up(Link *link) { return 0; } +static int link_admin_state_down(Link *link) { + + assert(link); + + if (!link->network) + return 0; + + if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) { + log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up"); + return link_up(link); + } + + return 0; +} + int link_update(Link *link, sd_netlink_message *m) { _cleanup_strv_free_ char **s = NULL; hw_addr_data hw_addr; @@ -2784,9 +2831,14 @@ int link_update(Link *link, sd_netlink_message *m) { r = link_admin_state_up(link); if (r < 0) return r; - } else if (link_was_admin_up && !(link->flags & IFF_UP)) + } else if (link_was_admin_up && !(link->flags & IFF_UP)) { log_link_info(link, "Link DOWN"); + r = link_admin_state_down(link); + if (r < 0) + return r; + } + r = link_update_lldp(link); if (r < 0) return r; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index b69e3125f35..3623b1a00dc 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -129,6 +129,7 @@ typedef struct Link { bool setting_genmode:1; bool ipv6_mtu_set:1; bool bridge_mdb_configured:1; + bool activated:1; sd_dhcp_server *dhcp_server; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 896a8840637..5152868f78a 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -66,6 +66,7 @@ Link.Multicast, config_parse_tristate, Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast) Link.Promiscuous, config_parse_tristate, 0, offsetof(Network, promiscuous) Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged) +Link.ActivationPolicy, config_parse_activation_policy, 0, offsetof(Network, activation_policy) Link.RequiredForOnline, config_parse_required_for_online, 0, 0 SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0 SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 32f91402379..ad4cd46276d 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -225,9 +225,6 @@ int network_verify(Network *network) { if (network->dhcp_use_gateway < 0) network->dhcp_use_gateway = network->dhcp_use_routes; - if (network->ignore_carrier_loss < 0) - network->ignore_carrier_loss = network->configure_without_carrier; - if (network->dhcp_critical >= 0) { if (network->keep_configuration >= 0) log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. " @@ -239,6 +236,30 @@ int network_verify(Network *network) { network->keep_configuration = KEEP_CONFIGURATION_NO; } + if (!strv_isempty(network->bind_carrier)) { + if (!IN_SET(network->activation_policy, _ACTIVATION_POLICY_INVALID, ACTIVATION_POLICY_BOUND)) + log_warning("%s: ActivationPolicy=bound is required with BindCarrier=. " + "Setting ActivationPolicy=bound.", network->filename); + network->activation_policy = ACTIVATION_POLICY_BOUND; + } else if (network->activation_policy == ACTIVATION_POLICY_BOUND) { + log_warning("%s: ActivationPolicy=bound requires BindCarrier=. " + "Ignoring ActivationPolicy=bound.", network->filename); + network->activation_policy = ACTIVATION_POLICY_UP; + } + + if (network->activation_policy == _ACTIVATION_POLICY_INVALID) + network->activation_policy = ACTIVATION_POLICY_UP; + + if (network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) { + if (network->ignore_carrier_loss == false) + log_warning("%s: IgnoreCarrierLoss=false conflicts with ActivationPolicy=always-up. " + "Setting IgnoreCarrierLoss=true.", network->filename); + network->ignore_carrier_loss = true; + } + + if (network->ignore_carrier_loss < 0) + network->ignore_carrier_loss = network->configure_without_carrier; + if (network->keep_configuration < 0) network->keep_configuration = KEEP_CONFIGURATION_NO; @@ -316,6 +337,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .required_for_online = true, .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT, + .activation_policy = _ACTIVATION_POLICY_INVALID, .arp = -1, .multicast = -1, .allmulticast = -1, @@ -1247,3 +1269,15 @@ static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode"); + +static const char* const activation_policy_table[_ACTIVATION_POLICY_MAX] = { + [ACTIVATION_POLICY_UP] = "up", + [ACTIVATION_POLICY_ALWAYS_UP] = "always-up", + [ACTIVATION_POLICY_MANUAL] = "manual", + [ACTIVATION_POLICY_ALWAYS_DOWN] = "always-down", + [ACTIVATION_POLICY_DOWN] = "down", + [ACTIVATION_POLICY_BOUND] = "bound", +}; + +DEFINE_STRING_TABLE_LOOKUP(activation_policy, ActivationPolicy); +DEFINE_CONFIG_PARSE_ENUM(config_parse_activation_policy, activation_policy, ActivationPolicy, "Failed to parse activation policy"); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index bd419f6ef41..52aae6a31e6 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -47,6 +47,17 @@ typedef enum IPv6LinkLocalAddressGenMode { _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID = -1 } IPv6LinkLocalAddressGenMode; +typedef enum ActivationPolicy { + ACTIVATION_POLICY_UP, + ACTIVATION_POLICY_ALWAYS_UP, + ACTIVATION_POLICY_MANUAL, + ACTIVATION_POLICY_ALWAYS_DOWN, + ACTIVATION_POLICY_DOWN, + ACTIVATION_POLICY_BOUND, + _ACTIVATION_POLICY_MAX, + _ACTIVATION_POLICY_INVALID = -1 +} ActivationPolicy; + typedef struct Manager Manager; typedef struct NetworkDHCPServerEmitAddress { @@ -93,6 +104,7 @@ struct Network { bool unmanaged; bool required_for_online; /* Is this network required to be considered online? */ LinkOperationalStateRange required_operstate_for_online; + ActivationPolicy activation_policy; /* misc settings */ bool configure_without_carrier; @@ -334,6 +346,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online); CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode); CONFIG_PARSER_PROTOTYPE(config_parse_rx_tx_queues); +CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy); const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length); @@ -342,3 +355,6 @@ KeepConfiguration keep_configuration_from_string(const char *s) _pure_; const char* ipv6_link_local_address_gen_mode_to_string(IPv6LinkLocalAddressGenMode s) _const_; IPv6LinkLocalAddressGenMode ipv6_link_local_address_gen_mode_from_string(const char *s) _pure_; + +const char* activation_policy_to_string(ActivationPolicy i) _const_; +ActivationPolicy activation_policy_from_string(const char *s) _pure_; diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index cc91437c164..e6fb9f6a80b 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -30,6 +30,7 @@ Host= MACAddress= PermanentMACAddress= [Link] +ActivationPolicy= RequiredForOnline= ARP= AllMulticast= From 2236d75df955118ad5d84c5ab787484c0921dfda Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 18 Jun 2020 18:31:18 -0400 Subject: [PATCH 2/3] test: add ActivationPolicy= unit tests --- .../conf/25-activation-policy.network | 6 +++ .../always-down.conf | 2 + .../always-up.conf | 2 + .../25-activation-policy.network.d/down.conf | 2 + .../manual.conf | 2 + .../25-activation-policy.network.d/up.conf | 2 + test/test-network/systemd-networkd-tests.py | 48 +++++++++++++++++++ 7 files changed, 64 insertions(+) create mode 100644 test/test-network/conf/25-activation-policy.network create mode 100644 test/test-network/conf/25-activation-policy.network.d/always-down.conf create mode 100644 test/test-network/conf/25-activation-policy.network.d/always-up.conf create mode 100644 test/test-network/conf/25-activation-policy.network.d/down.conf create mode 100644 test/test-network/conf/25-activation-policy.network.d/manual.conf create mode 100644 test/test-network/conf/25-activation-policy.network.d/up.conf diff --git a/test/test-network/conf/25-activation-policy.network b/test/test-network/conf/25-activation-policy.network new file mode 100644 index 00000000000..c53e0919c41 --- /dev/null +++ b/test/test-network/conf/25-activation-policy.network @@ -0,0 +1,6 @@ +[Match] +Name=test1 + +[Network] +Address=192.168.10.30/24 +Gateway=192.168.10.1 diff --git a/test/test-network/conf/25-activation-policy.network.d/always-down.conf b/test/test-network/conf/25-activation-policy.network.d/always-down.conf new file mode 100644 index 00000000000..edfd12e067e --- /dev/null +++ b/test/test-network/conf/25-activation-policy.network.d/always-down.conf @@ -0,0 +1,2 @@ +[Link] +ActivationPolicy=always-down diff --git a/test/test-network/conf/25-activation-policy.network.d/always-up.conf b/test/test-network/conf/25-activation-policy.network.d/always-up.conf new file mode 100644 index 00000000000..b8e0fffedb6 --- /dev/null +++ b/test/test-network/conf/25-activation-policy.network.d/always-up.conf @@ -0,0 +1,2 @@ +[Link] +ActivationPolicy=always-up diff --git a/test/test-network/conf/25-activation-policy.network.d/down.conf b/test/test-network/conf/25-activation-policy.network.d/down.conf new file mode 100644 index 00000000000..65af49f73e7 --- /dev/null +++ b/test/test-network/conf/25-activation-policy.network.d/down.conf @@ -0,0 +1,2 @@ +[Link] +ActivationPolicy=down diff --git a/test/test-network/conf/25-activation-policy.network.d/manual.conf b/test/test-network/conf/25-activation-policy.network.d/manual.conf new file mode 100644 index 00000000000..8b81ccc5445 --- /dev/null +++ b/test/test-network/conf/25-activation-policy.network.d/manual.conf @@ -0,0 +1,2 @@ +[Link] +ActivationPolicy=manual diff --git a/test/test-network/conf/25-activation-policy.network.d/up.conf b/test/test-network/conf/25-activation-policy.network.d/up.conf new file mode 100644 index 00000000000..537380b74c4 --- /dev/null +++ b/test/test-network/conf/25-activation-policy.network.d/up.conf @@ -0,0 +1,2 @@ +[Link] +ActivationPolicy=up diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index b50da26f105..da81d48ecbc 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1755,6 +1755,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '25-address-peer-ipv4.network', '25-address-preferred-lifetime-zero.network', '25-address-static.network', + '25-activation-policy.network', '25-bind-carrier.network', '25-bond-active-backup-slave.netdev', '25-fibrule-invert.network', @@ -2711,6 +2712,53 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1') self.wait_operstate('test1', 'routable') + def _test_activation_policy(self, test): + self.setUp() + conffile = '25-activation-policy.network' + if test: + conffile = f'{conffile}.d/{test}.conf' + copy_unit_to_networkd_unit_path('11-dummy.netdev', conffile, dropins=False) + start_networkd() + + always = test.startswith('always') + if test == 'manual': + initial_up = 'UP' in check_output('ip link show test1') + else: + initial_up = not test.endswith('down') # note: default is up + expect_up = initial_up + next_up = not expect_up + + # if initial expected state is down, must wait for setup_state to reach configuring + # so systemd-networkd considers it 'activated' + setup_state = None if initial_up else 'configuring' + + for iteration in range(4): + with self.subTest(iteration=iteration, expect_up=expect_up): + operstate = 'routable' if expect_up else 'off' + self.wait_operstate('test1', operstate, setup_state=setup_state, setup_timeout=20) + setup_state = None + + if expect_up: + self.assertIn('UP', check_output('ip link show test1')) + self.assertIn('192.168.10.30/24', check_output('ip address show test1')) + self.assertIn('default via 192.168.10.1', check_output('ip route show')) + else: + self.assertIn('DOWN', check_output('ip link show test1')) + + if next_up: + check_output('ip link set dev test1 up') + else: + check_output('ip link set dev test1 down') + expect_up = initial_up if always else next_up + next_up = not next_up + + self.tearDown() + + def test_activation_policy(self): + for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']: + with self.subTest(test=test): + self._test_activation_policy(test) + def test_domain(self): copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network') start_networkd() From a853652ae983699460b160bc2bf72f6fae0bfcd6 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 13 Aug 2020 11:52:53 -0400 Subject: [PATCH 3/3] save link activation policy to state file and display in networkctl --- src/libsystemd/sd-network/sd-network.c | 21 +++++++++++++++++++++ src/network/networkctl.c | 12 +++++++++++- src/network/networkd-link.c | 3 +++ src/systemd/sd-network.h | 5 +++++ test/test-network/systemd-networkd-tests.py | 1 + 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index b9b1099399f..060d84ae966 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -212,6 +212,27 @@ _public_ int sd_network_link_get_required_operstate_for_online(int ifindex, char return 0; } +_public_ int sd_network_link_get_activation_policy(int ifindex, char **policy) { + _cleanup_free_ char *s = NULL; + int r; + + assert_return(policy, -EINVAL); + + r = network_link_get_string(ifindex, "ACTIVATION_POLICY", &s); + if (r < 0) { + if (r != -ENODATA) + return r; + + /* For compatibility, assuming up. */ + s = strdup("up"); + if (!s) + return -ENOMEM; + } + + *policy = TAKE_PTR(s); + return 0; +} + _public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) { return network_link_get_string(ifindex, "LLMNR", llmnr); } diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 512f9ba63ba..a37618c8e0b 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -1387,7 +1387,7 @@ static int link_status_one( _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL; _cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL, - *setup_state = NULL, *operational_state = NULL, *lease_file = NULL; + *setup_state = NULL, *operational_state = NULL, *lease_file = NULL, *activation_policy = NULL; const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL, *on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup; _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL; @@ -2062,6 +2062,16 @@ static int link_status_one( if (r < 0) return r; + r = sd_network_link_get_activation_policy(info->ifindex, &activation_policy); + if (r >= 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Activation Policy:", + TABLE_STRING, activation_policy); + if (r < 0) + return table_log_add_error(r); + } + if (lease) { const void *client_id; size_t client_id_len; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index d1599748818..204e0c57ff6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -3011,6 +3011,9 @@ int link_save(Link *link) { st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "", st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : ""); + fprintf(f, "ACTIVATION_POLICY=%s\n", + activation_policy_to_string(link->network->activation_policy)); + fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); /************************************************************/ diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 7e062514189..884dba81b9b 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -103,6 +103,11 @@ int sd_network_link_get_address_state(int ifindex, char **state); */ int sd_network_link_get_required_for_online(int ifindex); +/* Get activation policy for ifindex. + * Possible values are as specified for ActivationPolicy= + */ +int sd_network_link_get_activation_policy(int ifindex, char **policy); + /* Get path to .network file applied to link */ int sd_network_link_get_network_file(int ifindex, char **filename); diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index da81d48ecbc..ecd3eaf8f9b 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -3033,6 +3033,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): self.assertRegex(data, r'OPER_STATE=routable') self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes') self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable') + self.assertRegex(data, r'ACTIVATION_POLICY=up') self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network') self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com') self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')