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