ot_ci_function.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: Unlicense OR CC0-1.0
  3. # !/usr/bin/env python3
  4. # this file defines some functions for testing cli and br under pytest framework
  5. import re
  6. import socket
  7. import struct
  8. import subprocess
  9. import time
  10. from typing import Tuple
  11. import netifaces
  12. import pexpect
  13. from pytest_embedded_idf.dut import IdfDut
  14. class thread_parameter:
  15. def __init__(self, deviceRole:str='', dataset:str='', channel:str='', exaddr:str='', bbr:bool=False):
  16. self.deviceRole = deviceRole
  17. self.dataset = dataset
  18. self.channel = channel
  19. self.exaddr = exaddr
  20. self.bbr = bbr
  21. self.networkname = ''
  22. self.panid = ''
  23. self.extpanid = ''
  24. self.networkkey = ''
  25. self.pskc = ''
  26. def setnetworkname(self, networkname:str) -> None:
  27. self.networkname = networkname
  28. def setpanid(self, panid:str) -> None:
  29. self.panid = panid
  30. def setextpanid(self, extpanid:str) -> None:
  31. self.extpanid = extpanid
  32. def setnetworkkey(self, networkkey:str) -> None:
  33. self.networkkey = networkkey
  34. def setpskc(self, pskc:str) -> None:
  35. self.pskc = pskc
  36. class wifi_parameter:
  37. def __init__(self, ssid:str='', psk:str='', retry_times:int=10):
  38. self.ssid = ssid
  39. self.psk = psk
  40. self.retry_times = retry_times
  41. def joinThreadNetwork(dut:IdfDut, thread:thread_parameter) -> None:
  42. if thread.dataset:
  43. command = 'dataset set active ' + thread.dataset
  44. execute_command(dut, command)
  45. dut.expect('Done', timeout=5)
  46. else:
  47. execute_command(dut, 'dataset init new')
  48. dut.expect('Done', timeout=5)
  49. if thread.channel:
  50. command = 'dataset channel ' + thread.channel
  51. execute_command(dut, command)
  52. dut.expect('Done', timeout=5)
  53. if thread.exaddr:
  54. command = 'extaddr ' + thread.exaddr
  55. execute_command(dut, command)
  56. dut.expect('Done', timeout=5)
  57. if thread.networkname:
  58. command = 'dataset networkname ' + thread.networkname
  59. execute_command(dut, command)
  60. dut.expect('Done', timeout=5)
  61. if thread.panid:
  62. command = 'dataset panid ' + thread.panid
  63. execute_command(dut, command)
  64. dut.expect('Done', timeout=5)
  65. if thread.extpanid:
  66. command = 'dataset extpanid ' + thread.extpanid
  67. execute_command(dut, command)
  68. dut.expect('Done', timeout=5)
  69. if thread.networkkey:
  70. command = 'dataset networkkey ' + thread.networkkey
  71. execute_command(dut, command)
  72. dut.expect('Done', timeout=5)
  73. if thread.pskc:
  74. command = 'dataset pskc ' + thread.pskc
  75. execute_command(dut, command)
  76. dut.expect('Done', timeout=5)
  77. execute_command(dut, 'dataset commit active')
  78. dut.expect('Done', timeout=5)
  79. if thread.bbr:
  80. execute_command(dut, 'bbr enable')
  81. dut.expect('Done', timeout=5)
  82. if thread.deviceRole == 'router':
  83. execute_command(dut, 'routerselectionjitter 1')
  84. dut.expect('Done', timeout=5)
  85. execute_command(dut, 'ifconfig up')
  86. dut.expect('Done', timeout=5)
  87. execute_command(dut, 'thread start')
  88. assert wait_for_join(dut, thread.deviceRole)
  89. def wait_for_join(dut:IdfDut, role:str) -> bool:
  90. for _ in range(1, 30):
  91. if getDeviceRole(dut) == role:
  92. wait(dut, 5)
  93. return True
  94. wait(dut, 1)
  95. return False
  96. def joinWiFiNetwork(dut:IdfDut, wifi:wifi_parameter) -> Tuple[str, int]:
  97. clean_buffer(dut)
  98. ip_address = ''
  99. information = ''
  100. for order in range(1, wifi.retry_times):
  101. command = 'wifi connect -s ' + str(wifi.ssid) + ' -p ' + str(wifi.psk)
  102. tmp = get_ouput_string(dut, command, 10)
  103. if 'sta ip' in str(tmp):
  104. ip_address = re.findall(r'sta ip: (\w+.\w+.\w+.\w+),', str(tmp))[0]
  105. if 'wifi sta' in str(tmp):
  106. information = re.findall(r'wifi sta (\w+ \w+ \w+)\W', str(tmp))[0]
  107. if information == 'is connected successfully':
  108. break
  109. assert information == 'is connected successfully'
  110. return ip_address, order
  111. def getDeviceRole(dut:IdfDut) -> str:
  112. clean_buffer(dut)
  113. execute_command(dut, 'state')
  114. role = dut.expect(r'\W+(\w+)\W+Done', timeout=5)[1].decode()
  115. print(role)
  116. return str(role)
  117. def changeDeviceRole(dut:IdfDut, role:str) -> None:
  118. command = 'state ' + role
  119. execute_command(dut, command)
  120. def getDataset(dut:IdfDut) -> str:
  121. clean_buffer(dut)
  122. execute_command(dut, 'dataset active -x')
  123. dut_data = dut.expect(r'\n(\w{212})\r', timeout=5)[1].decode()
  124. return str(dut_data)
  125. def init_thread(dut:IdfDut) -> None:
  126. dut.expect('>', timeout=10)
  127. wait(dut, 3)
  128. reset_thread(dut)
  129. def reset_thread(dut:IdfDut) -> None:
  130. clean_buffer(dut)
  131. execute_command(dut, 'factoryreset')
  132. dut.expect('OpenThread attached to netif', timeout=20)
  133. dut.expect('>', timeout=10)
  134. wait(dut, 3)
  135. clean_buffer(dut)
  136. # get the mleid address of the thread
  137. def get_mleid_addr(dut:IdfDut) -> str:
  138. dut_adress = ''
  139. clean_buffer(dut)
  140. execute_command(dut, 'ipaddr mleid')
  141. dut_adress = dut.expect(r'\n((?:\w+:){7}\w+)\r', timeout=5)[1].decode()
  142. return dut_adress
  143. # get the rloc address of the thread
  144. def get_rloc_addr(dut:IdfDut) -> str:
  145. dut_adress = ''
  146. clean_buffer(dut)
  147. execute_command(dut, 'ipaddr rloc')
  148. dut_adress = dut.expect(r'\n((?:\w+:){7}\w+)\r', timeout=5)[1].decode()
  149. return dut_adress
  150. # get the linklocal address of the thread
  151. def get_linklocal_addr(dut:IdfDut) -> str:
  152. dut_adress = ''
  153. clean_buffer(dut)
  154. execute_command(dut, 'ipaddr linklocal')
  155. dut_adress = dut.expect(r'\n((?:\w+:){7}\w+)\r', timeout=5)[1].decode()
  156. return dut_adress
  157. # get the global unicast address of the thread:
  158. def get_global_unicast_addr(dut:IdfDut, br:IdfDut) -> str:
  159. dut_adress = ''
  160. clean_buffer(br)
  161. omrprefix = get_omrprefix(br)
  162. clean_buffer(dut)
  163. execute_command(dut, 'ipaddr')
  164. dut_adress = dut.expect(r'(%s(?:\w+:){3}\w+)\r' % str(omrprefix), timeout=5)[1].decode()
  165. return dut_adress
  166. # ping of thread
  167. def ot_ping(dut:IdfDut, target:str, times:int) -> Tuple[int, int]:
  168. command = 'ping ' + str(target) + ' 0 ' + str(times)
  169. execute_command(dut, command)
  170. transmitted = dut.expect(r'(\d+) packets transmitted', timeout=30)[1].decode()
  171. tx_count = int(transmitted)
  172. received = dut.expect(r'(\d+) packets received', timeout=30)[1].decode()
  173. rx_count = int(received)
  174. return tx_count, rx_count
  175. def reset_host_interface() -> None:
  176. interface_name = get_host_interface_name()
  177. flag = False
  178. try:
  179. command = 'ifconfig ' + interface_name + ' down'
  180. subprocess.call(command, shell=True, timeout=5)
  181. time.sleep(1)
  182. command = 'ifconfig ' + interface_name + ' up'
  183. subprocess.call(command, shell=True, timeout=10)
  184. time.sleep(1)
  185. flag = True
  186. finally:
  187. time.sleep(1)
  188. assert flag
  189. def set_interface_sysctl_options() -> None:
  190. interface_name = get_host_interface_name()
  191. flag = False
  192. try:
  193. command = 'sysctl -w net/ipv6/conf/' + interface_name + '/accept_ra=2'
  194. subprocess.call(command, shell=True, timeout=5)
  195. time.sleep(1)
  196. command = 'sysctl -w net/ipv6/conf/' + interface_name + '/accept_ra_rt_info_max_plen=128'
  197. subprocess.call(command, shell=True, timeout=5)
  198. time.sleep(1)
  199. flag = True
  200. finally:
  201. time.sleep(2)
  202. assert flag
  203. def init_interface_ipv6_address() -> None:
  204. interface_name = get_host_interface_name()
  205. flag = False
  206. try:
  207. command = 'ip -6 route | grep ' + interface_name + " | grep ra | awk {'print $1'} | xargs -I {} ip -6 route del {}"
  208. subprocess.call(command, shell=True, timeout=5)
  209. time.sleep(0.5)
  210. subprocess.call(command, shell=True, timeout=5)
  211. time.sleep(1)
  212. command = 'ip -6 address show dev ' + interface_name + \
  213. " scope global | grep 'inet6' | awk {'print $2'} | xargs -I {} ip -6 addr del {} dev " + interface_name
  214. subprocess.call(command, shell=True, timeout=5)
  215. time.sleep(1)
  216. flag = True
  217. finally:
  218. time.sleep(1)
  219. assert flag
  220. def get_host_interface_name() -> str:
  221. interfaces = netifaces.interfaces()
  222. interface_name = [s for s in interfaces if 'wl' in s][0]
  223. return str(interface_name)
  224. def clean_buffer(dut:IdfDut) -> None:
  225. str_length = str(len(dut.expect(pexpect.TIMEOUT, timeout=0.1)))
  226. dut.expect(r'[\s\S]{%s}' % str(str_length), timeout=10)
  227. def check_if_host_receive_ra(br:IdfDut) -> bool:
  228. interface_name = get_host_interface_name()
  229. clean_buffer(br)
  230. omrprefix = get_omrprefix(br)
  231. command = 'ip -6 route | grep ' + str(interface_name)
  232. out_str = subprocess.getoutput(command)
  233. print('br omrprefix: ', str(omrprefix))
  234. print('host route table:\n', str(out_str))
  235. return str(omrprefix) in str(out_str)
  236. def host_connect_wifi() -> None:
  237. command = '. /home/test/wlan_connection_OTTE.sh'
  238. subprocess.call(command, shell=True, timeout=30)
  239. time.sleep(5)
  240. def is_joined_wifi_network(br:IdfDut) -> bool:
  241. return check_if_host_receive_ra(br)
  242. thread_ipv6_group = 'ff04:0:0:0:0:0:0:125'
  243. def check_ipmaddr(dut:IdfDut) -> bool:
  244. info = get_ouput_string(dut, 'ipmaddr', 2)
  245. if thread_ipv6_group in str(info):
  246. return True
  247. return False
  248. def thread_is_joined_group(dut:IdfDut) -> bool:
  249. command = 'mcast join ' + thread_ipv6_group
  250. execute_command(dut, command)
  251. dut.expect('Done', timeout=5)
  252. order = 0
  253. while order < 3:
  254. if check_ipmaddr(dut):
  255. return True
  256. execute_command(dut, command)
  257. wait(dut, 2)
  258. order = order + 1
  259. return False
  260. class udp_parameter:
  261. def __init__(self, udp_type:str='', addr:str='::', port:int=5090, group:str='', init_flag:bool=False, timeout:float=15.0, udp_bytes:bytes=b''):
  262. self.udp_type = udp_type
  263. self.addr = addr
  264. self.port = port
  265. self.group = group
  266. self.init_flag = init_flag
  267. self.timeout = timeout
  268. self.udp_bytes = udp_bytes
  269. def create_host_udp_server(myudp:udp_parameter) -> None:
  270. interface_name = get_host_interface_name()
  271. try:
  272. if myudp.udp_type == 'INET6':
  273. AF_INET = socket.AF_INET6
  274. else:
  275. AF_INET = socket.AF_INET
  276. print('The host start to create udp server!')
  277. if_index = socket.if_nametoindex(interface_name)
  278. sock = socket.socket(AF_INET, socket.SOCK_DGRAM)
  279. sock.bind((myudp.addr, myudp.port))
  280. if myudp.udp_type == 'INET6' and myudp.group != '':
  281. sock.setsockopt(
  282. socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP,
  283. struct.pack('16si', socket.inet_pton(socket.AF_INET6, myudp.group),
  284. if_index))
  285. sock.settimeout(myudp.timeout)
  286. myudp.init_flag = True
  287. print('The host start to receive message!')
  288. myudp.udp_bytes = (sock.recvfrom(1024))[0]
  289. print('The host has received message: ', myudp.udp_bytes)
  290. except socket.error:
  291. print('The host did not receive message!')
  292. finally:
  293. print('Close the socket.')
  294. sock.close()
  295. def host_udp_send_message(udp_target:udp_parameter) -> None:
  296. interface_name = get_host_interface_name()
  297. try:
  298. if udp_target.udp_type == 'INET6':
  299. AF_INET = socket.AF_INET6
  300. else:
  301. AF_INET = socket.AF_INET
  302. sock = socket.socket(AF_INET, socket.SOCK_DGRAM)
  303. sock.bind(('::', 12350))
  304. sock.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, interface_name.encode())
  305. sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 32)
  306. print('Host is sending message')
  307. sock.sendto(udp_target.udp_bytes, (udp_target.addr, udp_target.port))
  308. except socket.error:
  309. print('Host cannot send message')
  310. finally:
  311. sock.close()
  312. def wait(dut:IdfDut, wait_time:float) -> None:
  313. dut.expect(pexpect.TIMEOUT, timeout=wait_time)
  314. def get_host_ipv4_address() -> str:
  315. interface_name = get_host_interface_name()
  316. command = 'ifconfig ' + interface_name + " | grep -w 'inet' | awk '{print $2}'"
  317. out_bytes = subprocess.check_output(command, shell=True, timeout=5)
  318. out_str = out_bytes.decode('utf-8')
  319. host_ipv4_address = ''
  320. host_ipv4_address = re.findall(r'((?:\d+.){3}\d+)', str(out_str))[0]
  321. return host_ipv4_address
  322. def start_avahi() -> None:
  323. time.sleep(1)
  324. command = '/etc/init.d/dbus start'
  325. subprocess.Popen(command, shell=True)
  326. time.sleep(5)
  327. command = 'avahi-daemon'
  328. subprocess.Popen(command, shell=True)
  329. time.sleep(5)
  330. def host_publish_service() -> None:
  331. command = 'avahi-publish-service testxxx _testxxx._udp 12347 test=1235 dn="for_ci_br_test"'
  332. subprocess.Popen(command, shell=True)
  333. time.sleep(2)
  334. def host_close_service() -> None:
  335. command = "ps | grep avahi-publish-s | awk '{print $1}'"
  336. out_bytes = subprocess.check_output(command, shell=True, timeout=5)
  337. out_str = out_bytes.decode('utf-8')
  338. the_pid = re.findall(r'(\d+)\n', str(out_str))
  339. for pid in the_pid:
  340. command = 'kill -9 ' + pid
  341. subprocess.call(command, shell=True, timeout=5)
  342. time.sleep(1)
  343. def close_host_interface() -> None:
  344. interface_name = get_host_interface_name()
  345. flag = False
  346. try:
  347. command = 'ifconfig ' + interface_name + ' down'
  348. subprocess.call(command, shell=True, timeout=5)
  349. time.sleep(1)
  350. flag = True
  351. finally:
  352. time.sleep(1)
  353. assert flag
  354. def open_host_interface() -> None:
  355. interface_name = get_host_interface_name()
  356. flag = False
  357. try:
  358. command = 'ifconfig ' + interface_name + ' up'
  359. subprocess.call(command, shell=True, timeout=5)
  360. time.sleep(1)
  361. flag = True
  362. finally:
  363. time.sleep(1)
  364. assert flag
  365. def get_domain() -> str:
  366. hostname = socket.gethostname()
  367. print('hostname is: ', hostname)
  368. command = 'ps -aux | grep avahi-daemon | grep running'
  369. out_str = subprocess.getoutput(command)
  370. print('avahi status:\n', out_str)
  371. role = re.findall(r'\[([\w\W]+)\.local\]', str(out_str))[0]
  372. print('active host is: ', role)
  373. return str(role)
  374. class tcp_parameter:
  375. def __init__(self, tcp_type:str='', addr:str='::', port:int=12345, listen_flag:bool=False, recv_flag:bool=False, timeout:float=15.0, tcp_bytes:bytes=b''):
  376. self.tcp_type = tcp_type
  377. self.addr = addr
  378. self.port = port
  379. self.listen_flag = listen_flag
  380. self.recv_flag = recv_flag
  381. self.timeout = timeout
  382. self.tcp_bytes = tcp_bytes
  383. def create_host_tcp_server(mytcp:tcp_parameter) -> None:
  384. try:
  385. if mytcp.tcp_type == 'INET6':
  386. AF_INET = socket.AF_INET6
  387. else:
  388. AF_INET = socket.AF_INET
  389. print('The host start to create a tcp server!')
  390. sock = socket.socket(AF_INET, socket.SOCK_STREAM)
  391. sock.bind((mytcp.addr, mytcp.port))
  392. sock.listen(5)
  393. mytcp.listen_flag = True
  394. print('The tcp server is waiting for connection!')
  395. sock.settimeout(mytcp.timeout)
  396. connfd,addr = sock.accept()
  397. print('The tcp server connected with ',addr)
  398. mytcp.recv_flag = True
  399. mytcp.tcp_bytes = connfd.recv(1024)
  400. print('The tcp server has received message: ', mytcp.tcp_bytes)
  401. except socket.error:
  402. if mytcp.recv_flag:
  403. print('The tcp server did not receive message!')
  404. else:
  405. print('The tcp server fail to connect!')
  406. finally:
  407. print('Close the socket.')
  408. sock.close()
  409. def get_ipv6_from_ipv4(ipv4_address:str, br:IdfDut) -> str:
  410. clean_buffer(br)
  411. nat64prefix = get_nat64prefix(br)
  412. ipv4_find = re.findall(r'\d+', ipv4_address)
  413. ipv6_16_1 = decimal_to_hex(ipv4_find[0]) + decimal_to_hex(ipv4_find[1])
  414. ipv6_16_2 = decimal_to_hex(ipv4_find[2]) + decimal_to_hex(ipv4_find[3])
  415. ipv6_get_from_ipv4 = nat64prefix + ':' + ipv6_16_1 + ':' + ipv6_16_2
  416. return str(ipv6_get_from_ipv4)
  417. def decimal_to_hex(decimal_str:str) -> str:
  418. decimal_int = int(decimal_str)
  419. hex_str = hex(decimal_int)[2:]
  420. return hex_str
  421. def get_omrprefix(br:IdfDut) -> str:
  422. clean_buffer(br)
  423. execute_command(br, 'br omrprefix')
  424. omrprefix = br.expect(r'Local: ((?:\w+:){4}):/\d+\r', timeout=5)[1].decode()
  425. return str(omrprefix)
  426. def get_onlinkprefix(br:IdfDut) -> str:
  427. clean_buffer(br)
  428. execute_command(br, 'br onlinkprefix')
  429. onlinkprefix = br.expect(r'Local: ((?:\w+:){4}):/\d+\r', timeout=5)[1].decode()
  430. return str(onlinkprefix)
  431. def get_nat64prefix(br:IdfDut) -> str:
  432. clean_buffer(br)
  433. execute_command(br, 'br nat64prefix')
  434. nat64prefix = br.expect(r'Local: ((?:\w+:){6}):/\d+', timeout=5)[1].decode()
  435. return str(nat64prefix)
  436. def execute_command(dut:IdfDut, command:str) -> None:
  437. dut.write(command)
  438. dut.expect(command, timeout=3)
  439. def get_ouput_string(dut:IdfDut, command:str, wait_time:int) -> str:
  440. clean_buffer(dut)
  441. execute_command(dut, command)
  442. tmp = dut.expect(pexpect.TIMEOUT, timeout=wait_time)
  443. clean_buffer(dut)
  444. return str(tmp)