mirror of
https://github.com/Telecominfraproject/wlan-lanforge-scripts.git
synced 2025-11-02 19:58:03 +00:00
smw_lf_throughput.py: original script from Netgear repo
Signed-off-by: Scott Wedge <scott.wedge@candelatech.com>
This commit is contained in:
678
py-scripts/sandbox/smw_lf_throughput.py
Executable file
678
py-scripts/sandbox/smw_lf_throughput.py
Executable file
@@ -0,0 +1,678 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""throughput.py will create stations and layer-3 traffic to calculate the throughput of AP.
|
||||
|
||||
This script will create a VAP and apply some load by creating stations in AP's channel under VAP in order to make the channel
|
||||
utilized after the channel utilized to specific level again create specific number of stations each with their own set of cross-connects and endpoints.
|
||||
It will then create layer 3 traffic over a specified amount of time, testing for increased traffic at regular intervals.
|
||||
This test will pass if all stations increase traffic over the full test duration.
|
||||
|
||||
Use './throughput_ver_2.py --help' to see command line usage and options
|
||||
Copyright 2021 Candela Technologies Inc
|
||||
License: Free to distribute and modify. LANforge systems must be licensed.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os, paramiko, pprint
|
||||
if sys.version_info[0] != 3:
|
||||
print("This script requires Python 3")
|
||||
exit(1)
|
||||
|
||||
if 'py-json' not in sys.path:
|
||||
sys.path.append(os.path.join(os.path.abspath('..'), 'py-json'))
|
||||
|
||||
import argparse
|
||||
from LANforge import LFUtils
|
||||
from realm import Realm
|
||||
from LANforge import LFRequest
|
||||
from station_profile import StationProfile
|
||||
from LANforge import set_port
|
||||
import throughput_report
|
||||
#import create_vap
|
||||
import time,datetime,traceback
|
||||
import pandas as pd
|
||||
from lf_report import lf_report
|
||||
from lf_graph import lf_bar_graph
|
||||
from lf_csv import lf_csv
|
||||
|
||||
# this class create VAP, station, and traffic
|
||||
class IPV4VariableTime(Realm):
|
||||
def __init__(self, ssid=None, security=None, password=None, sta_list=[], name_prefix=None, upstream=None,
|
||||
radio=None, host="localhost", port=8080, mode=0, ap=None, side_a_min_rate= 56,
|
||||
side_a_max_rate=0, side_b_min_rate=56, side_b_max_rate=0, number_template="00000",
|
||||
test_duration="5m", use_ht160=False, _debug_on=False, _exit_on_error=False,
|
||||
_exit_on_fail=False, _vap_radio=None, _vap_list = 'vap0000', _dhcp = True ):
|
||||
super().__init__(lfclient_host=host, lfclient_port=port),
|
||||
self.upstream = upstream
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.ssid = ssid
|
||||
self.sta_list = sta_list
|
||||
self.vap_list = _vap_list
|
||||
self.security = security
|
||||
self.password = password
|
||||
self.radio = radio
|
||||
self.vap_radio = _vap_radio
|
||||
self.mode = mode
|
||||
self.ap = ap
|
||||
self.number_template = number_template
|
||||
self.debug = _debug_on
|
||||
self.name_prefix = name_prefix
|
||||
self.test_duration = test_duration
|
||||
self._dhcp = _dhcp
|
||||
|
||||
|
||||
# initializing station profile
|
||||
self.station_profile = StationProfile(lfclient_url=self.lfclient_url, local_realm=super(), debug_=self.debug, up=False,
|
||||
dhcp = self._dhcp, ssid = self.ssid, ssid_pass = self.password,
|
||||
security = self.security, number_template_ = self.number_template, use_ht160 = use_ht160)#self.new_station_profile()##
|
||||
|
||||
if self.station_profile.use_ht160:
|
||||
self.station_profile.mode = 9
|
||||
self.station_profile.mode = mode
|
||||
|
||||
|
||||
# initializing VAP profile
|
||||
self.vap_profile = self.new_vap_profile()
|
||||
self.vap_profile.vap_name = self.vap_list
|
||||
self.vap_profile.ssid = self.ssid
|
||||
self.vap_profile.security = self.security
|
||||
self.vap_profile.ssid_pass = self.password
|
||||
self.vap_profile.mode = self.mode
|
||||
if self.debug:
|
||||
print("----- VAP List ----- ----- ----- ----- ----- ----- \n")
|
||||
pprint.pprint(self.vap_list)
|
||||
print("---- ~VAP List ----- ----- ----- ----- ----- ----- \n")
|
||||
|
||||
# initializing traffic profile
|
||||
self.cx_profile = self.new_l3_cx_profile()
|
||||
self.cx_profile.host = self.host
|
||||
self.cx_profile.port = self.port
|
||||
self.cx_profile.name_prefix = self.name_prefix
|
||||
self.cx_profile.side_a_min_bps = side_a_min_rate
|
||||
self.cx_profile.side_a_max_bps = side_a_max_rate
|
||||
self.cx_profile.side_b_min_bps = side_b_min_rate
|
||||
self.cx_profile.side_b_max_bps = side_b_max_rate
|
||||
|
||||
|
||||
def start(self, print_pass=False, print_fail=False):
|
||||
self.station_profile.admin_up() # admin up the stations
|
||||
# to-do- check here if upstream port got IP
|
||||
temp_stas = self.station_profile.station_names.copy()
|
||||
|
||||
if self.wait_for_ip(temp_stas):
|
||||
print("admin-up....")
|
||||
self._pass("All stations got IPs")
|
||||
else:
|
||||
self._fail("Stations failed to get IPs")
|
||||
self.exit_fail()
|
||||
self.cx_profile.start_cx() # run the traffic
|
||||
|
||||
def stop(self,trf = True, ad_dwn = True):
|
||||
if trf:
|
||||
self.cx_profile.stop_cx() # stop the traffic
|
||||
if ad_dwn:
|
||||
self.station_profile.admin_down() # admin down the stations
|
||||
|
||||
def pre_cleanup(self):
|
||||
# deleting the previously created stations
|
||||
print("clearing...")
|
||||
exist_sta = []
|
||||
for u in self.json_get("/port/?fields=port+type,alias")['interfaces']:
|
||||
if list(u.values())[0]['port type'] not in ['Ethernet', 'WIFI-Radio', 'NA']:
|
||||
exist_sta.append(list(u.values())[0]['alias'])
|
||||
self.station_profile.cleanup(desired_stations=exist_sta)
|
||||
# deleting the previously created traffic
|
||||
try:
|
||||
exist_l3 = list(filter(lambda cx_name: cx_name if (cx_name != 'handler' and cx_name != 'uri') else False,
|
||||
self.json_get("/cx/?fields=name")))
|
||||
list(map(lambda i: self.rm_cx(cx_name=i), exist_l3))
|
||||
list(map(lambda cx_name: [self.rm_endp(ename=i) for i in [f"{cx_name}-A", f"{cx_name}-B"]], exist_l3))
|
||||
except Exception as e:
|
||||
print("###",e,'###')
|
||||
|
||||
def build_vaps(self,chn = 36):
|
||||
# create VAPs with static IP_addr, netmask, gateway_IP
|
||||
self.vap_profile.use_security(self.security, self.ssid, passwd=self.password)
|
||||
self.vap_profile.set_command_param("set_port", "ip_addr", "192.168.0.1")
|
||||
self.vap_profile.set_command_flag("set_port", "ip_address", 1)
|
||||
self.vap_profile.set_command_param("set_port", "netmask", "255.255.255.0")
|
||||
self.vap_profile.set_command_flag("set_port", "ip_Mask", 1)
|
||||
self.vap_profile.set_command_param("set_port", "gateway", "192.168.0.1")
|
||||
self.vap_profile.set_command_flag("set_port", "ip_gateway", 1)
|
||||
print("Creating VAPs")
|
||||
self.vap_profile.create(resource = 1, radio = self.vap_radio, channel = int(chn), up_ = True, debug = False,
|
||||
suppress_related_commands_ = True, use_radius = True, hs20_enable = False,
|
||||
create_bridge = False)
|
||||
self._pass("PASS: VAP build finished")
|
||||
|
||||
def build(self):
|
||||
# creating stations using static IP and DHCP enabled stations
|
||||
self.station_profile.use_security(self.security, self.ssid, self.password)
|
||||
self.station_profile.set_number_template(self.number_template)
|
||||
self.station_profile.set_command_flag("add_sta", "create_admin_down", 1)
|
||||
self.station_profile.set_command_param("set_port", "report_timer", 1500)
|
||||
self.station_profile.set_command_flag("set_port", "rpt_timer", 1)
|
||||
print("Creating stations")
|
||||
start_ip = 2
|
||||
if self._dhcp:
|
||||
self.station_profile.create(radio=self.radio, sta_names_=self.sta_list, debug=self.debug)
|
||||
else:
|
||||
for sta_name in self.sta_list:
|
||||
ip = "192.168.0."+ str(start_ip)
|
||||
self.station_profile.set_command_param("set_port", "ip_addr", ip)
|
||||
self.station_profile.set_command_flag("set_port", "ip_address", 1)
|
||||
self.station_profile.set_command_param("set_port", "netmask", "255.255.255.0")
|
||||
self.station_profile.set_command_flag("set_port", "ip_Mask", 1)
|
||||
self.station_profile.set_command_param("set_port", "gateway", "192.168.0.1")
|
||||
self.station_profile.set_command_flag("set_port", "ip_gateway", 1)
|
||||
|
||||
self.station_profile.create(radio=self.radio, sta_names_=[sta_name], debug=self.debug)
|
||||
start_ip += 1
|
||||
self.cx_profile.create(endp_type="lf_udp", side_a=self.station_profile.station_names, side_b=self.upstream, sleep_time=0)
|
||||
self._pass("PASS: Station build finished")
|
||||
|
||||
def chn_util(self,ssh_root, ssh_passwd,channnel=0):
|
||||
# To find the channel utilization
|
||||
cmd = 'iwpriv wifi1vap0 get_chutil' # command to get channel utilization
|
||||
try:
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(ssh_root, 22, 'root', ssh_passwd)
|
||||
time.sleep(20)
|
||||
#ssh.exec_command(f"conf_set system:wlanSettings:wlanSettingTable:wlan1:channel {channnel}")
|
||||
stdout = ssh.exec_command(cmd)
|
||||
stdout = (((stdout[1].readlines())[0].split(':'))[1].split(' '))[0]
|
||||
print(stdout, "----- channel utilization")
|
||||
return int(stdout)
|
||||
except paramiko.ssh_exception.NoValidConnectionsError as e:
|
||||
print("####", e, "####")
|
||||
exit(1)
|
||||
except TimeoutError as e:
|
||||
print("####", e, "####")
|
||||
exit(1)
|
||||
|
||||
def re_run_traff(self, adj_trf_rate, add_sub_rate):
|
||||
# if channel utilization level is not met re-run the traffic
|
||||
print("Re-run the traffic...")
|
||||
self.cx_profile.cleanup_prefix()
|
||||
time.sleep(.5)
|
||||
# give data rate to run the traffic
|
||||
if add_sub_rate == "sub":
|
||||
self.cx_profile.side_a_min_bps = abs(int(self.cx_profile.side_a_min_bps) - adj_trf_rate)
|
||||
self.cx_profile.side_b_min_bps = abs(int(self.cx_profile.side_b_min_bps) - adj_trf_rate)
|
||||
elif add_sub_rate == "add":
|
||||
self.cx_profile.side_a_min_bps = int(self.cx_profile.side_a_min_bps) + adj_trf_rate
|
||||
self.cx_profile.side_b_min_bps = int(self.cx_profile.side_b_min_bps) + adj_trf_rate
|
||||
self.cx_profile.created_cx.clear()
|
||||
self.cx_profile.create(endp_type="lf_udp", side_a=self.station_profile.station_names, side_b=self.upstream,
|
||||
sleep_time=0)
|
||||
self.cx_profile.start_cx()
|
||||
print(f"-------side_a_min_bps {self.cx_profile.side_a_min_bps}\n-------side_b_min_bps {self.cx_profile.side_b_min_bps}")
|
||||
|
||||
def table(self,report, title, data, dis=""):
|
||||
# creating table
|
||||
report.set_obj_html(_obj_title="",_obj=dis)
|
||||
report.set_table_title(title)
|
||||
report.build_table_title()
|
||||
report.build_objective()
|
||||
report.set_table_dataframe(data)
|
||||
report.build_table()
|
||||
|
||||
def grph(self,report, dis="", data_set=None, xaxis_name="stations", yaxis_name="Throughput 2 (Mbps)",
|
||||
xaxis_categories=None, label=None, graph_image_name="", multi_bar_width = 0,
|
||||
xticks_font=10,step = 1):
|
||||
# creating bar graph
|
||||
report.set_obj_html(_obj_title=graph_image_name, _obj=dis)
|
||||
#report.set_graph_title(graph_image_name)
|
||||
#report.build_graph_title()
|
||||
report.build_objective()
|
||||
graph = lf_bar_graph(_data_set = data_set,
|
||||
_xaxis_name = xaxis_name,
|
||||
_yaxis_name = yaxis_name,
|
||||
_xaxis_categories = xaxis_categories,
|
||||
_graph_image_name = graph_image_name.replace(" ","_"),
|
||||
_label = label,
|
||||
_color = ['darkorange','forestgreen','blueviolet'],
|
||||
_color_edge = 'black',
|
||||
_figsize = (10, 5),
|
||||
_xticks_font= xticks_font,_xaxis_value_location=multi_bar_width,
|
||||
_xaxis_step = step,_legend_handles=None, _legend_loc="best", _legend_box=(1.0,0.5), _legend_ncol=1)
|
||||
graph_png = graph.build_bar_graph()
|
||||
print("graph name {}".format(graph_png))
|
||||
report.set_graph_image(graph_png)
|
||||
report.move_graph_image()
|
||||
report.build_graph()
|
||||
|
||||
def report(self,util, sta_num, bps_rx_a,bps_rx_b, rep_title, upload = 1000000, download = 1000000,
|
||||
test_setup_info = None,input_setup_info = None,threshold=None):
|
||||
'''if len(threshold) < len(util):
|
||||
for i in range(len(util)):
|
||||
try:
|
||||
tmp = threshold[i]
|
||||
except IndexError as e:
|
||||
print(f"Threshold {threshold} and utilization {util}")
|
||||
threshold.append(100 - int(util[i]))
|
||||
print(f"threshold {threshold} and utilization {util}")'''
|
||||
rx_a,rx_b,pas_fail_up,pas_fail_down,pas_fail_info_up,pas_fail_info_down = [],[],[],[],[],[]
|
||||
thrp_b = upload * len(sta_num) # get overall upload values
|
||||
thrp_a = download * len(sta_num) # get overall download values
|
||||
print(f"given upload--{thrp_b} and download--{thrp_a} values")
|
||||
index = -1
|
||||
for a in bps_rx_a:
|
||||
index += 1
|
||||
if len(a):
|
||||
rx_a.append(f'Min: {min(a)} | Max: {max(a)} | Avg: {(sum(a) / len(a)):.2f} | Total: {sum(a):.2f}')
|
||||
if thrp_a:
|
||||
print(f"Expected throughput for util-{util[index]} and threshold-{threshold[index]}---- "
|
||||
f"{(thrp_a / 100) * (int(threshold[index]))} \nGot overall download values for util "
|
||||
f"'{util[index]}'----- {sum(a)} \n ")
|
||||
if (thrp_a / 100) * int(threshold[index]) <= sum(a) and min(a) != 0:
|
||||
pas_fail_down.append(f"PASS")# {len(sta_num)}-stations ran download traffic and met threshold")
|
||||
#pas_fail_info_down.append(f"")
|
||||
else:
|
||||
pas_fail_down.append(f"FAIL")# {a.count(0)}-stations got zero throughput and overall throughput- {sum(a):.2f}")
|
||||
#pas_fail_info_down.append(f"")
|
||||
else:
|
||||
pas_fail_down.append("NA")
|
||||
rx_a.append(0)
|
||||
|
||||
if len(bps_rx_b[index]):
|
||||
rx_b.append(f'Min: {min(bps_rx_b[index])} | Max: {max(bps_rx_b[index])} | '
|
||||
f'Avg: {(sum(bps_rx_b[index]) / len(bps_rx_b[index])):.2f} | Total: {sum(bps_rx_b[index]):.2f}')
|
||||
if thrp_b:
|
||||
print(f"Expected throughput for util-{util[index]} and threshold-{threshold[index]}---- "
|
||||
f"{(thrp_b / 100) * (int(threshold[index]))} \nGot overall upload values for util "
|
||||
f"'{util[index]}'----- {sum(bps_rx_b[index])} \n ")
|
||||
if (thrp_b / 100) * int(threshold[index]) <= sum(bps_rx_b[index]) and min(bps_rx_b[index])!= 0:
|
||||
pas_fail_up.append(f"PASS")# {len(sta_num)}-stations ran upload traffic and met threshold")
|
||||
#pas_fail_info_up.append()
|
||||
else:
|
||||
pas_fail_up.append(f"FAIL")# {bps_rx_b[index].count(0)}-stations got zero throughput and overall throughput- "
|
||||
#f"{sum(bps_rx_b[index]):.2f}")
|
||||
#pas_fail_info_up.append(f"")
|
||||
else:
|
||||
pas_fail_up.append("NA")
|
||||
rx_b.append(0)
|
||||
|
||||
util[index] = f'{util[index]}%' # append % to the util values
|
||||
|
||||
overall_tab = pd.DataFrame({
|
||||
'Channel Utilization (%)': util, "No.of.clients": [len(sta_num)] * len(util),
|
||||
'Intended Throughput(Mbps)': [f'upload: {upload} | download: {download}'] * len(util),
|
||||
'Achieved Upload Throughput(Mbps)': rx_b, 'Achieved Download Throughput(Mbps)': rx_a
|
||||
})
|
||||
print(f"overall table \n{overall_tab}")
|
||||
|
||||
pasfail_tab = pd.DataFrame({
|
||||
'Channel Utilization (%)': util,
|
||||
'Upload': pas_fail_up,
|
||||
'Download': pas_fail_down
|
||||
})
|
||||
print(f"pass-fail table \n {pasfail_tab}")
|
||||
report = lf_report(_results_dir_name="Throughput_Under_Channel_Load",_output_html="throughput_channel_load.html",
|
||||
_output_pdf="throughput_channel_load.pdf")
|
||||
report.set_title(rep_title)
|
||||
report.build_banner()
|
||||
report.set_obj_html(_obj_title="Objective",
|
||||
_obj=f"This test is designed to measure the throughput of {len(sta_num)} clients connected on 5GHz"
|
||||
" radio when the channel was already utilized with different percentage")
|
||||
report.build_objective()
|
||||
# test setup information
|
||||
report.set_table_title("Test Setup Information")
|
||||
report.build_table_title()
|
||||
report.test_setup_table(test_setup_data=test_setup_info, value="Device Under Test")
|
||||
self.table(report, "Min, Max, Avg Throughput", overall_tab,
|
||||
dis=f"The below table gives the information about Min, Max, and Avg throughput "
|
||||
f"for the clients when channel utilized with {', '.join(util)}")
|
||||
self.table(report, "Pass/Fail Criteria", pasfail_tab, dis=f"This table briefs about Pass/Fail criteria "
|
||||
f"for {', '.join(util)} channel utilization. If all the stations are able to run traffic and the overall throughput "
|
||||
f"should meet the given threshold then the test is considered to be PASS. The test fails if the overall throughput "
|
||||
f"is below the threshold value also if any one of the station is not able to run the layer-3 traffic.")
|
||||
if download:
|
||||
self.grph(report,
|
||||
data_set=[[min(i) for i in bps_rx_a], [max(i) for i in bps_rx_a], [sum(i) / len(i) for i in bps_rx_a]],
|
||||
dis=f"This graph represents the minimum, maximum and average throughput of "
|
||||
f"stations when channel was utilized with {', '.join(util)} for download traffic",
|
||||
xaxis_name="Utilizations", yaxis_name="Throughput (Mbps)",
|
||||
xaxis_categories=util, label=["min", "max", 'avg'],multi_bar_width = 0.25,
|
||||
graph_image_name="Download Throughput for all channel utilizations",step=1)
|
||||
if upload:
|
||||
self.grph(report,
|
||||
data_set=[[min(i) for i in bps_rx_b], [max(i) for i in bps_rx_b], [sum(i) / len(i) for i in bps_rx_b]],
|
||||
dis=f"This graph represents the minimum, maximum and average throughput of "
|
||||
f"stations when channel was utilized with {', '.join(util)} for upload traffic",
|
||||
xaxis_name="Utilizations", yaxis_name="Throughput (Mbps)",
|
||||
xaxis_categories=util, label=["min", "max", 'avg'],multi_bar_width = 0.25,
|
||||
graph_image_name="Upload Throughput for all channel utilization",step= 1)
|
||||
if len(sta_num) <= 40:
|
||||
step = 1
|
||||
elif 40 < len(sta_num) <= 80:
|
||||
step = 3
|
||||
elif 80 < len(sta_num) <= 100:
|
||||
step = 5
|
||||
else:
|
||||
step = 10
|
||||
for i in range(len(util)):
|
||||
if download:
|
||||
self.grph(report, data_set=[bps_rx_a[i]],
|
||||
dis=f"The graph shows the individual throughput for all the connected stations on 5GHz radio "
|
||||
f"when channel was utilized with {util[i]} in download traffic",
|
||||
xaxis_name="Stations",yaxis_name="Throughput (Mbps)", xaxis_categories=range(1, len(sta_num) + 1,step),
|
||||
label=[util[i]], graph_image_name=f"Individual download throughput - CH{util[i]}", xticks_font=7,step = step
|
||||
,multi_bar_width = 0)
|
||||
if upload:
|
||||
self.grph(report, data_set=[bps_rx_b[i]],
|
||||
dis=f"The graph shows the individual throughput for all the connected stations on 5GHz radio "
|
||||
f"when channel was utilized with {util[i]} in upload traffic",
|
||||
xaxis_name="stations", yaxis_name="Throughput (Mbps)", xaxis_categories=range(1, len(sta_num) + 1,step),
|
||||
label=[util[i]], graph_image_name=f"Individual upload throughput - CH{util[i]}", xticks_font=7,step = step
|
||||
,multi_bar_width = 0)
|
||||
# input setup information
|
||||
report.set_table_title("Input Setup Information")
|
||||
report.build_table_title()
|
||||
report.test_setup_table(test_setup_data=input_setup_info, value="Information")
|
||||
report.build_footer()
|
||||
html_file = report.write_html()
|
||||
print("returned file {}".format(html_file))
|
||||
print(html_file)
|
||||
report.write_pdf()
|
||||
colmn = ['Stations']#'No.of.times(download/upload']
|
||||
colmn.extend(range(1, len(self.bps_rx) + 1))
|
||||
data = list(self.bps_rx.values())
|
||||
data.insert(0, self.sta_list)
|
||||
csv = lf_csv(_columns= colmn, _rows= data,
|
||||
_filename='throughput_under_channel_load.csv')
|
||||
csv.generate_csv()
|
||||
report.csv_file_name = "throughput_under_channel_load.csv"
|
||||
report.move_csv_file()
|
||||
|
||||
def monitor(self, duration_sec, monitor_interval, created_cx, col_names, iterations):
|
||||
try:
|
||||
duration_sec = Realm.parse_time(duration_sec).seconds
|
||||
except:
|
||||
if (duration_sec is None) or (duration_sec <= 1):
|
||||
raise ValueError("L3CXProfile::monitor wants duration_sec > 1 second")
|
||||
if (duration_sec <= monitor_interval):
|
||||
raise ValueError("L3CXProfile::monitor wants duration_sec > monitor_interval")
|
||||
if created_cx == None:
|
||||
raise ValueError("Monitor needs a list of Layer 3 connections")
|
||||
if (monitor_interval is None) or (monitor_interval < 1):
|
||||
raise ValueError("L3CXProfile::monitor wants monitor_interval >= 1 second")
|
||||
|
||||
# monitor columns
|
||||
start_time = datetime.datetime.now()
|
||||
end_time = start_time + datetime.timedelta(seconds=duration_sec)
|
||||
# bps-rx-a (download) and bps-rx-b(upload) values are taken
|
||||
self.bps_rx_a, self.bps_rx_b, self.bps_rx, index = [], [], {}, -1
|
||||
bps_rx_a_avg,bps_rx_b_avg = [],[]
|
||||
[(self.bps_rx_a.append([]), self.bps_rx_b.append([])) for i in range(len(created_cx))]
|
||||
for test in range(1 + iterations):
|
||||
while datetime.datetime.now() < end_time:
|
||||
index += 1
|
||||
response = list(self.json_get('/cx/%s?fields=%s' % (','.join(created_cx),",".join(col_names))).values())[2:]
|
||||
self.bps_rx[index] = list(map(lambda i: [float(f"{x / (1000000):.2f}") for x in i.values()],response))
|
||||
time.sleep(monitor_interval)
|
||||
# bps_rx list is calculated
|
||||
print("rx rate values are with [bps-rx-a, bps-rx-b] :-\n", self.bps_rx,"\n\n")
|
||||
for index, key in enumerate(self.bps_rx):
|
||||
for i in range(len(self.bps_rx[key])):
|
||||
if self.cx_profile.side_b_min_bps != '0' and self.cx_profile.side_b_min_bps != 0:
|
||||
self.bps_rx_a[i].append(self.bps_rx[key][i][0])
|
||||
if self.cx_profile.side_a_min_bps != '0' and self.cx_profile.side_a_min_bps != 0:
|
||||
self.bps_rx_b[i].append(self.bps_rx[key][i][1])
|
||||
print(f"bps-rx-a values-: \n{self.bps_rx_a}\nbps-rx-b values-: \n{self.bps_rx_b}")
|
||||
if self.cx_profile.side_a_min_bps != '0' and self.cx_profile.side_a_min_bps != 0:
|
||||
bps_rx_b_avg = [float(f"{sum(i) / len(i): .2f}") for i in self.bps_rx_b]
|
||||
if self.cx_profile.side_b_min_bps != '0' and self.cx_profile.side_b_min_bps != 0:
|
||||
bps_rx_a_avg = [float(f"{sum(i) / len(i): .2f}") for i in self.bps_rx_a]
|
||||
return bps_rx_a_avg,bps_rx_b_avg
|
||||
|
||||
def check_util(self,real_cli_obj = None, util_list = None, real_cli = None,
|
||||
ssh_root = None, ssh_passwd = None,test_time = 0,up_down = [0],threshold=None,channnel=0):
|
||||
# check the utilization and run the traffic
|
||||
bps_rx_a,bps_rx_b,sta_create,count = [],[],1,0
|
||||
for util in util_list: # get throughput for every utilization values
|
||||
if count > 0:
|
||||
if len(up_down) > 0: # give data rate value and delete aggigned
|
||||
self.cx_profile.side_a_min_bps, self.cx_profile.side_b_min_bps = int(float(up_down[0])),int(float( up_down[0]))
|
||||
up_down.pop(0)
|
||||
count += 1
|
||||
stop_channel_load = 0
|
||||
util_flag = 1
|
||||
'''Loop until the expected channel utilization will get'''
|
||||
while util_flag:
|
||||
stop_channel_load += 1
|
||||
'''STOP the script if unable to set the utilization for 20 times'''
|
||||
if stop_channel_load >= 20:
|
||||
print(f"Tried loading the channel with {util}% for {stop_channel_load} times...\n"
|
||||
f"Unable to load the channel with {util}%\nScript exiting...")
|
||||
exit(1)
|
||||
|
||||
util_val = self.chn_util(ssh_root, ssh_passwd, channnel=channnel) # find the channel utilization
|
||||
if (util - 3) <= util_val <= (util + 3):
|
||||
util_flag = 0
|
||||
if sta_create:
|
||||
sta_create = 0
|
||||
real_cli_obj.build() # create specified no.of clients once
|
||||
real_cli_obj.start(False, False)
|
||||
time.sleep(20)
|
||||
_bps_rx_a, _bps_rx_b = real_cli_obj.monitor(duration_sec=float(self.test_duration) * 60, monitor_interval=1,
|
||||
created_cx=real_cli_obj.cx_profile.created_cx.keys(),
|
||||
col_names=['bps rx a', 'bps rx b'], iterations=0)
|
||||
#_bps_rx_a, _bps_rx_b = real_cli_obj.throughput(util,real_cli)
|
||||
bps_rx_a.append(_bps_rx_a)
|
||||
bps_rx_b.append(_bps_rx_b)
|
||||
real_cli_obj.stop(trf=True,ad_dwn=False)
|
||||
else:
|
||||
# channel utilization is less than the expected utilization value
|
||||
if util_val < (util - 3):
|
||||
print("less than {}% util...".format(util))
|
||||
if ((util ) - util_val) <= 4:
|
||||
self.re_run_traff(100000, "add")
|
||||
elif ((util ) - util_val) <= 8:
|
||||
self.re_run_traff(300000, "add")
|
||||
elif ((util ) - util_val) <= 12:
|
||||
self.re_run_traff(500000, "add")
|
||||
elif (util ) - util_val <= 16:
|
||||
self.re_run_traff(1000000, "add")
|
||||
elif (util ) - util_val > 16:
|
||||
self.re_run_traff(1500000, "add")
|
||||
|
||||
# channel utilization is less than the expected utilization value
|
||||
elif util_val > (util + 3):
|
||||
print("greater than {}% util...".format(util))
|
||||
if (util_val - (util )) <= 4:
|
||||
self.re_run_traff(100000, "sub")
|
||||
elif (util_val - (util )) <= 8:
|
||||
self.re_run_traff(300000, "sub")
|
||||
elif (util_val - (util )) <= 12:
|
||||
self.re_run_traff(500000, "sub")
|
||||
elif util_val - (util ) <= 16:
|
||||
self.re_run_traff(1000000, "sub")
|
||||
elif util_val - (util ) > 16:
|
||||
self.re_run_traff(1500000, "sub")
|
||||
|
||||
print(f"bps_rx_a {bps_rx_a}\nbps_rx_b {bps_rx_b}")
|
||||
|
||||
test_end = datetime.datetime.now().strftime("%b %d %H:%M:%S")
|
||||
print("Test ended at ", test_end)
|
||||
|
||||
if len(threshold) < len(util_list):
|
||||
for i in range(len(util_list)):
|
||||
try:
|
||||
tmp = threshold[i]
|
||||
except IndexError as e:
|
||||
print(f"Threshold {threshold} and utilization {util_list}")
|
||||
threshold.append(100 - int(util_list[i]))
|
||||
print(f"threshold {threshold} and utilization {util_list}")
|
||||
test_setup_info = {
|
||||
"AP Name": self.ap,
|
||||
"SSID": real_cli_obj.ssid,
|
||||
'No.of stations': len(real_cli),
|
||||
'Vap channel': channnel,
|
||||
'Utilization': ', '.join(map(str,util_list)),
|
||||
'Threshold': ', '.join(map(str,threshold)),
|
||||
"Total Test Duration": datetime.datetime.strptime(test_end, '%b %d %H:%M:%S') - datetime.datetime.strptime(test_time, '%b %d %H:%M:%S')
|
||||
}
|
||||
|
||||
input_setup_info = {
|
||||
"Contact": "support@candelatech.com"
|
||||
}
|
||||
# send all the collected data to genarate report
|
||||
real_cli_obj.report(util = util_list, sta_num = real_cli,
|
||||
bps_rx_a = bps_rx_a, bps_rx_b= bps_rx_b,
|
||||
rep_title = "Throughput Under Channel Load",
|
||||
upload = int(real_cli_obj.cx_profile.side_a_min_bps)/1000000,
|
||||
download = int(real_cli_obj.cx_profile.side_b_min_bps)/1000000,
|
||||
test_setup_info = test_setup_info,input_setup_info = input_setup_info,threshold= threshold)
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
optional,required = [],[]
|
||||
optional.append({'name': '--mode', 'help': 'Used to force mode of stations','default': 9})
|
||||
optional.append({'name': '--ap_name', 'help': 'AP name'})
|
||||
required.append({'name': '--ap_ip', 'help': 'IP of AP which was connected'})
|
||||
optional.append({'name': '--test_duration', 'help': 'Sets the duration of the test in minutes', 'default': 1})
|
||||
optional.append({'name':'--vap_channel', 'help':'To create VAP provide the AP channel', 'default': 36})
|
||||
required.append({'name':'--vap_radio', 'help':'VAP radio', 'default': "wiphy3"})
|
||||
optional.append({'name':'--util', 'help':'Channel utilization(provide whole number eg: 11,23,30,etc) with data_rate(bps) for that utilization',
|
||||
'default': "20-3000000,40-6000000"})
|
||||
optional.append({'name':'--threshold', 'help':'Set the threshold for each utilization. '
|
||||
'By default it will take 100-util_value (eg: 100-20=80) in case user not providing the Threshold'})
|
||||
required.append({'name':'--ap_password','help':'Password for AP'})
|
||||
optional.append({'name': '--upload', 'help': 'Upload bps rate minimum for side_a of netgear', 'default': 0})
|
||||
optional.append({'name': '--download', 'help': 'Download bps rate minimum for side_b of netgear', 'default': 0})
|
||||
parser = Realm.create_basic_argparse(
|
||||
prog='throughput.py',
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
epilog='''\
|
||||
Measure the throughput for no.of clients when the channel was already utilized by specific load
|
||||
''',
|
||||
description='''\
|
||||
throughput.py:
|
||||
--------------------
|
||||
Generic command layout:
|
||||
Note:-
|
||||
**** In case user providing 'Fractional part' to the input values while running, the script will automatically truncate
|
||||
the Fractional part except the test_duration.
|
||||
**** The script will automatically stop its execution when the channel is unable to load.
|
||||
python3 ./throughput.py
|
||||
--mode 1 {"auto" : "0",
|
||||
"a" : "1",
|
||||
"b" : "2",
|
||||
"g" : "3",
|
||||
"abg" : "4",
|
||||
"abgn" : "5",
|
||||
"bgn" : "6",
|
||||
"bg" : "7",
|
||||
"abgnAC" : "8",
|
||||
"anAC" : "9",
|
||||
"an" : "10",
|
||||
"bgnAC" : "11",
|
||||
"abgnAX" : "12",
|
||||
"bgnAX" : "13"}
|
||||
--upstream_port eth1
|
||||
--vap_radio wiphy0
|
||||
--vap_channel 36
|
||||
--radio wiphy1
|
||||
--num_stations 40
|
||||
--security {open|wep|wpa|wpa2|wpa3}
|
||||
--ssid netgear
|
||||
--password admin123
|
||||
--test_duration 1 (default)
|
||||
--upload 3000000
|
||||
--download 3000000
|
||||
--util 20-2000000,40-000000
|
||||
--threshold 80,50
|
||||
--ap_ip 192.168.208.22
|
||||
--ap_name WAC505
|
||||
--ap_password Password@123xzsawq@!
|
||||
--debug
|
||||
===============================================================================
|
||||
''', more_optional=optional, more_required = required)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
util_rate = args.util.split(',')
|
||||
if args.threshold != None:
|
||||
threshold = [int(float(i)) for i in args.threshold.split(',')]
|
||||
else:
|
||||
threshold = []
|
||||
util_list, rate_list = [],[]
|
||||
for i in range(len(util_rate)):
|
||||
util_list.append(util_rate[i].split('-')[0])
|
||||
rate_list.append(util_rate[i].split('-')[1])
|
||||
util_list = [int(float(i)) for i in util_list]
|
||||
num_sta = lambda ars: ars if (ars != None and ars != 0) else 40 # if num station is None by deafault it create 2 stations
|
||||
|
||||
# 4 stations created under VAP by default
|
||||
station_list = LFUtils.portNameSeries(prefix_="sta", start_id_=0, end_id_= 3 , padding_number_=10000, radio=args.radio)
|
||||
# vap name
|
||||
vap_name = 'vap0000'
|
||||
print("List of stations under VAP--",station_list,'\nVAP name--',vap_name)
|
||||
# traffic data rate for stations under vap
|
||||
vap_sta_upload, vap_sta_download = int(float(rate_list[0])), int(float(rate_list[0]))
|
||||
rate_list.pop(0)
|
||||
|
||||
# create stations and run traffic under VAP
|
||||
ip_var_test = IPV4VariableTime(host=args.mgr, port=args.mgr_port, number_template="0000",
|
||||
sta_list=station_list, name_prefix="VT", upstream=vap_name,
|
||||
ssid="vap_ssid", password='[BLANK]', radio=args.radio,
|
||||
security='open', test_duration=args.test_duration,
|
||||
use_ht160=False, side_a_min_rate= vap_sta_upload,
|
||||
side_b_min_rate=vap_sta_download,
|
||||
mode=args.mode, ap=args.ap_name, _debug_on=args.debug,
|
||||
_vap_list = vap_name, _vap_radio = args.vap_radio, _dhcp = False)
|
||||
|
||||
# ip_var_test.stop()
|
||||
# time.sleep(30)
|
||||
test_time = datetime.datetime.now().strftime("%b %d %H:%M:%S")
|
||||
print("Test started at ", test_time)
|
||||
|
||||
ip_var_test.pre_cleanup() # clear existing clients
|
||||
ip_var_test.build_vaps(chn = int(float(args.vap_channel))) # create VAPs
|
||||
ip_var_test.build() # create Stations and traffic
|
||||
|
||||
if not ip_var_test.passes():
|
||||
print(ip_var_test.get_fail_message())
|
||||
ip_var_test.exit_fail()
|
||||
|
||||
try:
|
||||
layer3connections = ','.join([[*x.keys()][0] for x in ip_var_test.json_get('endp')['endpoint']])
|
||||
except:
|
||||
raise ValueError('Try setting the upstream port flag if your device does not have an eth1 port')
|
||||
|
||||
ip_var_test.start(False, False) # start the traffic and admin-up the sta
|
||||
|
||||
station_list1 = LFUtils.portNameSeries(prefix_="Thsta", start_id_=0, end_id_=int(num_sta(args.num_stations))-1, padding_number_=10000,
|
||||
radio=args.radio)
|
||||
print("Station list for netgear AP.....\n",station_list1)
|
||||
ip_var_test1 = IPV4VariableTime(host=args.mgr, port=args.mgr_port, number_template="0000",
|
||||
sta_list=station_list1, name_prefix="Thrp", upstream=args.upstream_port,
|
||||
ssid= args.ssid, password=args.passwd, radio=args.radio,
|
||||
security=args.security, test_duration=args.test_duration,
|
||||
use_ht160=False, side_a_min_rate=int(float(args.upload)), side_b_min_rate=int(float(args.download)),
|
||||
mode=args.mode, ap=args.ap_name, _debug_on=args.debug, _dhcp = True)
|
||||
|
||||
# check the channel utilization
|
||||
ip_var_test.check_util(real_cli_obj = ip_var_test1, util_list = util_list,real_cli = station_list1, ssh_root = args.ap_ip,
|
||||
ssh_passwd = args.ap_password,test_time = test_time, up_down=rate_list,threshold = threshold,channnel = int(float(args.vap_channel)))
|
||||
|
||||
if not ip_var_test.passes():
|
||||
print(ip_var_test.get_fail_message())
|
||||
ip_var_test.exit_fail()
|
||||
|
||||
ip_var_test.pre_cleanup() # clean the existing sta and traffics
|
||||
if ip_var_test.passes():
|
||||
ip_var_test.exit_success()
|
||||
|
||||
except Exception as e:
|
||||
print("###",e,"###\nUnable to run the script...\nProvide the right values with the help of --help command\n"
|
||||
"OR Re-run the script if the script stopped by some unexpected behavior..")
|
||||
print(traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user