tests/client: test nm-cloud-setup

Some fairly rudimentary testing of nm-cloud-setup.
This commit is contained in:
Lubomir Rintel 2023-03-02 19:35:38 +01:00
parent ad6878d50a
commit d89d42bf23
4 changed files with 188 additions and 2 deletions

View file

@ -5472,6 +5472,13 @@ check-local-tests-client: src/nmcli/nmcli src/tests/client/test-client.py
check_local += check-local-tests-client
check-local-tests-cloud-setup: src/nm-cloud-setup/nm-cloud-setup src/tests/client/test-client.py
LIBTOOL="$(LIBTOOL)" "$(srcdir)/src/tests/client/test-client.sh" "$(builddir)" "$(srcdir)" "$(PYTHON)" -- TestNmCloudSetup
if BUILD_NM_CLOUD_SETUP
check_local += check-local-tests-cloud-setup
endif
CLEANFILES += src/tests/client/test-client.log
EXTRA_DIST += \

View file

@ -15,3 +15,20 @@ test(
],
timeout: 120,
)
if enable_nm_cloud_setup
test(
'check-local-tests-cloud-setup',
find_program(join_paths(source_root, 'src/tests/client/test-client.sh')),
args: [
build_root,
source_root,
python.path(),
'--',
'TestNmCloudSetup',
],
env: [
'LIBTOOL=',
],
)
endif

View file

@ -68,6 +68,10 @@ ENV_NM_TEST_CLIENT_BUILDDIR = "NM_TEST_CLIENT_BUILDDIR"
# In particular, you can test also a nmcli binary installed somewhere else.
ENV_NM_TEST_CLIENT_NMCLI_PATH = "NM_TEST_CLIENT_NMCLI_PATH"
# (optional) Path to nm-cloud-setup. By default, it looks for nm-cloud-setup
# in build dir.
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH = "NM_TEST_CLIENT_CLOUD_SETUP_PATH"
# (optional) The test also compares tranlsated output (l10n). This requires,
# that you first install the translation in the right place. So, by default,
# if a test for a translation fails, it will mark the test as skipped, and not
@ -140,6 +144,12 @@ try:
except ImportError:
pexpect = None
try:
from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler
except ImportError:
HTTPServer = None
###############################################################################
@ -164,6 +174,14 @@ class PathConfiguration:
assert os.path.exists(v), 'Cannot find test server at "%s"' % (v)
return v
@staticmethod
def test_cloud_meta_mock_path():
v = os.path.abspath(
PathConfiguration.top_srcdir() + "/tools/test-cloud-meta-mock.py"
)
assert os.path.exists(v), 'Cannot find cloud metadata mock server at "%s"' % (v)
return v
@staticmethod
def canonical_script_filename():
p = "src/tests/client/test-client.py"
@ -551,6 +569,20 @@ class Configuration:
pass
if not os.path.exists(v):
raise Exception("Missing nmcli binary. Set NM_TEST_CLIENT_NMCLI_PATH?")
elif name == ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH:
v = os.environ.get(ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH, None)
if v is None:
try:
v = os.path.abspath(
self.get(ENV_NM_TEST_CLIENT_BUILDDIR)
+ "/src/nm-cloud-setup/nm-cloud-setup"
)
except:
pass
if not os.path.exists(v):
raise Exception(
"Missing nm-cloud-setup binary. Set NM_TEST_CLIENT_CLOUD_SETUP_PATH?"
)
elif name == ENV_NM_TEST_CLIENT_CHECK_L10N:
# if we test locales other than 'C', the output of nmcli depends on whether
# nmcli can load the translations. Unfortunately, I cannot find a way to
@ -751,6 +783,16 @@ class NMStubServer:
iface_name = ""
self.op_SetProperties([(path, [(iface_name, [(propname, value)])])])
def addAndActivateConnection(
self, connection, device, specific_object="", delay=None
):
if delay is not None:
self.op_SetActiveConnectionStateChangedDelay(device, delay)
nm_iface = self._conn_get_main_object(self._conn)
self.op_AddAndActivateConnection(
connection, device, specific_object, dbus_iface=nm_iface
)
###############################################################################
@ -2094,6 +2136,125 @@ class TestNmcli(TestNmClient):
###############################################################################
class TestNmCloudSetup(TestNmClient):
def cloud_setup_test(func):
"""
Runs the mock NetworkManager along with a mock cloud metadata service.
"""
def f(self):
if pexpect is None:
raise unittest.SkipTest("pexpect not available")
s = socket.socket()
s.set_inheritable(True)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(("localhost", 0))
# The same value as Python's TCPServer uses.
# Chosen by summoning the sprit of TCP under influence of
# hallucinogenic substances.
s.listen(5)
def pass_socket():
os.dup2(s.fileno(), 3, inheritable=True)
service_path = PathConfiguration.test_cloud_meta_mock_path()
env = os.environ.copy()
env["LISTEN_FDS"] = "1"
p = subprocess.Popen(
[sys.executable, service_path],
stdin=subprocess.PIPE,
env=env,
pass_fds=(s.fileno(),),
preexec_fn=pass_socket,
)
self.md_url = "http://%s:%d" % s.getsockname()
s.close()
self.srv_start()
func(self)
self._nm_test_post()
p.terminate()
p.wait()
return f
@cloud_setup_test
def test_ec2(self):
# Add a device with an active connection that has IPv4 configured
self.srv.op_AddObj("WiredDevice", iface="eth0")
self.srv.addAndActivateConnection(
{
"connection": {"type": "802-3-ethernet", "id": "con-eth0"},
"ipv4": {"method": "auto"},
},
"/org/freedesktop/NetworkManager/Devices/1",
delay=0,
)
# The second connection has no IPv4
self.srv.op_AddObj("WiredDevice", iface="eth1")
self.srv.addAndActivateConnection(
{"connection": {"type": "802-3-ethernet", "id": "con-eth1"}},
"/org/freedesktop/NetworkManager/Devices/2",
"",
delay=0,
)
# Run nm-cloud-setup for the first time
nmc = self.call_pexpect(
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
[],
{
"NM_CLOUD_SETUP_EC2_HOST": self.md_url,
"NM_CLOUD_SETUP_LOG": "trace",
"NM_CLOUD_SETUP_EC2": "yes",
},
)
nmc.pexp.expect("provider ec2 detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("get-config: starting")
nmc.pexp.expect("get-config: success")
nmc.pexp.expect("meta data received")
# One of the devices has no IPv4 configuration to be modified
nmc.pexp.expect("device has no suitable applied connection. Skip")
# The other one was lacking an address set it up.
nmc.pexp.expect("some changes were applied for provider ec2")
nmc.pexp.expect(pexpect.EOF)
# Run nm-cloud-setup for the second time
nmc = self.call_pexpect(
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
[],
{
"NM_CLOUD_SETUP_EC2_HOST": self.md_url,
"NM_CLOUD_SETUP_LOG": "trace",
"NM_CLOUD_SETUP_EC2": "yes",
},
)
nmc.pexp.expect("provider ec2 detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("get-config: starting")
nmc.pexp.expect("get-config: success")
nmc.pexp.expect("meta data received")
# No changes this time
nmc.pexp.expect('device needs no update to applied connection "con-eth0"')
nmc.pexp.expect("no changes were applied for provider ec2")
nmc.pexp.expect(pexpect.EOF)
Util.valgrind_check_log(nmc.valgrind_log, "test_ec2")
###############################################################################
def main():
global dbus_session_inited

View file

@ -71,7 +71,6 @@ fi
test -d "$BUILDDIR" || die "BUILDDIR \"$BUILDDIR\" does not exist?"
test -d "$SRCDIR" || die "SRCDIR \"$SRCDIR\" does not exist?"
test -f "$BUILDDIR/src/nmcli/nmcli" || die "\"$BUILDDIR/src/nmcli/nmcli\" does not exist?"
if test -f "$BUILDDIR/src/libnm-client-impl/.libs/libnm.so" ; then
LIBDIR="$BUILDDIR/src/libnm-client-impl/.libs"
@ -84,6 +83,7 @@ fi
mkdir -p "$BUILDDIR/src/tests/client/" || die "failure to create build output directory \"$BUILDDIR/src/tests/client/\""
export NM_TEST_CLIENT_NMCLI_PATH="$BUILDDIR/src/nmcli/nmcli"
export NM_TEST_CLIENT_CLOUD_SETUP_PATH="$BUILDDIR/src/nm-cloud-setup/nm-cloud-setup"
export GI_TYPELIB_PATH="$BUILDDIR/src/libnm-client-impl${GI_TYPELIB_PATH:+:$GI_TYPELIB_PATH}"
export LD_LIBRARY_PATH="$LIBDIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export NM_TEST_CLIENT_BUILDDIR="$BUILDDIR"
@ -91,7 +91,8 @@ export NM_TEST_CLIENT_BUILDDIR="$BUILDDIR"
# Run nmcli at least once. With libtool, nmcli is a shell script and with LTO
# this seems to perform some slow setup during the first run. If we do that
# during the test, it will timeout and fail.
"$NM_TEST_CLIENT_NMCLI_PATH" --version &>/dev/null
"$NM_TEST_CLIENT_NMCLI_PATH" --version &>/dev/null || :
"$NM_TEST_CLIENT_CLOUD_SETUP_PATH" --invalid &>/dev/null || :
# we first collect all the output in "test-client.log" and print it at once
# afterwards. The only reason is that when you run with `make -j` that the