From c2959cf41e72b64f603d35c4acb51ed2d99efa5d Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 22 Jan 2025 14:11:37 +0100 Subject: [PATCH 1/5] yanger: Handle the case when a container interface can not be found Due to #902 (which has not been root caused at this time), podman may indicate the existance of an interface that yanger is not able to locate in any namespace. Therefore, make sure that we verify that the interface was actually found before trying to create the operational data for it. --- src/statd/python/yanger/ietf_interfaces/container.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/statd/python/yanger/ietf_interfaces/container.py b/src/statd/python/yanger/ietf_interfaces/container.py index d7caf424..31c56cd4 100644 --- a/src/statd/python/yanger/ietf_interfaces/container.py +++ b/src/statd/python/yanger/ietf_interfaces/container.py @@ -16,6 +16,8 @@ def find_interface(cifname): ipaddrs = common.ipaddrs(ifname=iplink["ifname"], netns=ns["name"]) return (iplink, next(iter(ipaddrs.values()))) + return (None, None) + def podman_interfaces(): interfaces = {} @@ -31,6 +33,7 @@ def podman_interfaces(): return interfaces + def interfaces(ifname): interfaces = [] @@ -39,6 +42,9 @@ def interfaces(ifname): continue iplink, ipaddr = find_interface(cifname) + if not (iplink and ipaddr): + continue + interface = link.interface_common(iplink, ipaddr) # The original interface name is stored in ifalias by podman - From 19c1f49fd318632b1c569e7af4b48466b0f8b803 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 22 Jan 2025 15:53:41 +0100 Subject: [PATCH 2/5] test: Whitespace cleanup --- test/case/infix_services/ssh_key_authentication/test.py | 8 ++++---- test/infamy/ssh.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/case/infix_services/ssh_key_authentication/test.py b/test/case/infix_services/ssh_key_authentication/test.py index 0a68386f..862f8304 100755 --- a/test/case/infix_services/ssh_key_authentication/test.py +++ b/test/case/infix_services/ssh_key_authentication/test.py @@ -42,8 +42,8 @@ eTVndLHpDiTZO+tCIP+OO45uYA/8O+IMc+wvIsHCpZC7e3bskg6z2gt5B0DwSnf2Q4zXxP OFXdOBTFO8XcgWKRo9BIPV6BNH9qrx0Z0m5G45rY6SE9c5Ypv0ExjXRZ/iPWLBSarsv1fF lrH5aNT6hzZKsc8AAAAMcm9vdEBpbmZhbXkwAQIDBAUGBw== -----END OPENSSH PRIVATE KEY----- -""" - +""" + public_key_alg = "ssh-rsa" public_key_data = "AAAAB3NzaC1yc2EAAAADAQABAAABAQCffMvdK1VBkuw/12etfEr89Piba2NbQz7YY3ol2IhRnIoj0oodZp/a4c11c1Q+jQkbCDKPWB45w3ZMR2Q93XXeY72qtLzvCBLOzeCkX3czMAJLLxznl3EXmDnTAWetE1nUmByLLOvKdw1CPFiS9tushwdfj6iOfex3HvQwltV5i/rQz0h7JeSwEQ0Xn6sBgDxWlD6TtwYl4ch0zHoxRHCjqdQig7NMonhaF53BMKRYaUSLET3w+zKfK8HYiHxry6Jf+dcts9PQyxpCiEW3RoU65nwnq1pG7XAdmT/DeGKSQqXkdnmXvgZkrQYZ+4HW2veQlHzwwEIAhMTscS+QiOX/" @@ -69,7 +69,7 @@ lrH5aNT6hzZKsc8AAAAMcm9vdEBpbmZhbXkwAQIDBAUGBw== } } }) - + with test.step("Write private key to a temporary file"): with tempfile.NamedTemporaryFile(delete=False, mode='w') as key_file: key_file.write(private_key) @@ -90,5 +90,5 @@ lrH5aNT6hzZKsc8AAAAMcm9vdEBpbmZhbXkwAQIDBAUGBw== check=True, remove=True ) - + test.succeed() diff --git a/test/infamy/ssh.py b/test/infamy/ssh.py index 7e3395d1..9883f532 100644 --- a/test/infamy/ssh.py +++ b/test/infamy/ssh.py @@ -18,7 +18,7 @@ import os def fetch_file(remote_user, remote_address, remote_file, local_file, key_file, check=False, remove=False): """ Fetches a file over SSH using scp and the provided private key. - + :param remote_user: The user on the remote machine. :param remote_address: The address of the remote machine. :param remote_file: The file to fetch from the remote machine. @@ -36,13 +36,13 @@ def fetch_file(remote_user, remote_address, remote_file, local_file, key_file, c if check: if result.returncode != 0: raise RuntimeError("Failed to copy file from remote host") - + if not os.path.exists(local_file): raise RuntimeError(f"File {local_file} does not exist after copy") - + if os.path.getsize(local_file) == 0: raise RuntimeError(f"File {local_file} is empty after copy") - + except Exception as e: print(f"Error during file transfer: {e}") raise From 1d13c5ea9e9c1f6fd8ef1d331ea2611b01a97bcc Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 22 Jan 2025 20:16:47 +0100 Subject: [PATCH 3/5] test: infamy: Generalize netconf_syn() to test for any TCP service Being able to check for a TCP based service is generally useful, not only for NETCONF. Therefore, break it out to a separate function that allows any port to be queried. --- test/infamy/netconf.py | 10 +++------- test/infamy/netutil.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 test/infamy/netutil.py diff --git a/test/infamy/netconf.py b/test/infamy/netconf.py index bceba56c..1bb1f040 100644 --- a/test/infamy/netconf.py +++ b/test/infamy/netconf.py @@ -17,18 +17,14 @@ import netconf_client.connect import netconf_client.ncclient from infamy.transport import Transport,infer_put_dict from netconf_client.error import RpcError -from . import env +from . import env, netutil def netconf_syn(addr): - try: - ai = socket.getaddrinfo(addr, 830, 0, 0, socket.SOL_TCP) - sock = socket.socket(ai[0][0], ai[0][1], 0) - sock.connect(ai[0][4]) - sock.close() + if netutil.tcp_port_is_open(addr, 830): print(f"{addr} answers to TCP connections on port 830 (NETCONF)") return True - except Exception: + else: return False diff --git a/test/infamy/netutil.py b/test/infamy/netutil.py new file mode 100644 index 00000000..115d6e94 --- /dev/null +++ b/test/infamy/netutil.py @@ -0,0 +1,11 @@ +import socket + +def tcp_port_is_open(host, port): + try: + ai = socket.getaddrinfo(host, port, 0, 0, socket.SOL_TCP) + sock = socket.socket(ai[0][0], ai[0][1], 0) + sock.connect(ai[0][4]) + sock.close() + return True + except Exception: + return False From 1fd55c484ad69a5b1c73b9422efa6f17ee275689 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 22 Jan 2025 20:19:51 +0100 Subject: [PATCH 4/5] test: ssh_key_authentication: Consolidate and mark static data Use CAPTIALIZED names for static data and collect them at the top. Make a note about for future readers about why a test called ssh_key_authentication suspiciously does not enable the SSH service :) --- .../ssh_key_authentication/test.py | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/test/case/infix_services/ssh_key_authentication/test.py b/test/case/infix_services/ssh_key_authentication/test.py index 862f8304..0ae591e0 100755 --- a/test/case/infix_services/ssh_key_authentication/test.py +++ b/test/case/infix_services/ssh_key_authentication/test.py @@ -10,12 +10,9 @@ import os import tempfile from infamy import ssh -with infamy.Test() as test: - with test.step("Connect to the target device"): - env = infamy.Env() - target = env.attach("target", "mgmt") - - private_key = """-----BEGIN OPENSSH PRIVATE KEY----- +USER = "guest" +KEY = { + "private": """-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn NhAAAAAwEAAQAAAQEAn3zL3StVQZLsP9dnrXxK/PT4m2tjW0M+2GN6JdiIUZyKI9KKHWaf 2uHNdXNUPo0JGwgyj1geOcN2TEdkPd113mO9qrS87wgSzs3gpF93MzACSy8c55dxF5g50w @@ -42,14 +39,19 @@ eTVndLHpDiTZO+tCIP+OO45uYA/8O+IMc+wvIsHCpZC7e3bskg6z2gt5B0DwSnf2Q4zXxP OFXdOBTFO8XcgWKRo9BIPV6BNH9qrx0Z0m5G45rY6SE9c5Ypv0ExjXRZ/iPWLBSarsv1fF lrH5aNT6hzZKsc8AAAAMcm9vdEBpbmZhbXkwAQIDBAUGBw== -----END OPENSSH PRIVATE KEY----- -""" +""", + "public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQCffMvdK1VBkuw/12etfEr89Piba2NbQz7YY3ol2IhRnIoj0oodZp/a4c11c1Q+jQkbCDKPWB45w3ZMR2Q93XXeY72qtLzvCBLOzeCkX3czMAJLLxznl3EXmDnTAWetE1nUmByLLOvKdw1CPFiS9tushwdfj6iOfex3HvQwltV5i/rQz0h7JeSwEQ0Xn6sBgDxWlD6TtwYl4ch0zHoxRHCjqdQig7NMonhaF53BMKRYaUSLET3w+zKfK8HYiHxry6Jf+dcts9PQyxpCiEW3RoU65nwnq1pG7XAdmT/DeGKSQqXkdnmXvgZkrQYZ+4HW2veQlHzwwEIAhMTscS+QiOX/", +} - public_key_alg = "ssh-rsa" - public_key_data = "AAAAB3NzaC1yc2EAAAADAQABAAABAQCffMvdK1VBkuw/12etfEr89Piba2NbQz7YY3ol2IhRnIoj0oodZp/a4c11c1Q+jQkbCDKPWB45w3ZMR2Q93XXeY72qtLzvCBLOzeCkX3czMAJLLxznl3EXmDnTAWetE1nUmByLLOvKdw1CPFiS9tushwdfj6iOfex3HvQwltV5i/rQz0h7JeSwEQ0Xn6sBgDxWlD6TtwYl4ch0zHoxRHCjqdQig7NMonhaF53BMKRYaUSLET3w+zKfK8HYiHxry6Jf+dcts9PQyxpCiEW3RoU65nwnq1pG7XAdmT/DeGKSQqXkdnmXvgZkrQYZ+4HW2veQlHzwwEIAhMTscS+QiOX/" +with infamy.Test() as test: + with test.step("Connect to the target device"): + env = infamy.Env() + target = env.attach("target", "mgmt") with test.step("Create a guest user on the target device"): - USER = "guest" - + # Upon attachment, target reverts to `test-config`, in which + # the SSH service is enabled, hence we do not have to + # explicitly enable it here. target.put_config_dict("ietf-system", { "system": { "authentication": { @@ -59,8 +61,8 @@ lrH5aNT6hzZKsc8AAAAMcm9vdEBpbmZhbXkwAQIDBAUGBw== "authorized-key": [ { "name":"guest-ssh-key", - "algorithm":public_key_alg, - "key-data":public_key_data + "algorithm": "ssh-rsa", + "key-data": KEY["public"] } ], "infix-system:shell":"bash" @@ -72,7 +74,7 @@ lrH5aNT6hzZKsc8AAAAMcm9vdEBpbmZhbXkwAQIDBAUGBw== with test.step("Write private key to a temporary file"): with tempfile.NamedTemporaryFile(delete=False, mode='w') as key_file: - key_file.write(private_key) + key_file.write(KEY["private"]) key_file_name = key_file.name os.chmod(key_file_name, 0o600) From 0b2f53e3b4f3fb8113ade4196ad1e3b46f0a43df Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 22 Jan 2025 20:32:33 +0100 Subject: [PATCH 5/5] test: ssh_*: Wait for server start before further testing commences On slow systems, we don't want test steps involving tools like scp or ssh-keyscan to fail because the server had not come up yet. Therefore, make sure that the server is ready to accept connections before moving on to subsequent test steps. --- test/case/infix_services/ssh_key_authentication/test.py | 5 ++++- test/case/infix_services/ssh_server_config/test.py | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/test/case/infix_services/ssh_key_authentication/test.py b/test/case/infix_services/ssh_key_authentication/test.py index 0ae591e0..3be65326 100755 --- a/test/case/infix_services/ssh_key_authentication/test.py +++ b/test/case/infix_services/ssh_key_authentication/test.py @@ -8,7 +8,7 @@ Verify that 'guest' user can fetch data using only the 'public' key import infamy import os import tempfile -from infamy import ssh +from infamy import ssh, until, netutil USER = "guest" KEY = { @@ -72,6 +72,9 @@ with infamy.Test() as test: } }) + with test.step("Wait until SSH server is ready to accept connections"): + until(lambda: netutil.tcp_port_is_open(target.get_mgmt_ip(), 22)) + with test.step("Write private key to a temporary file"): with tempfile.NamedTemporaryFile(delete=False, mode='w') as key_file: key_file.write(KEY["private"]) diff --git a/test/case/infix_services/ssh_server_config/test.py b/test/case/infix_services/ssh_server_config/test.py index 5766c08c..72f72da2 100755 --- a/test/case/infix_services/ssh_server_config/test.py +++ b/test/case/infix_services/ssh_server_config/test.py @@ -9,7 +9,9 @@ Test SSH server functionality with pre-defined key pair: """ import subprocess + import infamy +from infamy import until, netutil PRIVATE_KEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCtzyoT8/23hSyo7trLaqc6Auj5jvwhhCxUh8WIyfd5G/R9R+/wFEtGo6c6h75/GFCotFCQYvLlqHkrI0QiLYCPo0Rcxzfpy8TZGYjlyD8aYTYeXR2Oow6cjHE3ajQEEPbr5eiV/NBezg00SrCazDN4VHEXcjhl4egaKxDyG1yi98kQISY0+ehNjR/CKOBvOxHqB0N7gUnasbBiiN/iCCkCuDFKnBM6cYrUAvwP/aj+f9dq4lImJ6YpHaSjFKIa2ZFmOi20X0cb0AS1cshjSU2qf9eS2nysbmlC50X8HL9gaIeVInsWTLxEHTrd2gyBCTPO3X6oLJWW7HoB+yA9wp6xAgMBAAECggEACDNXrsaSrFmfFm7jmZikAHmR7LFlfb7W2RupUyeFUrxiDBWscVFBznjK+3jbYPOAnb8ZNIDIqVOKOQHyVWL8d3p6b56yKYik1mHtKrtIl+npg+P8kKXqmvII5vaOsvjqb42izE3X9nsmFhcmjz0uegFQ7yxjUxJGMVLiGyw1khZHFLxAcCzwN2qnxni7MjU2d+ZAtNd4ilSjXZ46Q/6+CyrhoTDUhc+5iqCgXU2wtYWrnvEhCBFd3AYh1vWZuh1TxMgnsfYePk5fHM1AG10XUvI5jOjSkSN+AlJxuXeUSeLyUV4hekem/j/UT3KwVPAiEsBil4KWyneiildXxU5MaQKBgQDm1667T06I/ty6/KZSdfm4EpDKylHohdN8Vr0MfgZSzZgc3bNNMQxAXhGdTIi2keIpitoF+/vLiMhxa9q692XY85eKSOC0Lvv5IRUC9/fUtoKrESOoxwX8SJ3bHj/Xel7Ye0WOXVJcO1/PXO0KFgs2YDRdmQKMFNKS/CdK+2TuCQKBgQDAwEzQ7B3cRYp4R2s230wpSSsPkiJXDQLno82S8K2/vLuWnlwIL3A1833l7PDfp5APABU3EVpQ7EYE6usnO1/HDSZ208uiprx6LIbX0gZVoRnPOKFwRVD7zrYo1n11Lydg8OgKtey5GsruPRbLtAw3/ugayUDCUExXmYlFQLRVaQKBgG+NTrzpiDQfpR8fNGio5jITlrDIsGhDM33klJrS089z1swsPpdQ2nDIhI6VC4PeX4JfvRgjOvySbvqQejTblPYQUOzcZunrwowTdonmtnauc9qi/65x7uyJUu8uYP+J/Qd0Gpq/citr7dLRPyMen/B48RVB+b8j2NZ6z6ombhGxAoGAY2OE+IGX0Bnnkae55/xyKCO7WXcPz/U8lzbGbMs/vEtUKxETAYF8icU5GNL5TUn4pVN0nQWMnYeHf0em437hHyFvwPvq177EFvdYvHZmn8bHKSvZSqvjW0Q2d45J+J/M3Va7P7KZEsV2+Ct10qnPVxxQkGdPxiJjixP3TUdU9WkCgYEAtHa4cwsbgy0HWtNT2smc80jLGFfsX8+/MtgTVdx6zaTybl50hJeVG4kW+7Fvstr78iVl31qPWx14MjoXKTEeVMo6ulrEijnbCx6DgkOwq+EOUvZn0W7ly4RhDDA9W8qdBIAzAGumkCx4456Un3z8wbIVgSZB52IELCBKpbyhSWE=" @@ -99,6 +101,9 @@ with infamy.Test() as test: } }) + with test.step("Wait until SSH server is ready to accept connections"): + until(lambda: netutil.tcp_port_is_open(target.get_mgmt_ip(), 22)) + with test.step("Verify SSH public keys"): _, hport1 = env.ltop.xlate("host", "data1") _, hport2 = env.ltop.xlate("host", "data2")