Merge pull request #12708 from yuwata/test-network-networkctl-tests

udevadm: add --wait-for-initialization option and use it in systemd-networkd-tests.py
This commit is contained in:
Yu Watanabe
2019-06-04 03:58:47 +09:00
committed by GitHub
9 changed files with 182 additions and 50 deletions
+8
View File
@@ -175,6 +175,14 @@
<para>Cleanup the udev database.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-w<optional>SECONDS</optional></option></term>
<term><option>--wait-for-initialization<optional>=SECONDS</optional></option></term>
<listitem>
<para>Wait for device to be initialized. If argument <replaceable>SECONDS</replaceable>
is not specified, the default is to wait forever.</para>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
+2 -1
View File
@@ -48,7 +48,8 @@ _udevadm() {
local -A OPTS=(
[COMMON]='-h --help -V --version'
[DEBUG]='-d --debug'
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db'
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
-w --wait-for-initialization'
[INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file'
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -w --settle --wait-daemon'
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
+1 -1
View File
@@ -109,7 +109,7 @@ static int determine_state_file(
if (r < 0)
return r;
r = device_wait_for_initialization(d, "rfkill", &device);
r = device_wait_for_initialization(d, "rfkill", USEC_INFINITY, &device);
if (r < 0)
return r;
+2 -2
View File
@@ -181,7 +181,7 @@ static int wait_for_partitions_to_appear(
continue;
if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
r = device_wait_for_initialization(q, "block", NULL);
r = device_wait_for_initialization(q, "block", USEC_INFINITY, NULL);
if (r < 0)
return r;
}
@@ -251,7 +251,7 @@ static int loop_wait_for_partitions_to_appear(
log_debug("Waiting for device (parent + %d partitions) to appear...", num_partitions);
if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
r = device_wait_for_initialization(d, "block", &device);
r = device_wait_for_initialization(d, "block", USEC_INFINITY, &device);
if (r < 0)
return r;
} else
+27 -6
View File
@@ -3,6 +3,7 @@
#include <errno.h>
#include "alloc-util.h"
#include "device-util.h"
#include "env-file.h"
#include "log.h"
#include "parse-util.h"
@@ -112,14 +113,18 @@ static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device,
return 0;
}
int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret) {
static int device_timeout_handler(sd_event_source *s, uint64_t usec, void *userdata) {
return sd_event_exit(sd_event_source_get_event(s), -ETIMEDOUT);
}
int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout, sd_device **ret) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *timeout_source = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
struct DeviceMonitorData data = {};
int r;
assert(device);
assert(subsystem);
if (sd_device_get_is_initialized(device) > 0) {
if (ret)
@@ -139,9 +144,17 @@ int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_
if (r < 0)
return log_error_errno(r, "Failed to acquire monitor: %m");
r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, NULL);
if (r < 0)
return log_error_errno(r, "Failed to add %s subsystem match to monitor: %m", subsystem);
if (!subsystem) {
r = sd_device_get_subsystem(device, &subsystem);
if (r < 0 && r != -ENOENT)
return log_device_error_errno(device, r, "Failed to get subsystem: %m");
}
if (subsystem) {
r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, NULL);
if (r < 0)
return log_error_errno(r, "Failed to add %s subsystem match to monitor: %m", subsystem);
}
r = sd_device_monitor_attach_event(monitor, event);
if (r < 0)
@@ -151,6 +164,14 @@ int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
if (timeout != USEC_INFINITY) {
r = sd_event_add_time(event, &timeout_source,
CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + timeout, 0,
device_timeout_handler, NULL);
if (r < 0)
return log_error_errno(r, "Failed to add timeout event source: %m");
}
/* Check again, maybe things changed. Udev will re-read the db if the device wasn't initialized
* yet. */
if (sd_device_get_is_initialized(device) > 0) {
@@ -161,7 +182,7 @@ int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Event loop failed: %m");
return log_error_errno(r, "Failed to wait for device to be initialized: %m");
if (ret)
*ret = TAKE_PTR(data.device);
+1 -1
View File
@@ -27,6 +27,6 @@ static inline int udev_parse_config(void) {
return udev_parse_config_full(NULL, NULL, NULL, NULL);
}
int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret);
int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout, sd_device **ret);
int device_is_renaming(sd_device *dev);
bool device_for_action(sd_device *dev, DeviceAction action);
+32 -13
View File
@@ -20,6 +20,7 @@
#include "fd-util.h"
#include "string-table.h"
#include "string-util.h"
#include "udev-util.h"
#include "udevadm-util.h"
#include "udevadm.h"
@@ -40,6 +41,7 @@ typedef enum QueryType {
static bool arg_root = false;
static bool arg_export = false;
static const char *arg_export_prefix = NULL;
static usec_t arg_wait_for_initialization_timeout = 0;
static bool skip_attribute(const char *name) {
static const char* const skip[] = {
@@ -329,6 +331,8 @@ static int help(void) {
" -P --export-prefix Export the key name with a prefix\n"
" -e --export-db Export the content of the udev database\n"
" -c --cleanup-db Clean up the udev database\n"
" -w --wait-for-initialization[=SECONDS]\n"
" Wait for device to be initialized\n"
, program_invocation_short_name);
return 0;
@@ -340,25 +344,26 @@ int info_main(int argc, char *argv[], void *userdata) {
int c, r;
static const struct option options[] = {
{ "name", required_argument, NULL, 'n' },
{ "path", required_argument, NULL, 'p' },
{ "query", required_argument, NULL, 'q' },
{ "attribute-walk", no_argument, NULL, 'a' },
{ "cleanup-db", no_argument, NULL, 'c' },
{ "export-db", no_argument, NULL, 'e' },
{ "root", no_argument, NULL, 'r' },
{ "device-id-of-file", required_argument, NULL, 'd' },
{ "export", no_argument, NULL, 'x' },
{ "export-prefix", required_argument, NULL, 'P' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{ "name", required_argument, NULL, 'n' },
{ "path", required_argument, NULL, 'p' },
{ "query", required_argument, NULL, 'q' },
{ "attribute-walk", no_argument, NULL, 'a' },
{ "cleanup-db", no_argument, NULL, 'c' },
{ "export-db", no_argument, NULL, 'e' },
{ "root", no_argument, NULL, 'r' },
{ "device-id-of-file", required_argument, NULL, 'd' },
{ "export", no_argument, NULL, 'x' },
{ "export-prefix", required_argument, NULL, 'P' },
{ "wait-for-initialization", optional_argument, NULL, 'w' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{}
};
ActionType action = ACTION_QUERY;
QueryType query = QUERY_ALL;
while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:Vh", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
switch (c) {
case 'n':
case 'p': {
@@ -414,6 +419,14 @@ int info_main(int argc, char *argv[], void *userdata) {
arg_export = true;
arg_export_prefix = optarg;
break;
case 'w':
if (optarg) {
r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
if (r < 0)
return log_error_errno(r, "Failed to parse timeout value: %m");
} else
arg_wait_for_initialization_timeout = USEC_INFINITY;
break;
case 'V':
return print_version();
case 'h':
@@ -453,6 +466,12 @@ int info_main(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Unknown device \"%s\": %m", *p);
if (arg_wait_for_initialization_timeout > 0) {
r = device_wait_for_initialization(device, NULL, arg_wait_for_initialization_timeout, NULL);
if (r < 0)
return r;
}
if (action == ACTION_QUERY)
r = query_device(query, device);
else if (action == ACTION_ATTRIBUTE_WALK)
@@ -0,0 +1,4 @@
[NetDev]
Name=test1
Kind=dummy
MTUBytes=1600
+105 -26
View File
@@ -79,6 +79,23 @@ def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
return f
def expectedFailureIfEthtoolDoesNotSupportDriver():
def f(func):
support = False
rc = subprocess.call(['ip', 'link', 'add', 'name', 'dummy99', 'type', 'dummy'])
if rc == 0:
ret = subprocess.run(['udevadm', 'info', '-w10s', '/sys/class/net/dummy99'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
if ret.returncode == 0 and 'E: ID_NET_DRIVER=dummy' in ret.stdout.rstrip():
support = True
subprocess.call(['ip', 'link', 'del', 'dummy99'])
if support:
return func
else:
return unittest.expectedFailure(func)
return f
def setUpModule():
os.makedirs(network_unit_file_path, exist_ok=True)
os.makedirs(networkd_ci_path, exist_ok=True)
@@ -109,6 +126,8 @@ def setUpModule():
drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
if ubsan_options:
drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
if asan_options or lsan_options or ubsan_options:
drop_in += ['SystemCallFilter=']
if use_valgrind or asan_options or lsan_options or ubsan_options:
drop_in += ['MemoryDenyWriteExecute=no']
@@ -283,6 +302,91 @@ class Utilities():
else:
self.assertRegex(output, address_regex)
class NetworkctlTests(unittest.TestCase, Utilities):
links = [
'test1',
'veth99',
]
units = [
'11-dummy.netdev',
'11-dummy-mtu.netdev',
'11-dummy.network',
'25-veth.netdev',
'netdev-link-local-addressing-yes.network',
]
def setUp(self):
self.remove_links(self.links)
def tearDown(self):
self.remove_links(self.links)
self.remove_unit_from_networkd_path(self.units)
def test_glob(self):
self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
self.start_networkd(0)
self.wait_online(['test1:degraded'])
output = subprocess.check_output(networkctl_cmd + ['list'], universal_newlines=True, env=env).rstrip()
self.assertRegex(output, '1 lo ')
self.assertRegex(output, 'test1')
output = subprocess.check_output(networkctl_cmd + ['list', 'test1'], universal_newlines=True, env=env).rstrip()
self.assertNotRegex(output, '1 lo ')
self.assertRegex(output, 'test1')
output = subprocess.check_output(networkctl_cmd + ['list', 'te*'], universal_newlines=True, env=env).rstrip()
self.assertNotRegex(output, '1 lo ')
self.assertRegex(output, 'test1')
output = subprocess.check_output(networkctl_cmd + ['status', 'te*'], universal_newlines=True, env=env).rstrip()
self.assertNotRegex(output, '1: lo ')
self.assertRegex(output, 'test1')
output = subprocess.check_output(networkctl_cmd + ['status', 'tes[a-z][0-9]'], universal_newlines=True, env=env).rstrip()
self.assertNotRegex(output, '1: lo ')
self.assertRegex(output, 'test1')
def test_mtu(self):
self.copy_unit_to_networkd_unit_path('11-dummy-mtu.netdev', '11-dummy.network')
self.start_networkd(0)
self.wait_online(['test1:degraded'])
output = subprocess.check_output(networkctl_cmd + ['status', 'test1'], universal_newlines=True, env=env).rstrip()
self.assertRegex(output, 'MTU: 1600')
@expectedFailureIfEthtoolDoesNotSupportDriver()
def test_udev_driver(self):
self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
'25-veth.netdev', 'netdev-link-local-addressing-yes.network')
self.start_networkd(0)
self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
output = subprocess.check_output(networkctl_cmd + ['status', 'test1'], universal_newlines=True, env=env).rstrip()
self.assertRegex(output, 'Driver: dummy')
output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
self.assertRegex(output, 'Driver: veth')
output = subprocess.check_output(networkctl_cmd + ['status', 'veth-peer'], universal_newlines=True, env=env).rstrip()
self.assertRegex(output, 'Driver: veth')
def test_delete_links(self):
self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
'25-veth.netdev', 'netdev-link-local-addressing-yes.network')
self.start_networkd(0)
self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
subprocess.check_call(networkctl_cmd + ['delete', 'test1', 'veth99'])
self.assertFalse(self.link_exists('test1'))
self.assertFalse(self.link_exists('veth99'))
self.assertFalse(self.link_exists('veth-peer'))
class NetworkdNetDevTests(unittest.TestCase, Utilities):
@@ -451,41 +555,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
self.remove_links(self.links)
self.remove_unit_from_networkd_path(self.units)
def test_dropin_and_networkctl_glob(self):
def test_dropin_and_name_conflict(self):
self.copy_unit_to_networkd_unit_path('10-dropin-test.netdev', '15-name-conflict-test.netdev')
self.start_networkd(0)
self.wait_online(['dropin-test:off'])
# This also tests NetDev.Name= conflict and basic networkctl functionalities
output = subprocess.check_output(['ip', 'link', 'show', 'dropin-test'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '00:50:56:c0:00:28')
output = subprocess.check_output(networkctl_cmd + ['list'], universal_newlines=True, env=env).rstrip()
self.assertRegex(output, '1 lo ')
self.assertRegex(output, 'dropin-test')
output = subprocess.check_output(networkctl_cmd + ['list', 'dropin-test'], universal_newlines=True, env=env).rstrip()
self.assertNotRegex(output, '1 lo ')
self.assertRegex(output, 'dropin-test')
output = subprocess.check_output(networkctl_cmd + ['list', 'dropin-*'], universal_newlines=True, env=env).rstrip()
self.assertNotRegex(output, '1 lo ')
self.assertRegex(output, 'dropin-test')
output = subprocess.check_output(networkctl_cmd + ['status', 'dropin-*'], universal_newlines=True, env=env).rstrip()
self.assertNotRegex(output, '1: lo ')
self.assertRegex(output, 'dropin-test')
ret = subprocess.run(['ethtool', '--driver', 'dropin-test'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
print(ret.stdout.rstrip())
if ret.returncode == 0 and re.search('driver: dummy', ret.stdout.rstrip()) != None:
self.assertRegex(output, 'Driver: dummy')
else:
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)