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