Files
wlan-lanforge-scripts/py-scripts/lf_wifi_mobility_test.py
2024-05-21 01:13:54 +05:30

499 lines
21 KiB
Python

#!/usr/bin/env python3
"""
NAME: lf_wifi_mobility_test.py
PURPOSE: The Candela Roam test uses the forced roam method to create and roam hundreds of WiFi stations between two
or more APs with the same SSID on the same channel or different channels. The user can run thousands of roams over
long durations and the test measures roaming delay for each roam, station connection times, network down time,
packet loss etc.. The user can run this test using different security methods and compare the roaming performance.
The expected behavior is the roaming delay should be 50msecs or less for all various kinds of fast roaming methods to
avoid any form of service interruption to real-time delay sensitive applications
EXAMPLE:
example 1:
python3 lf_wifi_mobility.py --mgr 192.168.200.96 --port 8080 --lf_user lanforge --lf_password lanforge
--bssid_list "90:3c:b3:9d:69:3e,34:EF:B6:AF:49:08" --stations "1.1.sta0000"
--pull_report == If specified, this will pull reports from lanforge to your code directory,
from where you are running this code
Suggested: To have a scenario already built.
SCRIPT_CLASSIFICATION : Test
SCRIPT_CATEGORIES: Monitoring, Functional, Report Generation.
STATUS: BETA RELEASE
VERIFIED_ON:
Working date - 19/02/2024
Build version - 5.4.7
kernel version - 6.7.3+
LICENSE:
Free to distribute and modify. LANforge systems must be licensed.
Copyright 2023 Candela Technologies Inc
INCLUDE_IN_README: False
Example of raw text config for WiFi Mobility, to show other possible options:
sel_port-0: 1.1.sta0000
sel_port-1: 1.1.sta0001
show_events: 1
show_log: 0
log_stdout: 0
port_sorting: 0
kpi_id: WiFi Mobility
bg: 0xE0ECF8
dut_info_override:
test_rig:
test_tag:
show_scan: 1
auto_helper: 1
allow_11w: 0
skip_ac: 0
skip_ax: 0
skip_2: 0
skip_6: 1
skip_5: 0
skip_5b: 1
skip_dual: 0
skip_tri: 1
default_sleep: 250
auto_verify: 30000
max_rpt_time: 500
skip_roam_self: 1
loop_check: 1
clear_on_start: 0
ap_editor0: do_cli scan 1 1 sta1 NA 'trigger freq 5180'
ap_editor1: sleep 2.0
ap_editor2: roam 1 sta1 34:ef:b6:af:49:07
ap_editor3: sleep 10.0
ap_editor4: do_cli scan 1 1 sta1 NA 'trigger freq 5180'
ap_editor5: sleep 2.0
ap_editor6: roam 1 sta1 90:3c:b3:9d:69:2e
ap_editor7: sleep 10.0
bss_query_reason: 16
url: http://candelatech.com
beacon_req_ie: 51000000000002ffffffffffff
sta_addrs_ap: 04:f0:21:c3:b4:cc 1.1.12 sta0000
sta_ports_sta: 1.1.sta0000
ap_addrs_sta: 00:00:c1:01:88:15 DUT-bssid1: 0000c1018812
ap_addrs_ap: 00:00:c1:01:88:15 DUT-bssid1: 0000c1018812
use_civic: 0
use_lci: 0
gen_sleep_interval: 10000
gen_scan_sleep_interval: 2000
gen_scan_freqs: 5180
gen_ds: 0
gen_aps0: # Queried via RRM
gen_aps1: 90:3c:b3:9d:69:2e
gen_aps2: 34:EF:B6:AF:49:07
"""
import random
import sys
import os
import importlib
import argparse
import json
import time
import logging
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")
cv_test_manager = importlib.import_module("py-json.cv_test_manager")
cv_test = cv_test_manager.cv_test
cv_test_reports = importlib.import_module("py-json.cv_test_reports")
lf_report = cv_test_reports.lanforge_reports
lf_logger_config = importlib.import_module("py-scripts.lf_logger_config")
logger = logging.getLogger(__name__)
class WifiMobility(cv_test):
def __init__(self,
lfclient_host="localhost",
lf_port=8080,
ssh_port=22,
lf_user="lanforge",
lf_password="lanforge",
blob_test="WiFi-Mobility-",
instance_name="roam-inst-0",
config_name="roam_test_cfg",
pull_report=True,
load_old_cfg=False,
raw_lines=None,
raw_lines_file="",
enables=None,
disables=None,
sets=None,
cfg_options=None,
sort="interleave",
stations="1.1.sta0000",
bssid_list=None,
gen_scan_freqs=None,
gen_sleep_interval="10000",
gen_scan_sleep_interval="2000",
gen_ds=0,
duration="60000",
default_sleep="250",
auto_verify="30000",
max_rpt_time='500',
skip_roam_self='1',
loop_check='1',
clear_on_start='1',
show_events='1',
report_dir="",
graph_groups=None,
test_rig="Testbed-01",
test_tag="",
local_lf_report_dir="",
verbosity="5"
):
super().__init__(lfclient_host=lfclient_host, lfclient_port=lf_port)
if enables is None:
enables = []
if disables is None:
disables = []
if raw_lines is None:
raw_lines = []
if sets is None:
sets = []
self.lfclient_host = lfclient_host
self.lf_port = lf_port
self.lf_user = lf_user
self.lf_password = lf_password
self.ssh_port = ssh_port
self.pull_report = pull_report
self.load_old_cfg = load_old_cfg
self.blob_test = blob_test
self.instance_name = instance_name
self.config_name = config_name
self.test_name = "Roam Test"
self.stations = stations
self.bssid_list = bssid_list
self.gen_scan_freqs = gen_scan_freqs
self.gen_sleep_interval = gen_sleep_interval
self.gen_scan_sleep_interval = gen_scan_sleep_interval
self.gen_ds = gen_ds
self.sort = sort
self.duration = duration
self.default_sleep = default_sleep
self.auto_verify = auto_verify
self.max_rpt_time = max_rpt_time
self.skip_roam_self = skip_roam_self
self.loop_check = loop_check
self.clear_on_start = clear_on_start
self.show_events = show_events
self.raw_lines = raw_lines
self.raw_lines_file = raw_lines_file
self.sets = sets
self.enables = enables
self.disables = disables
self.cfg_options = cfg_options
self.report_dir = report_dir
self.graph_groups = graph_groups
self.test_rig = test_rig
self.test_tag = test_tag
self.local_lf_report_dir = local_lf_report_dir
self.verbosity = verbosity
def create_scenario(self, scenario_name="Automation", raw_line=""):
self.pass_raw_lines_to_cv(scenario_name=scenario_name, Rawline=raw_line) # creates a dummy scenario
def clean_cv_scenario(self, cv_type="Network-Connectivity", scenario_name=None):
self.rm_cv_text_blob(cv_type, scenario_name)
def run(self):
self.sync_cv()
time.sleep(2)
self.sync_cv()
self.rm_text_blob(self.config_name, "WiFi-Mobility-") # To delete old config with same name
self.show_text_blob(None, None, False)
# Test related settings
if self.cfg_options is None:
self.cfg_options = []
port_list = []
if self.stations != "":
stas = None
if self.stations:
stas = self.stations.split(",")
for s in stas:
port_list.append(s)
else:
stas = self.station_map() # See realm
for eid in stas.keys():
port_list.append(eid)
logger.info(f"Selected Port list: {port_list}")
idx = 0
for eid in port_list:
self.cfg_options.append("sel_port-" + str(idx) + ": " + str(eid))
idx += 1
if self.bssid_list is not None:
bssid_list = self.bssid_list.split(",")
for i, v in enumerate(bssid_list, 0):
# For example:
# gen_aps0: 04:42:1a:51:49:90
# gen_aps1: 04:42:1a:51:49:94
self.cfg_options.append("gen_aps" + str(i) + ": " + str(v))
# TODO: Scan sleep interval
if self.gen_scan_freqs:
# For example:
# gen_scan_freqs: 2412 5180
gen_scan_freqs = self.gen_scan_freqs.split(",")
gen_scan_freqs = "gen_scan_freqs: " + " ".join(gen_scan_freqs)
self.cfg_options.append(gen_scan_freqs)
if self.gen_sleep_interval != "":
self.cfg_options.append("gen_sleep_interval: " + str(self.gen_sleep_interval))
if self.gen_scan_sleep_interval != "":
self.cfg_options.append("gen_scan_sleep_interval: " + str(self.gen_scan_sleep_interval))
if self.gen_ds != 0:
self.cfg_options.append("gen_ds: " + str(self.gen_ds))
if self.duration != "":
self.cfg_options.append("duration: " + str(self.duration))
if self.default_sleep != "":
self.cfg_options.append("default_sleep: " + str(self.default_sleep))
if self.auto_verify != "":
self.cfg_options.append("auto_verify: " + str(self.auto_verify))
if self.max_rpt_time != "":
self.cfg_options.append("max_rpt_time: " + str(self.max_rpt_time))
if self.skip_roam_self != "":
self.cfg_options.append("skip_roam_self: " + str(self.skip_roam_self))
if self.loop_check != "":
self.cfg_options.append("loop_check: " + str(self.loop_check))
if self.clear_on_start != "":
self.cfg_options.append("clear_on_start: " + str(self.clear_on_start))
if self.test_rig != "":
self.cfg_options.append("test_rig: " + self.test_rig)
if self.test_tag != "":
self.cfg_options.append("test_tag: " + self.test_tag)
# self.apply_cfg_options(self.cfg_options, self.enables, self.disables, self.raw_lines, self.raw_lines_file)
# blob_test = "WiFi-Mobility-"
# Build config & set values to pass into test parameters
self.build_cfg(self.config_name, self.blob_test, self.cfg_options)
cv_cmds = []
if not self.bssid_list:
cmd = "cv click '%s' 'Query Neighbors'" % self.instance_name
cv_cmds.append(cmd)
cmd = "cv set '%s' 'VERBOSITY' '%s'" % (self.instance_name, self.verbosity)
cv_cmds.append(cmd)
cmd = "cv click '%s' 'Generate Script'" % self.instance_name
cv_cmds.append(cmd)
try:
self.create_and_run_test(self.load_old_cfg, self.test_name, self.instance_name,
self.config_name, self.sets,
self.pull_report, self.lfclient_host, self.lf_user, self.lf_password,
cv_cmds, ssh_port=self.ssh_port, graph_groups_file=self.graph_groups,
local_lf_report_dir=self.local_lf_report_dir)
except Exception as e:
logger.error(str(e))
exit(0)
self.rm_text_blob(self.config_name, self.blob_test) # To delete old config with same name
def main():
help_summary = '''\
The lf_wifi_mobility_test script is used to Monitor the connection status of all the clients for user specified duration.
This report shows the connection status of all the clients in the test. This information is very useful
when running long duration tests with 1000s of WiFi clients connecting across various bands, channels and SSIDs.
The report shows over time counts of number of clients in scanning, connect and IP address acquired states.
The report also shows number of clients connected over time per SSID, per Channel, per band and per client type
'''
parser = argparse.ArgumentParser(
prog="lf_wifi_mobility_test.py",
formatter_class=argparse.RawTextHelpFormatter,
epilog='''\
lf_wifi_mobility_test.py
''',
description="""
NAME: lf_wifi_mobility_test.py
PURPOSE: The Candela Roam test uses the forced roam method to create and roam hundreds of WiFi stations between two
or more APs with the same SSID on the same channel or different channels. The user can run thousands of roams over
long durations and the test measures roaming delay for each roam, station connection times, network down time,
packet loss etc.. The user can run this test using different security methods and compare the roaming performance.
The expected behavior is the roaming delay should be 50msecs or less for all various kinds of fast roaming methods to
avoid any form of service interruption to real-time delay sensitive applications
EXAMPLE:
example 1:
python3 lf_wifi_mobility.py --mgr 192.168.200.96 --port 8080 --lf_user lanforge --lf_password lanforge
--bssid_list "90:3c:b3:9d:69:3e,34:EF:B6:AF:49:08" --stations "1.1.sta0000"
--pull_report == If specified, this will pull reports from lanforge to your code directory,
from where you are running this code
Suggested: To have a scenario already built.
SCRIPT_CLASSIFICATION : Test
SCRIPT_CATEGORIES: Monitoring, Functional, Report Generation
STATUS: BETA RELEASE
VERIFIED_ON:
Working date - 7/05/2024
Build version - 5.4.7
kernel version - 6.7.3+
LICENSE:
Free to distribute and modify. LANforge systems must be licensed.
Copyright 2023 Candela Technologies Inc
INCLUDE_IN_README: False
""")
required = parser.add_argument_group('Required arguments')
optional = parser.add_argument_group('Optional arguments')
required.add_argument("-m", "--mgr", type=str, default="localhost",
help="address of the LANforge GUI machine (localhost is default)")
required.add_argument("-p", "--port", type=int, default=8080,
help="IP Port the LANforge GUI is listening on (8080 is default)")
required.add_argument("--lf_user", type=str, default="lanforge",
help="LANforge username to pull reports")
required.add_argument("--lf_password", type=str, default="lanforge",
help="LANforge Password to pull reports")
required.add_argument("-s", "--stations", type=str, default="1.1.sta0000",
help="If specified, these stations will be used. If not specified, all available stations "
"will be selected. Example: 1.1.sta001,1.1.wlan0,...")
required.add_argument("--bssid_list", type=str, help="pass the list of bssid's of AP1,AP2,etc.,",
default="90:3c:b3:9d:69:2e,34:EF:B6:AF:49:07")
required.add_argument("-pull_report", "--pull_report", default=True, action='store_true',
help="pull reports from lanforge reports directory to current working directory")
required.add_argument('--help_summary', default=None, action="store_true",
help='Show summary of what this script does')
optional.add_argument("-i", "--instance_name", type=str, default="roam-inst-0",
help="Instance name of the ROAM Test Window")
optional.add_argument('--test_duration', type=str, help='Test Duration (in ms)',
default="60000")
optional.add_argument('--default_sleep', type=str, help='delay to pause between roam commands (in ms)',
default="250")
optional.add_argument('--auto_verify', type=str,
help='check the stations to verify that the migration was successful or not (in ms)',
default="30000")
optional.add_argument('--max_rpt_time', type=str,
help='Maximum roam time to be set as an upper bound in graphs (in ms)', default="500")
optional.add_argument('--skip_roam_self', type=str, help='flag to skip roam to current AP', default="1")
optional.add_argument('--loop_check', type=str, help='flag to run the roaming script in Wifi Mobility in a loop',
default="1")
optional.add_argument('--clear_on_start', type=str, help='flag to clear counter on start', default="0")
optional.add_argument('--show_events', type=str, help='show LF events in the text Log window', default="1")
optional.add_argument('--gen_sleep_interval', type=str, help='sleep interval between a roam (in ms)',
default="10000")
optional.add_argument('--gen_scan_sleep_interval', type=str, help='sleep interval after each scan (in ms)',
default="2000")
required.add_argument('--gen_scan_freqs', type=str, help='List of frequencies to scan, eg: 5180,5300 (pass as '
'per the bssid list sequence provided)', default="5180,2437")
optional.add_argument('--gen_ds', type=str, help='flag to enable FT-DS roam', default="0")
optional.add_argument("--local_report_dir",
help="--local_report_dir <where to pull reports to> default is '' , i.e reports will be "
"saved in the current location.",
default="")
optional.add_argument("--lf_logger_config_json",
help="--lf_logger_config_json <json file> , json configuration of logger")
optional.add_argument("--graph_groups", help="File to save graph groups to", default=None)
optional.add_argument("--verbosity", default="5", help="Specify verbosity of the report values 1 - 11 default 5")
optional.add_argument("-c", "--config_name", type=str, default="roam_test_cfg",
help="Config file name")
optional.add_argument("--raw_lines", action='append', nargs=1, default=[],
help="Specify lines of the raw config file. Example: --raw_line 'test_rig: "
"Ferndale-01-Basic' See example raw text config for possible options. This is "
"catch-all for any options not available to be specified elsewhere. May be specified "
"multiple times.")
optional.add_argument("--raw_lines_file", default="",
help="Specify a file of raw lines to apply.")
optional.add_argument("--sets", action='append', nargs=2, default=[],
help="Specify options to set values based on their label in the GUI. Example: --set 'Basic "
"Client Connectivity' 1 May be specified multiple times.")
optional.add_argument("--test_rig", default="",
help="Specify the test rig info for reporting purposes, for instance: testbed-01")
optional.add_argument('--log_level', default='info',
help='Set logging level: debug | info | warning | error | critical')
args = parser.parse_args()
print(args)
if args.help_summary:
print(help_summary)
exit(0)
# set up logger
logger_config = lf_logger_config.lf_logger_config()
# set the logger level to debug
if args.log_level:
logger_config.set_level(level=args.log_level)
# lf_logger_config_json will take presidence to changing debug levels
if args.lf_logger_config_json:
logger_config.lf_logger_config_json = args.lf_logger_config_json
logger_config.load_lf_logger_config()
wifi_mobility_obj = WifiMobility(lfclient_host=args.mgr,
lf_port=args.port,
lf_user=args.lf_user,
lf_password=args.lf_password,
instance_name=args.instance_name,
config_name=args.config_name,
stations=args.stations,
bssid_list=args.bssid_list,
gen_scan_freqs=args.gen_scan_freqs,
gen_sleep_interval=args.gen_sleep_interval,
gen_scan_sleep_interval=args.gen_scan_sleep_interval,
gen_ds=args.gen_ds,
duration=args.test_duration,
default_sleep=args.default_sleep,
auto_verify=args.auto_verify,
max_rpt_time=args.max_rpt_time,
skip_roam_self=args.skip_roam_self,
loop_check=args.loop_check,
clear_on_start=args.clear_on_start,
show_events=args.show_events,
raw_lines=args.raw_lines,
raw_lines_file=args.raw_lines_file,
pull_report=args.pull_report,
load_old_cfg=False,
sets=args.sets,
graph_groups=args.graph_groups,
test_rig=args.test_rig,
local_lf_report_dir=args.local_report_dir,
verbosity=args.verbosity
)
if wifi_mobility_obj.instance_name.endswith('-0'):
wifi_mobility_obj.instance_name = wifi_mobility_obj.instance_name + str(random.randint(1, 9999))
wifi_mobility_obj.run()
if wifi_mobility_obj.get_exists(wifi_mobility_obj.instance_name):
wifi_mobility_obj.delete_instance(instance_name=wifi_mobility_obj.instance_name)
if __name__ == "__main__":
main()