From 4f76a0a24156e52580f662dd5e248cc4b1cdd7d3 Mon Sep 17 00:00:00 2001 From: rickwu4444 Date: Thu, 30 Nov 2023 11:14:33 +0800 Subject: [PATCH] Add TCP multi-connection test --- .../bin/tcp_multi_connections.py | 89 ++++++++++++------- .../units/ethernet/jobs.pxu | 22 +++++ .../units/ethernet/manifest.pxu | 9 ++ .../units/ethernet/test-plan.pxu | 25 ++++++ .../units/test-plan-ce-oem.pxu | 2 + 5 files changed, 113 insertions(+), 34 deletions(-) create mode 100644 checkbox-provider-ce-oem/units/ethernet/manifest.pxu diff --git a/checkbox-provider-ce-oem/bin/tcp_multi_connections.py b/checkbox-provider-ce-oem/bin/tcp_multi_connections.py index 42c37a1..2eb9f16 100755 --- a/checkbox-provider-ce-oem/bin/tcp_multi_connections.py +++ b/checkbox-provider-ce-oem/bin/tcp_multi_connections.py @@ -24,7 +24,7 @@ def server(start_port, end_port): - start_port (int): Starting port for the server. - end_port (int): Ending port for the server. """ - for port in range(start_port, end_port): + for port in range(start_port, end_port+1): threading.Thread(target=handle_port, args=(port,)).start() @@ -69,7 +69,7 @@ def handle_client(conn, addr): conn.sendall(received_data) -def client(host, start_port, end_port, payload, done_event, run_time): +def client(host, start_port, end_port, payload, done_event, start_time): """ Start the client to connect to a range of server ports. @@ -79,12 +79,12 @@ def client(host, start_port, end_port, payload, done_event, run_time): - end_port (int): Ending port for the client. - payload (str): Payload to send to the server. - done_event (threading.Event): Event to signal when the client is done. - - run_time (datetime): Time until which the client should run. + - start_time (datetime): Time until which the client should run. """ threads = [] - for port in range(start_port, end_port+5): + for port in range(start_port, end_port+1): thread = threading.Thread(target=send_payload, - args=(host, port, payload, run_time)) + args=(host, port, payload, start_time)) threads.append(thread) thread.start() @@ -95,7 +95,7 @@ def client(host, start_port, end_port, payload, done_event, run_time): done_event.set() -def send_payload(host, port, payload, run_time): +def send_payload(host, port, payload, start_time): """ Send a payload to the specified port and handle the server response. @@ -103,19 +103,27 @@ def send_payload(host, port, payload, run_time): - host (str): Server host. - port (int): Port to connect to. - payload (str): Payload to send to the server. - - run_time (datetime): Time until which the client should run. + - start_time (datetime): Time until which the client should run. """ try: with socket.create_connection( (host, port), timeout=120) as client_socket: logging.info("Connect to port {}".format(port)) - while datetime.now() < run_time: + while datetime.now() < start_time: time.sleep(1) - for i in range(10): - logging.info("Sending payload to port {}.".format(port)) + logging.info("Sending payload to port {}.".format(port)) + # Sending payload for 30 sec after start sending. + count = 0 + while datetime.now() < start_time + timedelta(seconds=30): client_socket.sendall(payload.encode()) - # Introduce a random interval between 0.1 and 0.5 seconds - interval_time = random.uniform(0.1, 0.5) + ''' + Introduce a random interval between 1 and 3 seconds. + We try to control each port will send 64K payload + at least 10 times in 30 sec, but not more than 15 times + since too much traffic may take too long to test. + ''' + interval_time = random.uniform(2, 3) + count = count+1 time.sleep(interval_time) client_socket.shutdown(socket.SHUT_WR) received_data = b'' @@ -125,16 +133,16 @@ def send_payload(host, port, payload, run_time): break received_data += data logging.info("Server response from port {}.".format(port)) - if received_data == payload.encode()*10: + if received_data == payload.encode()*count: results.append("{}:PASS".format(port)) else: results.append("{}:FAIL".format(port)) except socket.error as e: - logging.info("Error connecting to port {}: {}".format(port, e)) + logging.error("{} on port {}".format(e, port)) results.append("{}:ERROR".format(port)) except Exception as e: - logging.info("An unexpected error occurred for port {}: {}" - .format(port, e)) + logging.error("{}: An unexpected error occurred for port {}" + .format(e, port)) results.append("{}:ERROR".format(port)) @@ -148,21 +156,21 @@ def send_payload(host, port, payload, run_time): these ports to send a payload and receive a response from the server. Usage: - - To run as a server: ./script.py server + - To run as a server: ./script.py server -p -e - To run as a client: ./script.py client -H -p -e -P Arguments: - mode (str): Specify whether to run as a server or client. - - host (str): Server host (client mode). Default is "localhost". + - host (str): Server host IP (client mode). This is mandatory arg. - port (int): Starting port for the server or server port for the client. Default is 1024. - payload (int): Payload size in KB for the client. Default is 64. - - end_port (int): Ending port for the server. Default is 1224. + - end_port (int): Ending port for the server. Default is 1223. Server Mode: - The server listens on a range of ports concurrently, handling - incoming connections and logging received data. + incoming connections and send the received data back to client. Client Mode: - The client connects to a range of server ports, @@ -172,30 +180,43 @@ def send_payload(host, port, payload, run_time): parser = argparse.ArgumentParser( description="Client-server with payload check on multiple ports") - parser.add_argument("mode", choices=["server", "client"], - help="Run as server or client") - parser.add_argument("-H", "--host", default="localhost", - help="Server host (client mode)") - parser.add_argument("-p", "--port", type=int, default=1024, - help="Starting port for server or server " - "port (client mode)") - parser.add_argument("-P", "--payload", type=int, default=64, - help="Payload size in KB (client mode)") - parser.add_argument("-e", "--end-port", type=int, default=1224, - help="Ending port for server (client mode)") + + subparsers = parser.add_subparsers(dest="mode", + help="Run as server or client") + + # Subparser for the server command + server_parser = subparsers.add_parser("server", help="Run as server") + server_parser.add_argument("-p", "--port", + type=int, default=1024, + help="Starting port for the server") + server_parser.add_argument("-e", "--end-port", type=int, default=1223, + help="Ending port for the server") + + # Subparser for the client command + client_parser = subparsers.add_parser("client", help="Run as client") + client_parser.add_argument("-H", "--host", required=True, + help="Server host (client mode)") + client_parser.add_argument("-p", "--port", type=int, default=1024, + help="Starting port for the client") + client_parser.add_argument("-P", "--payload", type=int, default=64, + help="Payload size in KB (client mode)") + client_parser.add_argument("-e", "--end-port", type=int, default=1223, + help="Ending port for the client") args = parser.parse_args() done_event = threading.Event() results = [] - payload = 'A' * (args.payload * 1024) + # Ramp up time to wait until all ports are connected before # starting to send the payload. - run_time = datetime.now() + timedelta(seconds=30) + start_time = datetime.now() + timedelta(seconds=30) + if args.mode == "server": server(args.port, args.end_port) elif args.mode == "client": + payload = 'A' * (args.payload * 1024) client(args.host, args.port, args.end_port, - payload, done_event, run_time) + payload, done_event, start_time) # Wait for all threads to finish done_event.wait() diff --git a/checkbox-provider-ce-oem/units/ethernet/jobs.pxu b/checkbox-provider-ce-oem/units/ethernet/jobs.pxu index 37174f0..abaa1de 100644 --- a/checkbox-provider-ce-oem/units/ethernet/jobs.pxu +++ b/checkbox-provider-ce-oem/units/ethernet/jobs.pxu @@ -16,4 +16,26 @@ _description: environ: TCP_ECHO_SERVER_IP TCP_ECHO_SERVER_PORT TCP_ECHO_LOOP_ITERATIONS estimated_duration: 10h flags: also-after-suspend +requires: manifest.has_tcp_echo_stress_server == 'True' +imports: from com.canonical.plainbox import manifest command: tcpecho_stress.sh -s {{ interface }} -i "$TCP_ECHO_SERVER_IP" -p "$TCP_ECHO_SERVER_PORT" -l "$TCP_ECHO_LOOP_ITERATIONS" -o "${PLAINBOX_SESSION_SHARE}"/tcp_echo.log + +id: ce-oem-ethernet/tcp-multi-connections +plugin: shell +user: root +category_id: com.canonical.plainbox::ethernet +_summary: Check if the system can handle multiple connections on TCP without error. +_description: + This job will connect to server listened ports(200 ports in total), + and send the payload(64KB) for 10 times of each port. This job will + send the payload after all ports connection is established. + Need a server to run the following command before running the test. + e.g. Run a server to listen on port range from 1024 to 1223. + $ tcp_multi_connections.py server -p 1024 -e 1223 +environ: TCP_MULTI_CONNECTIONS_SERVER_IP TCP_MULTI_CONNECTIONS_START_PORT TCP_MULTI_CONNECTIONS_END_PORT TCP_MULTI_CONNECTIONS_PAYLOAD_SIZE +estimated_duration: 300 +flags: also-after-suspend +requires: manifest.has_tcp_multi_connection_server == 'True' +imports: from com.canonical.plainbox import manifest +command: + tcp_multi_connections.py client -H "$TCP_MULTI_CONNECTIONS_SERVER_IP" -p "$TCP_MULTI_CONNECTIONS_START_PORT" -e "$TCP_MULTI_CONNECTIONS_END_PORT" -P "$TCP_MULTI_CONNECTIONS_PAYLOAD_SIZE" diff --git a/checkbox-provider-ce-oem/units/ethernet/manifest.pxu b/checkbox-provider-ce-oem/units/ethernet/manifest.pxu new file mode 100644 index 0000000..cc61c89 --- /dev/null +++ b/checkbox-provider-ce-oem/units/ethernet/manifest.pxu @@ -0,0 +1,9 @@ +unit: manifest entry +id: has_tcp_multi_connection_server +_name: Has the TCP multi-connection server been set up? +value-type: bool + +unit: manifest entry +id: has_tcp_echo_stress_server +_name: Has the TCP echo stress server been set up? +value-type: bool diff --git a/checkbox-provider-ce-oem/units/ethernet/test-plan.pxu b/checkbox-provider-ce-oem/units/ethernet/test-plan.pxu index bddeffd..a191f2d 100644 --- a/checkbox-provider-ce-oem/units/ethernet/test-plan.pxu +++ b/checkbox-provider-ce-oem/units/ethernet/test-plan.pxu @@ -7,3 +7,28 @@ bootstrap_include: com.canonical.certification::device include: ce-oem-ethernet/tcp-echo-stress-.* + +id: ce-oem-tcp-full +unit: test plan +_name: TCP connection tests +_description: TCP connection test of the device +include: +nested_part: + ce-oem-ethernet-tcp-automated + after-suspend-ce-oem-ethernet-tcp-automated + +id: ce-oem-ethernet-tcp-automated +unit: test plan +_name: TCP connection test plan +_description: TCP connection test +estimated_duration: 300 +include: + ce-oem-ethernet/tcp-multi-connections + +id: after-suspend-ce-oem-ethernet-tcp-automated +unit: test plan +_name: TCP connection test plan +_description: TCP connection test +estimated_duration: 300 +include: + after-suspend-ce-oem-ethernet/tcp-multi-connections diff --git a/checkbox-provider-ce-oem/units/test-plan-ce-oem.pxu b/checkbox-provider-ce-oem/units/test-plan-ce-oem.pxu index 6dc72e4..62cefb9 100644 --- a/checkbox-provider-ce-oem/units/test-plan-ce-oem.pxu +++ b/checkbox-provider-ce-oem/units/test-plan-ce-oem.pxu @@ -73,6 +73,7 @@ nested_part: ce-oem-crypto-automated ce-oem-optee-automated ce-oem-socketcan-stress-automated + ce-oem-ethernet-tcp-automated com.canonical.certification::eeprom-automated com.canonical.certification::rtc-automated @@ -130,6 +131,7 @@ nested_part: after-suspend-ce-oem-crypto-automated after-suspend-ce-oem-optee-automated after-suspend-ce-oem-socketcan-stress-automated + after-suspend-ce-oem-ethernet-tcp-automated com.canonical.certification::after-suspend-eeprom-automated com.canonical.certification::after-suspend-rtc-automated