rutils.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. # Utils
  2. # Copyright (c) 2016, Tieto Corporation
  3. #
  4. # This software may be distributed under the terms of the BSD license.
  5. # See README for more details.
  6. import re
  7. import time
  8. from remotehost import Host
  9. import hostapd
  10. import config
  11. class TestSkip(Exception):
  12. def __init__(self, reason):
  13. self.reason = reason
  14. def __str__(self):
  15. return self.reason
  16. # get host based on name
  17. def get_host(devices, dev_name):
  18. dev = config.get_device(devices, dev_name)
  19. host = Host(host = dev['hostname'],
  20. ifname = dev['ifname'],
  21. port = dev['port'],
  22. name = dev['name'])
  23. host.dev = dev
  24. return host
  25. # Run setup_hw - hardware specific
  26. def setup_hw_host_iface(host, iface, setup_params, force_restart=False):
  27. try:
  28. setup_hw = setup_params['setup_hw']
  29. restart = ""
  30. try:
  31. if setup_params['restart_device'] == True:
  32. restart = "-R"
  33. except:
  34. pass
  35. if force_restart:
  36. restart = "-R"
  37. host.execute([setup_hw, "-I", iface, restart])
  38. except:
  39. pass
  40. def setup_hw_host(host, setup_params, force_restart=False):
  41. ifaces = re.split('; | |, ', host.ifname)
  42. for iface in ifaces:
  43. setup_hw_host_iface(host, iface, setup_params, force_restart)
  44. def setup_hw(hosts, setup_params, force_restart=False):
  45. for host in hosts:
  46. setup_hw_host(host, setup_params, force_restart)
  47. # get traces - hw specific
  48. def trace_start(hosts, setup_params):
  49. for host in hosts:
  50. trace_start_stop(host, setup_params, start=True)
  51. def trace_stop(hosts, setup_params):
  52. for host in hosts:
  53. trace_start_stop(host, setup_params, start=False)
  54. def trace_start_stop(host, setup_params, start):
  55. if setup_params['trace'] == False:
  56. return
  57. try:
  58. start_trace = setup_params['trace_start']
  59. stop_trace = setup_params['trace_stop']
  60. if start:
  61. cmd = start_trace
  62. else:
  63. cmd = stop_trace
  64. trace_dir = setup_params['log_dir'] + host.ifname + "/remote_traces"
  65. host.add_log(trace_dir + "/*")
  66. host.execute([cmd, "-I", host.ifname, "-D", trace_dir])
  67. except:
  68. pass
  69. # get perf
  70. def perf_start(hosts, setup_params):
  71. for host in hosts:
  72. perf_start_stop(host, setup_params, start=True)
  73. def perf_stop(hosts, setup_params):
  74. for host in hosts:
  75. perf_start_stop(host, setup_params, start=False)
  76. def perf_start_stop(host, setup_params, start):
  77. if setup_params['perf'] == False:
  78. return
  79. try:
  80. perf_start = setup_params['perf_start']
  81. perf_stop = setup_params['perf_stop']
  82. if start:
  83. cmd = perf_start
  84. else:
  85. cmd = perf_stop
  86. perf_dir = setup_params['log_dir'] + host.ifname + "/remote_perf"
  87. host.add_log(perf_dir + "/*")
  88. host.execute([cmd, "-I", host.ifname, "-D", perf_dir])
  89. except:
  90. pass
  91. # hostapd/wpa_supplicant helpers
  92. def run_hostapd(host, setup_params):
  93. log_file = None
  94. try:
  95. tc_name = setup_params['tc_name']
  96. log_dir = setup_params['log_dir']
  97. log_file = log_dir + tc_name + "_hostapd_" + host.name + "_" + host.ifname + ".log"
  98. host.execute(["rm", log_file])
  99. log = " -f " + log_file
  100. except:
  101. log = ""
  102. if log_file:
  103. host.add_log(log_file)
  104. status, buf = host.execute([setup_params['hostapd'], "-B", "-ddt", "-g", "udp:" + host.port, log])
  105. if status != 0:
  106. raise Exception("Could not run hostapd: " + buf)
  107. def run_wpasupplicant(host, setup_params):
  108. log_file = None
  109. try:
  110. tc_name = setup_params['tc_name']
  111. log_dir = setup_params['log_dir']
  112. log_file = log_dir + tc_name + "_wpa_supplicant_" + host.name + "_" + host.ifname + ".log"
  113. host.execute(["rm", log_file])
  114. log = " -f " + log_file
  115. except:
  116. log = ""
  117. if log_file:
  118. host.add_log(log_file)
  119. status, buf = host.execute([setup_params['wpa_supplicant'], "-B", "-ddt", "-g", "udp:" + host.port, log])
  120. if status != 0:
  121. raise Exception("Could not run wpa_supplicant: " + buf)
  122. def get_ap_params(channel="1", bw="HT20", country="US", security="open", ht_capab=None, vht_capab=None):
  123. ssid = "test_" + channel + "_" + security + "_" + bw
  124. if bw == "b_only":
  125. params = hostapd.b_only_params(channel, ssid, country)
  126. elif bw == "g_only":
  127. params = hostapd.g_only_params(channel, ssid, country)
  128. elif bw == "g_only_wmm":
  129. params = hostapd.g_only_params(channel, ssid, country)
  130. params['wmm_enabled'] = "1"
  131. elif bw == "a_only":
  132. params = hostapd.a_only_params(channel, ssid, country)
  133. elif bw == "a_only_wmm":
  134. params = hostapd.a_only_params(channel, ssid, country)
  135. params['wmm_enabled'] = "1"
  136. elif bw == "HT20":
  137. params = hostapd.ht20_params(channel, ssid, country)
  138. if ht_capab:
  139. try:
  140. params['ht_capab'] = params['ht_capab'] + ht_capab
  141. except:
  142. params['ht_capab'] = ht_capab
  143. elif bw == "HT40+":
  144. params = hostapd.ht40_plus_params(channel, ssid, country)
  145. if ht_capab:
  146. params['ht_capab'] = params['ht_capab'] + ht_capab
  147. elif bw == "HT40-":
  148. params = hostapd.ht40_minus_params(channel, ssid, country)
  149. if ht_capab:
  150. params['ht_capab'] = params['ht_capab'] + ht_capab
  151. elif bw == "VHT80":
  152. params = hostapd.ht40_plus_params(channel, ssid, country)
  153. if ht_capab:
  154. params['ht_capab'] = params['ht_capab'] + ht_capab
  155. if vht_capab:
  156. try:
  157. params['vht_capab'] = params['vht_capab'] + vht_capab
  158. except:
  159. params['vht_capab'] = vht_capab
  160. params['ieee80211ac'] = "1"
  161. params['vht_oper_chwidth'] = "1"
  162. params['vht_oper_centr_freq_seg0_idx'] = str(int(channel) + 6)
  163. else:
  164. params = {}
  165. # now setup security params
  166. if security == "tkip":
  167. sec_params = hostapd.wpa_params(passphrase="testtest")
  168. elif security == "ccmp":
  169. sec_params = hostapd.wpa2_params(passphrase="testtest")
  170. elif security == "mixed":
  171. sec_params = hostapd.wpa_mixed_params(passphrase="testtest")
  172. elif security == "wep":
  173. sec_params = { "wep_key0" : "123456789a",
  174. "wep_default_key" : "0",
  175. "auth_algs" : "1"}
  176. elif security == "wep_shared":
  177. sec_params = { "wep_key0" : "123456789a",
  178. "wep_default_key" : "0",
  179. "auth_algs" : "2" }
  180. else:
  181. sec_params = {}
  182. params.update(sec_params)
  183. return params
  184. # ip helpers
  185. def get_ipv4(client, ifname=None):
  186. if ifname is None:
  187. ifname = client.ifname
  188. status, buf = client.execute(["ifconfig", ifname])
  189. lines = buf.splitlines()
  190. for line in lines:
  191. res = line.find("inet addr:")
  192. if res != -1:
  193. break
  194. if res != -1:
  195. words = line.split()
  196. addr = words[1].split(":")
  197. return addr[1]
  198. return "unknown"
  199. def get_ipv6(client, ifname=None):
  200. res = -1
  201. if ifname is None:
  202. ifname = client.ifname
  203. status, buf = client.execute(["ifconfig", ifname])
  204. lines = buf.splitlines()
  205. for line in lines:
  206. res = line.find("Scope:Link")
  207. if res != -1:
  208. break
  209. if res != -1:
  210. words = line.split()
  211. if words[0] == "inet6" and words[1] == "addr:":
  212. addr_mask = words[2]
  213. addr = addr_mask.split("/")
  214. return addr[0]
  215. return "unknown"
  216. def get_ip(client, addr_type="ipv6", iface=None):
  217. if addr_type == "ipv6":
  218. return get_ipv6(client, iface)
  219. elif addr_type == "ipv4":
  220. return get_ipv4(client, iface)
  221. else:
  222. return "unknown addr_type: " + addr_type
  223. def get_ipv4_addr(setup_params, number):
  224. try:
  225. ipv4_base = setup_params['ipv4_test_net']
  226. except:
  227. ipv4_base = "172.16.12.0"
  228. parts = ipv4_base.split('.')
  229. ipv4 = parts[0] + "." + parts[1] + "." + parts[2] + "." + str(number)
  230. return ipv4
  231. def get_mac_addr(host, iface=None):
  232. if iface == None:
  233. iface = host.ifname
  234. status, buf = host.execute(["ifconfig", iface])
  235. if status != 0:
  236. raise Exception("ifconfig " + iface)
  237. words = buf.split()
  238. found = 0
  239. for word in words:
  240. if found == 1:
  241. return word
  242. if word == "HWaddr":
  243. found = 1
  244. raise Exception("Could not find HWaddr")
  245. # connectivity/ping helpers
  246. def get_ping_packet_loss(ping_res):
  247. loss_line = ""
  248. lines = ping_res.splitlines()
  249. for line in lines:
  250. if line.find("packet loss") != -1:
  251. loss_line = line
  252. break;
  253. if loss_line == "":
  254. return "100%"
  255. sections = loss_line.split(",")
  256. for section in sections:
  257. if section.find("packet loss") != -1:
  258. words = section.split()
  259. return words[0]
  260. return "100%"
  261. def ac_to_ping_ac(qos):
  262. if qos == "be":
  263. qos_param = "0x00"
  264. elif qos == "bk":
  265. qos_param = "0x20"
  266. elif qos == "vi":
  267. qos_param = "0xA0"
  268. elif qos == "vo":
  269. qos_param = "0xE0"
  270. else:
  271. qos_param = "0x00"
  272. return qos_param
  273. def ping_run(host, ip, result, ifname=None, addr_type="ipv4", deadline="5", qos=None):
  274. if ifname is None:
  275. ifname = host.ifname
  276. if addr_type == "ipv6":
  277. ping = ["ping6"]
  278. else:
  279. ping = ["ping"]
  280. ping = ping + ["-w", deadline, "-I", ifname]
  281. if qos:
  282. ping = ping + ["-Q", ac_to_ping_ac(qos)]
  283. ping = ping + [ip]
  284. flush_arp_cache(host)
  285. thread = host.execute_run(ping, result)
  286. return thread
  287. def ping_wait(host, thread, timeout=None):
  288. host.wait_execute_complete(thread, timeout)
  289. if thread.isAlive():
  290. raise Exception("ping thread still alive")
  291. def flush_arp_cache(host):
  292. host.execute(["ip", "-s", "-s", "neigh", "flush", "all"])
  293. def check_connectivity(a, b, addr_type = "ipv4", deadline="5", qos=None):
  294. addr_a = get_ip(a, addr_type)
  295. addr_b = get_ip(b, addr_type)
  296. if addr_type == "ipv4":
  297. ping = ["ping"]
  298. else:
  299. ping = ["ping6"]
  300. ping_a_b = ping + ["-w", deadline, "-I", a.ifname]
  301. ping_b_a = ping + ["-w", deadline, "-I", b.ifname]
  302. if qos:
  303. ping_a_b = ping_a_b + ["-Q", ac_to_ping_ac(qos)]
  304. ping_b_a = ping_b_a + ["-Q", ac_to_ping_ac(qos)]
  305. ping_a_b = ping_a_b + [addr_b]
  306. ping_b_a = ping_b_a + [addr_a]
  307. # Clear arp cache
  308. flush_arp_cache(a)
  309. flush_arp_cache(b)
  310. status, buf = a.execute(ping_a_b)
  311. if status == 2 and ping == "ping6":
  312. # tentative possible for a while, try again
  313. time.sleep(3)
  314. status, buf = a.execute(ping_a_b)
  315. if status != 0:
  316. raise Exception("ping " + a.name + "/" + a.ifname + " >> " + b.name + "/" + b.ifname)
  317. a_b = get_ping_packet_loss(buf)
  318. # Clear arp cache
  319. flush_arp_cache(a)
  320. flush_arp_cache(b)
  321. status, buf = b.execute(ping_b_a)
  322. if status != 0:
  323. raise Exception("ping " + b.name + "/" + b.ifname + " >> " + a.name + "/" + a.ifname)
  324. b_a = get_ping_packet_loss(buf)
  325. if int(a_b[:-1]) > 40:
  326. raise Exception("Too high packet lost: " + a_b)
  327. if int(b_a[:-1]) > 40:
  328. raise Exception("Too high packet lost: " + b_a)
  329. return a_b, b_a
  330. # iperf helpers
  331. def get_iperf_speed(iperf_res, pattern="Mbits/sec"):
  332. lines = iperf_res.splitlines()
  333. sum_line = ""
  334. last_line = ""
  335. count = 0
  336. res = -1
  337. # first find last SUM line
  338. for line in lines:
  339. res = line.find("[SUM]")
  340. if res != -1:
  341. sum_line = line
  342. # next check SUM status
  343. if sum_line != "":
  344. words = sum_line.split()
  345. for word in words:
  346. res = word.find(pattern)
  347. if res != -1:
  348. return words[count - 1] + " " + pattern
  349. count = count + 1
  350. # no SUM - one thread - find last line
  351. for line in lines:
  352. res = line.find(pattern)
  353. if res != -1:
  354. last_line = line
  355. if last_line == "":
  356. return "0 " + pattern
  357. count = 0
  358. words = last_line.split()
  359. for word in words:
  360. res = word.find(pattern)
  361. if res != -1:
  362. return words[count - 1] + " " + pattern
  363. break;
  364. count = count + 1
  365. return "0 " + pattern
  366. def ac_to_iperf_ac(qos):
  367. if qos == "be":
  368. qos_param = "0x00"
  369. elif qos == "bk":
  370. qos_param = "0x20"
  371. elif qos == "vi":
  372. qos_param = "0xA0"
  373. elif qos == "vo":
  374. qos_param = "0xE0"
  375. else:
  376. qos_param = "0x00"
  377. return qos_param
  378. def iperf_run(server, client, server_ip, client_res, server_res,
  379. l4="udp", bw="30M", test_time="30", parallel="5",
  380. qos="be", param=" -i 5 ", ifname=None, l3="ipv4",
  381. port="5001", iperf="iperf"):
  382. if ifname == None:
  383. ifname = client.ifname
  384. if iperf == "iperf":
  385. iperf_server = [iperf]
  386. elif iperf == "iperf3":
  387. iperf_server = [iperf, "-1"]
  388. if l3 == "ipv4":
  389. iperf_client = [iperf, "-c", server_ip, "-p", port]
  390. iperf_server = iperf_server + ["-p", port]
  391. elif l3 == "ipv6":
  392. iperf_client = [iperf, "-V", "-c", server_ip + "%" + ifname, "-p", port]
  393. iperf_server = iperf_server + ["-V", "-p", port]
  394. else:
  395. return -1, -1
  396. iperf_server = iperf_server + ["-s", "-f", "m", param]
  397. iperf_client = iperf_client + ["-f", "m", "-t", test_time]
  398. if parallel != "1":
  399. iperf_client = iperf_client + ["-P", parallel]
  400. if l4 == "udp":
  401. if iperf != "iperf3":
  402. iperf_server = iperf_server + ["-u"]
  403. iperf_client = iperf_client + ["-u", "-b", bw]
  404. if qos:
  405. iperf_client = iperf_client + ["-Q", ac_to_iperf_ac(qos)]
  406. flush_arp_cache(server)
  407. flush_arp_cache(client)
  408. server_thread = server.execute_run(iperf_server, server_res)
  409. time.sleep(1)
  410. client_thread = client.execute_run(iperf_client, client_res)
  411. return server_thread, client_thread
  412. def iperf_wait(server, client, server_thread, client_thread, timeout=None, iperf="iperf"):
  413. client.wait_execute_complete(client_thread, timeout)
  414. if client_thread.isAlive():
  415. raise Exception("iperf client thread still alive")
  416. server.wait_execute_complete(server_thread, 5)
  417. if server_thread.isAlive():
  418. server.execute(["killall", "-s", "INT", iperf])
  419. time.sleep(1)
  420. server.wait_execute_complete(server_thread, 5)
  421. if server_thread.isAlive():
  422. raise Execption("iperf server thread still alive")
  423. return
  424. def run_tp_test(server, client, l3="ipv4", iperf="iperf", l4="tcp", test_time="10", parallel="5",
  425. qos="be", bw="30M", ifname=None, port="5001"):
  426. client_res = []
  427. server_res = []
  428. server_ip = get_ip(server, l3)
  429. time.sleep(1)
  430. server_thread, client_thread = iperf_run(server, client, server_ip, client_res, server_res,
  431. l3=l3, iperf=iperf, l4=l4, test_time=test_time,
  432. parallel=parallel, qos=qos, bw=bw, ifname=ifname,
  433. port=port)
  434. iperf_wait(server, client, server_thread, client_thread, iperf=iperf, timeout=int(test_time) + 10)
  435. if client_res[0] != 0:
  436. raise Exception(iperf + " client: " + client_res[1])
  437. if server_res[0] != 0:
  438. raise Exception(iperf + " server: " + server_res[1])
  439. if client_res[1] is None:
  440. raise Exception(iperf + " client result issue")
  441. if server_res[1] is None:
  442. raise Exception(iperf + " server result issue")
  443. if iperf == "iperf":
  444. result = server_res[1]
  445. if iperf == "iperf3":
  446. result = client_res[1]
  447. speed = get_iperf_speed(result)
  448. return speed
  449. def get_iperf_bw(bw, parallel, spacial_streams=2):
  450. if bw == "b_only":
  451. max_tp = 11
  452. elif bw == "g_only" or bw == "g_only_wmm" or bw == "a_only" or bw == "a_only_wmm":
  453. max_tp = 54
  454. elif bw == "HT20":
  455. max_tp = 72 * spacial_streams
  456. elif bw == "HT40+" or bw == "HT40-":
  457. max_tp = 150 * spacial_streams
  458. elif bw == "VHT80":
  459. max_tp = 433 * spacial_streams
  460. else:
  461. max_tp = 150
  462. max_tp = 1.2 * max_tp
  463. return str(int(max_tp/int(parallel))) + "M"