mirror of
https://github.com/Telecominfraproject/wlan-lanforge-scripts.git
synced 2025-11-01 11:18:03 +00:00
998 lines
44 KiB
Python
Executable File
998 lines
44 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
'''
|
|
NAME: lf_test_max_association.py
|
|
|
|
NOTE: Script still in progress. Requires modification to l3_cxprofile.py (ln 227) to alleviate url > 2048 bytes error.
|
|
Temporary workaround: replace l3_cxprofile.py ln 227 with:
|
|
layer_3_response = self.json_get("/endp/?fields=%s" % (layer3_fields))
|
|
|
|
PURPOSE:
|
|
This script will conduct a maximum client overnight test for the ct521a system:
|
|
- create the maximum supported stations per installed radio.
|
|
- associate the created stations to their prospective SSID's.
|
|
- create sta-to-eth Layer-3 CX for 9.6Kbps bidirectional overnight maximum-client wifi test.
|
|
|
|
EXAMPLE:
|
|
./lf_test_max_association.py --mgr <localhost> --upstream_port <1.1.eth1> --wiphy0_ssid <ssid0> --wiphy1_ssid <ssid1>
|
|
--security <security> --passwd <passwd>
|
|
|
|
KPI.CSV:
|
|
./lf_test_max_association.py --mgr <localhost> --upstream_port <1.1.eth1> --wiphy0_ssid <ssid0> --wiphy1_ssid <ssid1>
|
|
--passwd <passwd> --security <wpa2> --csv_outfile lf_test_max_association.csv --test_rig CT_01
|
|
--test_tag MAX_STA --dut_hw_version 1.0 --dut_model_num lf0350 --dut_serial_num 361c --dut_sw_version 5.4.5
|
|
|
|
TODO: Add capability for MTK radios and ct523c systems.
|
|
|
|
'''
|
|
|
|
import argparse
|
|
import sys
|
|
import os
|
|
import logging
|
|
import importlib
|
|
from pprint import pformat
|
|
import time
|
|
import csv
|
|
import platform
|
|
|
|
if sys.version_info[0] != 3:
|
|
print("This script requires Python 3")
|
|
exit(1)
|
|
|
|
sys.path.append(os.path.join(os.path.abspath(__file__ + "../../../")))
|
|
|
|
LFUtils = importlib.import_module("py-json.LANforge.LFUtils")
|
|
removeCX = LFUtils.removeCX
|
|
removeEndps = LFUtils.removeEndps
|
|
realm = importlib.import_module("py-json.realm")
|
|
Realm = realm.Realm
|
|
lf_report = importlib.import_module("py-scripts.lf_report")
|
|
lf_graph = importlib.import_module("py-scripts.lf_graph")
|
|
lf_kpi_csv = importlib.import_module("py-scripts.lf_kpi_csv")
|
|
lf_radio_info = importlib.import_module("py-scripts.lf_radio_info")
|
|
lf_cleanup = importlib.import_module("py-scripts.lf_cleanup")
|
|
lf_logger_config = importlib.import_module("py-scripts.lf_logger_config")
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
OPEN = "open"
|
|
WEP = "wep"
|
|
WPA = "wpa"
|
|
WPA2 = "wpa2"
|
|
WPA3 = "wpa3"
|
|
MODE_AUTO = 0
|
|
|
|
|
|
class max_associate(Realm):
|
|
def __init__(self, _host, _port, wiphy0_ssid, wiphy1_ssid,
|
|
passwd="NA",
|
|
security=OPEN,
|
|
resource=1,
|
|
upstream_port="eth1",
|
|
download_bps=9830,
|
|
upload_bps=9830,
|
|
wiphy_info=None,
|
|
name_prefix=None,
|
|
_test_duration="21600s",
|
|
monitor_interval='10s',
|
|
layer3_columns=None,
|
|
report_file=None,
|
|
kpi_csv=None,
|
|
kpi_path=None,
|
|
output_format=None,
|
|
port_mgr_columns=None,
|
|
compared_report=None,
|
|
outfile=None,
|
|
clean_profile=None,
|
|
debug_=False,
|
|
_exit_on_fail=False):
|
|
if layer3_columns is None:
|
|
layer3_columns = ['name', 'tx bytes', 'rx bytes', 'tx rate', 'rx rate']
|
|
super().__init__(_host, _port, debug_=debug_, _exit_on_fail=_exit_on_fail)
|
|
self.host = _host
|
|
self.port = _port
|
|
self.debug = debug_
|
|
self.security = security
|
|
self.wiphy0_ssid = wiphy0_ssid
|
|
self.wiphy1_ssid = wiphy1_ssid
|
|
self.password = passwd
|
|
self.resource = resource
|
|
self.upstream_port = upstream_port
|
|
self.download_bps = download_bps
|
|
self.wiphy_info = wiphy_info
|
|
self.clean_profile = clean_profile
|
|
self.upload_bps = upload_bps
|
|
self.name_prefix = name_prefix
|
|
self.test_duration = _test_duration
|
|
self.monitor_interval = monitor_interval
|
|
self.layer3_columns = layer3_columns
|
|
self.report_file = report_file
|
|
self.kpi_csv = kpi_csv
|
|
self.kpi_path = kpi_path
|
|
self.output_format = output_format
|
|
self.port_mgr_columns = port_mgr_columns
|
|
self.compared_report = compared_report
|
|
self.outfile = outfile
|
|
self.epoch_time = int(time.time())
|
|
|
|
self.systeminfopath = None
|
|
self.report_path_format = None
|
|
self.report_file_format = None
|
|
self.layer3connections = None
|
|
self.name_prefix = "MA"
|
|
self.traffic_type = "lf_udp"
|
|
|
|
self.station_profile = self.new_station_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 = self.download_bps
|
|
self.cx_profile.side_b_min_bps = self.upload_bps
|
|
|
|
if self.outfile is not None:
|
|
results = self.outfile[:-4]
|
|
results = results + "-results.csv"
|
|
self.csv_results_file = open(results, "w")
|
|
self.csv_results_writer = csv.writer(self.csv_results_file, delimiter=",")
|
|
|
|
def get_kpi_results(self):
|
|
# make json call to get kpi results
|
|
endp_list = self.json_get(
|
|
"endp?fields=name,eid,delay,jitter,rx+rate,rx+rate+ll,rx+bytes,rx+drop+%25,rx+pkts+ll",
|
|
debug_=False)
|
|
logger.info("endp_list: {endp_list}".format(endp_list=endp_list))
|
|
|
|
def get_csv_name(self):
|
|
logger.info("self.csv_results_file {}".format(self.csv_results_file.name))
|
|
return self.csv_results_file.name
|
|
|
|
# Common code to generate timestamp for CSV files.
|
|
def time_stamp(self):
|
|
return time.strftime('%m_%d_%Y_%H_%M_%S', time.localtime(self.epoch_time))
|
|
|
|
def format_report_path(self):
|
|
if self.report_file is None:
|
|
# new_file_path = str(datetime.datetime.now().strftime("%Y-%m-%d-%H-h-%M-m-%S-s")).replace(':','-') + '_test_ip_variable_time'
|
|
# create path name
|
|
new_file_path = self.kpi_path
|
|
if os.path.exists('/home/lanforge/report-data'):
|
|
path = os.path.join('/home/lanforge/report-data/', new_file_path)
|
|
os.mkdir(path)
|
|
else:
|
|
logger.info(new_file_path)
|
|
# curr_dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
# curr_dir_path += '/py-scripts'
|
|
# path = os.path.join(curr_dir_path, new_file_path)
|
|
# os.mkdir(path)
|
|
# systeminfopath = str(path) + '/systeminfo.txt'
|
|
self.systeminfopath = str(new_file_path) + '/systeminfo.txt'
|
|
|
|
if self.output_format in ['csv', 'json', 'html', 'hdf', 'stata', 'pickle', 'pdf', 'png', 'parquet',
|
|
'xlsx']:
|
|
# self.report_path_format = str(path) + '/data.' + self.output_format
|
|
self.report_path_format = str(new_file_path) + '/data.' + self.output_format
|
|
self.report_file_format = self.output_format
|
|
else:
|
|
logger.info(
|
|
'Not supporting this report format or cannot find report format provided. Defaulting to csv data file '
|
|
'output type, naming it data.csv.')
|
|
# report_f = str(path) + '/data.csv'
|
|
self.report_path_format = str(new_file_path) + '/data.csv'
|
|
self.report_file_format = 'csv'
|
|
else:
|
|
self.systeminfopath = str(self.report_file).split('/')[-1]
|
|
self.report_path_format = self.report_file
|
|
if self.output_format is None:
|
|
self.report_file_format = str(self.report_file).split('.')[-1]
|
|
else:
|
|
self.report_file_format = self.output_format
|
|
|
|
def layer3_connections(self):
|
|
try:
|
|
self.layer3connections = ','.join([[*x.keys()][0] for x in self.json_get('endp')['endpoint']])
|
|
except ValueError:
|
|
raise ValueError('Try setting the upstream port flag if your device does not have an eth1 port')
|
|
|
|
if type(self.layer3_columns) is not list:
|
|
self.layer3_columns = list(self.layer3_columns.split(","))
|
|
# send col names here to file to reformat
|
|
else:
|
|
self.layer3_columns = self.layer3_columns
|
|
# send col names here to file to reformat
|
|
if type(self.port_mgr_columns) is not list:
|
|
self.port_mgr_columns = list(self.port_mgr_columns.split(","))
|
|
# send col names here to file to reformat
|
|
else:
|
|
self.port_mgr_columns = self.port_mgr_columns
|
|
# send col names here to file to reformat
|
|
if self.debug:
|
|
logger.debug("Layer 3 Endp column names are...")
|
|
logger.debug(self.layer3_columns)
|
|
logger.debug("Port Manager column names are...")
|
|
logger.debug(self.port_mgr_columns)
|
|
|
|
try:
|
|
self.monitor_interval = Realm.parse_time(self.monitor_interval).total_seconds()
|
|
except ValueError as error:
|
|
logger.critical(error)
|
|
logger.critical(
|
|
"The time string provided for monitor_interval argument is invalid. Please see supported time stamp increments and inputs for monitor_interval in --help. ")
|
|
return ValueError(
|
|
"The time string provided for monitor_interval argument is invalid. Please see supported time stamp increments and inputs for monitor_interval in --help. ")
|
|
|
|
# Query all endpoints to generate rx and other stats, returned
|
|
# as an array of objects.
|
|
def get_rx_values(self):
|
|
endp_list = self.json_get(
|
|
"endp?fields=name,eid,delay,jitter,rx+rate,rx+rate+ll,rx+bytes,rx+drop+%25,rx+pkts+ll",
|
|
debug_=True)
|
|
# logger.info("endp_list: {endp_list}".format(endp_list=endp_list))
|
|
endp_rx_drop_map = {}
|
|
endp_rx_map = {}
|
|
endps = []
|
|
|
|
total_ul_rate = 0
|
|
total_ul_ll = 0
|
|
total_ul_pkts_ll = 0
|
|
total_dl_rate = 0
|
|
total_dl_ll = 0
|
|
total_dl_pkts_ll = 0
|
|
|
|
'''
|
|
for e in self.cx_profile.created_endp.keys():
|
|
our_endps[e] = e
|
|
print("our_endps {our_endps}".format(our_endps=our_endps))
|
|
'''
|
|
for endp_name in endp_list['endpoint']:
|
|
if endp_name != 'uri' and endp_name != 'handler':
|
|
for item, endp_value in endp_name.items():
|
|
# if item in our_endps:
|
|
if True:
|
|
endps.append(endp_value)
|
|
logger.debug("endpoint: {item} value:\n".format(item=item))
|
|
logger.debug(endp_value)
|
|
|
|
# logger.info(endps)
|
|
|
|
for value_name, value in endp_value.items():
|
|
if value_name == 'rx bytes':
|
|
endp_rx_map[item] = value
|
|
# logger.info("rx_bytes {value}".format(value=value))
|
|
if value_name == 'rx rate':
|
|
endp_rx_map[item] = value
|
|
# logger.info("rx_rate {value}".format(value=value))
|
|
# This hack breaks for mcast or if someone names endpoints weirdly.
|
|
# logger.info("item: ", item, " rx-bps: ", value_rx_bps)
|
|
# info for upload test data
|
|
# logger.info(self.traffic_type)
|
|
if item.endswith("-A"):
|
|
total_ul_rate += int(value)
|
|
# logger.info(udp_ul_rate_bps)
|
|
else:
|
|
total_dl_rate += int(value)
|
|
# logger.info(udp_dl_rate_bps)
|
|
|
|
if value_name == 'rx rate ll':
|
|
endp_rx_map[item] = value
|
|
# logger.info("rx_rate_ll {value}".format(value=value))
|
|
if value_name == 'rx pkts ll':
|
|
endp_rx_map[item] = value
|
|
|
|
if item.endswith("-A"):
|
|
total_ul_pkts_ll += int(value)
|
|
# logger.info(total_ul_pkts_ll)
|
|
if value == '':
|
|
value = '0'
|
|
else:
|
|
total_dl_pkts_ll += int(value)
|
|
# logger.info(total_dl_pkts_ll)
|
|
|
|
if value_name == 'rx drop %':
|
|
endp_rx_drop_map[item] = value
|
|
if value_name == 'rx rate ll':
|
|
# This hack breaks for mcast or if someone
|
|
# names endpoints weirdly.
|
|
if item.endswith("-A"):
|
|
total_ul_ll += int(value)
|
|
else:
|
|
total_dl_ll += int(value)
|
|
|
|
# logger.debug("total-dl: ", total_dl, " total-ul: ", total_ul, "\n")
|
|
return endp_rx_map, endp_rx_drop_map, endps, total_ul_rate, total_dl_rate, total_dl_ll, total_ul_ll, total_ul_pkts_ll, total_dl_pkts_ll
|
|
|
|
def cleanup(self):
|
|
logger.info("Cleaning pre-existing l3-cx's, endps, and stations...")
|
|
self.clean_profile.cxs_clean()
|
|
self.clean_profile.endp_clean()
|
|
self.clean_profile.sta_clean()
|
|
LFUtils.wait_until_ports_disappear(base_url=self.lfclient_url, port_list=self.station_profile.station_names,
|
|
debug=self.debug)
|
|
|
|
def build(self):
|
|
self.cleanup()
|
|
# used to store data in memory:
|
|
all_wiphy_data = self.wiphy_info.get_lanforge_radio_information()
|
|
# logger.info(all_wiphy_data)
|
|
wiphy_radio_list = self.wiphy_info.get_radios()
|
|
# logger.info(wiphy_radio_list)
|
|
|
|
for wiphy_radio in wiphy_radio_list:
|
|
|
|
if wiphy_radio == "1." + self.resource + ".wiphy0":
|
|
# TODO: smw
|
|
num_stations = self.wiphy_info.get_max_vifs(wiphy_radio)
|
|
# num_stations = 2
|
|
# logger.info(num_stations)
|
|
station_list = LFUtils.portNameSeries(prefix_="sta", start_id_=0,
|
|
end_id_=int(num_stations) - 1,
|
|
padding_number_=10000,
|
|
radio=wiphy_radio)
|
|
|
|
# logger.info(station_list)
|
|
self.station_profile.lfclient_url = self.lfclient_url
|
|
self.station_profile.ssid = self.wiphy0_ssid
|
|
# logger.info(self.password)
|
|
self.station_profile.ssid_pass = self.password
|
|
self.station_profile.security = self.security
|
|
# self.station_profile.number_template_ = self.number_template
|
|
self.station_profile.debug = self.debug
|
|
self.station_profile.use_security(self.security, self.wiphy0_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)
|
|
|
|
logger.info("Creating stations")
|
|
self.station_profile.create(radio=wiphy_radio, sta_names_=station_list, debug=self.debug)
|
|
self._pass("PASS: Station build finished")
|
|
|
|
self.cx_profile.create(endp_type=self.traffic_type,
|
|
side_a=station_list,
|
|
side_b=self.upstream_port,
|
|
sleep_time=0)
|
|
|
|
if wiphy_radio == "1." + self.resource + ".wiphy1":
|
|
# TODO: smw
|
|
start_num_stations = self.wiphy_info.get_max_vifs("1.1.wiphy0")
|
|
# start_num_stations = 2
|
|
end_num_stations = self.wiphy_info.get_max_vifs(wiphy_radio)
|
|
# end_num_stations = 2
|
|
# logger.info(num_stations)
|
|
# TODO: make start_id = end_id + 1 of wiphy0 created stations, make dynamic:
|
|
station_list = LFUtils.portNameSeries(prefix_="sta",
|
|
start_id_=int(start_num_stations),
|
|
end_id_=int(end_num_stations) + int(start_num_stations) - 1,
|
|
padding_number_=10000,
|
|
radio=wiphy_radio)
|
|
|
|
# logger.info(station_list)
|
|
self.station_profile.lfclient_url = self.lfclient_url
|
|
self.station_profile.ssid = self.wiphy1_ssid
|
|
self.station_profile.ssid_pass = self.password
|
|
self.station_profile.security = self.security
|
|
# self.station_profile.number_template_ = self.number_template
|
|
self.station_profile.debug = self.debug
|
|
self.station_profile.use_security(self.security, self.wiphy1_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)
|
|
|
|
logger.info("Creating stations")
|
|
self.station_profile.create(radio=wiphy_radio, sta_names_=station_list, debug=self.debug)
|
|
self._pass("PASS: Station build finished")
|
|
|
|
self.cx_profile.create(endp_type=self.traffic_type,
|
|
side_a=station_list,
|
|
side_b=self.upstream_port,
|
|
sleep_time=0)
|
|
|
|
def start_cxs(self):
|
|
if self.station_profile is None:
|
|
self._fail("Incorrect setup")
|
|
logger.debug(pformat(self.station_profile))
|
|
if self.station_profile.up is None:
|
|
self._fail("Incorrect station profile, missing profile.up")
|
|
if not self.station_profile.up:
|
|
logger.info("Bringing ports up...")
|
|
# logger.info("start() - self.lf_client_url: %s", self.lfclient_url)
|
|
data = {"shelf": 1,
|
|
"resource": self.resource,
|
|
"port": "ALL",
|
|
"probe_flags": 1}
|
|
# logger.info("start() - data1: %s", data)
|
|
self.json_post("/cli-json/nc_show_ports", data)
|
|
# print("start() - ln 252 - self.j")
|
|
self.station_profile.admin_up()
|
|
self.csv_add_column_headers()
|
|
station_names = []
|
|
station_names.extend(self.station_profile.station_names.copy())
|
|
# logger.info(station_names)
|
|
LFUtils.waitUntilPortsAdminUp(self.resource, self.lfclient_url, station_names)
|
|
|
|
time.sleep(10)
|
|
self.cx_profile.start_cx()
|
|
|
|
def stop(self):
|
|
# stop cx traffic
|
|
logger.info("Stopping CX Traffic")
|
|
self.cx_profile.stop_cx()
|
|
end_time = time.ctime()
|
|
logger.info("Test End: %s", end_time)
|
|
|
|
# guide script during test run & collect data
|
|
def run(self):
|
|
self.start_cxs()
|
|
start_time = time.ctime()
|
|
logger.info("Test Start: %s", start_time)
|
|
# time.sleep(int(self.test_duration))
|
|
station_names = []
|
|
station_names.extend(self.station_profile.station_names.copy())
|
|
|
|
# Retrieve last data file
|
|
compared_rept = None
|
|
if self.compared_report:
|
|
compared_report_format = self.compared_report.split('.')[-1]
|
|
# if compared_report_format not in ['csv', 'json', 'dta', 'pkl','html','xlsx','parquet','h5']:
|
|
if compared_report_format != 'csv':
|
|
logger.critical("Cannot process this file type. Please select a different file and re-run script.")
|
|
raise ValueError("Cannot process this file type. Please select a different file and re-run script.")
|
|
else:
|
|
compared_rept = self.compared_report
|
|
|
|
# remove endpoints from layer3connections that do not begin with 'MA' prefix:
|
|
# convert layer3connections to list:
|
|
split_l3_endps = self.layer3connections.split(",")
|
|
# logger.info(split_l3_endps)
|
|
new_l3_endps_list = []
|
|
|
|
for item in split_l3_endps:
|
|
if item.startswith('MA'):
|
|
new_l3_endps_list.append(item)
|
|
# logger.info(new_l3_endps_list)
|
|
# convert new_l3_endps_list to str:
|
|
layer3endps = ','.join(str(l3endps) for l3endps in new_l3_endps_list)
|
|
# logger.info(layer3endps)
|
|
|
|
# logger.info(self.layer3_columns)
|
|
self.cx_profile.monitor(layer3_cols=self.layer3_columns,
|
|
# sta_list=self.sta_list,
|
|
sta_list=station_names,
|
|
port_mgr_cols=self.port_mgr_columns,
|
|
report_file=self.report_path_format,
|
|
systeminfopath=self.systeminfopath,
|
|
duration_sec=self.test_duration,
|
|
monitor_interval_ms=self.monitor_interval,
|
|
created_cx=layer3endps,
|
|
output_format=self.report_file_format,
|
|
compared_report=compared_rept,
|
|
script_name='lf_test_max_association',
|
|
adjust_cx_json=True,
|
|
debug=self.debug)
|
|
|
|
# fill out data kpi.csv and results reports
|
|
temp_stations_list = []
|
|
temp_stations_list.extend(self.station_profile.station_names.copy())
|
|
total_test = len(self.get_result_list())
|
|
total_pass = len(self.get_passed_result_list())
|
|
endp_rx_map, endp_rx_drop_map, endps, total_ul_rate, total_dl_rate, total_dl_ll, total_ul_ll, total_ul_pkts_ll, total_dl_pkts_ll = self.get_rx_values()
|
|
self.record_kpi_csv(temp_stations_list, total_test, total_pass, total_ul_rate, total_dl_rate, total_ul_pkts_ll, total_dl_pkts_ll, endp_rx_drop_map, endp_rx_map)
|
|
self.record_results(len(temp_stations_list), total_ul_rate, total_dl_rate, total_ul_pkts_ll, total_dl_pkts_ll)
|
|
|
|
logger.info(self.report_path_format)
|
|
logger.info(compared_rept)
|
|
logger.info(self.port_mgr_columns)
|
|
logger.info(self.report_file_format)
|
|
|
|
logger.info("Leaving existing stations...")
|
|
logger.info("IP Variable Time Test Report Data: {}".format(self.report_path_format))
|
|
|
|
# builds test data into kpi.csv report
|
|
def record_kpi_csv(
|
|
self,
|
|
sta_list,
|
|
total_test,
|
|
total_pass,
|
|
total_ul_rate,
|
|
total_dl_rate,
|
|
total_ul_pkts_ll,
|
|
total_dl_pkts_ll,
|
|
endp_rx_drop_map,
|
|
endp_rx_map):
|
|
|
|
sta_list_len = len(sta_list)
|
|
endp_list_len = len(endp_rx_map)
|
|
total_ul_dl_rate = total_ul_rate+total_dl_rate
|
|
|
|
# logic for Subtest-Pass & Subtest-Fail columns
|
|
# 0 = FAIL, 1 = PASS (on kpi.csv output)
|
|
subpass_udp_ul = 0
|
|
subpass_udp_dl = 0
|
|
subfail_udp_ul = 1
|
|
subfail_udp_dl = 1
|
|
subpass_pkts_ul = 0
|
|
subpass_pkts_dl = 0
|
|
subfail_pkts_ul = 1
|
|
subfail_pkts_dl = 1
|
|
subpass_traffic_loss = 0
|
|
subfail_traffic_loss = 1
|
|
|
|
if total_ul_rate > 0:
|
|
subpass_udp_ul = 1
|
|
subfail_udp_ul = 0
|
|
if total_dl_rate > 0:
|
|
subpass_udp_dl = 1
|
|
subfail_udp_dl = 0
|
|
if total_ul_pkts_ll > 0:
|
|
subpass_pkts_ul = 1
|
|
subfail_pkts_ul = 0
|
|
if total_dl_pkts_ll > 0:
|
|
subpass_pkts_dl = 1
|
|
subfail_pkts_dl = 0
|
|
|
|
# logic for pass/fail column
|
|
# total_test & total_pass values from lfcli_base.py
|
|
if total_test == total_pass:
|
|
pass_fail = "PASS"
|
|
else:
|
|
pass_fail = "FAIL"
|
|
|
|
# logic for endpoints that experience > 1% traffic loss:
|
|
l3_endp_drops = []
|
|
drop_value_sum = 0.0
|
|
total_endp_drops = 0
|
|
avg_drop = 0.0
|
|
for endp_drop in endp_rx_drop_map.keys():
|
|
logger.info(endp_rx_drop_map[endp_drop])
|
|
if endp_rx_drop_map[endp_drop] > 1.0:
|
|
drop_value_sum += endp_rx_drop_map[endp_drop]
|
|
l3_endp_drops.append(endp_drop)
|
|
total_endp_drops = len(l3_endp_drops)
|
|
avg_drop = drop_value_sum / total_endp_drops
|
|
# logger.info(avg_drop)
|
|
avg_drop_round = round(avg_drop, 2)
|
|
|
|
# sub-test fails if endp amount that has > 1% traffic loss exceeds 3% of total created endps
|
|
endp_loss = total_endp_drops / endp_list_len * 100
|
|
drop_tolerance = 3
|
|
if endp_loss < drop_tolerance:
|
|
subpass_traffic_loss = 1
|
|
subfail_traffic_loss = 0
|
|
|
|
results_dict = self.kpi_csv.kpi_csv_get_dict_update_time()
|
|
|
|
# kpi data for UDP upload traffic
|
|
results_dict['Graph-Group'] = "Total UDP Upload Rate"
|
|
results_dict['pass/fail'] = pass_fail
|
|
results_dict['Subtest-Pass'] = subpass_udp_ul
|
|
results_dict['Subtest-Fail'] = subfail_udp_ul
|
|
results_dict['short-description'] = "UDP-UL {upload_bps} bps {sta_list_len} STA".format(
|
|
upload_bps=self.upload_bps, sta_list_len=sta_list_len)
|
|
results_dict['numeric-score'] = "{}".format(total_ul_rate)
|
|
results_dict['Units'] = "bps"
|
|
self.kpi_csv.kpi_csv_write_dict(results_dict)
|
|
|
|
# kpi data for UDP download traffic
|
|
results_dict['Graph-Group'] = "Total UDP Download Rate"
|
|
results_dict['pass/fail'] = pass_fail
|
|
results_dict['Subtest-Pass'] = subpass_udp_dl
|
|
results_dict['Subtest-Fail'] = subfail_udp_dl
|
|
results_dict['short-description'] = "UDP-DL {download_bps} bps {sta_list_len} STA".format(
|
|
download_bps=self.download_bps, sta_list_len=sta_list_len)
|
|
results_dict['numeric-score'] = "{}".format(total_dl_rate)
|
|
results_dict['Units'] = "bps"
|
|
self.kpi_csv.kpi_csv_write_dict(results_dict)
|
|
|
|
# kpi data for total upload & download traffic rate
|
|
results_dict['Graph-Group'] = "Total UDP-UL/DL Rate"
|
|
results_dict['pass/fail'] = pass_fail
|
|
results_dict['Subtest-Pass'] = subpass_udp_ul
|
|
results_dict['Subtest-Fail'] = subfail_udp_ul
|
|
results_dict['short-description'] = "UDP-UL {upload_bps} bps UDP-DL {download_bps} bps {sta_list_len} STA".format(
|
|
upload_bps=self.upload_bps, download_bps=self.download_bps, sta_list_len=sta_list_len)
|
|
results_dict['numeric-score'] = "{}".format(total_ul_dl_rate)
|
|
results_dict['Units'] = "bps"
|
|
self.kpi_csv.kpi_csv_write_dict(results_dict)
|
|
|
|
# kpi data for UDP upload packets ll
|
|
results_dict['Graph-Group'] = "Total Transmitted Packet Count"
|
|
results_dict['pass/fail'] = pass_fail
|
|
results_dict['Subtest-Pass'] = subpass_pkts_ul
|
|
results_dict['Subtest-Fail'] = subfail_pkts_ul
|
|
results_dict['short-description'] = "UDP-UL {upload_bps} bps {sta_list_len} STA".format(
|
|
upload_bps=self.upload_bps, sta_list_len=sta_list_len)
|
|
results_dict['numeric-score'] = "{}".format(total_ul_pkts_ll)
|
|
results_dict['Units'] = "pkts"
|
|
self.kpi_csv.kpi_csv_write_dict(results_dict)
|
|
|
|
# kpi data for UDP download packets ll
|
|
results_dict['Graph-Group'] = "Total Received Packet Count"
|
|
results_dict['pass/fail'] = pass_fail
|
|
results_dict['Subtest-Pass'] = subpass_pkts_dl
|
|
results_dict['Subtest-Fail'] = subfail_pkts_dl
|
|
results_dict['short-description'] = "UDP-DL {download_bps} bps {sta_list_len} STA".format(
|
|
download_bps=self.download_bps, sta_list_len=sta_list_len)
|
|
results_dict['numeric-score'] = "{}".format(total_dl_pkts_ll)
|
|
results_dict['Units'] = "pkts"
|
|
self.kpi_csv.kpi_csv_write_dict(results_dict)
|
|
|
|
# kpi data for stations drop over 1%
|
|
results_dict['Graph-Group'] = "Avg Traffic Loss"
|
|
results_dict['pass/fail'] = pass_fail
|
|
results_dict['Subtest-Pass'] = subpass_traffic_loss
|
|
results_dict['Subtest-Fail'] = subfail_traffic_loss
|
|
results_dict['short-description'] = "{total_endp_drops} of {endp_list_len} ENDP Over 1% Traffic Loss".format(
|
|
total_endp_drops=total_endp_drops, endp_list_len=endp_list_len)
|
|
results_dict['numeric-score'] = "{}".format(avg_drop_round)
|
|
results_dict['Units'] = "percent"
|
|
self.kpi_csv.kpi_csv_write_dict(results_dict)
|
|
|
|
# record results for .html & .pdf reports
|
|
def record_results(
|
|
self,
|
|
sta_count,
|
|
udp_dl_rate_bps,
|
|
udp_ul_rate_bps,
|
|
total_ul_pkts_ll,
|
|
total_dl_pkts_ll):
|
|
|
|
dl = self.download_bps
|
|
ul = self.upload_bps
|
|
|
|
tags = dict()
|
|
tags['requested-ul-bps'] = ul
|
|
tags['requested-dl-bps'] = dl
|
|
tags['station-count'] = sta_count
|
|
# tags['attenuation'] = atten
|
|
tags["script"] = 'lf_test_max_association'
|
|
|
|
'''
|
|
# Add user specified tags
|
|
for k in self.user_tags:
|
|
tags[k[0]] = k[1]
|
|
'''
|
|
|
|
if self.csv_results_file:
|
|
row = [self.epoch_time, self.time_stamp(), sta_count,
|
|
ul, ul, dl, dl,
|
|
udp_ul_rate_bps, udp_dl_rate_bps,
|
|
(udp_ul_rate_bps + udp_dl_rate_bps),
|
|
total_ul_pkts_ll, total_dl_pkts_ll,
|
|
(total_ul_pkts_ll + total_dl_pkts_ll)
|
|
]
|
|
'''
|
|
# Add values for any user specified tags
|
|
for k in self.user_tags:
|
|
row.append(k[1])
|
|
'''
|
|
|
|
self.csv_results_writer.writerow(row)
|
|
self.csv_results_file.flush()
|
|
|
|
def csv_generate_results_column_headers(self):
|
|
csv_rx_headers = [
|
|
'Time epoch',
|
|
'Time',
|
|
'Station-Count',
|
|
'UL-Min-Requested',
|
|
'UL-Max-Requested',
|
|
'DL-Min-Requested',
|
|
'DL-Max-Requested',
|
|
# 'Attenuation',
|
|
'UDP-Upload-bps',
|
|
# 'TCP-Upload-bps',
|
|
'UDP-Download-bps',
|
|
# 'TCP-Download-bps',
|
|
# 'Total-UDP/TCP-Upload-bps',
|
|
# 'Total-UDP/TCP-Download-bps',
|
|
'Total-UDP-UL/DL-bps',
|
|
'UL-Rx-Pkts-ll',
|
|
'DL-Rx-Pkts-ll',
|
|
'Total-UL/DL-Pkts-ll']
|
|
|
|
return csv_rx_headers
|
|
|
|
# Write initial headers to csv file.
|
|
def csv_add_column_headers(self):
|
|
if self.csv_results_file is not None:
|
|
self.csv_results_writer.writerow(
|
|
self.csv_generate_results_column_headers())
|
|
self.csv_results_file.flush()
|
|
|
|
# ~class
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
prog="lf_test_max_association.py",
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
description="""
|
|
---------------------------
|
|
LANforge Unit Test: Create maximum stations per wiphy radio - lf_test_max_association.py
|
|
---------------------------
|
|
Summary:
|
|
This script will provide the following features for the ct521a system:
|
|
- create the maximum supported stations per installed radio.
|
|
- associate the created stations to their prospective SSID's.
|
|
- create sta-to-eth Layer-3 CX for 9.6Kbps bidirectional overnight maximum-client wifi test.
|
|
---------------------------
|
|
CLI Example:
|
|
./lf_test_max_association.py --mgr <localhost> --upstream_port <1.1.eth1> --wiphy0_ssid <ssid0>
|
|
--wiphy1_ssid <ssid1> --security <security> --passwd <passwd>
|
|
|
|
---------------------------
|
|
""")
|
|
parser.add_argument("-m", "--mgr", type=str, help="address of the LANforge GUI machine (localhost is default)",
|
|
default='localhost')
|
|
parser.add_argument("--port", type=str, help="LANforge Manager port", default='8080')
|
|
parser.add_argument("--wiphy0_ssid", type=str, help="DUT SSID that the wiphy0 stations will associate with.")
|
|
parser.add_argument("--wiphy1_ssid", type=str, help="DUT SSID that the wiphy1 stations will associate with.")
|
|
parser.add_argument("--security", type=str, help="DUT security: openLF, wpa, wpa2, wpa3")
|
|
parser.add_argument("--passwd", type=str, help="DUT PSK password. Do not set for OPEN auth")
|
|
parser.add_argument("--download_bps", type=int, help="Set the minimum bps value on test endpoint A. Default: 9.6Kbps", default=9830)
|
|
parser.add_argument("--upload_bps", type=int, help="Set the minimum bps value on test endpoint B. Default: 9.6Kbps", default=9830)
|
|
parser.add_argument('--test_duration', help='--test_duration sets the duration of the test, default is 8 hours', default="28800s")
|
|
parser.add_argument("--debug", help="enable debugging", action="store_true")
|
|
parser.add_argument('--report_file', help='where you want to store results', default=None)
|
|
parser.add_argument('--output_format', help='choose either csv or xlsx')
|
|
parser.add_argument("--prefix", type=str, help="Station prefix. Default: 'sta'", default='sta')
|
|
parser.add_argument("--resource", type=str, help="LANforge Station resource ID to use, default is 1", default="1")
|
|
parser.add_argument("--upstream_port", type=str, help="LANforge Ethernet port name, default is eth1",
|
|
default='1.1.eth1')
|
|
parser.add_argument("--sta_mode", type=str,
|
|
help="LANforge station-mode setting (see add_sta LANforge CLI documentation, default is 0 (auto))",
|
|
default=0)
|
|
parser.add_argument("--bringup_time", type=int,
|
|
help="Seconds to wait for stations to associate and aquire IP. Default: 300", default=300)
|
|
parser.add_argument('--debug_log', default=None, help="Specify a file to send debug output to")
|
|
parser.add_argument('--local_lf_report_dir', help='--local_lf_report_dir override the report path, primary use when running test in test suite', default="")
|
|
parser.add_argument('--monitor_interval',
|
|
help='how frequently do you want your monitor function to take measurements, 35s, 2h',
|
|
default='10s')
|
|
parser.add_argument('--compared_report', help='report path and file which is wished to be compared with new report',
|
|
default=None)
|
|
parser.add_argument('--layer3_cols', help='Columns wished to be monitored from layer 3 endpoint tab',
|
|
default=['name', 'tx bytes', 'rx bytes', 'tx rate', 'rx rate'])
|
|
parser.add_argument('--port_mgr_cols', help='Columns wished to be monitored from port manager tab',
|
|
default=['alias', 'ap', 'ip', 'parent dev', 'rx-rate'])
|
|
|
|
# kpi_csv arguments:
|
|
parser.add_argument(
|
|
"--test_rig",
|
|
default="",
|
|
help="test rig for kpi.csv, testbed that the tests are run on")
|
|
parser.add_argument(
|
|
"--test_tag",
|
|
default="",
|
|
help="test tag for kpi.csv, test specific information to differenciate the test")
|
|
parser.add_argument(
|
|
"--dut_hw_version",
|
|
default="",
|
|
help="dut hw version for kpi.csv, hardware version of the device under test")
|
|
parser.add_argument(
|
|
"--dut_sw_version",
|
|
default="",
|
|
help="dut sw version for kpi.csv, software version of the device under test")
|
|
parser.add_argument(
|
|
"--dut_model_num",
|
|
default="",
|
|
help="dut model for kpi.csv, model number / name of the device under test")
|
|
parser.add_argument(
|
|
"--dut_serial_num",
|
|
default="",
|
|
help="dut serial for kpi.csv, serial number / serial number of the device under test")
|
|
parser.add_argument(
|
|
"--test_priority",
|
|
default="",
|
|
help="dut model for kpi.csv, test-priority is arbitrary number")
|
|
parser.add_argument(
|
|
"--test_id",
|
|
default="L3 Data",
|
|
help="test-id for kpi.csv, script or test name")
|
|
parser.add_argument(
|
|
'--csv_outfile',
|
|
help="--csv_outfile <Output file for csv data>",
|
|
default="")
|
|
|
|
# logging configuration:
|
|
parser.add_argument("--lf_logger_config_json",
|
|
help="--lf_logger_config_json <json file> , json configuration of logger")
|
|
parser.add_argument('--log_level',
|
|
default=None,
|
|
help='Set logging level: debug | info | warning | error | critical')
|
|
|
|
# TODO: Try to start with: --radio 'radio==wiphy4,stations==1,ssid==asus11ax-2,ssid_pw==hello123,security==wpa2':
|
|
parser.add_argument(
|
|
'-r', '--radio',
|
|
action='append',
|
|
nargs=1,
|
|
help=(' --radio'
|
|
' "radio==<number_of_wiphy stations=<=number of stations>'
|
|
' ssid==<ssid> ssid_pw==<ssid password> security==<security> '
|
|
' wifi_settings==True wifi_mode==<wifi_mode>'
|
|
' enable_flags==<enable_flags> '
|
|
' reset_port_enable==True reset_port_time_min==<min>s'
|
|
' reset_port_time_max==<max>s" ')
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# set up logger
|
|
logger_config = lf_logger_config.lf_logger_config()
|
|
|
|
if (args.log_level):
|
|
logger_config.set_level(level=args.log_level)
|
|
|
|
if args.lf_logger_config_json:
|
|
# logger_config.lf_logger_config_json = "lf_logger_config.json"
|
|
logger_config.lf_logger_config_json = args.lf_logger_config_json
|
|
logger_config.load_lf_logger_config()
|
|
|
|
if args.debug:
|
|
logger_config.set_level("debug")
|
|
|
|
# for kpi.csv generation
|
|
local_lf_report_dir = args.local_lf_report_dir
|
|
test_rig = args.test_rig
|
|
test_tag = args.test_tag
|
|
dut_hw_version = args.dut_hw_version
|
|
dut_sw_version = args.dut_sw_version
|
|
dut_model_num = args.dut_model_num
|
|
dut_serial_num = args.dut_serial_num
|
|
# test_priority = args.test_priority # this may need to be set per test
|
|
test_id = args.test_id
|
|
|
|
if local_lf_report_dir != "":
|
|
report = lf_report.lf_report(
|
|
_path=local_lf_report_dir,
|
|
_results_dir_name="lf_test_max_association",
|
|
_output_html="lf_test_max_association.html",
|
|
_output_pdf="lf_test_max_association.pdf")
|
|
else:
|
|
report = lf_report.lf_report(
|
|
_results_dir_name="lf_test_max_association",
|
|
_output_html="lf_test_max_association.html",
|
|
_output_pdf="lf_test_max_association.pdf")
|
|
|
|
kpi_path = report.get_report_path()
|
|
# kpi_filename = "kpi.csv"
|
|
logger.info("kpi_path :{kpi_path}".format(kpi_path=kpi_path))
|
|
|
|
kpi_csv = lf_kpi_csv.lf_kpi_csv(
|
|
_kpi_path=kpi_path,
|
|
_kpi_test_rig=test_rig,
|
|
_kpi_test_tag=test_tag,
|
|
_kpi_dut_hw_version=dut_hw_version,
|
|
_kpi_dut_sw_version=dut_sw_version,
|
|
_kpi_dut_model_num=dut_model_num,
|
|
_kpi_dut_serial_num=dut_serial_num,
|
|
_kpi_test_id=test_id)
|
|
|
|
if args.csv_outfile is None:
|
|
current_time = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
|
|
csv_outfile = "{}_{}-test_ip_variable_time.csv".format(
|
|
args.csv_outfile, current_time)
|
|
csv_outfile = report.file_add_path(csv_outfile)
|
|
logger.info("csv output file : {}".format(csv_outfile))
|
|
|
|
# create here & pass in w/ MaxAssociate, or
|
|
wiphy_info = lf_radio_info.radio_information(args.mgr,
|
|
_resource=args.resource,
|
|
debug_=args.debug,
|
|
_exit_on_fail=True)
|
|
|
|
clean_profile = lf_cleanup.lf_clean(args.mgr,
|
|
resource=args.resource,
|
|
clean_cxs=True,
|
|
clean_endp=True,
|
|
clean_sta=True)
|
|
|
|
# add: ssid, passwd, wifi settings, enabled flags
|
|
max_association = max_associate(args.mgr, args.port,
|
|
wiphy0_ssid=args.wiphy0_ssid,
|
|
wiphy1_ssid=args.wiphy1_ssid,
|
|
passwd=args.passwd,
|
|
security=args.security,
|
|
resource=args.resource,
|
|
upstream_port=args.upstream_port,
|
|
download_bps=args.download_bps,
|
|
upload_bps=args.upload_bps,
|
|
wiphy_info=wiphy_info,
|
|
name_prefix="sta",
|
|
_test_duration=args.test_duration,
|
|
monitor_interval=args.monitor_interval,
|
|
layer3_columns=args.layer3_cols,
|
|
report_file=args.report_file,
|
|
kpi_csv=kpi_csv,
|
|
kpi_path=kpi_path,
|
|
output_format=args.output_format,
|
|
port_mgr_columns=args.port_mgr_cols,
|
|
compared_report=args.compared_report,
|
|
outfile=args.csv_outfile,
|
|
clean_profile=clean_profile,
|
|
debug_=args.debug,
|
|
_exit_on_fail=True)
|
|
|
|
max_association.format_report_path()
|
|
max_association.build()
|
|
max_association.layer3_connections()
|
|
max_association.run()
|
|
|
|
# Reporting Results (.pdf & .html)
|
|
csv_results_file = max_association.get_csv_name()
|
|
logger.info("csv_results_file: {}".format(csv_results_file))
|
|
# csv_results_file = kpi_path + "/" + kpi_filename
|
|
report.set_title("L3 Maximum Clients Test: lf_test_max_association.py")
|
|
# report.build_banner()
|
|
# report.set_table_title("L3 Test Max Association Key Performance Indexes")
|
|
|
|
report.build_banner_left()
|
|
report.start_content_div2()
|
|
|
|
report.set_obj_html("Objective", "The Maximum Client Association Test is designed to test the capability of a "
|
|
"newly built ct521a LANforge system. The objective of the test is to create "
|
|
"the maximum number of virtual interfaces per system installed WIFI radio, "
|
|
"associate the stations to aspecified AP, and to run a long duration layer-3 "
|
|
"UDP bidirectional traffic test."
|
|
)
|
|
report.build_objective()
|
|
|
|
test_setup_info = {
|
|
"DUT Name": args.dut_model_num,
|
|
"DUT Hardware Version": args.dut_hw_version,
|
|
"DUT Software Version": args.dut_sw_version,
|
|
"DUT Serial Number": args.dut_serial_num,
|
|
"2.4GHz SSID": args.wiphy0_ssid,
|
|
"5GHz SSID": args.wiphy1_ssid,
|
|
}
|
|
|
|
report.set_table_title("Device Under Test Information")
|
|
report.build_table_title()
|
|
report.test_setup_table(value="Device Under Test", test_setup_data=test_setup_info)
|
|
|
|
test_input_info = {
|
|
"LANforge ip": args.mgr,
|
|
"LANforge port": args.port,
|
|
"LANforge resource": args.resource,
|
|
"Upstream": args.upstream_port,
|
|
"2.4GHz Radio": "1."+ args.resource + ".wiphy0",
|
|
"2.4GHz SSID": args.wiphy0_ssid,
|
|
"5GHz Radio": "1."+ args.resource + ".wiphy1",
|
|
"5GHz SSID": args.wiphy1_ssid,
|
|
"Security": args.security,
|
|
# "A side pdu": args.side_a_pdu,
|
|
# "B side pdu": args.side_b_pdu,
|
|
"Download bps": args.download_bps,
|
|
"Upload bps": args.upload_bps,
|
|
"Test Duration": args.test_duration,
|
|
}
|
|
|
|
report.set_table_title("Test Configuration")
|
|
report.build_table_title()
|
|
report.test_setup_table(value="Test Configuration", test_setup_data=test_input_info)
|
|
|
|
report.set_table_title("Layer 3 Overnight Maximum Association Traffic Results")
|
|
report.build_table_title()
|
|
report.set_table_dataframe_from_csv(csv_results_file)
|
|
report.build_table()
|
|
report.build_footer()
|
|
|
|
report.write_html_with_timestamp()
|
|
report.write_index_html()
|
|
# report.write_pdf(_page_size = 'A3', _orientation='Landscape')
|
|
# report.write_pdf_with_timestamp(_page_size='A4', _orientation='Portrait')
|
|
if platform.system() == 'Linux':
|
|
report.write_pdf_with_timestamp(_page_size='A4', _orientation='Landscape')
|
|
|
|
max_association.stop()
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|