* udevadm control learnt a new option for --ping for testing whether a
systemd-udevd instance is running and reacting.
+ * udevadm trigger learnt a new option for --wait-daemon for waiting
+ systemd-udevd daemon to be initialized.
+
Contributions from: Aaron Plattner, Alberts Muktupāvels, Alex Mayer,
Ayman Bagabas, Beniamino Galvani, Burt P, Chris Down, Chris Lamb, Chris
Morin, Christian Hesse, Claudius Ellsel, dana, Daniel Axtens, Daniele
Miettinen, YiFei Zhu, YmrDtnJu, YunQiang Su, Yu Watanabe, Zbigniew
Jędrzejewski-Szmek, zsergeant77, Дамјан Георгиевски
- — Berlin, 2018-02-14
+ — Berlin, 2019-02-14
CHANGES WITH 240:
<a href="https://in.waw.pl/systemd-github-state/systemd-systemd-issues.svg"><img align="right" src="https://in.waw.pl/systemd-github-state/systemd-systemd-issues-small.svg" alt="Count of open issues over time"></a>
<a href="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests.svg"><img align="right" src="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests-small.svg" alt="Count of open pull requests over time"></a>
[![Semaphore CI Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)<br/>
+[![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)<br/>
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)<br/>
[![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)<br/>
[![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/systemd/systemd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/systemd/systemd/context:cpp)<br/>
ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change"
# watch metadata changes, caused by tools closing the device node which was opened for writing
-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*|dasd*", OPTIONS+="watch"
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*|dasd*|nbd*", OPTIONS+="watch"
#endif
/* netdevice.h */
+#ifndef NET_ADDR_PERM
+#define NET_ADDR_PERM 0
+#endif
+
#ifndef NET_ADDR_RANDOM
#define NET_ADDR_RANDOM 1
#endif
+#ifndef NET_ADDR_STOLEN
+#define NET_ADDR_STOLEN 2
+#endif
+
+#ifndef NET_ADDR_SET
+#define NET_ADDR_SET 3
+#endif
+
#ifndef NET_NAME_UNKNOWN
#define NET_NAME_UNKNOWN 0
#endif
state = unit_active_state(u);
if (state == UNIT_RELOADING)
- return -EALREADY;
+ return -EAGAIN;
if (state != UNIT_ACTIVE) {
log_unit_warning(u, "Unit cannot be reloaded because it is inactive.");
#include "alloc-util.h"
#include "condition.h"
#include "conf-parser.h"
+#include "device-util.h"
#include "dhcp-lease-internal.h"
#include "ether-addr-util.h"
#include "hexdecoct.h"
int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
size_t l, sz = 0;
- const char *name = NULL;
+ const char *name;
int r;
uint8_t *v;
assert(device);
+ /* net_get_name() will return one of the device names based on stable information about the
+ * device. If this is not available, we fall back to using the device name. */
name = net_get_name(device);
if (!name)
- return -ENOENT;
+ (void) sd_device_get_sysname(device, &name);
+ if (!name)
+ return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
+ "No stable identifying information found");
+ log_device_debug(device, "Using \"%s\" as stable identifying information", name);
l = strlen(name);
sz = sizeof(sd_id128_t) + l;
v = newa(uint8_t, sz);
- /* fetch some persistent data unique to this machine */
+ /* Fetch some persistent data unique to this machine */
r = sd_id128_get_machine((sd_id128_t*) v);
if (r < 0)
return r;
memcpy(v + sizeof(sd_id128_t), name, l);
- /* Let's hash the machine ID plus the device name. We
- * use a fixed, but originally randomly created hash
- * key here. */
+ /* Let's hash the machine ID plus the device name. We use
+ * a fixed, but originally randomly created hash key here. */
*result = htole64(siphash24(v, sz, HASH_KEY.bytes));
-
return 0;
}
goto fail;
session_set_user(session, user);
- session_set_leader(session, leader);
+ r = session_set_leader(session, leader);
+ if (r < 0)
+ goto fail;
session->type = t;
session->class = c;
#include "libudev-list-internal.h"
#include "libudev-util.h"
#include "log.h"
+#include "main-func.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "tests.h"
+
+static bool arg_monitor = false;
static void print_device(struct udev_device *device) {
const char *str;
log_info("*** device: %p ***", device);
str = udev_device_get_action(device);
- if (str != NULL)
+ if (str)
log_info("action: '%s'", str);
str = udev_device_get_syspath(device);
log_info("sysname: '%s'", str);
str = udev_device_get_sysnum(device);
- if (str != NULL)
+ if (str)
log_info("sysnum: '%s'", str);
str = udev_device_get_devpath(device);
log_info("devpath: '%s'", str);
str = udev_device_get_subsystem(device);
- if (str != NULL)
+ if (str)
log_info("subsystem: '%s'", str);
str = udev_device_get_devtype(device);
- if (str != NULL)
+ if (str)
log_info("devtype: '%s'", str);
str = udev_device_get_driver(device);
- if (str != NULL)
+ if (str)
log_info("driver: '%s'", str);
str = udev_device_get_devnode(device);
- if (str != NULL)
+ if (str)
log_info("devname: '%s'", str);
devnum = udev_device_get_devnum(device);
log_info("found %i properties", count);
str = udev_device_get_property_value(device, "MAJOR");
- if (str != NULL)
+ if (str)
log_info("MAJOR: '%s'", str);
str = udev_device_get_sysattr_value(device, "dev");
- if (str != NULL)
+ if (str)
log_info("attr{dev}: '%s'", str);
}
static void test_device(struct udev *udev, const char *syspath) {
_cleanup_(udev_device_unrefp) struct udev_device *device;
- log_info("looking at device: %s", syspath);
+ log_info("/* %s, device %s */", __func__, syspath);
device = udev_device_new_from_syspath(udev, syspath);
- if (device == NULL)
- log_warning_errno(errno, "udev_device_new_from_syspath: %m");
- else
+ if (device)
print_device(device);
+ else
+ log_warning_errno(errno, "udev_device_new_from_syspath: %m");
}
static void test_device_parents(struct udev *udev, const char *syspath) {
_cleanup_(udev_device_unrefp) struct udev_device *device;
struct udev_device *device_parent;
- log_info("looking at device: %s", syspath);
+ log_info("/* %s, device %s */", __func__, syspath);
device = udev_device_new_from_syspath(udev, syspath);
if (device == NULL)
return;
dev_t devnum = makedev(1, 3);
_cleanup_(udev_device_unrefp) struct udev_device *device;
- log_info("looking up device: %u:%u", major(devnum), minor(devnum));
+ log_info("/* %s, device %d:%d */", __func__, major(devnum), minor(devnum));
+
device = udev_device_new_from_devnum(udev, 'c', devnum);
- if (device == NULL)
- log_warning_errno(errno, "udev_device_new_from_devnum: %m");
- else
+ if (device)
print_device(device);
+ else
+ log_warning_errno(errno, "udev_device_new_from_devnum: %m");
}
static void test_device_subsys_name(struct udev *udev, const char *subsys, const char *dev) {
print_device(device);
}
-static int test_enumerate_print_list(struct udev_enumerate *enumerate) {
+static int enumerate_print_list(struct udev_enumerate *enumerate) {
struct udev_list_entry *list_entry;
int count = 0;
.data.fd = STDIN_FILENO,
};
+ log_info("/* %s */", __func__);
+
fd_ep = epoll_create1(EPOLL_CLOEXEC);
assert_se(fd_ep >= 0);
struct udev_queue *udev_queue;
bool empty;
- udev_queue = udev_queue_new(udev);
- assert_se(udev_queue);
+ log_info("/* %s */", __func__);
+
+ assert_se(udev_queue = udev_queue_new(udev));
empty = udev_queue_get_queue_is_empty(udev_queue);
log_info("queue is %s", empty ? "empty" : "not empty");
struct udev_enumerate *udev_enumerate;
int r;
+ log_info("/* %s */", __func__);
+
log_info("enumerate '%s'", subsystem == NULL ? "<all>" : subsystem);
udev_enumerate = udev_enumerate_new(udev);
if (udev_enumerate == NULL)
return -1;
udev_enumerate_add_match_subsystem(udev_enumerate, subsystem);
udev_enumerate_scan_devices(udev_enumerate);
- test_enumerate_print_list(udev_enumerate);
+ enumerate_print_list(udev_enumerate);
udev_enumerate_unref(udev_enumerate);
log_info("enumerate 'net' + duplicated scan + null + zero");
udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero");
udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero");
udev_enumerate_scan_devices(udev_enumerate);
- test_enumerate_print_list(udev_enumerate);
+ enumerate_print_list(udev_enumerate);
udev_enumerate_unref(udev_enumerate);
log_info("enumerate 'block'");
return r;
}
udev_enumerate_scan_devices(udev_enumerate);
- test_enumerate_print_list(udev_enumerate);
+ enumerate_print_list(udev_enumerate);
udev_enumerate_unref(udev_enumerate);
log_info("enumerate 'not block'");
return -1;
udev_enumerate_add_nomatch_subsystem(udev_enumerate, "block");
udev_enumerate_scan_devices(udev_enumerate);
- test_enumerate_print_list(udev_enumerate);
+ enumerate_print_list(udev_enumerate);
udev_enumerate_unref(udev_enumerate);
log_info("enumerate 'pci, mem, vc'");
udev_enumerate_add_match_subsystem(udev_enumerate, "mem");
udev_enumerate_add_match_subsystem(udev_enumerate, "vc");
udev_enumerate_scan_devices(udev_enumerate);
- test_enumerate_print_list(udev_enumerate);
+ enumerate_print_list(udev_enumerate);
udev_enumerate_unref(udev_enumerate);
log_info("enumerate 'subsystem'");
if (udev_enumerate == NULL)
return -1;
udev_enumerate_scan_subsystems(udev_enumerate);
- test_enumerate_print_list(udev_enumerate);
+ enumerate_print_list(udev_enumerate);
udev_enumerate_unref(udev_enumerate);
log_info("enumerate 'property IF_FS_*=filesystem'");
return -1;
udev_enumerate_add_match_property(udev_enumerate, "ID_FS*", "filesystem");
udev_enumerate_scan_devices(udev_enumerate);
- test_enumerate_print_list(udev_enumerate);
+ enumerate_print_list(udev_enumerate);
udev_enumerate_unref(udev_enumerate);
return 0;
}
struct udev_hwdb *hwdb;
struct udev_list_entry *entry;
+ log_info("/* %s */", __func__);
+
hwdb = udev_hwdb_new(udev);
+ if (!hwdb)
+ log_warning_errno(errno, "Failed to open hwdb: %m");
udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0))
log_info("'%s'='%s'", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
}
static void test_util_replace_whitespace(void) {
+ log_info("/* %s */", __func__);
+
test_util_replace_whitespace_one("hogehoge", "hogehoge");
test_util_replace_whitespace_one("hoge hoge", "hoge_hoge");
test_util_replace_whitespace_one(" hoge hoge ", "hoge_hoge");
}
static void test_util_resolve_subsys_kernel_one(const char *str, bool read_value, int retval, const char *expected) {
- char result[UTIL_PATH_SIZE];
+ char result[UTIL_PATH_SIZE] = "";
int r;
r = util_resolve_subsys_kernel(str, result, sizeof(result), read_value);
+ log_info("\"%s\" → expect: \"%s\", %d, actual: \"%s\", %d", str, strnull(expected), retval, result, r);
assert_se(r == retval);
if (r >= 0)
assert_se(streq(result, expected));
}
static void test_util_resolve_subsys_kernel(void) {
+ log_info("/* %s */", __func__);
+
test_util_resolve_subsys_kernel_one("hoge", false, -EINVAL, NULL);
test_util_resolve_subsys_kernel_one("[hoge", false, -EINVAL, NULL);
test_util_resolve_subsys_kernel_one("[hoge/foo", false, -EINVAL, NULL);
udev_list_cleanup(&list);
}
-int main(int argc, char *argv[]) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- bool arg_monitor = false;
+static int parse_args(int argc, char *argv[], const char **syspath, const char **subsystem) {
static const struct option options[] = {
{ "syspath", required_argument, NULL, 'p' },
{ "subsystem", required_argument, NULL, 's' },
{ "monitor", no_argument, NULL, 'm' },
{}
};
- const char *syspath = "/devices/virtual/mem/null";
- const char *subsystem = NULL;
int c;
- udev = udev_new();
- log_info("context: %p", udev);
- if (udev == NULL) {
- log_info("no context");
- return 1;
- }
-
while ((c = getopt_long(argc, argv, "p:s:dhVm", options, NULL)) >= 0)
switch (c) {
-
case 'p':
- syspath = optarg;
+ *syspath = optarg;
break;
case 's':
- subsystem = optarg;
+ *subsystem = optarg;
break;
case 'd':
- if (log_get_max_level() < LOG_INFO)
- log_set_max_level(LOG_INFO);
+ log_set_max_level(LOG_DEBUG);
break;
case 'h':
printf("--debug --syspath= --subsystem= --help\n");
- return EXIT_SUCCESS;
+ return 0;
case 'V':
printf("%s\n", GIT_VERSION);
- return EXIT_SUCCESS;
+ return 0;
case 'm':
arg_monitor = true;
break;
case '?':
- return EXIT_FAILURE;
+ return -EINVAL;
default:
assert_not_reached("Unhandled option code.");
}
+ return 1;
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(udev_unrefp) struct udev *udev = NULL;
+
+ const char *syspath = "/devices/virtual/mem/null";
+ const char *subsystem = NULL;
+ int r;
+
+ test_setup_logging(LOG_INFO);
+
+ r = parse_args(argc, argv, &syspath, &subsystem);
+ if (r <= 0)
+ return r;
+
+ assert_se(udev = udev_new());
+
/* add sys path if needed */
if (!startswith(syspath, "/sys"))
syspath = strjoina("/sys/", syspath);
test_list();
- return EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b;
}
-static int link_name_type(sd_device *device, unsigned *type) {
+static int link_unsigned_attribute(sd_device *device, const char *attr, unsigned *type) {
const char *s;
int r;
- r = sd_device_get_sysattr_value(device, "name_assign_type", &s);
+ r = sd_device_get_sysattr_value(device, attr, &s);
if (r < 0)
- return log_device_debug_errno(device, r, "Failed to query name_assign_type: %m");
+ return log_device_debug_errno(device, r, "Failed to query %s: %m", attr);
r = safe_atou(s, type);
if (r < 0)
- return log_device_warning_errno(device, r, "Failed to parse name_assign_type \"%s\": %m", s);
+ return log_device_warning_errno(device, r, "Failed to parse %s \"%s\": %m", attr, s);
- log_device_debug(device, "Device has name_assign_type=%d", *type);
+ log_device_debug(device, "Device has %s=%u", attr, *type);
return 0;
}
devtype,
sysname)) {
if (link->match_name) {
- unsigned char name_assign_type = NET_NAME_UNKNOWN;
- const char *attr_value;
+ unsigned name_assign_type = NET_NAME_UNKNOWN;
- if (sd_device_get_sysattr_value(device, "name_assign_type", &attr_value) >= 0)
- (void) safe_atou8(attr_value, &name_assign_type);
+ (void) link_unsigned_attribute(device, "name_assign_type", &name_assign_type);
if (name_assign_type == NET_NAME_ENUM) {
log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'",
return -ENOENT;
}
-static bool mac_is_random(sd_device *device) {
- const char *s;
- unsigned type;
+static int get_mac(sd_device *device, MACPolicy policy, struct ether_addr *mac) {
+ unsigned addr_type;
+ bool want_random = policy == MACPOLICY_RANDOM;
int r;
- /* if we can't get the assign type, assume it is not random */
- if (sd_device_get_sysattr_value(device, "addr_assign_type", &s) < 0)
- return false;
+ assert(IN_SET(policy, MACPOLICY_RANDOM, MACPOLICY_PERSISTENT));
- r = safe_atou(s, &type);
+ r = link_unsigned_attribute(device, "addr_assign_type", &addr_type);
if (r < 0)
- return false;
-
- return type == NET_ADDR_RANDOM;
-}
+ return r;
+ switch (addr_type) {
+ case NET_ADDR_SET:
+ return log_device_debug(device, "MAC on the device already set by userspace");
+ case NET_ADDR_STOLEN:
+ return log_device_debug(device, "MAC on the device already set based on another device");
+ case NET_ADDR_RANDOM:
+ case NET_ADDR_PERM:
+ break;
+ default:
+ return log_device_warning(device, "Unknown addr_assign_type %u, ignoring", addr_type);
+ }
-static int get_mac(sd_device *device, bool want_random,
- struct ether_addr *mac) {
- int r;
+ if (want_random == (addr_type == NET_ADDR_RANDOM))
+ return log_device_debug(device, "MAC on the device already matches policy *%s*",
+ mac_policy_to_string(policy));
- if (want_random)
+ if (want_random) {
+ log_device_debug(device, "Using random bytes to generate MAC");
random_bytes(mac->ether_addr_octet, ETH_ALEN);
- else {
+ } else {
uint64_t result;
r = net_get_unique_predictable_data(device, &result);
if (r < 0)
- return r;
+ return log_device_warning_errno(device, r, "Could not generate persistent MAC: %m");
assert_cc(ETH_ALEN <= sizeof(result));
memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
/* see eth_random_addr in the kernel */
mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
-
- return 0;
+ return 1;
}
int link_config_apply(link_config_ctx *ctx, link_config *config,
return log_device_warning_errno(device, r, "Could not find ifindex: %m");
- (void) link_name_type(device, &name_type);
+ (void) link_unsigned_attribute(device, "name_assign_type", &name_type);
if (IN_SET(name_type, NET_NAME_USER, NET_NAME_RENAMED)
&& !naming_scheme_has(NAMING_ALLOW_RERENAMES)) {
log_device_debug(device, "Policies didn't yield a name and Name= is not given, not renaming.");
no_rename:
- switch (config->mac_policy) {
- case MACPOLICY_PERSISTENT:
- if (mac_is_random(device)) {
- r = get_mac(device, false, &generated_mac);
- if (r == -ENOENT) {
- log_warning_errno(r, "Could not generate persistent MAC address for %s: %m", old_name);
- break;
- } else if (r < 0)
- return r;
- mac = &generated_mac;
- }
- break;
- case MACPOLICY_RANDOM:
- if (!mac_is_random(device)) {
- r = get_mac(device, true, &generated_mac);
- if (r == -ENOENT) {
- log_warning_errno(r, "Could not generate random MAC address for %s: %m", old_name);
- break;
- } else if (r < 0)
- return r;
- mac = &generated_mac;
- }
- break;
- case MACPOLICY_NONE:
- default:
- mac = config->mac;
- }
+ if (IN_SET(config->mac_policy, MACPOLICY_PERSISTENT, MACPOLICY_RANDOM)) {
+ if (get_mac(device, config->mac_policy, &generated_mac) > 0)
+ mac = &generated_mac;
+ } else
+ mac = config->mac;
r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu);
if (r < 0)
--- /dev/null
+[Match]
+Name=test1
+
+[Network]
+BindCarrier=dummy99 dummy98
+Address=192.168.10.30/24
+Gateway=192.168.10.1
+IPv6AcceptRA=no
links = [
'bond199',
'dummy98',
+ 'dummy99',
'test1']
units = [
'25-address-link-section.network',
'25-address-section-miscellaneous.network',
'25-address-section.network',
+ '25-bind-carrier.network',
'25-bond-active-backup-slave.netdev',
'25-fibrule-invert.network',
'25-fibrule-port-range.network',
self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1')
self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1')
+ def test_bind_carrier(self):
+ self.copy_unit_to_networkd_unit_path('25-bind-carrier.network', '11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('test1'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8')
+ print(output)
+
+ self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
+ self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
+ time.sleep(2)
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'UP,LOWER_UP')
+ self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
+ output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
+ self.assertRegex(output, 'State: routable \(configured\)')
+
+ self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy99', 'type', 'dummy']), 0)
+ self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy99', 'up']), 0)
+ time.sleep(2)
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'UP,LOWER_UP')
+ self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
+ output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
+ self.assertRegex(output, 'State: routable \(configured\)')
+
+ self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
+ time.sleep(2)
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'UP,LOWER_UP')
+ self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
+ output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
+ self.assertRegex(output, 'State: routable \(configured\)')
+
+ self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy99']), 0)
+ time.sleep(2)
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ print(output)
+ self.assertNotRegex(output, 'UP,LOWER_UP')
+ self.assertRegex(output, 'DOWN')
+ self.assertNotRegex(output, '192.168.10')
+ output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
+ self.assertRegex(output, 'State: off \(configured\)')
+
+ self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
+ self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
+ time.sleep(2)
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'UP,LOWER_UP')
+ self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
+ output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
+ self.assertRegex(output, 'State: routable \(configured\)')
+
class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities):
links = [
'bridge99',