From 0a7e204d2edea74f2da4f7415ca524fa08eaac69 Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Thu, 13 Oct 2022 11:55:36 +0800 Subject: [PATCH] CI:add openthread ipv6 ra check in openthread CI --- .gitlab/ci/rules.yml | 1 + examples/openthread/ot_ci_function.py | 147 +++++++++++++++++++++++--- examples/openthread/pytest_otbr.py | 145 +++++++++++++------------ 3 files changed, 206 insertions(+), 87 deletions(-) diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index 0900492745..51ca98639a 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -88,6 +88,7 @@ - "components/esp_phy/**/*" - "components/ieee802154/**/*" - "components/lwip/**/*" + - "components/openthread/**/*" - "examples/common_components/iperf/**/*" - "examples/openthread/**/*" diff --git a/examples/openthread/ot_ci_function.py b/examples/openthread/ot_ci_function.py index b71e7f19e7..ac49307dd8 100644 --- a/examples/openthread/ot_ci_function.py +++ b/examples/openthread/ot_ci_function.py @@ -4,6 +4,8 @@ # this file defines some functions for testing cli and br under pytest framework import re +import socket +import struct import subprocess import time from typing import Tuple, Union @@ -14,10 +16,12 @@ from pytest_embedded_idf.dut import IdfDut def reset_thread(dut:IdfDut) -> None: - time.sleep(1) + dut.write(' ') + dut.write('state') + clean_buffer(dut) + wait(dut, 1) dut.write('factoryreset') - time.sleep(3) - dut.expect('OpenThread attached to netif', timeout=10) + dut.expect('OpenThread attached to netif', timeout=20) dut.write(' ') dut.write('state') @@ -92,18 +96,30 @@ def start_thread(dut:IdfDut) -> str: return role +def wait_key_str(leader:IdfDut, child:IdfDut) -> None: + wait(leader, 1) + leader.expect('OpenThread attached to netif', timeout=20) + leader.write(' ') + leader.write('state') + child.expect('OpenThread attached to netif', timeout=20) + child.write(' ') + child.write('state') + + +def config_network(leader:IdfDut, child:IdfDut, leader_name:str, thread_dataset_model:str, + thread_dataset:str, wifi:IdfDut, wifi_ssid:str, wifi_psk:str) -> str: + wait_key_str(leader, child) + return form_network_using_manual_configuration(leader, child, leader_name, thread_dataset_model, + thread_dataset, wifi, wifi_ssid, wifi_psk) + + # config br and cli manually def form_network_using_manual_configuration(leader:IdfDut, child:IdfDut, leader_name:str, thread_dataset_model:str, thread_dataset:str, wifi:IdfDut, wifi_ssid:str, wifi_psk:str) -> str: - time.sleep(3) - leader.expect('OpenThread attached to netif', timeout=10) - leader.write(' ') - leader.write('state') - child.expect('OpenThread attached to netif', timeout=10) - child.write(' ') - child.write('state') reset_thread(leader) + clean_buffer(leader) reset_thread(child) + clean_buffer(child) leader.write('channel 12') leader.expect('Done', timeout=2) child.write('channel 12') @@ -130,6 +146,7 @@ def form_network_using_manual_configuration(leader:IdfDut, child:IdfDut, leader_ child.expect('Done', timeout=2) role = start_thread(child) assert role == 'child' + wait(leader, 10) return res @@ -152,7 +169,8 @@ def connect_wifi(dut:IdfDut, ssid:str, psk:str, nums:int) -> Tuple[str, int]: for order in range(1, nums): dut.write('wifi connect -s ' + str(ssid) + ' -p ' + str(psk)) tmp = dut.expect(pexpect.TIMEOUT, timeout=5) - ip_address = re.findall(r'sta ip: (\w+.\w+.\w+.\w+),', str(tmp))[0] + if 'sta ip' in str(tmp): + ip_address = re.findall(r'sta ip: (\w+.\w+.\w+.\w+),', str(tmp))[0] information = dut.expect(r'wifi sta (\w+ \w+ \w+)\W', timeout=5)[1].decode() if information == 'is connected successfully': break @@ -166,13 +184,13 @@ def reset_host_interface() -> None: try: command = 'ifconfig ' + interface_name + ' down' subprocess.call(command, shell=True, timeout=5) - time.sleep(10) + time.sleep(1) command = 'ifconfig ' + interface_name + ' up' subprocess.call(command, shell=True, timeout=10) - time.sleep(20) + time.sleep(1) flag = True finally: - time.sleep(10) + time.sleep(1) assert flag @@ -185,10 +203,10 @@ def set_interface_sysctl_options() -> None: time.sleep(1) command = 'sysctl -w net/ipv6/conf/' + interface_name + '/accept_ra_rt_info_max_plen=128' subprocess.call(command, shell=True, timeout=5) - time.sleep(5) + time.sleep(1) flag = True finally: - time.sleep(5) + time.sleep(2) assert flag @@ -207,7 +225,7 @@ def init_interface_ipv6_address() -> None: time.sleep(1) flag = True finally: - time.sleep(5) + time.sleep(1) assert flag @@ -220,3 +238,98 @@ def get_host_interface_name() -> str: def clean_buffer(dut:IdfDut) -> None: str_length = str(len(dut.expect(pexpect.TIMEOUT, timeout=0.1))) dut.expect(r'[\s\S]{%s}' % str(str_length), timeout=10) + + +def check_if_host_receive_ra(br:IdfDut) -> bool: + interface_name = get_host_interface_name() + clean_buffer(br) + br.write('br omrprefix') + omrprefix = br.expect(r'\n((?:\w+:){4}):/\d+\r', timeout=5)[1].decode() + command = 'ip -6 route | grep ' + str(interface_name) + out_str = subprocess.getoutput(command) + print('br omrprefix: ', str(omrprefix)) + print('host route table:\n', str(out_str)) + return str(omrprefix) in str(out_str) + + +def host_connect_wifi() -> None: + command = '. /home/test/wlan_connection_OTTE.sh' + subprocess.call(command, shell=True, timeout=30) + time.sleep(5) + + +def is_joined_wifi_network(br:IdfDut) -> bool: + return check_if_host_receive_ra(br) + + +thread_ipv6_group = 'ff04:0:0:0:0:0:0:125' + + +def check_ipmaddr(dut:IdfDut) -> bool: + clean_buffer(dut) + dut.write('ipmaddr') + info = dut.expect(pexpect.TIMEOUT, timeout=2) + if thread_ipv6_group in str(info): + return True + return False + + +def thread_is_joined_group(dut:IdfDut) -> bool: + command = 'mcast join ' + thread_ipv6_group + dut.write(command) + dut.expect('Done', timeout=2) + order = 0 + while order < 3: + if check_ipmaddr(dut): + return True + dut.write(command) + wait(dut, 2) + order = order + 1 + return False + + +host_ipv6_group = 'ff04::125' + + +def host_joined_group() -> bool: + interface_name = get_host_interface_name() + command = 'netstat -g | grep ' + str(interface_name) + out_str = subprocess.getoutput(command) + print('groups:\n', str(out_str)) + return host_ipv6_group in str(out_str) + + +class udp_parameter: + + def __init__(self, group:str='', try_join_udp_group:bool=False, timeout:float=15.0, udp_bytes:bytes=b''): + self.group = group + self.try_join_udp_group = try_join_udp_group + self.timeout = timeout + self.udp_bytes = udp_bytes + + +def create_host_udp_server(myudp:udp_parameter) -> None: + interface_name = get_host_interface_name() + try: + print('The host start to create udp server!') + if_index = socket.if_nametoindex(interface_name) + sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + sock.bind(('::', 5090)) + sock.setsockopt( + socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, + struct.pack('16si', socket.inet_pton(socket.AF_INET6, myudp.group), + if_index)) + myudp.try_join_udp_group = True + sock.settimeout(myudp.timeout) + print('The host start to receive message!') + myudp.udp_bytes = (sock.recvfrom(1024))[0] + print('The host has received message: ', myudp.udp_bytes) + except socket.error: + print('The host did not receive message!') + finally: + print('Close the socket.') + sock.close() + + +def wait(dut:IdfDut, wait_time:float) -> None: + dut.expect(pexpect.TIMEOUT, timeout=wait_time) diff --git a/examples/openthread/pytest_otbr.py b/examples/openthread/pytest_otbr.py index 2130183641..6689dcf52a 100644 --- a/examples/openthread/pytest_otbr.py +++ b/examples/openthread/pytest_otbr.py @@ -5,9 +5,8 @@ import os.path import re -import socket -import struct import subprocess +import threading import time from typing import Tuple @@ -34,32 +33,37 @@ from pytest_embedded_idf.dut import IdfDut def fixture_Init_interface() -> bool: ocf.init_interface_ipv6_address() ocf.reset_host_interface() + time.sleep(30) ocf.set_interface_sysctl_options() return True +wifi_ssid = 'OTCITE' +wifi_psk = 'otcitest888' + + # Case 1: Thread network formation and attaching @pytest.mark.esp32s3 @pytest.mark.esp32h2 +@pytest.mark.timeout(40 * 60) @pytest.mark.i154_multi_dut -@pytest.mark.flaky(reruns=2, reruns_delay=10) @pytest.mark.parametrize( 'port, config, count, app_path, beta_target, target', [ - ('/dev/USB_BR|/dev/USB_CLI|/dev/USB_RCP', 'br|cli|rcp', 3, - f'{os.path.join(os.path.dirname(__file__), "ot_br")}' + ('/dev/USB_RCP|/dev/USB_CLI|/dev/USB_BR', 'rcp|cli|br', 3, + f'{os.path.join(os.path.dirname(__file__), "ot_rcp")}' f'|{os.path.join(os.path.dirname(__file__), "ot_cli")}' - f'|{os.path.join(os.path.dirname(__file__), "ot_rcp")}', - 'esp32s3|esp32h2beta2|esp32h2beta2', 'esp32s3|esp32h2|esp32h2'), + f'|{os.path.join(os.path.dirname(__file__), "ot_br")}', + 'esp32h2beta2|esp32h2beta2|esp32s3', 'esp32h2|esp32h2|esp32s3'), ], indirect=True, ) -def test_thread_connect(dut:Tuple[IdfDut, IdfDut]) -> None: - br = dut[0] +def test_thread_connect(dut:Tuple[IdfDut, IdfDut, IdfDut]) -> None: + br = dut[2] cli = dut[1] + dut[0].close() dataset = '-1' - ocf.form_network_using_manual_configuration(br, cli, 'br', 'random', dataset, br, 'OTCITE', '0000') - time.sleep(1) + ocf.config_network(br, cli, 'br', 'random', dataset, br, wifi_ssid, '0000') flag = False try: cli_mleid_addr = ocf.get_mleid_addr(cli) @@ -79,32 +83,34 @@ def test_thread_connect(dut:Tuple[IdfDut, IdfDut]) -> None: # Case 2: Bidirectional IPv6 connectivity @pytest.mark.esp32s3 @pytest.mark.esp32h2 +@pytest.mark.timeout(40 * 60) @pytest.mark.i154_multi_dut -@pytest.mark.flaky(reruns=5, reruns_delay=10) @pytest.mark.parametrize( 'port, config, count, app_path, beta_target, target', [ - ('/dev/USB_BR|/dev/USB_CLI|/dev/USB_RCP', 'br|cli|rcp', 3, - f'{os.path.join(os.path.dirname(__file__), "ot_br")}' + ('/dev/USB_RCP|/dev/USB_CLI|/dev/USB_BR', 'rcp|cli|br', 3, + f'{os.path.join(os.path.dirname(__file__), "ot_rcp")}' f'|{os.path.join(os.path.dirname(__file__), "ot_cli")}' - f'|{os.path.join(os.path.dirname(__file__), "ot_rcp")}', - 'esp32s3|esp32h2beta2|esp32h2beta2', 'esp32s3|esp32h2|esp32h2'), + f'|{os.path.join(os.path.dirname(__file__), "ot_br")}', + 'esp32h2beta2|esp32h2beta2|esp32s3', 'esp32h2|esp32h2|esp32s3'), ], indirect=True, ) -def test_Bidirectional_IPv6_connectivity(Init_interface:bool, dut: Tuple[IdfDut, IdfDut]) -> None: - br = dut[0] +def test_Bidirectional_IPv6_connectivity(Init_interface:bool, dut: Tuple[IdfDut, IdfDut, IdfDut]) -> None: + br = dut[2] cli = dut[1] assert Init_interface + dut[0].close() dataset = '-1' - ocf.form_network_using_manual_configuration(br, cli, 'br', 'random', dataset, br, 'OTCITE', 'otcitest888') - time.sleep(5) - cli_global_unicast_addr = ocf.get_global_unicast_addr(cli, br) + ocf.config_network(br, cli, 'br', 'random', dataset, br, wifi_ssid, wifi_psk) flag = False try: + assert ocf.is_joined_wifi_network(br) + cli_global_unicast_addr = ocf.get_global_unicast_addr(cli, br) + print('cli_global_unicast_addr', cli_global_unicast_addr) command = 'ping ' + str(cli_global_unicast_addr) + ' -c 10' - out_bytes = subprocess.check_output(command, shell=True, timeout=60) - out_str = out_bytes.decode('utf-8') + out_str = subprocess.getoutput(command) + print('ping result:\n', str(out_str)) role = re.findall(r' (\d+)%', str(out_str))[0] assert role != '100' interface_name = ocf.get_host_interface_name() @@ -128,37 +134,36 @@ def test_Bidirectional_IPv6_connectivity(Init_interface:bool, dut: Tuple[IdfDut, # Case 3: Multicast forwarding from Wi-Fi to Thread network @pytest.mark.esp32s3 @pytest.mark.esp32h2 +@pytest.mark.timeout(40 * 60) @pytest.mark.i154_multi_dut -@pytest.mark.flaky(reruns=5, reruns_delay=10) @pytest.mark.parametrize( 'port, config, count, app_path, beta_target, target', [ - ('/dev/USB_BR|/dev/USB_CLI|/dev/USB_RCP', 'br|cli|rcp', 3, - f'{os.path.join(os.path.dirname(__file__), "ot_br")}' + ('/dev/USB_RCP|/dev/USB_CLI|/dev/USB_BR', 'rcp|cli|br', 3, + f'{os.path.join(os.path.dirname(__file__), "ot_rcp")}' f'|{os.path.join(os.path.dirname(__file__), "ot_cli")}' - f'|{os.path.join(os.path.dirname(__file__), "ot_rcp")}', - 'esp32s3|esp32h2beta2|esp32h2beta2', 'esp32s3|esp32h2|esp32h2'), + f'|{os.path.join(os.path.dirname(__file__), "ot_br")}', + 'esp32h2beta2|esp32h2beta2|esp32s3', 'esp32h2|esp32h2|esp32s3'), ], indirect=True, ) -def test_multicast_forwarding_A(Init_interface:bool, dut: Tuple[IdfDut, IdfDut]) -> None: - br = dut[0] +def test_multicast_forwarding_A(Init_interface:bool, dut: Tuple[IdfDut, IdfDut, IdfDut]) -> None: + br = dut[2] cli = dut[1] assert Init_interface + dut[0].close() dataset = '-1' - ocf.form_network_using_manual_configuration(br, cli, 'br', 'random', dataset, br, 'OTCITE', 'otcitest888') - time.sleep(5) + ocf.config_network(br, cli, 'br', 'random', dataset, br, wifi_ssid, wifi_psk) flag = False try: + assert ocf.is_joined_wifi_network(br) br.write('bbr') br.expect('server16', timeout=2) - cli.write('mcast join ff04::125') - cli.expect('Done', timeout=2) - time.sleep(1) + assert ocf.thread_is_joined_group(cli) interface_name = ocf.get_host_interface_name() command = 'ping -I ' + str(interface_name) + ' -t 64 ff04::125 -c 10' - out_bytes = subprocess.check_output(command, shell=True, timeout=60) - out_str = out_bytes.decode('utf-8') + out_str = subprocess.getoutput(command) + print('ping result:\n', str(out_str)) role = re.findall(r' (\d+)%', str(out_str))[0] assert role != '100' flag = True @@ -172,52 +177,52 @@ def test_multicast_forwarding_A(Init_interface:bool, dut: Tuple[IdfDut, IdfDut]) # Case 4: Multicast forwarding from Thread to Wi-Fi network @pytest.mark.esp32s3 @pytest.mark.esp32h2 +@pytest.mark.timeout(40 * 60) @pytest.mark.i154_multi_dut -@pytest.mark.flaky(reruns=5, reruns_delay=5) @pytest.mark.parametrize( 'port, config, count, app_path, beta_target, target', [ - ('/dev/USB_BR|/dev/USB_CLI|/dev/USB_RCP', 'br|cli|rcp', 3, - f'{os.path.join(os.path.dirname(__file__), "ot_br")}' + ('/dev/USB_RCP|/dev/USB_CLI|/dev/USB_BR', 'rcp|cli|br', 3, + f'{os.path.join(os.path.dirname(__file__), "ot_rcp")}' f'|{os.path.join(os.path.dirname(__file__), "ot_cli")}' - f'|{os.path.join(os.path.dirname(__file__), "ot_rcp")}', - 'esp32s3|esp32h2beta2|esp32h2beta2', 'esp32s3|esp32h2|esp32h2'), + f'|{os.path.join(os.path.dirname(__file__), "ot_br")}', + 'esp32h2beta2|esp32h2beta2|esp32s3', 'esp32h2|esp32h2|esp32s3'), ], indirect=True, ) -def test_multicast_forwarding_B(Init_interface:bool, dut: Tuple[IdfDut, IdfDut]) -> None: - br = dut[0] +def test_multicast_forwarding_B(Init_interface:bool, dut: Tuple[IdfDut, IdfDut, IdfDut]) -> None: + br = dut[2] cli = dut[1] assert Init_interface + dut[0].close() dataset = '-1' - ocf.form_network_using_manual_configuration(br, cli, 'br', 'random', dataset, br, 'OTCITE', 'otcitest888') - time.sleep(5) - br.write('bbr') - br.expect('server16', timeout=2) - interface_name = ocf.get_host_interface_name() - if_index = socket.if_nametoindex(interface_name) - sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) - sock.bind(('::', 5090)) - sock.setsockopt( - socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, - struct.pack('16si', socket.inet_pton(socket.AF_INET6, 'ff04::125'), - if_index)) - time.sleep(1) - cli.write('udp open') - cli.expect('Done', timeout=2) - cli.write('udp send ff04::125 5090 hello') - cli.expect('Done', timeout=2) - data = b'' + ocf.config_network(br, cli, 'br', 'random', dataset, br, wifi_ssid, wifi_psk) try: - print('The host start to receive message!') - sock.settimeout(5) - data = (sock.recvfrom(1024))[0] - print('The host has received message!') - except socket.error: - print('The host did not received message!') + assert ocf.is_joined_wifi_network(br) + br.write('bbr') + br.expect('server16', timeout=2) + cli.write('udp open') + cli.expect('Done', timeout=2) + ocf.wait(cli, 1) + cli.write('udp open') + cli.expect('Already', timeout=2) + myudp = ocf.udp_parameter('ff04::125', False, 15.0, b'') + udp_mission = threading.Thread(target=ocf.create_host_udp_server, args=(myudp, )) + udp_mission.start() + start_time = time.time() + while not myudp.try_join_udp_group: + if (time.time() - start_time) > 10: + assert False + assert ocf.host_joined_group() + for num in range(0, 3): + command = 'udp send ff04::125 5090 hello' + str(num) + cli.write(command) + cli.expect('Done', timeout=2) + ocf.wait(cli, 0.5) + while udp_mission.is_alive(): + time.sleep(1) finally: - sock.close() br.write('factoryreset') cli.write('factoryreset') time.sleep(3) - assert data == b'hello' + assert b'hello' in myudp.udp_bytes