Compare commits

...

5 Commits

Author SHA1 Message Date
Federico Giovanardi 10e9b48c57
Merge 3d9cd50309 into 2e5b0412f9 2024-11-21 00:11:57 +00:00
Luca Boccassi 2e5b0412f9
network: update state files before replying bus method (#35255)
Follow-up for 2b07a3211b.

Fixes the failure found in
https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/noble/amd64/s/systemd-upstream/20241115_182040_92382@/log.gz
. Relevant logs:
```
Nov 16 02:48:36 systemd-networkd[2706]: veth99: Reconfiguring with /run/systemd/network/25-dhcp-client-ipv6-only.network.
Nov 16 02:48:36 systemd-networkd[2706]: veth99: NDISC: Started IPv6 Router Solicitation client
Nov 16 02:48:36 systemd-networkd[2706]: veth99: IPv6 Router Discovery is configured and started.
Nov 16 02:48:36 systemd-networkd[2706]: veth99: NDISC: Sent Router Solicitation, next solicitation in 3s
Nov 16 02:48:36 systemd-networkd[2706]: veth99: NDISC: Received Router Advertisement from fe80::1034:56ff:fe78:9abd: flags=0xc0(managed, other), preference=medium, lifetime=30min
Nov 16 02:48:36 systemd-networkd[2706]: veth99: NDISC: Invoking callback for 'router' event.
Nov 16 02:48:36 systemd-networkd[2706]: veth99: link_check_ready(): dynamic addressing protocols are enabled but none of them finished yet.
Nov 16 02:48:36 systemd-networkd[2706]: veth99: DHCPv6 client: Starting in Solicit mode
Nov 16 02:48:36 systemd-networkd[2706]: veth99: DHCPv6 client: State changed: stopped -> solicitation
Nov 16 02:48:36 systemd-networkd[2706]: veth99: Acquiring DHCPv6 lease on NDisc request
Nov 16 02:48:36 systemd-networkd[2706]: veth99: DHCPv6 client: Sent Solicit
Nov 16 02:48:36 systemd-networkd[2706]: veth99: DHCPv6 client: Next retransmission in 1s
Nov 16 02:48:37 systemd-networkd[2706]: veth99: DHCPv6 client: Sent Solicit
Nov 16 02:48:37 systemd-networkd[2706]: veth99: DHCPv6 client: Next retransmission in 1s
Nov 16 02:48:39 systemd-networkd[2706]: veth99: NDISC: Received Neighbor Advertisement from fe80::1034:56ff:fe78:9abd: Router=yes, Solicited=yes, Override=no
Nov 16 02:48:39 systemd-networkd[2706]: veth99: NDISC: Invoking callback for 'neighbor' event.
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: Processed Reply message
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: T1 expires in 50s
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: T2 expires in 55s
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: Valid lifetime expires in 2min
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: State changed: solicitation -> bound
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 address 2600::15/128 (valid for 1min 59s, preferred for 1min 59s)
Nov 16 02:48:41 systemd-networkd[2706]: veth99: Received updated DHCPv6 address (configured): 2600::15/128 (valid for 1min 58s, preferred for 1min 58s), flags: no-prefixroute, scope: global
Nov 16 02:48:41 systemd-networkd[2706]: veth99: DHCPv6 addresses and routes set.
Nov 16 02:48:41 systemd-networkd[2706]: veth99: link_check_ready(): IPv4LL:no DHCPv4:no DHCPv6:yes DHCP-PD:no NDisc:no
Nov 16 02:48:41 systemd-networkd[2706]: veth99: State changed: configuring -> configured
```
The interface veth99 entered the configured state after 5 seconds, but
at the same time, the `wait_online()` in the test script considered the
test failed.
The function `wait_online()` first invokes
`systemd-networkd-wait-online` with `--timeout=20`, then check setup
states of interfaces with 5 seconds timeout. So, the failure suggests
that `systemd-networkd-wait-online` finishes immediately, as the state
file was not updated when it is invoked, and thus it handles the
interface veth99 already in the configured state.
2024-11-20 23:36:35 +00:00
Yu Watanabe 2b397d43ab test-network: actually check metric and preference
Otherwise, nexthop ID may contain e.g. 300, then
===
AssertionError: '300' unexpectedly found in
'default nhid 3860882700 via fe80::1034:56ff:fe78:9a99 proto ra metric 512 expires 1798sec pref high\n
 default nhid 2639230080 via fe80::1034:56ff:fe78:9a98 proto ra metric 2048 expires 1798sec pref low'
===
2024-11-21 03:43:35 +09:00
Yu Watanabe 9ad294efd0 network: update state files before replying bus method
Follow-up for 2b07a3211b.
2024-11-21 03:42:06 +09:00
Federico Giovanardi 3d9cd50309 udev: add option to trigger parent devices despite filters
This commit add the `-i` option to `udevadm trigger` that force it to
match parent devices even if they're excluded from filters.
The rationale is that some embedded devices have a huge number of
platform devices ( ~ 4k for MX8 ) they are there because they're defined
in the device tree but there isn't any action or udev rules associated
with them.

So at boot a significant time is spend triggering and processing rules
for devices that don't produce any effect and we would like to filter
them by calling:

```
udevadm trigger --type=device --action=add -s block -s tty
```

instead of the normal

```
udevadm trigger --type=device --action=add
```

so we can use filter to filter out only subsystems for we we know that
we have rules in place that do something useful.

On the other side action / rules are not triggered until the parent is
triggered ( which is part of another subsystem), so the additional option
will allows udev to complete the coldplug with only the devices we care.

Example on iMX8:

.Without the new option
```
root@dev:~# udevadm trigger --dry-run  -s block --action=add -v
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot1
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p1
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p2
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p3
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p4
```

.With the new option
```
root@dev:~# udevadm trigger --dry-run -i -s block --action=add -v
/sys/devices/platform
/sys/devices/platform/bus@5b000000
/sys/devices/platform/bus@5b000000/5b010000.mmc
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot1
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p1
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p2
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p3
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p4
```
Boot time reduction with this is place is ~ 1 second.
2024-11-20 17:11:13 +01:00
10 changed files with 110 additions and 22 deletions

View File

@ -577,6 +577,17 @@
<xi:include href="version-info.xml" xpointer="v251"/> <xi:include href="version-info.xml" xpointer="v251"/>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--include-parents</option></term>
<listitem>
<para>Trigger parent devices of found devices even if the parents
won't match the filter condition.
This is useful if we are interested to limit the coldplug activities to
some devices or subsystems.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-w</option></term> <term><option>-w</option></term>
<term><option>--settle</option></term> <term><option>--settle</option></term>

View File

@ -58,7 +58,7 @@ _udevadm() {
--json --subsystem-match --subsystem-nomatch --attr-match --attr-nomatch --property-match --json --subsystem-match --subsystem-nomatch --attr-match --attr-nomatch --property-match
--tag-match --sysname-match --name-match --parent-match' --tag-match --sysname-match --name-match --parent-match'
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid
--initialized-match --initialized-nomatch' --initialized-match --initialized-nomatch --include-parents'
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
-a --attr-match -A --attr-nomatch -p --property-match -a --attr-match -A --attr-nomatch -p --property-match
-g --tag-match -y --sysname-match --name-match -b --parent-match -g --tag-match -y --sysname-match --name-match -b --parent-match

View File

@ -38,6 +38,7 @@ _udevadm_trigger(){
'--tag-match=[Trigger events for devices with a matching tag.]:TAG' \ '--tag-match=[Trigger events for devices with a matching tag.]:TAG' \
'--sysname-match=[Trigger events for devices with a matching sys device name.]:NAME' \ '--sysname-match=[Trigger events for devices with a matching sys device name.]:NAME' \
'--parent-match=[Trigger events for all children of a given device.]:NAME' \ '--parent-match=[Trigger events for all children of a given device.]:NAME' \
'--include-parents[Also trigger parent devices of found devices.]' \
'--initialized-match[Trigger events for devices that are already initialized.]' \ '--initialized-match[Trigger events for devices that are already initialized.]' \
'--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \ '--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \
'--uuid[Print synthetic uevent UUID.]' \ '--uuid[Print synthetic uevent UUID.]' \

View File

@ -1058,3 +1058,8 @@ global:
sd_device_monitor_get_timeout; sd_device_monitor_get_timeout;
sd_device_monitor_receive; sd_device_monitor_receive;
} LIBSYSTEMD_256; } LIBSYSTEMD_256;
LIBSYSTEMD_258 {
global:
sd_device_enumerator_add_all_parents;
} LIBSYSTEMD_257;

View File

@ -24,6 +24,17 @@ typedef enum DeviceEnumerationType {
_DEVICE_ENUMERATION_TYPE_INVALID = -EINVAL, _DEVICE_ENUMERATION_TYPE_INVALID = -EINVAL,
} DeviceEnumerationType; } DeviceEnumerationType;
typedef enum MatchFlag {
MATCH_NONE = 0,
MATCH_BASIC = 1u << 0,
MATCH_SYSNAME = 1u << 1,
MATCH_SUBSYSTEM = 1u << 2,
MATCH_PARENT = 1u << 3,
MATCH_TAG = 1u << 4,
MATCH_ALL = (1u << 5) - 1,
} MatchFlag;
struct sd_device_enumerator { struct sd_device_enumerator {
unsigned n_ref; unsigned n_ref;
@ -46,6 +57,7 @@ struct sd_device_enumerator {
Set *match_tag; Set *match_tag;
Set *match_parent; Set *match_parent;
MatchInitializedType match_initialized; MatchInitializedType match_initialized;
MatchFlag parent_match_flags;
}; };
_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
@ -61,6 +73,7 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
.n_ref = 1, .n_ref = 1,
.type = _DEVICE_ENUMERATION_TYPE_INVALID, .type = _DEVICE_ENUMERATION_TYPE_INVALID,
.match_initialized = MATCH_INITIALIZED_COMPAT, .match_initialized = MATCH_INITIALIZED_COMPAT,
.parent_match_flags = MATCH_ALL,
}; };
*ret = TAKE_PTR(enumerator); *ret = TAKE_PTR(enumerator);
@ -273,6 +286,15 @@ _public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enum
return 1; return 1;
} }
_public_ int sd_device_enumerator_add_all_parents(sd_device_enumerator *enumerator) {
assert_return(enumerator, -EINVAL);
enumerator->parent_match_flags = MATCH_NONE;
enumerator->scan_uptodate = false;
return 1;
}
int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type) { int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type) {
assert_return(enumerator, -EINVAL); assert_return(enumerator, -EINVAL);
@ -567,15 +589,6 @@ static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsys
return set_fnmatch(enumerator->match_subsystem, enumerator->nomatch_subsystem, subsystem); return set_fnmatch(enumerator->match_subsystem, enumerator->nomatch_subsystem, subsystem);
} }
typedef enum MatchFlag {
MATCH_SYSNAME = 1u << 0,
MATCH_SUBSYSTEM = 1u << 1,
MATCH_PARENT = 1u << 2,
MATCH_TAG = 1u << 3,
MATCH_ALL = (1u << 4) - 1,
} MatchFlag;
static int test_matches( static int test_matches(
sd_device_enumerator *enumerator, sd_device_enumerator *enumerator,
sd_device *device, sd_device *device,
@ -618,18 +631,20 @@ static int test_matches(
!match_tag(enumerator, device)) !match_tag(enumerator, device))
return false; return false;
r = match_initialized(enumerator, device); if (FLAGS_SET(flags, MATCH_BASIC)) {
if (r <= 0) r = match_initialized(enumerator, device);
return r; if (r <= 0)
return r;
if (!match_property(enumerator->match_property, device, /* match_all = */ false)) if (!match_property(enumerator->match_property, device, /* match_all = */ false))
return false; return false;
if (!match_property(enumerator->match_property_required, device, /* match_all = */ true)) if (!match_property(enumerator->match_property_required, device, /* match_all = */ true))
return false; return false;
if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr)) if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
return false; return false;
}
return true; return true;
} }
@ -743,7 +758,7 @@ static int enumerator_scan_dir_and_add_devices(
/* Also include all potentially matching parent devices in the enumeration. These are things /* Also include all potentially matching parent devices in the enumeration. These are things
* like root busses e.g. /sys/devices/pci0000:00/ or /sys/devices/pnp0/, which ar not * like root busses e.g. /sys/devices/pci0000:00/ or /sys/devices/pnp0/, which ar not
* linked from /sys/class/ or /sys/bus/, hence pick them up explicitly here. */ * linked from /sys/class/ or /sys/bus/, hence pick them up explicitly here. */
k = enumerator_add_parent_devices(enumerator, device, MATCH_ALL); k = enumerator_add_parent_devices(enumerator, device, enumerator->parent_match_flags);
if (k < 0) if (k < 0)
r = k; r = k;
} }

View File

@ -499,6 +499,45 @@ TEST(sd_device_enumerator_add_match_parent) {
} }
} }
TEST(sd_device_enumerator_add_all_parents) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
unsigned int devices_count_with_parents = 0;
unsigned int devices_count_without_parents = 0;
/* STEP 1: enumerate all block devices without all_parents() */
ASSERT_OK(sd_device_enumerator_new(&e) >= 0);
ASSERT_OK(sd_device_enumerator_allow_uninitialized(e) >= 0);
/* filter in only a subsystem */
ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true) >= 0);
ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition"));
FOREACH_DEVICE(e, dev) {
const char *subsystem;
ASSERT_OK(sd_device_get_subsystem(dev, &subsystem) >= 0);
ASSERT_OK(device_in_subsystem(dev, "block") != 0);
devices_count_without_parents++;
}
log_debug("found %u devices",devices_count_without_parents);
ASSERT_OK(devices_count_without_parents > 0);
/* STEP 2: enumerate again with all_parents() */
ASSERT_OK(sd_device_enumerator_add_all_parents(e) >= 0);
unsigned int not_filtered_parent_count = 0;
FOREACH_DEVICE(e, dev) {
const char *subsystem;
ASSERT_OK(sd_device_get_subsystem(dev, &subsystem) >= 0);
if (!device_in_subsystem(dev, "block"))
not_filtered_parent_count++;
devices_count_with_parents++;
}
log_debug("found %u devices out of %u that would have been excluded without all_parents()",not_filtered_parent_count,devices_count_with_parents);
ASSERT_OK(not_filtered_parent_count > 0);
ASSERT_OK(devices_count_with_parents > devices_count_without_parents);
}
TEST(sd_device_get_child) { TEST(sd_device_get_child) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
int r; int r;

View File

@ -1443,6 +1443,7 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) {
} }
typedef struct LinkReconfigurationData { typedef struct LinkReconfigurationData {
Manager *manager;
Link *link; Link *link;
LinkReconfigurationFlag flags; LinkReconfigurationFlag flags;
sd_bus_message *message; sd_bus_message *message;
@ -1473,6 +1474,12 @@ static void link_reconfiguration_data_destroy_callback(LinkReconfigurationData *
} }
if (!data->counter || *data->counter <= 0) { if (!data->counter || *data->counter <= 0) {
/* Update the state files before replying the bus method. Otherwise,
* systemd-networkd-wait-online following networkctl reload/reconfigure may read an
* outdated state file and wrongly handle an interface is already in the configured
* state. */
(void) manager_clean_all(data->manager);
r = sd_bus_reply_method_return(data->message, NULL); r = sd_bus_reply_method_return(data->message, NULL);
if (r < 0) if (r < 0)
log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m"); log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m");
@ -1521,6 +1528,7 @@ int link_reconfigure_full(Link *link, LinkReconfigurationFlag flags, sd_bus_mess
} }
*data = (LinkReconfigurationData) { *data = (LinkReconfigurationData) {
.manager = link->manager,
.link = link_ref(link), .link = link_ref(link),
.flags = flags, .flags = flags,
.message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */ .message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */

View File

@ -137,6 +137,7 @@ int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, c
int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);
int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent); int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent);
int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator);
int sd_device_enumerator_add_all_parents(sd_device_enumerator *enumerator);
/* device monitor */ /* device monitor */

View File

@ -266,6 +266,7 @@ static int help(void) {
" -y --sysname-match=NAME Trigger devices with this /sys path\n" " -y --sysname-match=NAME Trigger devices with this /sys path\n"
" --name-match=NAME Trigger devices with this /dev name\n" " --name-match=NAME Trigger devices with this /dev name\n"
" -b --parent-match=NAME Trigger devices with that parent device\n" " -b --parent-match=NAME Trigger devices with that parent device\n"
" --include-parents Trigger parent devices of found devices\n"
" --initialized-match Trigger devices that are already initialized\n" " --initialized-match Trigger devices that are already initialized\n"
" --initialized-nomatch Trigger devices that are not initialized yet\n" " --initialized-nomatch Trigger devices that are not initialized yet\n"
" -w --settle Wait for the triggered events to complete\n" " -w --settle Wait for the triggered events to complete\n"
@ -287,6 +288,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
ARG_PRIORITIZED_SUBSYSTEM, ARG_PRIORITIZED_SUBSYSTEM,
ARG_INITIALIZED_MATCH, ARG_INITIALIZED_MATCH,
ARG_INITIALIZED_NOMATCH, ARG_INITIALIZED_NOMATCH,
ARG_INCLUDE_PARENTS,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -304,6 +306,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
{ "sysname-match", required_argument, NULL, 'y' }, { "sysname-match", required_argument, NULL, 'y' },
{ "name-match", required_argument, NULL, ARG_NAME }, { "name-match", required_argument, NULL, ARG_NAME },
{ "parent-match", required_argument, NULL, 'b' }, { "parent-match", required_argument, NULL, 'b' },
{ "include-parents", no_argument, NULL, ARG_INCLUDE_PARENTS },
{ "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH }, { "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH },
{ "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH }, { "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH },
{ "settle", no_argument, NULL, 'w' }, { "settle", no_argument, NULL, 'w' },
@ -428,6 +431,11 @@ int trigger_main(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
break; break;
} }
case ARG_INCLUDE_PARENTS:
r = sd_device_enumerator_add_all_parents(e);
if (r < 0)
return log_error_errno(r, "Failed to always include all parents: %m");
break;
case 'w': case 'w':
arg_settle = true; arg_settle = true;
break; break;

View File

@ -6406,11 +6406,11 @@ class NetworkdRATests(unittest.TestCase, Utilities):
for i in [100, 200, 300, 512, 1024, 2048]: for i in [100, 200, 300, 512, 1024, 2048]:
if i not in [metric_1, metric_2]: if i not in [metric_1, metric_2]:
self.assertNotIn(f'{i}', output) self.assertNotIn(f'metric {i} ', output)
for i in ['low', 'medium', 'high']: for i in ['low', 'medium', 'high']:
if i not in [preference_1, preference_2]: if i not in [preference_1, preference_2]:
self.assertNotIn(f'{i}', output) self.assertNotIn(f'pref {i}', output)
def test_router_preference(self): def test_router_preference(self):
copy_network_unit('25-veth-client.netdev', copy_network_unit('25-veth-client.netdev',