Skip to content

Added Virtual, Real + Virtual, Existing Stations, Real + Existing Stations Scenarios to QOS Script.#71

Open
manoj9088 wants to merge 3 commits into
masterfrom
qos_vr
Open

Added Virtual, Real + Virtual, Existing Stations, Real + Existing Stations Scenarios to QOS Script.#71
manoj9088 wants to merge 3 commits into
masterfrom
qos_vr

Conversation

@manoj9088

Copy link
Copy Markdown
Collaborator

No description provided.

Comment thread py-scripts/lf_interop_qos.py Outdated
@@ -59,6 +60,10 @@
./lf_interop_qos.py --ap_name Cisco --mgr 192.168.244.97 --test_duration 1m --upstream_port eth1 --upload 1000000
--mgr_port 8080 --traffic_type lf_udp --tos "VI,VO,BE,BK" --file_name g219 --group_name grp1 --profile_name Open3

# Command Line Interface to run upload scenario by Configuring Devices in Groups with Specific Profiles for Real Devices
./lf_interop_qos.py --ap_name Cisco --mgr 192.168.244.97 --test_duration 1m --upstream_port eth1 --upload 1000000
--mgr_port 8080 --traffic_type lf_udp --tos "VI,VO,BE,BK" --file_name g219 --group_name grp1 --profile_name Open3 --bands 2.4G

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this as it is same as above

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed this extra same CLI.

super().__init__(lfclient_host=host,
lfclient_port=port)
self.ssid_list = []

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't change the position. it shows like new change

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced back to original position.

self.ssid = ssid
self.security = security
self.password = password
self.num_stations = num_stations

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't change the position. it shows like new change

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repositioned to old place.

@@ -488,7 +630,7 @@ def phantom_check(self):
for eid in same_eid_list:
for device in self.devices_available:
if eid in device:
print(eid + ' ' + device)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't change the old one

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed back to print statement.

Comment thread py-scripts/lf_interop_qos.py Outdated
@@ -557,9 +700,9 @@ def phantom_check(self):
self.num_stations = len(self.real_client_list)

for eid in resource_eid_list2:
for i in self.mac_id1_list:
for i in self.mac_id1_list: # ['1.11 be:5d:07:76:c8:81', '1.20 30:35:ad:c5:e1:be', '1.23 ca:84:bc:fa:cb:7a']

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert to back

print("cx build finished")

def create_cx(self):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

place create_cx here only instead of the above

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintained the original position inorder to decrease the diff.

logger.info(f"Creating CX for real device port: {self.input_devices_list[device]}")
self.cx_profile.create(
endp_type=self.traffic_type,
side_a=[self.input_devices_list[device]],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't change the old one use that one as both are same

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just added a logger in-front of cx_profile.create in-order to increase the readability of code.

def monitor_cx(self):
"""
This function waits for up to 20 iterations to allow all CXs (connections) to be created.

@Durga-CT Durga-CT Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why one line missed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the empty white line is removed in the "autopep8" check. not at all an issue with empty white line.

@@ -669,6 +1102,7 @@ def monitor_cx(self):
while current_retry < max_retry:
not_running_cx = []
overallresponse = self.json_get('/cx/all') # Get all current CXs from the layer-3 tab

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why new line added

if eid in i:
self.mac_id_list.append(i.split(' ', 1)[1])
self.mac_id_list.append(i.strip(eid + ' '))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there is any reason for using strip instead of split ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both are same.
in the inline comment - we have specified how we collect mac_id1_list.

['1.11 be:5d:07:76:c8:81', '1.20 30:35:ad:c5:e1:be', '1.23 ca:84:bc:fa:cb:7a']

so just modified into a simple logic.

Comment on lines +811 to +814
def first_available():
for s, p, e in [
(self.ssid_2g, self.password_2g, self.security_2g),
(self.ssid_5g, self.password_5g, self.security_5g),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add docstring

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docstring.

Comment on lines +821 to +822
def resolve(band_ssid, band_passwd, band_sec):
if band_ssid is not None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring here

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docstring.

Comment on lines +830 to +832
def station_count(specific):
if specific and specific > 0:
return specific

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring here

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docstring.


band_pref = 0

if key in ("2.4G", "2.4g"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check with lower

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the uppercase and lowercase versions are already being handled.

Comment on lines -757 to -791
for endp in connections_download_realtime.keys():
self.real_time_data.update(
{
endp: {
'BE': {
'time': [],
'bps rx a': [],
'bps rx b': [],
'rx drop % a': [],
'rx drop % b': []
},
'BK': {
'time': [],
'bps rx a': [],
'bps rx b': [],
'rx drop % a': [],
'rx drop % b': []
},
'VI': {
'time': [],
'bps rx a': [],
'bps rx b': [],
'rx drop % a': [],
'rx drop % b': []
},
'VO': {
'time': [],
'bps rx a': [],
'bps rx b': [],
'rx drop % a': [],
'rx drop % b': []
}
}
}
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactoring is good , but it should contain in different commit.

Comment on lines -794 to -797
# Added background_run to allow the test to continue running, bypassing the duration limit for nile requirement.
rates_data = defaultdict(list)
individual_device_data = {}
cx_list = list(self.cx_profile.created_cx.keys())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this code is missing ?

Comment on lines +1227 to +1228
def map_tos(tos_val):
_map = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring comment here

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docstring - clearly specifying why this method is required inorder to overcome the keyerror's being occured from the script run.

Comment on lines +1237 to +1239
def resolve_client_key(cx_name):
if self.sta_list:
for sta in self.sta_list:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring here

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docstring.

Comment on lines +1535 to +1544
elif _elapsed <= timedelta(hours=6):
time_break = 5 if (end_time - now) < timedelta(seconds=10) else 10
elif _elapsed <= timedelta(hours=12):
time_break = 5 if (end_time - now) < timedelta(seconds=30) else 30
elif _elapsed <= timedelta(hours=24):
time_break = 5 if (end_time - now) < timedelta(seconds=60) else 60
elif _elapsed <= timedelta(hours=48):
time_break = 5 if (end_time - now) < timedelta(seconds=60) else 90
else:
time_break = 5 if (end_time - now) < timedelta(seconds=120) else 120

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert to orginal

@manoj9088 manoj9088 Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the script previously had a time_break, we have a added a new feature "timebreak" and a variable in-order to obtain stats periodically after the specified "timebreak" period. inorder to differentiate the changes, we have refactored the code.

Comment thread py-scripts/lf_interop_qos.py Outdated
Comment on lines +1518 to +1531
df1.to_csv('{}/overall_throughput_{}.csv'.format(webgui_dir, curr_coordinate), index=False)
try:
with open(webgui_dir + "/../../Running_instances/{}_{}_running.json".format(self.ip, self.test_name), 'r') as file:
_run_data = json.load(file)
if _run_data.get("status") != "Running":
self.test_stopped_by_user = True
logger.warning('Test is stopped by the user')
if self.do_bandsteering:
df = pd.DataFrame(self.band_steering_df)
# *******
return df
break
except Exception:
pass

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try & except ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added this try-catch in-order have a check whether running_json is present in running instances. But will add logger.error in-order to trace the error if any.

Comment on lines +1868 to +1870
def set_report_data_virtual(self, data):
rate_down = str(str(int(self.cx_profile.side_b_min_bps) / 1000000) + ' ' + 'Mbps')
rate_up = str(str(int(self.cx_profile.side_a_min_bps) / 1000000) + ' ' + 'Mbps')

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docstring explaining the method usage.

Comment thread py-scripts/lf_interop_qos.py Outdated
Comment on lines +1950 to +1952
elif self.direction == "Download":
load = rate_down

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seperate commit

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will refactor back to the previous code logic.

Comment on lines +1960 to +1970
if len(overall_list) >= 8:
overall_throughput[0].append(round(sum(overall_list[0] + overall_list[4]), 2))
overall_throughput[1].append(round(sum(overall_list[1] + overall_list[5]), 2))
overall_throughput[2].append(round(sum(overall_list[2] + overall_list[6]), 2))
overall_throughput[3].append(round(sum(overall_list[3] + overall_list[7]), 2))
else:
# Fallback: only one direction present in graph_df
overall_throughput[0].append(round(sum(overall_list[0]), 2) if overall_list else 0)
overall_throughput[1].append(round(sum(overall_list[1]), 2) if len(overall_list) > 1 else 0)
overall_throughput[2].append(round(sum(overall_list[2]), 2) if len(overall_list) > 2 else 0)
overall_throughput[3].append(round(sum(overall_list[3]), 2) if len(overall_list) > 3 else 0)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no zero append

Comment on lines +1984 to +1989
for key, val in interfaces_dict.items():
if sta in key:
ssid_list.append(val.get('ssid', '-'))
ssid_found = True
break
if not ssid_found:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactoring ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while monitoring the port-manager data, sometimes the clients get disconnected randomly. Inorder to validate whether a client is having ssid connected, we are having a boolean logic for validation purpose.

Comment on lines +3257 to +3259
def generate_individual_graph_virtual(self, res, report):
rate_down = str(str(int(self.cx_profile.side_b_min_bps) / 1000000) + ' ' + 'Mbps')
rate_up = str(str(int(self.cx_profile.side_a_min_bps) / 1000000) + ' ' + 'Mbps')

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring

Comment on lines +3277 to +3307
def _safe(lst, fill='-'):
lst = list(lst) if lst else []
return (lst + [fill] * n)[:n]

mac_list_n = _safe(self.macid_list)
channel_list_n = _safe(self.channels_list)
ssid_list_n = _safe(self.ssid_list)
rssi_list_n = _safe(self.rssi_list)
mode_list_n = _safe(self.mode_list)
bssid_list_n = _safe(self.bssid_list)

# Bi-direction running totals — flat lists: [dl_total, ul_total]
# Matches throughput_qos.py list[0..3] exactly
list_vi = []
list_vo = []
list_bk = []
list_be = []
load = ""
data_set = {}

# Per-TOS per-station values for Upload / Download table column
tos_dl = {}
tos_ul = {}
drop_dl = {'BK': [], 'BE': [], 'VI': [], 'VO': []}
drop_ul = {'BK': [], 'BE': [], 'VI': [], 'VO': []}

# Virtual Scenario Only: data is band-keyed → res[case]['test_results']
# Both scenario: data is flat → res['test_results']
# _get_case_res() returns the inner dict that holds 'test_results'
# for whichever structure is present, making the loop below work
# identically for both.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove if not requred

Comment on lines +4342 to +4345
def parse_timebreak(self, tb_str):
if not tb_str:
return None
tb_str = tb_str.strip().lower()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docstring

Comment on lines +4951 to +5009
if getattr(args, 'create_sta', False):
if band in ("2.4G", "2.4g"):
count = args.num_stations_2g or any_sta_count()
args.mode = 13
if count > 0:
station_list.extend(LFUtils.portNameSeries(
prefix_="sta", start_id_=0,
end_id_=count - 1,
padding_number_=10000, radio=args.radio_2g))
elif band in ("5G", "5g"):
count = args.num_stations_5g or any_sta_count()
args.mode = 14
if count > 0:
station_list.extend(LFUtils.portNameSeries(
prefix_="sta", start_id_=0,
end_id_=count - 1,
padding_number_=10000, radio=args.radio_5g))
elif band in ("6G", "6g"):
count = args.num_stations_6g or any_sta_count()
args.mode = 15
if count > 0:
station_list.extend(LFUtils.portNameSeries(
prefix_="sta", start_id_=0,
end_id_=count - 1,
padding_number_=10000, radio=args.radio_6g))
elif band in ("dualband", "DUALBAND"):
args.mode = 0
if int(args.num_stations_2g) > 0:
station_list.extend(LFUtils.portNameSeries(
prefix_="sta", start_id_=0,
end_id_=int(args.num_stations_2g) - 1,
padding_number_=10000, radio=args.radio_2g))
if int(args.num_stations_5g) > 0:
station_list.extend(LFUtils.portNameSeries(
prefix_="sta",
start_id_=int(args.num_stations_2g),
end_id_=int(args.num_stations_2g) + int(args.num_stations_5g) - 1,
padding_number_=10000, radio=args.radio_5g))
elif band in ("triband", "TRIBAND"):
args.mode = 0
if int(args.num_stations_2g) > 0:
station_list.extend(LFUtils.portNameSeries(
prefix_="sta", start_id_=0,
end_id_=int(args.num_stations_2g) - 1,
padding_number_=10000, radio=args.radio_2g))
if int(args.num_stations_5g) > 0:
station_list.extend(LFUtils.portNameSeries(
prefix_="sta",
start_id_=int(args.num_stations_2g),
end_id_=int(args.num_stations_2g) + int(args.num_stations_5g) - 1,
padding_number_=10000, radio=args.radio_5g))
if int(args.num_stations_6g) > 0:
station_list.extend(LFUtils.portNameSeries(
prefix_="sta",
start_id_=int(args.num_stations_2g) + int(args.num_stations_5g),
end_id_=int(args.num_stations_2g) + int(args.num_stations_5g) + int(args.num_stations_6g) - 1,
padding_number_=10000, radio=args.radio_6g))
else:
print(f"Band '{band}' not recognised : skipping station list generation.")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mve to seperate function

- Integrated Virtual Clients support into the script.
- Script now supports Real, Virtual, Real + Virtual Scenarios.
- Added Custom Timebreak functionality to help monitoring longer test runs.
- Added support for generation of per client csv's.

VERIFIED CLI:

python3 lf_interop_qos.py --ap_name NETGEAR --mgr 192.168.207.78 --mgr_port 8080 --num_stations_2g 1 --num_stations_5g 2 --radio_2g wiphy0 --ssid_2g NETGEAR_2G_wpa2 --passwd_2g Password@123 --security_2g wpa2 --radio_5g wiphy1 --ssid_5g NETGEAR_5G_wpa2 --passwd_5g Password@123 --security_5g wpa2 --bands dualband --upstream eth1 --test_duration 1m --timebreak 5s --download 10000000 --upload 2000000 --traffic_type lf_udp --tos "BK,BE,VI,VO" --create_sta --client_type Virtual

python3 lf_interop_qos.py --ap_name NETGEAR --mgr 192.168.207.78 --mgr_port 8080 --num_stations_2g 4 --num_stations_5g 4 --radio_2g wiphy0 --ssid_2g NETGEAR_2G_wpa2 --passwd_2g Password@123 --security_2g wpa2 --radio_5g wiphy1 --ssid_5g NETGEAR_5G_wpa2 --passwd_5g Password@123 --security_5g wpa2 --bands dualband --upstream eth1 --test_duration 1m --timebreak 5s --download 1000000 --upload 2000000 --traffic_type lf_tcp --tos "BK,BE,VI,VO" --create_sta --client_type Both

Signed-off-by: Manoj9088 <[email protected]>
- Script now supports Existing_Stations only and Real + Existing Stations Scenario's
  along with previous scenarios.
- Added Support and Validation while using Existing Virtual Stations for Testing,
  by specifying the flags --use_existing_station_list and --existing_station_list [list]
- Ensured No precleanup and deletion if existing station list is provided.
- Ensured there is no station build if existing station list is provided.

VERIFIED CLI:

python3 lf_interop_qos.py --client_type Virtual --ap_name NETGEAR --mgr 192.168.207.78 --mgr_port 8080 --bands 5g  --upstream eth1 --test_duration 1m  --download 2000000 --traffic_type lf_tcp --tos "VO,BK,BE,VI" --use_existing_station_list --existing_station_list 1.1.sta9090,1.1.sta8888

python3 lf_interop_qos.py --client_type Both --ap_name NETGEAR --mgr 192.168.207.78 --mgr_port 8080 --bands 5g  --upstream eth1 --test_duration 1m  --download 2000000 --traffic_type lf_tcp --tos "VO,BK,BE,VI" --use_existing_station_list --existing_station_list 1.1.sta9090,1.1.sta8888

Signed-off-by: Manoj9088 <[email protected]>
…ata collection.

- Used "Batching" strategy inorder to resolve the issue when test is triggered on 100+ clients.
- Fixed reporting issue's w.r.t to new changes made.

Signed-off-by: Manoj9088 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants