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