Files
wlan-lanforge-scripts/lf_tx_power.py
Chuck SmileyRekiere 669ba08ef2 lf_tx_power.py : expect (FileAdapter) logging level to debug
wifi_ctl_9800_3504.py : expect (FileAdapter) logging level to debug

Signed-off-by: Chuck SmileyRekiere <chuck.smileyrekiere@candelatech.com>
2022-07-12 15:46:31 +05:30

3096 lines
168 KiB
Python
Executable File

#!/usr/bin/python3
'''
NAME: lf_tx_power
CLASSIFICATION: lanforge test
PURPOSE:
Perform tx power testing
SETUP:
You might need to install perl
Fedora : dnf install perl-Net-Telnet
Ubunto : sudo apt install libnet-telnet-perl
NOTE: To convert the per spatial stream dBm to combined dBm
convert dBm to watts (power) for each spatial stream, add the power values then convert back to dBm
https://www.rapidtables.com/convert/power/dBm_to_Watt.html
https://www.rapidtables.com/convert/power/Watt_to_dBm.html
Discussion
https://www.thepacketologist.com/2021/10/power-conversion-in-python/
EXAMPLE:
TODO : add sample command
RESULTS: csv xlsx
COPYRIGHT:
Copyright 2021 Candela Technologies Inc
License: Free to distribute and modify. LANforge systems must be licensed.
INCLUDE_IN_README
'''
import math
import xlsxwriter
import subprocess
import argparse
from time import sleep
import time
import logging
import re
import sys
import datetime
import importlib
import os
import traceback
sys.path.append(os.path.join(os.path.abspath(__file__ + "../../")))
# TODO change the name from logg to logger
# to match consistency with other files.
logger = logging.getLogger(__name__)
lf_logger_config = importlib.import_module("py-scripts.lf_logger_config")
lf_report = importlib.import_module("py-scripts.lf_report")
lf_kpi_csv = importlib.import_module("py-scripts.lf_kpi_csv")
EPILOG = '''\
#############################################################################################
# RSSI adjust
be root
sudo -s
manually disable it run-time by echo-ing a zero to the debugfs file, like:
echo 0 > /debug/ieee80211/wiphy0/ath10k/ofdm_peak_power_rssi
manually enable
echo 1 > /debug/ieee80211/wiphy0/ath10k/ofdm_peak_power_rssi
/* QCA seems to report a max-power average over the bandwidth, where mtk and intel radios
* report a ofdm peak power. The ofdm peak power corresponds more closely to tx-power minus
* pathloss, so I think that is preferred output. After some extensive measurements in
* a fully cabled environment, it looks like these adjustments are appropriate to make
* QCA be similar to MTK7915 and ax210:
* 2.4Ghz:
* 1x1 +8 (+13 to match txpower - pathloss. Less confident on anything above 1x1 for this column)
* 2x2 +4 +11
* 3x3 +3 +10
* 4x4 +3 +10
* 5Ghz
* 1x1 +12 +18
* 2x2 +12 +18
* 3x3 +10 +18
* 4x4 +10 +18
*/
/* OFDM RSSI adjustments, for nss 1-4 */
const int adjust_24[4] = {8, 4, 3, 3};
const int adjust_5[4] = {12, 12, 10, 10};
const int adjust_zero[4] = {0, 0, 0, 0};
note the second collumn, that is what we calculate from actual received peak OFDM power,
but I was not sure we'd want to go that far, so intead I tried to make it match ax210 and mtk7915
##############################################################################################
# Support History
##############################################################################################
2/25/2022 - adding 6E support
formula:
5GHz channel = (freq_mhz - 5180) / 5 + 36
6GHz channel = (freq_mhz - 5955) / 5 + 1
6E has the concept of slot
WLC1#ap name APCCC9C.3EF4.DDE0 dot11 6ghz ?
cleanair Enable 802.11 6Ghz cleanair management
slot Set slot number
WLC1#ap name APCCC9C.3EF4.DDE0 dot11 6ghz slot ?
<2-3> Enter Slot Id
WLC1#ap name APCCC9C.3EF4.DDE0 dot11 6ghz slot 3 ?
antenna Configures the 802.11 6Ghz antenna
channel Configure advanced 802.11 6Ghz channel assignment parameters for Cisco AP
dot11ax Configure 802.11ax-6GHz parameters
radio Configures the 802.11 6Ghz radio
rrm Radio resource management
shutdown Disable 802.11 Ghz radio on Cisco AP
txpower Configures the 802.11 6Ghz Tx Power Level
# 6G example : use existing station
./lf_ts_power.py
[controller configuration ]
--scheme ssh
--dest localhost
--port 8887
--user admin
--passwd Cisco123
--prompt WLC1
--series 9800
--band 6g
--module cc_module_9800_3504
--timeout 3
[AP Configuration]
--ap AP687D.B45C.25EC
--ap_band_slot_6g 3
[wlan configuration]
--wlan 6G-wpa3-AP3
--wlan_id 15
--wlan_ssid 6G-wpa3-AP3
--tag_policy RM204-TB1-AP4
--policy_profile default-policy-profile
[tx power configuration]
--pathloss 59
--antenna_gain 6
[traffic generation configuration (LANforge)]
--lfmgr 192.168.100.139
--upstream_port eth2
--lfresource 1
--radio wiphy0
--station wlan0
--ssid 6G-wpa3-AP3
--ssidpw hello123
--security wpa3
--bssid DEFAULT or aa:bb:cc:00:11:22
--no_cleanup_station
[test configuration]
--channel 1
--bandwidth 160
--vht160
--nss 2
--txpower 3
--duration 25
--outfile tx_power_AP4_AX210_2x2_6E
--no_cleanup
--test_rig Cisco-WLC1-AP4
# Command on one line
./lf_tx_power.py --scheme ssh --dest localhost --port 8887 --user admin --passwd Cisco123 --prompt 'WLC1' --series 9800 --band 6g --module cc_module_9800_3504 --timeout 3 --ap AP687D.B45C.25EC --ap_band_slot_6g 3 --wlan '6G-wpa3-AP3' --wlan_id 15 --wlan_ssid '6G-wpa3-AP3' --tag_policy 'RM204-TB1-AP4' --policy_profile 'default-policy-profile' --pathloss 69 --antenna_gain 6 --lfmgr '192.168.100.139' --upstream_port eth2 --lfresource 1 --radio wiphy0 --station 'wlan0' --ssid '6G-wpa3-AP3' --ssidpw 'hello123' --security wpa3 --no_cleanup_station --channel 1 --bandwidth 160 --vht160 --nss 2 --txpower 3 --duration 25 --outfile 'tx_power_AP4_AX210_2x2_6E' --no_cleanup 2>&1 |tee tx_output_AP4_AX210_2x2_6E.txt
# Verified 3/1/2022
./lf_tx_power.py -d localhost -u admin -p Cisco123 --port 8887 --scheme ssh --ap AP687D.B45C.25EC --bandwidth "160" --vht160 --channel "1" --nss 2 --txpower "2" --pathloss 59 --antenna_gain 6 --band 6g --upstream_port eth2 --series 9800 --radio wiphy0 --ap_band_slot_6g 3 --ssid 6G-wpa3-AP3 --prompt "WLC1" --station 'wlan0' --lfmgr '192.168.100.139' --ssidpw hello123 --security wpa3 --wlan 6G-wpa3-AP3 --wlanID 15 --wlanSSID 6G-wpa3-AP3 --lfresource 1 --tag_policy "RM204-TB1-AP4" --policy_profile "default-policy-profile" --testbed_id 'Cisco-WLC1-AP4' --module 'cc_module_9800_3504' --no_cleanup --outfile 'tx_power_AP4_AX210_2x2_6E' --duration 25 2>&1 |tee tx_output_AP4_AX210_2x2_6E.txt
##############################################################################################
##############################################################################################
make sure pexpect is installed:
$ sudo yum install python3-pexpect
$ sudo yum install python3-xlsxwriter
You might need to install pexpect-serial using pip:
$ pip3 install pexpect-serial
$ pip3 install XlsxWriter
You might need to install perl
Fedora : dnf install perl-Net-Telnet
Ubunto : sudo apt install libnet-telnet-perl
This script will automatically create and start a layer-3 UDP connection between the
configured upstream port and station.
The user also has the option of setting up the station oustide of this script, however.
# Examples:
# See cisco_power_results.txt when complete.
# See cisco_power_results.xlsx when complete.
NOTE: Telnet port 23 unless specified , ssh port 22 unless specified, scheme defaults to ssh
# TODO update , OLD EXAMPLE - Connecting to AP
##############################################################################################
# read AP for powercfg values using : show controllers dot11Radio 1 powercfg | g T1'
##############################################################################################
./lf_tx_power.py -d 172.19.27.55 -u admin -p Wnbulab@123 --port 2013 --scheme telnet \
--ap 9120_Candela --bandwidth "20" --channel "149" --nss 4 --txpower "1" \
--pathloss 56 --band a --upstream_port eth2 --series 9800 --radio wiphy5 --slot 1 --ssid open-wlan \
--prompt "katar_candela" --create_station --station sta0001 --ssidpw [BLANK] --security open \
--antenna_gain "6" --wlanID 1 --wlan open-wlan --wlanSSID open-wlan\
--ap_info "ap_scheme==telnet ap_prompt==9120_Candela ap_ip==172.19.27.55 ap_port==2008 ap_user==admin ap_pw==Wnbulab@123"
# TODO update OLD EXAMPLE replaced by BATCH mode
##############################################################################################
# Long duration test -- need to create the ---wlanID 1 --wlan open-wlan --wlanSSID open-wlan
##############################################################################################
./lf_tx_power.py -d 172.19.36.168 -u admin -p Wnbulab@123 --port 23 --scheme telnet --ap "APA453.0E7B.CF60" \
--bandwidth "20 40 80" --channel "36 40 44 48 52 56 60 64 100 104 108 112 116 120 124 128 132 136 140 144 149 153 157 161 165" \
--nss 4 --txpower "1 2 3 4 5 6 7 8" --pathloss 54 --antenna_gain 6 --band a --upstream_port eth2 --series 9800 \
--wlanID 1 --wlan open-wlan --wlanSSID open-wlan --create_station --station sta0001 --radio wiphy1 --ssid open-wlan --ssidpw [BLANK] --security open \
--outfile cisco_power_results_60_chan_ALL --cleanup --slot 1
##############################################################################################
# Per-channel path-loss example station present
##############################################################################################
./lf_tx_power.py -d 192.168.100.112 -u admin -p Cisco123 -s ssh --port 22 -a VC --lfmgr 192.168.100.178 \
--station sta00000 --bandwidth "20 40 80 160" --channel "36:64 149:60" --antenna_gain 5 --nss 4 --txpower "1 2 3 4 5 6 7 8" --pathloss 64 \
--band a --upstream_port eth2 --lfresource2 2
##############################################################################################
# To create a station run test against station create open-wlan
##############################################################################################
./lf_tx_power.py -d <router IP> -u admin -p Cisco123 -port 23 --scheme telnet --ap AP6C71.0DE6.45D0 \
--station sta2222 --bandwidth "20" --channel "36" --nss 4 --txpower "1 2 3 4 5 6 7 8" --pathloss 54 --antenna_gain 6 --band a \
--upstream_port eth2 --series 9800 --wlanID 1 --wlan open-wlan --wlanSSID open-wlan --create_station --station sta2222 --radio wiphy1 --ssid open-wlan \
--ssidpw [BLANK] --security open
##############################################################################################
# station already present
##############################################################################################
./lf_tx_power.py -d <router IP> -u admin -p Cisco123 -port 23 --scheme telnet --ap AP6C71.0DE6.45D0 \
--station sta0000 --bandwidth "20" --channel "36" --nss 4 --txpower "1 2 3 4 5 6 7 8" --pathloss 64 --antenna_gain 5 --band a \
--upstream_port eth2 --series 9800 --wlanID 1 --wlan open-wlan --wlanSSID open-wlan
##############################################################################################
# to create a station
##############################################################################################
./lf_associate_ap.pl --mgr 192.168.100.178 --radio wiphy2 --ssid 6G-wpa3-AP3 --passphrase hello123 ssecurity wpa3 --bssid DEFAULT --upstream 1.1.eth2 --first_ip DHCP --first_sta sta0000 --duration 25 --cxtype udp --bps_min 1000000000--ieee80211w 2 --wifi_mode abgnAX --action add
Changing regulatory domain should happen outside of this script.
##############################################################################################
# OUTPUT in XLSX file - Spread sheet how values determined
##############################################################################################
Tx Power : Input from command line (1-8)
Allowed Per Path : Read from the Controller
Cabling Pathloss : Input from command line, best if verified prior to testing
Antenna Gain : Input from command line, if AP cannot detect antenna connection
Beacon RSSI (beacon_sig) : From Lanforge probe, command ./lf_portmod.pl with cli parameter probe_port 1 (~line 1183, ~line 1209)
Combined RSSI User (sig) : From Lanforge probe, command ./lf_portmod.pl with cli parameter probe_port 1 (~line 1183, ~line 1193)
RSSI 1, RSSI 2, RSSI 3, RSSI 4 : (~line 1160)
ants[q] (antX) read from Lanforge probe, command ./lf_portmod.pl with cli parameter probe_port 1
Ant 1, Ant 2, Ant 3, Ant 4 : ()
Starting Value for antX read from lanforge probe, using command ./lf_portmod.pl with cli parameter porbe_port 1
_noise_bear (_noise_i) = from Lanforge returning NOISE from command (!line 1070) lf_portmod.pl reading --show_port
"AP, IP, Mode, NSS, Bandwith, Channel, Signal, NOISE, Status, RX-Rate
rssi_adj (only used if --adjust_nf and _noise_bare != None) (~line 1263) _noise_i(_noise_bear) - nf_at_calibration (fixed value of -105)
Thus calc_antX = int(antX read from Lanforge) + pi (path loss from command line) + rssi_adj + ag (antenna gain from command line)
calc_antX is put on the spread sheet under Ant X
Offset 1, Offset 2, Offset 3, Offset 4: which in the code is diff_aX = calc_antX - allowed_per_path (adjusted based on number of streams)
Pass/Fail : (~line 1286) If the diff / offset is greater than the pfrange determins the pass or fail
Show the tx_power for a specific station:
iw dev <station> station dump
iw dev sta0000 station dump
#####################################################################
5g dual band restrictions
#####################################################################
when both 5g (slot 1) is enabled and dual-band 5g (slot 2) is enabled .
5g slot 1 will used the 5g channels to 64, the 5g dual-band will use channels 100 -> 165.
When 5g (slot 1) and dual-band 6g (slot 2) is enabled then 5g (slot 1) has all ba
Path lost
24g 5g : 50dB inline attenuation + cables and splitters, so I think last time we estimated the path loss should be about 56dB
that is to the 5ghz radio
6g : wiphy2 has only 30dB inline attenuation to the 6ghz radio on the 9136
'''
if sys.version_info[0] != 3:
print("This script requires Python 3")
exit()
NL = "\n"
CR = "\r\n"
Q = '"'
A = "'"
FORMAT = '%(asctime)s %(name)s %(levelname)s: %(message)s'
lfmgr = "127.0.0.1"
lfstation = "sta00000"
lfresource = "1"
lfresource2 = "1"
outfile = "cisco_power_results.txt"
full_outfile = "cisco_power_results_full.txt"
outfile_xlsx = "cisco_power_results.xlsx"
upstream_port = "eth1"
pf_dbm = 3
# kpi notes
# Maybe subtest pass/failed is interesting,
# and for KPI, could do min/max/avg of the beacon power offset
# from expected, and same for data power offset from expected?
# Channel notes
# setting channel' typically means setting the bandwidth range,
# and also setting the control channel.
# So, running on ctrl channel 36 at HT40 and ch 40 at HT40 uses same total bandwdith range,
# but in second case, the beacons and control frames should be on ch 40.
# At least that is normally how things are implemented.
# Allow one chain to have a lower signal, since customer's DUT has
# lower tx-power on one chain when doing high MCS at 4x4.
pf_ignore_offset = 0
# Threshold for allowing a pass
failed_low_threshold = 0
# This below is only used when --adjust_nf is used.
# Noise floor on ch 36 where we calibrated -54 path loss (based on hard-coded -95 noise-floor in driver)
nf_at_calibration = -105
# older ath10k driver hard-codes noise-floor to -95 when calculating RSSI
# RSSI = NF + reported_power
# Shift RSSI by difference in actual vs calibrated noise-floor since driver hard-codes
# the noise floor.
# rssi_adjust = (current_nf - nf_at_calibration)
def usage():
print("Incorrect inputs: ./lf_tx_power.py --help to show usage ")
#
# see https://stackoverflow.com/a/13306095/11014343
# TODO use common logger library
class FileAdapter(object):
def __init__(self, logger):
self.logger = logger
def write(self, data):
# NOTE: data can be a partial line, multiple lines
data = data.strip() # ignore leading/trailing whitespace
if data: # non-blank
self.logger.debug(data)
def flush(self):
pass # leave it to logging to flush properly
def close_workbook(workbook):
workbook.close()
sleep(0.5)
def main():
global lfmgr
global lfstation
global lfresource
global lfresource2
global outfile
global outfile_xlsx
global full_outfile
global upstream_port
global pf_dbm
global pf_ignore_offset
global failed_low_threshold
parser = argparse.ArgumentParser(description="Cisco TX Power report Script", epilog=EPILOG,
formatter_class=argparse.RawTextHelpFormatter)
# controller configuration
parser.add_argument("-s", "--scheme", type=str, choices=["serial", "ssh", "telnet"], help="[controller configuration] Connect via serial, ssh or telnet --scheme ssh", required=True)
parser.add_argument("-d", "--controller_ip", "--dest", dest="dest", type=str, help="[controller configuration] address of the controller --dest localhost", required=True)
parser.add_argument("-o", "--port", type=str, help="[controller configuration] controller port on the controller --port 8887", required=True)
parser.add_argument("-u", "--user", type=str, help="[controller configuration] controller login/username --user admin", required=True)
parser.add_argument("-p", "--passwd", type=str, help="[controller configuration] credential password --passwd Cisco123", required=True)
parser.add_argument('-ccp', '--prompt', type=str, help="[controller configuration] controller prompt --prompt WLC1", required=True)
parser.add_argument("--series", type=str, help="[controller configuration] controller series --series 9800", required=True)
parser.add_argument("--band", type=str, help="band testing --band 6g", choices=["5g", "24g", "6g", "dual_band_5g", "dual_band_6g"])
parser.add_argument("--module", type=str, help="[controller configuration] series module (cc_module_9800_3504.py) --module cc_module_9800_3504 ", required=True)
parser.add_argument("--timeout", type=str, help="[controller configuration] controller command timeout --timeout 3 ", default=3)
# AP configuration
parser.add_argument("-a", "--ap", type=str, help="[AP configuration] select AP ", required=True)
parser.add_argument("--ap_dual_band_slot_6g", type=str, help="[AP configuration] --ap_dual_band_slot_6g 2 , 9800 AP dual-band slot , for 6g dual-band use show ap dot11 dual-band summary", default='2')
parser.add_argument("--ap_dual_band_slot_5g", type=str, help="[AP configuration] --ap_dual_band_slot_5g 2 , 9800 AP dual-band slot , for 5g dual-band use show ap dot11 dual-band summary", default='2')
parser.add_argument("--ap_band_slot_6g", type=str, help="[AP configuration] --ap_band_slot_6g 2 , 9800 AP band slot , use show ap dot11 6ghz summary", default='2')
parser.add_argument("--ap_band_slot_5g", type=str, help="[AP configuration] --ap_band_slot_5g 1 , 9800 AP band slot , use show ap dot11 5ghz summary", default='1')
parser.add_argument("--ap_band_slot_24g", type=str, help="[AP configuration] --ap_band_slot_24g 0 , 9800 AP band slot , use show ap dot11 24ghz summary", default='0')
# wlan configuration
parser.add_argument("--create_wlan", help="[wlan configuration] --create_wlan", action='store_true')
parser.add_argument("--wlan", type=str, help="[wlan configuration] controller wlan name --wlan 6G-wpa3-AP3 ", required=True)
parser.add_argument("--wlan_id", "--wlanID", dest="wlanID", type=str, help="[wlan configuration] controller wlan id --wlan_id 15", required=True)
parser.add_argument("--wlan_ssid", "--wlanSSID", dest="wlanSSID", type=str, help="[wlan configuration] controller wlan ssid --wlan_ssid 6G-wpa3-AP3, wlan ssid must match station ssid", required=True)
parser.add_argument("--tag_policy", type=str, help="[wlan configuration] controller tag policy --tag_policy RM204-TB1-AP4")
parser.add_argument("--policy_profile", type=str, help="[wlan configuration] --policy_profile default-policy-profile")
# ap interface configuration
parser.add_argument('-api', '--ap_info', action='append', nargs=1, type=str, help="[ap configuration] --ap_info ap_scheme==<telnet,ssh or serial> ap_prompt==<ap_prompt> ap_ip==<ap ip> ap_port==<ap port number> ap_user==<ap user> ap_pw==<ap password>")
# if args.ap_admin_down_up_6g: - work around for 6G
parser.add_argument("--ap_admin_down_up_6g", help="[ap admin down up] --6g_ap_admin_down_up will admin down and up the AP", action='store_true')
# tx power pathloss configuration
parser.add_argument("--pathloss", type=str, help="[tx power configuration] Calculated pathloss between LANforge Station and AP --pathloss 59", required=True)
parser.add_argument("--antenna_gain", type=str, help="[tx power configuration] Antenna gain, take into account the gain due to the antenna --antenna_gain 6", required=True)
parser.add_argument("--pf_ignore_offset", type=str, help="[tx power configuration] Allow a chain to have lower tx-power and still pass. default 0 so disabled", default="0")
parser.add_argument("--adjust_nf", action='store_true', help="[tx power configuration] Adjust RSSI based on noise-floor. ath10k without the use-real-noise-floor fix needs this option")
parser.add_argument('--beacon_dbm_diff', type=str, help="[tx power configuration] --beacon_dbm_diff <value> is the delta that is allowed between the controller tx and the beacon measured", default="7")
# pass / fail criteria
parser.add_argument("--pf_dbm", type=str, help="[tx power pass / fail criteria] Pass/Fail threshold per Spetial Stream. Default is 3", default="3")
# traffic generation configuration (LANforge)
parser.add_argument("--lfmgr", type=str, help="[traffic generation configuration (LANforge)] LANforge Manager IP address --lfmgr 192.168.100.178", required=True)
parser.add_argument("--upstream_port", type=str, help="[traffic generation configuration (LANforge)] LANforge upsteram-port to use (eth1, etc) --upstream_port eth2", required=True)
parser.add_argument("--lfresource", type=str, help="[traffic generation configuration (LANforge)] LANforge resource ID for the station --lfresource 1")
parser.add_argument("--lfresource2", type=str, help="[traffic generation configuration (LANforge)] LANforge resource ID for the upstream port system ")
# LANforge station configuration
parser.add_argument("--radio", type=str, help="[LANforge station configuration] LANforge radio station created on --radio wiphy0")
parser.add_argument("--create_station", help="[LANforge station configuration] create LANforge station at the beginning of the test", action='store_true')
parser.add_argument("--station", type=str, help="[LANforge station configuration] Use already created LANforge station, use --no_cleanup also --station wlan0", required=True)
parser.add_argument("--ssid", type=str, help="[station configuration] station ssid, ssid of station must match the wlan created --ssid 6G-wpa3-AP3", required=True)
parser.add_argument("--ssidpw", "--security_key", dest='ssidpw', type=str, help="[station configuration] station security key --ssidpw hello123", required=True)
parser.add_argument("--bssid", "--ap_bssid", dest='bssid', type=str, help="[station configuration] station AP bssid ", required=True)
parser.add_argument("--security", type=str, help="[station configuration] security type open wpa wpa2 wpa3", required=True)
parser.add_argument("--wifi_mode", type=str, help="[station configuration] --wifi_mode auto types auto|a|abg|abgn|abgnAC|abgnAX|an|anAC|anAX|b|bg|bgn|bgnAC|bgnAX|g ", default='auto')
parser.add_argument("--vht160", action='store_true', help="[station configuration] --vht160 , Enable VHT160 in lanforge ")
parser.add_argument("--ieee80211w", type=str, help="[station configuration] --ieee80211w 0 (Disabled) 1 (Optional) 2 (Required) (Required needs to be set to Required for 6g and wpa3 default Optional ", default='1')
parser.add_argument("--wave2", help="[station configuration] --wave2 , wave2 (9984) has restrictions : 160Mhz is 2x2", action='store_true')
parser.add_argument("--no_cleanup_station", action='store_true', help="[station configuration] --no_cleanup_station , do not clean up station after test completes ")
# test configuration
parser.add_argument("-c", "--channel", type=str, help="[test configuration] --channel '1 33' List of channels to test, with optional path-loss, 36:64 149:60. NA means no change")
parser.add_argument("-b", "--bandwidth", type=str, help="[test configuration] --bandwidth '20 40 80 160' List of bandwidths to test. NA means no change")
parser.add_argument("-n", "--nss", type=str, help="[test configuration] --nss '2' List of spatial streams to test. NA means no change")
parser.add_argument("--nss_4x4_override", help="[test configuration] --nss_4x4_override controller nss is 4 client nss is 2, set expected power to 1/4", action='store_true')
parser.add_argument("--nss_4x4_ap_adjust", help="[test configuration] --nss_4x4_ap_adjust read ap to know number of spatial stream to take into account", action='store_true')
parser.add_argument("--set_nss", help="[test configuration] --set_nss configure controller to spatial streams to test", action='store_true')
parser.add_argument("-T", "--txpower", type=str, help="[test configuration] List of txpowers to test. NA means no change")
parser.add_argument('-D', '--duration', type=str, help='[test configuration] --traffic <how long to run in seconds> example -D 30 (seconds) default: 30 ', default='30')
parser.add_argument('--wait_time', type=str, help='[test configuration] --wait_time <how long to wait for station to connect seconds> example --wait_time 180 (seconds) default: 180 ', default='180')
parser.add_argument("--outfile", help="[test configuration] Output file for csv data --outfile 'tx_power_AX210_2x2_6E")
parser.add_argument("-k", "--keep_state", "--no_cleanup", dest="keep_state", action="store_true", help="[test configuration] --no_cleanup, keep the state, no configuration change at the end of the test")
# TODO may want to remove enable_all_bands , all bands need to be enabled for 6E testing for 6E to know the domain
parser.add_argument("-enb", "--enable_all_bands", dest="enable_all_bands", action="store_true", help="[test configuration] --enable_all_bands, enable 6g, 5g, 24b bands at end of test")
parser.add_argument('--tx_power_adjust_6E', action="store_true", help="[test configuration] --power_adjust_6E stores true, 6E: 20 Mhz pw 1-6, 40 Mhz pw 1-7 ")
# parser.add_argument('--per_ss', action="store_true", help="[test configuration] --per_ss stores true, per spatial stream used in pass fail criteria")
# test configuration
parser.add_argument("--testbed_id", "--test_rig", dest='test_rig', type=str, help="[testbed configuration] --test_rig", default="")
parser.add_argument("--testbed_location", dest='testbed_location', type=str, help="[testbed configuration] --testbed_location <from show ap summary Location>", default="default location")
# kpi_csv arguments:
parser.add_argument("--test_tag", default="", help="[kpi configuration] test tag for kpi.csv, test specific information to differenciate the test")
parser.add_argument("--dut_hw_version", default="", help="[kpi configuration] dut hw version for kpi.csv, hardware version of the device under test")
parser.add_argument("--dut_sw_version", default="", help="[kpi configuration] dut sw version for kpi.csv, software version of the device under test")
parser.add_argument("--dut_model_num", default="", help="[kpi configuration] dut model for kpi.csv, model number / name of the device under test")
parser.add_argument("--dut_serial_num", default="", help="[kpi configuration] dut serial for kpi.csv, serial number / serial number of the device under test")
parser.add_argument("--test_priority", default="", help="[kpi configuration] dut model for kpi.csv, test-priority is arbitrary number")
parser.add_argument("--test_id", default="TX power", help="[kpi configuration] test-id for kpi.csv, script or test name")
parser.add_argument("--html_report", help="[html configuration] --html_report store True , will create html and pdf reports", action='store_true')
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="")
# TODO ADD KP configuration
# debug configuration
parser.add_argument("--wait_forever", action='store_true', help="[debug configuration] Wait forever for station to associate, may aid debugging if STA cannot associate properly")
parser.add_argument('--show_lf_portmod', action='store_true', help="[debug configuration] --show_lf_portmod, show the output of lf_portmod after traffic to verify RSSI values measured by lanforge")
parser.add_argument("--exit_on_fail", action='store_true', help="[debug configuration] --exit_on_fail, exit on test failure")
parser.add_argument("--exit_on_error", action='store_true', help="[debug configuration] --exit_on_error, exit on test error, test mechanics failed")
# logg information
parser.add_argument("--lf_logger_config_json", help="[log configuration] --lf_logger_config_json <json file> , json configuration of logger")
parser.add_argument("--log_level", help="[log configuration] --log_level debug info warning error critical")
# current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "{:.3f}".format(time.time() - (math.floor(time.time())))[1:]
# print(current_time)
# usage()
args = None
# Parcing the input parameters and assignment
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()
# TODO refactor to be logger for consistency
logg = logging.getLogger(__name__)
# logg.setLevel(logging.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
# put in test information in title name
# this only works for single test passed in.
if args.tx_power_adjust_6E and args.band == '6g':
txpowers = args.txpower.split()
if args.bandwidth == '20':
if '8' in txpowers:
txpowers.remove('8')
if '7' in txpowers:
txpowers.remove('7')
elif args.bandwidth == '40':
if '8' in txpowers:
txpowers.remove('8')
txpowers_str = '_'.join(txpowers)
results_dir_name = ("tx_power"
+ '_band_' + args.band
+ '_ch_' + args.channel.replace(' ', '_')
+ '_nss_' + args.nss.replace(' ', '_')
+ '_bw_' + args.bandwidth.replace(' ', '_')
+ '_txpw_' + txpowers_str)
else:
results_dir_name = ("tx_power"
+ '_band_' + args.band
+ '_ch_' + args.channel.replace(' ', '_')
+ '_nss_' + args.nss.replace(' ', '_')
+ '_bw_' + args.bandwidth.replace(' ', '_')
+ '_txpw_' + args.txpower.replace(' ', '_'))
if local_lf_report_dir != "":
report = lf_report.lf_report(
_path=local_lf_report_dir,
_results_dir_name=results_dir_name,
_output_html="{results_dir}.html".format(results_dir=results_dir_name),
_output_pdf="{results_dir}.pdf".format(results_dir=results_dir_name))
else:
report = lf_report.lf_report(
_results_dir_name=results_dir_name,
_output_html="{results_dir}.html".format(results_dir=results_dir_name),
_output_pdf="{results_dir}.pdf".format(results_dir=results_dir_name))
kpi_path = report.get_report_path()
# kpi_filename = "kpi.csv"
logg.info("kpi_path :{kpi_path}".format(kpi_path=kpi_path))
# create kpi_csv object and record the common data
# TX power is not a class so access kpi directly in function
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)
lfstation = args.station
upstream_port = args.upstream_port
lfmgr = args.lfmgr
# TODO
if (args.lfresource is not None):
lfresource = args.lfresource
if (args.lfresource2 is not None):
lfresource2 = args.lfresource2
outfile_path = report.get_report_path()
current_time = time.strftime("%m_%d_%Y_%H_%M_%S", time.localtime())
if (args.outfile):
test_name = ('Tx Power:' + args.outfile + 'AP: ' + args.ap + ', Band: ' + args.band + ', Channel: ' + args.channel
+ ', NSS: ' + args.nss
+ ', BW: ' + args.bandwidth
+ ', Tx Power: ' + args.txpower)
outfile_tmp = (outfile_path + '/' + current_time + '_' + args.outfile
+ '_AP_' + args.ap
+ '_band_' + args.band
+ '_ch_' + args.channel.replace(' ', '_')
+ '_nss_' + args.nss.replace(' ', '_')
+ '_bw_' + args.bandwidth.replace(' ', '_')
+ '_tx_pw_' + args.txpower.replace(' ', '_'))
else:
test_name = ('Tx Power:' + 'AP: ' + args.ap + ', Band: ' + args.band + ', Channel: ' + args.channel
+ ', NSS: ' + args.nss
+ ', BW: ' + args.bandwidth
+ ', Tx Power: ' + args.txpower)
outfile_tmp = (outfile_path + '/' + current_time + '_' + 'tx_power'
+ '_AP_' + args.ap
+ '_band_' + args.band
+ '_ch_' + args.channel.replace(' ', '_')
+ '_nss_' + args.nss.replace(' ', '_')
+ '_bw_' + args.bandwidth.replace(' ', '_')
+ '_tx_pw_' + args.txpower.replace(' ', '_'))
print("outfile_tmp {outfile_tmp}".format(outfile_tmp=outfile_tmp))
# note: there would always be an args.outfile due to the default
full_outfile = "{}_full.txt".format(outfile_tmp)
outfile_xlsx = "{}.xlsx".format(outfile_tmp)
outfile = "{}.txt".format(outfile_tmp)
print("output file: {}".format(outfile))
print("output file full: {}".format(full_outfile))
print("output file xlsx: {}".format(outfile_xlsx))
if (args.pf_dbm is not None):
pf_dbm = int(args.pf_dbm)
if (args.pf_ignore_offset is not None):
pf_ignore_offset = int(args.pf_ignore_offset)
if (args.wlanSSID != args.ssid):
print("####### ERROR ################################")
print("wlanSSID: {} must equal the station ssid: {}".format(args.wlanSSID, args.ssid))
print("####### ERROR ################################")
exit(1)
if (args.create_wlan):
if(args.tag_policy is None or args.policy_profile is None):
print("####### ERROR ######################################################")
print(" For create_wlan both tag_policy and policy_profile must be entered")
print("####### ERROR #######################################################")
exit(1)
ap_dict = []
if args.ap_info:
ap_info = args.ap_info
for _ap_info in ap_info:
print("ap_info {}".format(_ap_info))
ap_keys = ['ap_scheme', 'ap_prompt', 'ap_ip', 'ap_port', 'ap_user', 'ap_pw']
ap_dict = dict(map(lambda x: x.split('=='), str(_ap_info).replace('[', '').replace(']', '').replace("'", "").split()))
for key in ap_keys:
if key not in ap_dict:
print("missing ap config, for the {}, all these need to be set {} ".format(key, ap_keys))
exit(1)
print("ap_dict: {}".format(ap_dict))
# except Exception as e:
# logging.exception(e)
# usage()
# exit(2)
# dynamic import of the controller module
series = importlib.import_module(args.module)
# create the controller , cs is controller scheme
cs = series.create_controller_series_object(
scheme=args.scheme,
dest=args.dest,
user=args.user,
passwd=args.passwd,
prompt=args.prompt,
series=args.series,
ap=args.ap,
ap_band_slot_6g=args.ap_band_slot_6g,
port=args.port,
band=args.band,
timeout=args.timeout)
cs.wlan = args.wlan
cs.wlanID = args.wlanID
cs.wlanSSID = args.wlanSSID
# TODO change to use args.security_key
cs.security_key = args.ssidpw
if args.create_wlan:
cs.tag_policy = args.tag_policy
cs.policy_profile = args.policy_profile
if bool(ap_dict):
logg.info("ap_dict {}".format(ap_dict))
if args.outfile is not None:
logg.info("output file: {}".format(outfile))
logg.info("output file full: {}".format(full_outfile))
logg.info("output file xlsx: {}".format(outfile_xlsx))
if (args.bandwidth is None):
usage()
logg.info("ERROR: Must specify bandwidths")
exit(1)
if (args.channel is None):
usage()
logg.info("ERROR: Must specify channels")
exit(1)
if (args.nss is None):
usage()
logg.info("ERROR: Must specify NSS")
exit(1)
if (args.txpower is None):
usage()
logg.info("ERROR: Must specify txpower")
exit(1)
if (args.pathloss is None):
logg.info("ERROR: Pathloss must be specified.")
exit(1)
if (args.antenna_gain is None):
usage()
logg.info("ERROR: Antenna gain must be specified.")
exit(1)
# Full spread-sheet data
csv = open(full_outfile, "w")
csv.write("Regulatory Domain\tCabling Pathloss\tAntenna Gain\tCfg-Channel\tCfg-NSS\tCfg-AP-BW\tTx Power\tBeacon-Signal\tCombined-Signal\tRSSI 1\tRSSI 2\tRSSI 3\tRSSI 4\tAP-BSSID\tRpt-BW\tRpt-Channel\tRpt-Mode\tRpt-NSS\tRpt-Noise\tRpt-Rxrate\tCtrl-AP-MAC\tCtrl-Channel\tCtrl-Power\tCtrl-dBm\tCalc-dBm-Combined\tDiff-dBm-Combined\tAnt-1\tAnt-2\tAnt-3\tAnt-4\tOffset-1\tOffset-2\tOffset-3\tOffset-4\tPASS/FAIL(+-%sdB)\tTimeStamp\tWarnings-and-Errors" % (pf_dbm))
csv.write("\n")
csv.flush()
# Summary spread-sheet data
csvs = open(outfile, "w")
csvs.write("Regulatory Domain\tCabling Pathloss\tAntenna Gain\tAP Channel\tNSS\tAP BW\tTx Power\tAllowed Per-Path\tRSSI 1\tRSSI 2\tRSSI 3\tRSSI 4\tAnt-1\tAnt-2\tAnt-3\tAnt-4\tOffset-1\tOffset-2\tOffset-3\tOffset-4\tPASS/FAIL(+-%sdB)\tTimeStamp\tWarnings-and-Errors" % (pf_dbm))
csvs.write("\n")
csvs.flush()
# XLSX file
workbook = xlsxwriter.Workbook(outfile_xlsx)
worksheet = workbook.add_worksheet()
# bold = workbook.add_format({'bold': True, 'align': 'center'})
dblue_bold = workbook.add_format({'bold': True, 'align': 'center'})
dblue_bold.set_bg_color("#b8cbe4")
# dblue_bold.set_bg_color("#00FFFF")
dblue_bold.set_border(1)
dtan_bold = workbook.add_format({'bold': True, 'align': 'center'})
dtan_bold.set_bg_color("#dcd8c3")
dtan_bold.set_border(1)
dpeach_bold = workbook.add_format({'bold': True, 'align': 'center'})
dpeach_bold.set_bg_color("#ffd8bb")
dpeach_bold.set_border(1)
dpink_bold = workbook.add_format({'bold': True, 'align': 'center'})
dpink_bold.set_bg_color("#fcc8ca")
dpink_bold.set_border(1)
dyel_bold = workbook.add_format({'bold': True, 'align': 'center'})
dyel_bold.set_bg_color("#ffe699")
dyel_bold.set_border(1)
dgreen_bold = workbook.add_format({'bold': True, 'align': 'center'})
dgreen_bold.set_bg_color("#c6e0b4")
dgreen_bold.set_border(1)
dgreen_bold_left = workbook.add_format({'bold': True, 'align': 'left'})
dgreen_bold_left.set_bg_color("#c6e0b4")
dgreen_bold_left.set_border(1)
# center = workbook.add_format({'align': 'center'})
center_blue = workbook.add_format({'align': 'center'})
center_blue.set_bg_color("#dbe5f1")
center_blue.set_border(1)
center_tan = workbook.add_format({'align': 'center'})
center_tan.set_bg_color("#edede1")
center_tan.set_border(1)
center_peach = workbook.add_format({'align': 'center'})
center_peach.set_bg_color("#fce4d6")
center_peach.set_border(1)
center_yel = workbook.add_format({'align': 'center'})
center_yel.set_bg_color("#fdf2cc")
center_yel.set_border(1)
center_yel_red = workbook.add_format({'align': 'center', 'color': 'red'})
center_yel_red.set_bg_color("#fdf2cc")
center_yel_red.set_border(1)
center_pink = workbook.add_format({'align': 'center'})
center_pink.set_bg_color("ffd2d3")
center_pink.set_border(1)
red = workbook.add_format({'color': 'red', 'align': 'center'})
red.set_bg_color("#e0efda")
red.set_border(1)
red_left = workbook.add_format({'color': 'red', 'align': 'left'})
red_left.set_bg_color("#e0efda")
red_left.set_border(1)
green = workbook.add_format({'color': 'green', 'align': 'center'})
green.set_bg_color("#e0efda")
green.set_border(1)
green_left = workbook.add_format({'color': 'green', 'align': 'left'})
green_left.set_bg_color("#e0efda")
green_left.set_border(1)
orange_left = workbook.add_format({'color': 'orange', 'align': 'left'})
orange_left.set_bg_color("#e0efda")
orange_left.set_border(1)
# Set up some formats to use.
dark_green = workbook.add_format({'color': '#006400', 'bold': True})
black = workbook.add_format({'color': 'black', 'bold': True})
black_not_bold = workbook.add_format({'color': 'black', 'bold': False})
# #e68b15
title_format = workbook.add_format({
'bold': 1,
'border': 10,
'align': 'left',
'valign': 'vcenter',
'fg_color': "#FFD700"})
row = 0
col = 0
worksheet.set_row(0, 40)
worksheet.set_column(0, 0, 10)
# Create a format to use in the merged range.
# https://xlsxwriter.readthedocs.io/worksheet.html
# parameters merge_range(first_row, first_col, last_row, last_col, data[, cell_format])
# Can only write simple types to merged ranges so write a blank string
test_notes = ' Pass / Fail criteria based on Offset per spatial stream being greater then {pf_dbm} dBm'.format(pf_dbm=pf_dbm)
worksheet.merge_range(0, 0, 0, 38, ' ', title_format)
worksheet.write_rich_string(0, 0, dark_green, ' Candela Technologies : ', black, '{test_name} '.format(test_name=test_name), black_not_bold, '\n{test_notes}'.format(test_notes=test_notes), title_format)
worksheet.set_row(1, 75) # Set height
worksheet.set_column(0, 0, 10) # Set width
row = 1
col = 0
worksheet.write(row, col, 'Regulatory\nDomain', dblue_bold)
col += 1
worksheet.set_column(col, col, 16) # Set width
worksheet.write(row, col, 'Controller\nTest Rig:\n{test_rig}\nLocation\n{location}'.format(test_rig=args.test_rig, location=args.testbed_location), dblue_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Controller\nChannel', dblue_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, '{ap}\nClient Reported\nChannel'.format(ap=args.ap), dblue_bold)
col += 1
worksheet.write(row, col, 'Client\nNSS', dblue_bold)
col += 1
worksheet.set_column(col, col, 10) # Set width
worksheet.write(row, col, 'Controller\nBW', dblue_bold)
col += 1
worksheet.set_column(col, col, 10) # Set width
worksheet.write(row, col, 'Station\nReported\nBW', dblue_bold)
col += 1
worksheet.write(row, col, 'Tx\nPower\nSetting', dtan_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'Controller Reported\nTotal\nTx Power dBm\nFrom AP Summary', dtan_bold)
if (bool(ap_dict)):
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'AP Reported\nTotal \nTx Power dBm', dtan_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'Allowed dBm\nPer Spatial Stream\n cc_dbm', dtan_bold)
if (bool(ap_dict)):
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'AP reported\nAllowed dBm\nPer Spatial Steam\n ap_dbm', dtan_bold)
col += 1
worksheet.write(row, col, 'Cabling\nPathloss', dtan_bold)
col += 1
worksheet.write(row, col, 'Antenna\nGain', dtan_bold)
col += 1
worksheet.set_column(col, col, 10) # Set width
worksheet.write(row, col, 'Client\n Reported\n Noise', dpeach_bold)
col += 1
worksheet.set_column(col, col, 15) # Set width
worksheet.write(row, col, 'rssi_adj:\nnoise bare\n - noise floor', dpeach_bold)
col += 1
if (args.adjust_nf):
worksheet.write(row, col, 'Noise Floor\nAdjust\n(vs -105)', dpeach_bold)
col += 1
worksheet.set_column(col, col, 15) # Set width
worksheet.write(row, col, 'Client Reported\nRx Rate', dpeach_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'Client Reported\nBeacon Signal dBm\nRSSI', dpeach_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'Client Reported\nCombined Signal dBm\nRSSI\n(Signal ave)', dpeach_bold)
col += 1
worksheet.set_column(col, col, 15) # Set width
worksheet.write(row, col, 'Client Reported\nAntenna\nSignal\ndBm\n SS 1', dpeach_bold)
col += 1
worksheet.set_column(col, col, 15) # Set width
worksheet.write(row, col, 'Client Reported\nAntenna\nSignal\ndBm\n SS 2', dpeach_bold)
col += 1
worksheet.set_column(col, col, 15) # Set width
worksheet.write(row, col, 'Client Reported\nAntenna\nSignal\ndBm\n SS 3', dpeach_bold)
col += 1
worksheet.set_column(col, col, 15) # Set width
worksheet.write(row, col, 'Client Reported\nAntenna\nSignal\ndBm\n SS 4', dpeach_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Calculated Antenna 1 =\n Antenna Sig dBm\n + pathloss\n + rssi_adj\n + ant gain', dpink_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Calculated Antenna 2 =\n Antenna Sig dBm\n + pathloss\n + rssi_adj\n + ant gain', dpink_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Calculated Antenna 3 =\n Antenna Sig dBm\n + pathloss\n + rssi_adj\n + ant gain', dpink_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Calculated Antenna 4 =\n Antenna Sig dBm\n + pathloss\n + rssi_adj\n + ant gain', dpink_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'Offset 1 = \nCalculated Antenna 1\n - cc_dbm(per SS)', dyel_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'Offset 2 = \nCalculated Antenna 2\n - cc_dbm(per SS)', dyel_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'Offset 3 = \nCalculated Antenna 3\n - cc_dbm(per SS)', dyel_bold)
col += 1
worksheet.set_column(col, col, 20) # Set width
worksheet.write(row, col, 'Offset 4 = \nCalculated Antenna 4\n - cc_dbm(per SS)', dyel_bold)
col += 1
worksheet.set_column(col, col, 15) # Set width
worksheet.write(row, col, 'Controller\nReported\ndBm', dblue_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Client Calc Beacon dBm\n beacon + pathloss\n + rssi_adj - antenna gain', dblue_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Difference Between\n Controller dBm\n & Client Calc Beacon dBm \n (+/- {diff} dBm)'.format(diff=args.beacon_dbm_diff), dblue_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Client Calc\n Combined Signal dBm\n total signal dBm + pathloss\n + rssi_adj - antenna gain', dblue_bold)
col += 1
worksheet.set_column(col, col, 25) # Set width
worksheet.write(row, col, 'Difference Between\n Controller dBm\n& Client Calc Combined\n Signal dBm', dblue_bold)
col += 1
worksheet.set_column(col, col, 12) # Set width
worksheet.write(row, col, "PASS /\nFAIL\n( += %s dBm)" % (pf_dbm), dgreen_bold)
col += 1
worksheet.set_column(col, col, 24) # Set width
worksheet.write(row, col, 'Time Stamp\n', dgreen_bold)
col += 1
worksheet.set_column(col, col, 24) # Set width
worksheet.write(row, col, 'Run Time Single Test\n', dgreen_bold)
col += 1
worksheet.set_column(col, col, 24) # Set width
worksheet.write(row, col, 'Total Run Time\n', dgreen_bold)
col += 1
worksheet.set_column(col, col, 100) # Set width
worksheet.write(row, col, 'Information, Warnings, Errors', dgreen_bold_left)
col += 1
row += 1
bandwidths = args.bandwidth.split()
channels = args.channel.split()
nss = args.nss.split()
# args.tx_power.split() will be read later since 6E 20 Mhz does not do tx power 7 8, 40 Mhz does not do power 8
txpowers = args.txpower.split()
# The script has the ability to create a station if one does not exist
if (args.create_station):
if (args.radio is None):
logg.info("WARNING --create needs a radio")
close_workbook(workbook)
exit(1)
if (args.band == '6g' or args.band == 'dual_band_6g'):
if (args.vht160):
logg.info("creating station with VHT160 set: {} on radio {}".format(args.station, args.radio))
logg.info("cwd lf_associate_ap.pl: {dir}".format(dir=os.getcwd()))
subprocess.run(["./lf_associate_ap.pl", "--mgr", lfmgr, "--radio", args.radio, "--ssid", args.ssid, "--passphrase", args.ssidpw, "--bssid", args.bssid,
"--security", args.security, "--upstream", args.upstream_port, "--first_ip", "DHCP",
"--first_sta", args.station, "--ieee80211w", args.ieee80211w, "--wifi_mode", args.wifi_mode, "--action", "add", "--xsec", "ht160_enable"], timeout=20, capture_output=True)
sleep(3)
else:
logg.info("creating station: {} on radio {}".format(args.station, args.radio))
subprocess.run(["./lf_associate_ap.pl", "--mgr", lfmgr, "--radio", args.radio, "--ssid", args.ssid, "--passphrase", args.ssidpw, "--bssid", args.bssid,
"--security", args.security, "--upstream", args.upstream_port, "--first_ip", "DHCP",
"--first_sta", args.station, "--ieee80211w", args.ieee80211w, "--wifi_mode", args.wifi_mode, "--action", "add"], timeout=20, capture_output=True)
else:
if (args.vht160):
logg.info("creating station with VHT160 set: {} on radio {}".format(args.station, args.radio))
print()
subprocess.run(["./lf_associate_ap.pl", "--mgr", lfmgr, "--radio", args.radio, "--ssid", args.ssid, "--passphrase", args.ssidpw, "--bssid", args.bssid,
"--security", args.security, "--upstream", args.upstream_port, "--first_ip", "DHCP",
"--first_sta", args.station, "--ieee80211w", args.ieee80211w, "--wifi_mode", args.wifi_mode, "--action", "add", "--xsec", "ht160_enable"], timeout=20, capture_output=False)
sleep(3)
else:
logg.info("creating station: {} on radio {}".format(args.station, args.radio))
subprocess.run(["./lf_associate_ap.pl", "--mgr", lfmgr, "--radio", args.radio, "--ssid", args.ssid, "--passphrase", args.ssidpw, "--bssid", args.bssid,
"--security", args.security, "--upstream", args.upstream_port, "--first_ip", "DHCP",
"--first_sta", args.station, "--ieee80211w", args.ieee80211w, "--wifi_mode", args.wifi_mode, "--action", "add"], timeout=20, capture_output=False)
sleep(3)
# Find LANforge station parent radio
parent = None
logg.info("portmod command: ./lf_portmod.pl --manager {lfmgr} --card {lfresource} --port_name {lfstation} --show_port Parent/Peer".format(
lfmgr=lfmgr, lfresource=lfresource, lfstation=lfstation))
port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", lfresource, "--port_name", lfstation,
"--show_port", "Parent/Peer"], capture_output=True)
pss = port_stats.stdout.decode('utf-8', 'ignore')
for line in pss.splitlines():
m = re.search('Parent/Peer:\\s+(.*)', line)
if (m is not None):
parent = m.group(1)
# Create downstream connection
# First, delete any old one
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "rm_cx all c-udp-power"], capture_output=False)
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "rm_endp c-udp-power-A"], capture_output=False)
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource2, "--action", "do_cmd",
"--cmd", "rm_endp c-udp-power-B"], capture_output=False)
# Now, create the new connection
# higher is better because it means more frames at the signal level to be measured vs leaked frames at low signal lev
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "create_endp", "--port_name", lfstation,
"--endp_type", "lf_udp", "--endp_name", "c-udp-power-A", "--speed", "0", "--report_timer", "1000"], capture_output=False)
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource2, "--action", "create_endp", "--port_name", upstream_port,
"--endp_type", "lf_udp", "--endp_name", "c-udp-power-B", "--speed", "100000000", "--report_timer", "1000"], capture_output=False)
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "create_cx", "--cx_name", "c-udp-power",
"--cx_endps", "c-udp-power-A,c-udp-power-B", "--report_timer", "1000"], capture_output=False)
command = ["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "create_endp", "--port_name", lfstation,
"--endp_type", "lf_udp", "--endp_name", "c-udp-power-A", "--speed", "9600", "--report_timer", "1000"]
logg.info("command: {command}".format(command=command))
summary_output = ''
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.debug(line)
summary_output += line
summary.wait()
logger.info(summary_output)
command = ["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource2, "--action", "create_endp", "--port_name", upstream_port,
"--endp_type", "lf_udp", "--endp_name", "c-udp-power-B", "--speed", "100000000", "--report_timer", "1000"]
logg.info("command: {command}".format(command=command))
summary_output = ''
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.debug(line)
summary_output += line
summary.wait()
logger.info(summary_output)
# ./lf_firemod.pl --manager 192.168.100.178 --resource 1 --action create_cx --cx_name c-udp-power --cx_endps c-udp-power-A,c-udp-power-B --report_timer 1000 --endp_type lf_udp --port_name sta0003 --use_speeds 0,1000000
command = ["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "create_cx", "--cx_name", "c-udp-power",
"--cx_endps", "c-udp-power-A,c-udp-power-B", "--report_timer", "1000", "--endp_type", "lf_udp", "--port_name", lfstation,
"--use_speeds","9600,100000000"]
logg.info("command: {command}".format(command=command))
summary_output = ''
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.debug(line)
summary_output += line
summary.wait()
logger.info(summary_output)
# Notes:
# These need to be verified with test_ip_variable_time.py - which calculates the speeds
# 1 antenna 80 Mhz 433 Mbps
# 2 antenna 80 Mhz 867 Mbps
# 3 antenna 80 Mhz 1,300 Mbps
# 4 antenna 80 Mhz 1,733 Mbps
# 1 antenna 160 Mhz 867 Mbps
# 2 antenna 160 Mhz 1,733 Mbps
# 3 antenna 160 Mhz 2,600 Mbps
# 4 antenna 160 Mhz 3,466 Mbps
# ./lf_firemod.pl --manager 192.168.100.178 --resource 1 --action create_cx --cx_name c-udp-power --cx_endps c-udp-power-A,c-udp-power-B --report_timer 1000 --endp_type udp --port_name sta0000 --speed 1000000
# 5ghz speeds
myrd = ""
# The script supports both the 9800 series controller and the 3504 series controller , the controllers have different interfaces
if args.series == "9800":
cs.no_logging_console()
cs.line_console_0()
# TODO
pss = cs.show_ap_summary()
logg.info(pss)
# Find our current regulatory domain so we can report it properly
searchap = False
for line in pss.splitlines():
if (line.startswith("---------")):
searchap = True
continue
# the summaries are different between the 9800 series controller and the 3504 series
# if the output changes then the following pattern/regular expression parcing needs to be changed
# this site may help: https://regex101.com/
# when using https://regex101.com/ for tool beginning of string begins with ^
if (searchap):
if args.series == "9800":
pat = "%s\\s+\\S+\\s+\\S+\\s+\\S+\\s+\\S+\\s+%s\\s+(\\S+)" % (args.ap, args.testbed_location)
else:
pat = "%s\\s+\\S+\\s+\\S+\\s+\\S+\\s+\\S+.* (\\S+)\\s+\\S+\\s*\\S+\\s+\\[" % (args.ap)
m = re.search(pat, line)
if (m is not None):
myrd = m.group(1)
logger.info("Regulatory Domain from show AP Summary : {domain}".format(domain=myrd))
if myrd == "":
logger.error("Regulatory domain is blank: --testbed_location <show ap summary Location> : location entered {location}".format(location=args.testbed_location))
# these are set to configure the number of spatial streams and MCS values
# 5g has 8 spatial streams , MCS is 7, 9, 11
# ap dot11 6ghz dot11ax mcs tx index 7 spatial-stream 1 << - turn on
# no ap dot11 6ghz dot11ax mcs tx index 7 www.you-stream 2 <<-- turn off
# Loop through all iterations and run txpower tests.
# The is the main loop of loops: Channels, spatial streams (nss), bandwidth (bw), txpowers (tx)
# Note: supports 9800 and 3504 controllers
wlan_created = False
# create blank time stamp
total_run_duration = datetime.timedelta(0)
run_start_time = datetime.datetime.now()
run_start_time_str = str(datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")).replace(':', '-')
logger.info("run_start_time : {run_start_time}".format(run_start_time=run_start_time_str))
for ch in channels:
pathloss = args.pathloss
antenna_gain = args.antenna_gain
ch_colon = ch.count(":")
if (ch_colon == 1):
cha = ch.split(":")
pathloss = cha[1]
ch = cha[0]
for n in nss:
if (n != "NA" and args.set_nss):
# Disable the wlan to set the spatial streams
# Disable wlan, apply settings, Enable wlan
if args.band == "dual_band_6g":
cs.ap_dot11_dual_band_6ghz_shutdown()
cs.ap_dot11_6ghz_shutdown()
elif args.band == "dual_band_5g":
cs.ap_dot11_dual_band_5ghz_shutdown()
cs.ap_dot11_5ghz_shutdown()
elif args.band == "6g":
cs.ap_dot11_6ghz_shutdown()
elif args.band == "5g":
cs.ap_dot11_5ghz_shutdown()
elif args.band == "24g":
cs.ap_dot11_24ghz_shutdown()
# the band will be set
num_spatial_streams = int(n)
# set the spatial streams for - need to disable the wlan and re-enable
# ap dot11 dot11ax mcs tx index 7 spatial-stream 1 << - turn on
# no ap dot11 dot11ax mcs tx index 7 spatial-stream 2 <<-- turn off
# Cannot disable MCS lower data rates when higher data rates are enabled
# enabling MCS from lower MCS to higher MCS
# disabling MCS from higher MCS to lower MCS
if num_spatial_streams == 1 or num_spatial_streams == 2 or num_spatial_streams == 3 or num_spatial_streams == 4:
cs.spatial_stream = 1
cs.mcs_tx_index = 7
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 11
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
if num_spatial_streams == 2 or num_spatial_streams == 3 or num_spatial_streams == 4:
cs.spatial_stream = 2
cs.mcs_tx_index = 7
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 11
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
else:
cs.spatial_stream = 2
cs.mcs_tx_index = 11
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 7
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
if num_spatial_streams == 3 or num_spatial_streams == 4:
cs.spatial_stream = 3
cs.mcs_tx_index = 7
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 11
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
else:
cs.spatial_stream = 3
cs.mcs_tx_index = 11
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 7
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
if num_spatial_streams == 4:
cs.spatial_stream = 3
cs.mcs_tx_index = 7
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 11
cs.ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
else:
cs.spatial_stream = 4
cs.mcs_tx_index = 11
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 7
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
if args.band == '5g' or args.band == 'dual_band_5g':
# turn off spatial streams 5 - 8
# disable spatial stream 5
cs.spatial_stream = 5
cs.mcs_tx_index = 11
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 7
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
# disable spatial stream 6
cs.spatial_stream = 6
cs.mcs_tx_index = 11
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 7
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
# disable spatial stream 7
cs.spatial_stream = 7
cs.mcs_tx_index = 11
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 7
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
# disable spatial stream 8
cs.spatial_stream = 8
cs.mcs_tx_index = 11
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 9
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
cs.mcs_tx_index = 7
cs.no_ap_dot11_dot11ax_mcs_tx_index_spatial_stream()
for bw in bandwidths:
if (n != "NA"):
ni = int(n)
if (parent is None):
logg.info("ERROR: Skipping setting the spatial streams because cannot find Parent radio for station: %s." % (lfstation))
else:
# Set nss on LANforge Station, not sure it can be done on AP
# for ax210, it can do any bandwidth at up to 2 NSS
# for 9984 (wave-2), it does have restrictions
# 9984 can do 4x4 at 80Mhz, and 2x2 at 160Mhz
if (bw == "160"):
if(args.vht160):
# for 9984 (wave-2) for 160 Mhz set for 160 set ni = 2
if(args.wave2):
ni = int(2)
logg.info("NOTE: wave2 (9984) has restrictions : 160Mhz is 2x2 --vht160 set and will set ni : {}".format(ni))
else:
logg.info("NOTE: Skipping NSS %s for 160Mhz, LANforge needs 160Mhz enabled." % (n))
logg.info("NOTE: use --vht160 to force 160Mhz")
continue
antset = 0 # all available
if (ni == 1):
antset = 1
if (ni == 2):
antset = 4
if (ni == 3):
antset = 7
if (ni == 4):
antset = 8
set_cmd = "set_wifi_radio 1 %s %s NA NA NA NA NA NA NA NA NA %s" % (lfresource, parent, antset)
logg.info("Setting LANforge radio to %s NSS with command: %s" % (ni, set_cmd))
subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", lfresource, "--port_name", parent,
"--cli_cmd", set_cmd], capture_output=True)
# tx power 1 is the highest power , 2 power is 1/2 of 1 power etc till power 8 the lowest.
# 6E 20Mhz tx power 1 - 6
# 6E 40Mhz tx power 1 - 7
# TODO make txpower file into object ,
if args.tx_power_adjust_6E:
txpowers = args.txpower.split()
if args.band == '6g' or args.band == 'dual_band_6g':
if bw == '20':
if '8' in txpowers:
txpowers.remove('8')
if '7' in txpowers:
txpowers.remove('7')
elif bw == '40':
if '8' in txpowers:
txpowers.remove('8')
for tx in txpowers:
# e_tot is the errors, w_tot is the warning, i_tot is information
e_tot = ""
w_tot = ""
i_tot = ""
# Stop traffic , if traffic was running , this is on the lanforge side. Commands that start with lf_ are directed
# towards the lanforge
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "set_cx_state all c-udp-power STOPPED"], capture_output=True)
command = ["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "set_cx_state all c-udp-power STOPPED"]
summary_output = ''
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.debug(line)
summary_output += line
summary.wait()
logger.info(summary_output)
# Down station
# port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", lfresource, "--port_name", lfstation,
# "--set_ifstate", "down"])
# CMR TODO this looks to be an issue
command = ["./lf_portmod.pl", "--manager", lfmgr, "--card", lfresource, "--port_name", lfstation,
"--set_ifstate", "down"]
summary_output = ''
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.debug(line)
summary_output += line
summary.wait()
logger.info(summary_output)
cs.show_ap_summary()
# when both 5g (slot 1) is enabled and dual-band 5g (slot 2) is enabled .
# 5g slot 1 will used the 5g channels to 64, the 5g dual-band will use channels 100 -> 165.
# When 5g (slot 1) and dual-band 6g (slot 2) is enabled then 5g (slot 1) has all bands.
# if dual band : disable dual-band mode, config mode, enable dual-band mode
# disable dual-band mode
# for other bands just disable the radio
if args.band == "dual_band_6g":
logg.info("ap_dot11_dual_band_mode_shutdown_6ghz")
cs.ap_dot11_dual_band_mode_shutdown_6ghz()
elif args.band == "dual_band_5g":
logg.info("ap_dot11_dual_band_mode_shutdown_5ghz")
cs.ap_dot11_dual_band_mode_shutdown_5ghz()
elif args.band == "6g":
logg.info("ap_dot11_shutdown_6ghz")
cs.show_ap_dot11_6gz_shutdown()
elif args.band == "5g":
logg.info("ap_dot11_shutdown_5ghz")
cs.show_ap_dot11_5gz_shutdown()
elif args.band == "24g":
logg.info("ap_dot11_shutdown_24ghz")
cs.show_ap_dot11_24gz_shutdown()
# if dual band : disable dual-band mode, config mode, enable dual-band mode
# disable dual-band mode
# set the radio role selection
if args.band == 'dual_band_6g':
logg.info("ap_dot11_dual_band_6ghz_radio_role_manual_client_serving")
cs.ap_dot11_dual_band_6ghz_radio_role_manual_client_serving()
elif args.band == 'dual_band_5g':
logg.info("ap_dot11_dual_band_5ghz_radio_role_manual_client_serving")
cs.ap_dot11_dual_band_5ghz_radio_role_manual_client_serving()
elif args.band == '6g':
cs.ap_dot11_6ghz_radio_role_manual_client_serving()
logg.info("ap_dot11_6ghz_radio_role_manual_client_serving")
elif args.band == '5g':
cs.ap_dot11_5ghz_radio_role_manual_client_serving()
logg.info("ap_dot11_5ghz_radio_role_manual_client_serving")
elif args.band == '24g':
cs.ap_dot11_24ghz_radio_role_manual_client_serving()
logg.info("ap_dot11_24ghz_radio_role_manual_client_serving")
# config dual-band mode
if args.band == "dual_band_6g":
cs.config_ap_dot11_dual_band_to_6ghz()
elif args.band == "dual_band_5g":
cs.config_ap_dot11_dual_band_to_5ghz()
# enable dual-band mode
if args.band == "dual_band_6g":
cs.ap_dot11_dual_band_no_mode_shutdown_6ghz()
elif args.band == "dual_band_5g":
cs.ap_dot11_dual_band_no_mode_shutdown_5ghz()
# Disable AP, apply settings, enable AP
if args.band == "dual_band_6g":
cs.ap_dot11_dual_band_6ghz_shutdown()
elif args.band == "dual_band_5g":
cs.ap_dot11_dual_band_5ghz_shutdown()
elif args.band == "6g":
cs.ap_dot11_6ghz_shutdown()
elif args.band == "5g":
cs.ap_dot11_5ghz_shutdown()
elif args.band == "24g":
cs.ap_dot11_24ghz_shutdown()
if args.series == "9800":
# 9800 series need to "Configure radio for manual channel assignment"
logg.info("9800 Configure radio for manual channel assignment")
cs.wlan_shutdown()
if args.band == 'dual_band_6g':
cs.ap_dot11_dual_band_6ghz_shutdown()
elif args.band == 'dual_band_5g':
cs.ap_dot11_dual_band_5ghz_shutdown()
elif args.band == '6g':
cs.ap_dot11_6ghz_shutdown()
elif args.band == '5g':
cs.ap_dot11_5ghz_shutdown()
elif args.band == '24g':
cs.ap_dot11_24ghz_shutdown()
else:
cs.ap_dot11_5ghz_shutdown()
cs.ap_dot11_24ghz_shutdown()
logg.info("9800/3504 test_parameters_summary: set : tx: {tx_power} ch: {channel} bw: {bandwidth}".format(
tx_power=tx, channel=ch, bandwidth=bw))
if (tx != "NA"):
logg.info("9800/3504 test_parameters: set txPower: {tx_power}".format(tx_power=tx))
cs.tx_power = tx
if args.band == 'dual_band_6g':
cs.config_dot11_dual_band_6ghz_tx_power()
elif args.band == 'dual_band_5g':
cs.config_dot11_dual_band_5ghz_tx_power()
elif args.band == '6g':
cs.config_dot11_6ghz_tx_power()
elif args.band == '5g':
cs.config_dot11_5ghz_tx_power()
elif args.band == '24g':
cs.config_dot11_24ghz_tx_power()
# NSS is set on the station earlier...
if (ch != "NA"):
logg.info("9800/3504 test_parameters set channel: {}".format(ch))
cs.channel = ch
if args.band == 'dual_band_6g':
cs.config_dot11_dual_band_6ghz_channel()
elif args.band == 'dual_band_5g':
cs.config_dot11_dual_band_5ghz_channel()
elif args.band == '6g':
cs.config_dot11_6ghz_channel()
elif args.band == '5g':
cs.config_dot11_5ghz_channel()
elif args.band == '24g':
cs.config_dot11_24ghz_channel()
if (bw != "NA"):
logg.info("9800/3504 test_parameters bandwidth: set : {}".format(bw))
cs.bandwidth = bw
if args.band == 'dual_band_6g':
cs.config_dot11_dual_band_6ghz_channel_width()
elif args.band == 'dual_band_5g':
cs.config_dot11_dual_band_5ghz_channel_width()
elif args.band == '6g':
cs.config_dot11_6ghz_channel_width()
elif args.band == '5g':
cs.config_dot11_5ghz_channel_width()
elif args.band == '24g':
# 24g can only be 20 Mhz
pass
# only create the wlan the first time
if args.series == "9800":
if args.create_wlan is False:
wlan_created = True
if wlan_created:
pss = cs.show_wlan_summary()
logg.info(pss)
logg.info(
"wlan already present, no need to create wlanID {} wlan {} wlanSSID {} port {}".format(
args.wlanID, args.wlan, args.wlanSSID, args.port))
if args.band == 'dual_band_6g':
pss = cs.show_ap_dot11_dual_band_6gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_dual_band_6ghz()
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.show_ap_dot11_dual_band_5gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_dual_band_5ghz()
logg.info(pss)
elif args.band == '6g':
pss = cs.show_ap_dot11_6gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_6ghz()
logg.info(pss)
elif args.band == '5g':
pss = cs.show_ap_dot11_5gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_5ghz()
logg.info(pss)
elif args.band == '24g':
pss = cs.show_ap_dot11_24gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_24ghz()
logg.info(pss)
else:
# Verify that a wlan does not exist on wlanID
# delete the wlan if already exists
pss = cs.show_wlan_summary()
logg.info(pss)
if args.band == 'dual_band_6g':
cs.show_ap_dot11_dual_band_6gz_summary()
elif args.band == 'dual_band_5g':
cs.show_ap_dot11_dual_band_5gz_summary()
elif args.band == '6g':
cs.show_ap_dot11_6gz_summary()
elif args.band == '5g':
cs.show_ap_dot11_5gz_summary()
elif args.band == '24g':
cs.show_ap_dot11_24gz_summary()
# "number of WLANs:\s+(\S+)"
# https://regex101.com/
search_wlan = False
for line in pss.splitlines():
logg.info(line)
if (line.startswith("---------")):
search_wlan = True
continue
if (search_wlan):
pat = "{}\\s+(\\S+)\\s+(\\S+)".format(args.wlanID)
m = re.search(pat, line)
if (m is not None):
cc_wlan = m.group(1)
cc_wlan_ssid = m.group(2)
# wlanID is in use
logg.info("###############################################################################")
logg.info("Need to remove wlanID: {} cc_wlan: {} cc_wlan_ssid: {}".format(args.wlanID, cc_wlan, cc_wlan_ssid))
logg.info("###############################################################################")
cs.config_no_wlan()
# Create wlan
wlan_created = True
logg.info("create wlan {} wlanID {} port {}".format(args.wlan, args.wlanID, args.port))
# TODO be able to do for WPA2 , WPA3
# TODO check for failure to close cookbook
# this needs to be configurable
if args.security == 'open':
cs.config_wlan_open()
elif args.security == 'wpa2':
cs.config_wlan_wpa2()
elif args.security == 'wpa3':
cs.config_wlan_wpa3()
cs.config_wireless_tag_policy_and_policy_profile()
# enable_wlan
cs.config_enable_wlan_send_no_shutdown()
# enable transmission for the entier 802.11z network
# the wlan may not care about dual_band
# enable_network_6ghz or enable_network_5ghz or enable_network_24ghz
if args.band == 'dual_band_6g':
# enable 6g wlan
pss = cs.config_no_ap_dot11_dual_band_6ghz_shutdown()
logg.info(pss)
# enable 6g operation status
pss = cs.config_ap_no_dot11_dual_band_6ghz_shutdown()
logg.info(pss)
# enable 6g wlan
pss = cs.config_no_ap_dot11_6ghz_shutdown()
logg.info(pss)
# enable 6g operation status
pss = cs.config_ap_no_dot11_6ghz_shutdown()
logg.info(pss)
# enable 5g wlan to show scans
pss = cs.config_no_ap_dot11_5ghz_shutdown()
logger.info(pss)
# enable 5g operation status
pss = cs.config_ap_no_dot11_5ghz_shutdown()
logger.info(pss)
elif args.band == 'dual_band_5g':
# enable 5g wlan - dual band
pss = cs.config_no_ap_dot11_dual_band_5ghz_shutdown()
logg.info(pss)
# enable 5g operation status
pss = cs.config_ap_no_dot11_dual_band_5ghz_shutdown()
logg.info(pss)
# enable 5g wlan
pss = cs.config_no_ap_dot11_5ghz_shutdown()
logger.info(pss)
# enable 5g operation status
pss = cs.config_ap_no_dot11_5ghz_shutdown()
logger.info(pss)
elif args.band == '6g':
# enable 6g wlan
pss = cs.config_no_ap_dot11_6ghz_shutdown()
logg.info(pss)
# enable 6g operation status
pss = cs.config_ap_no_dot11_6ghz_shutdown()
logg.info(pss)
# 6g needs to see the 5g bands
# enable 5g wlan
pss = cs.config_no_ap_dot11_5ghz_shutdown()
logger.info(pss)
# enable 5g operation status
pss = cs.config_ap_no_dot11_5ghz_shutdown()
logger.info(pss)
elif args.band == '5g':
# enable 5g wlan
pss = cs.config_no_ap_dot11_5ghz_shutdown()
logg.info(pss)
# enable 5g operation status
pss = cs.config_ap_no_dot11_5ghz_shutdown()
logg.info(pss)
elif args.band == '24g':
# enable wlan 24ghz
pss = cs.config_no_ap_dot11_24ghz_shutdown()
logg.info(pss)
# enable 24ghz operation status
cs.config_ap_no_dot11_24ghz_shutdown()
logg.info(pss)
# Wait a bit for AP to come back up
time.sleep(3)
loop_count = 0
cc_dbm_rcv = False
if args.series == "9800":
while cc_dbm_rcv is False and loop_count <= 3:
logg.info("9800 read controller dBm")
loop_count += 1
time.sleep(1)
if args.band == 'dual_band_6g':
pss = cs.show_ap_dot11_dual_band_6gz_summary()
logg.info("show ap dot11 dual-band (6ghz) summary")
logg.info("ap: {ap} ap_band_slot_6g: {slot} ".format(ap=args.ap, slot=args.ap_band_slot_6g))
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.show_ap_dot11_dual_band_5gz_summary()
logg.info("show ap dot11 dual-band (5ghz) summary")
logg.info("ap: {ap} ap_band_slot_5g: {slot} ".format(ap=args.ap, slot=args.ap_band_slot_5g))
logg.info(pss)
elif args.band == '6g':
pss = cs.show_ap_dot11_6gz_summary()
logg.info("show ap dot11 6ghz summary")
logg.info("ap: {ap} ap_band_slot_6g: {slot} ".format(ap=args.ap, slot=args.ap_band_slot_6g))
logg.info(pss)
elif args.band == '5g':
logg.info("show ap dot11 5ghz summary")
logg.info("ap: {ap} ap_band_slot_5g: {slot} ".format(ap=args.ap, slot=args.ap_band_slot_5g))
pss = cs.show_ap_dot11_5gz_summary()
logg.info(pss)
else:
logg.info("show ap dot11 24ghz summary")
logg.info("ap: {ap} ap_band_slot_24g: {slot} ".format(ap=args.ap, slot=args.ap_band_slot_24g))
pss = cs.show_ap_dot11_24gz_summary()
logg.info(pss)
searchap = False
cc_mac = ""
cc_ch = ""
cc_bw = ""
cc_power = ""
cc_dbm = ""
for line in pss.splitlines():
if (line.startswith("---------")):
searchap = True
continue
# if the pattern changes save the output of the advanced command and re parse https://regex101.com
if (searchap):
logg.info("##### line #####")
logg.info(line)
if args.band == 'dual_band_6g':
logg.info("ap : {ap} ap_dual_band_slot_6g: {slot}".format(ap=args.ap, slot=args.ap_dual_band_slot_6g))
elif args.band == 'dual_band_5g':
logg.info("ap : {ap} ap_dual_band_slot_5g: {slot}".format(ap=args.ap, slot=args.ap_dual_band_slot_5g))
elif args.band == '6g':
logg.info("ap : {ap} ap_band_slot_6g: {slot}".format(ap=args.ap, slot=args.ap_band_slot_6g))
elif args.band == '5g':
logg.info("ap : {ap} ap_band_slot_5g: {slot}".format(ap=args.ap, slot=args.ap_band_slot_5g))
elif args.band == '24g':
logg.info("ap : {ap} ap_band_slot_24g: {slot}".format(ap=args.ap, slot=args.ap_band_slot_24g))
if args.band == 'dual_band_6g' or args.band == 'dual_band_5g':
pat = "%s\\s+(\\S+)\\s+(\\S+)\\s+\\S+\\s+\\S+\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+dBm\\)+\\s+\\S+\\s+\\S+\\s+(\\S+)" % (args.ap)
else:
pat = "%s\\s+(\\S+)\\s+(\\S+)\\s+\\S+\\s+\\S+\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+dBm\\)+\\s+(\\S+)+\\s" % (args.ap)
logg.info(pat)
m = re.search(pat, line)
logg.info(m)
ap_band_slot = None
if (m is not None):
if args.band == 'dual_band_6g':
logg.info("checking of dual-band slot for 6g {band_slot} present in the show ap dot11 dual-band summary".format(band_slot=args.ap_dual_band_slot_6g))
ap_band_slot = args.ap_dual_band_slot_6g
elif args.band == 'dual_band_5g':
logg.info("checking of dual-band slot for 5g {band_slot} present in the show ap dot11 dual-band summary".format(band_slot=args.ap_dual_band_slot_5g))
ap_band_slot = args.ap_dual_band_slot_5g
elif args.band == '6g':
logg.info("checking of band slot 6g {band_slot} present in the show ap dot11 6ghz summary".format(band_slot=args.ap_band_slot_6g))
ap_band_slot = args.ap_band_slot_6g
elif args.band == '5g':
logg.info("checking of band slot 5g {band_slot} present in the show ap dot11 5ghz summary".format(band_slot=args.ap_band_slot_5g))
ap_band_slot = args.ap_band_slot_5g
elif args.band == '24g':
logg.info("checking of band slot 24g {band_slot} present in the show ap dot11 24ghz summary".format(band_slot=args.ap_band_slot_24g))
ap_band_slot = args.ap_band_slot_24g
else:
logg.warning("band_slot not set, results will be incomplete setting ap_band_slot to 1")
ap_band_slot = '1'
if(m.group(2) == ap_band_slot):
cc_ap = args.ap
cc_mac = m.group(1)
cc_slot = m.group(2)
cc_ch = m.group(6) # (132,136,140,144)
cc_power = m.group(4)
cc_power = cc_power.replace("/", " of ") # spread-sheets turn 1/8 into a date
cc_dbm = m.group(5)
cc_dbm = cc_dbm.replace("(", "")
logg.info("ap slot {cc_slot} present in the show ap dot11 {band}hz summary".format(cc_slot=cc_slot, band=args.band))
cc_ch_count = cc_ch.count(",") + 1
cc_bw = m.group(3)
logg.info("show ap summary : {summary}".format(summary=m.group(0)))
logg.info(
("(ap): {ap}, group 1 (cc_mac): {mac}, group 2(ap band slot): {slot}, group 3 (bw): {admin} "
"group 4 (cc_Txpwr): {Txpwr}, group 5 (cc_dbm): {dbm}, group 6 (cc_ch): {chan}".format(
ap=args.ap, mac=m.group(1), slot=m.group(2), admin=m.group(3), Txpwr=m.group(4),
dbm=m.group(5), chan=m.group(6))))
logg.info("9800 test_parameters_summary: read: tx: {} ch: {} bw: {}".format(tx, ch, bw))
logg.info("9800 cc_ap: read : {}".format(cc_ap))
logg.info("9800 from ap summary cc_mac: read : {}".format(cc_mac))
logg.info("9800 from ap summary cc_slot: read : {}".format(cc_slot))
logg.info("9800 from ap summary cc_count: read : {}".format(cc_ch_count))
logg.info("9800 from ap summary cc_bw: read : {}".format(cc_bw))
logg.info("9800 from ap summary cc_power: read : {}".format(cc_power))
logg.info("9800 from ap summary cc_dbm: read : {}".format(cc_dbm))
logg.info("9800 from ap summary cc_ch: read : {}".format(cc_ch))
break
if (cc_dbm == ""):
if loop_count >= 3:
# Could not talk to controller? Not this may not be a reason to exit
# Some of the tests run for 32 plus hours , do not kill the whole test unless trying to
# debug an issue with the test. Sometimes the controller is taking time to configure.
err = "ERROR: cc_dmp not found from query of controller: is the AP --slot set correctly?"
logg.info(err)
logg.info("run show ap dot11 6/5/24gghz summary ")
e_tot += err
e_tot += " "
else:
logg.info("9800 read controller dBm loop_count {} try again".format(loop_count))
else:
cc_dbm_rcv = True
cs.show_wlan_summary()
else:
pss = cs.show_ap_dot11_5gz_summary()
logg.info(pss)
pss = cs.show_ap_dot11_24gz_summary()
logg.info(pss)
pss = cs.show_wlan_summary()
logg.info(pss)
searchap = False
cc_mac = ""
cc_ch = ""
cc_bw = ""
cc_power = ""
cc_dbm = ""
ch_count = ""
for line in pss.splitlines():
if (line.startswith("---------")):
searchap = True
continue
if (searchap):
pat = "%s\\s+(\\S+)\\s+\\S+\\s+\\S+\\s+\\S+\\s+(\\S+)\\s+(\\S+)\\s+\\(\\s*(\\S+)\\s+dBm" % (args.ap)
m = re.search(pat, line)
if (m is not None):
cc_mac = m.group(1)
cc_ch = m.group(2) # (132,136,140,144)
cc_power = m.group(3)
cc_power = cc_power.replace("/", " of ", 1) # spread-sheets turn 1/8 into a date
cc_dbm = m.group(4)
ch_count = cc_ch.count(",")
cc_bw = 20 * (ch_count + 1)
break
if (cc_dbm == ""):
# Could not talk to controller?
err = "Warning : Could not query dBm from controller"
logg.info(err)
e_tot += err
e_tot += " "
logg.info("3504 test_parameters cc_mac: read : {}".format(cc_mac))
logg.info("3504 test_parameters cc_count: read : {}".format(ch_count))
logg.info("3504 test_parameters cc_bw: read : {}".format(cc_bw))
logg.info("3504 test_parameters cc_power: read : {}".format(cc_power))
logg.info("3504 test_parameters cc_dbm: read : {}".format(cc_dbm))
logg.info("3504 test_parameters cc_ch: read : {}".format(cc_ch))
# Up station
subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", lfresource, "--port_name", lfstation,
"--set_ifstate", "up"])
i = 0
wait_ip_print = False
wait_assoc_print = False
# Temporary Work around
# disable the AP for 6g and enable
# if args.ap_admin_down_up_6g is True and (args.band == '6g' or args.band == 'dual_band_6g'):
# TODO this is needed after an upgrade
cs.ap_name_shutdown()
sleep(5)
cs.ap_name_no_shutdown()
# Wait untill LANforge station connects
while True:
port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", lfresource, "--port_name", lfstation,
"--show_port", "AP,IP,Mode,NSS,Bandwidth,Channel,Signal,Noise,Status,RX-Rate"], capture_output=True, check=True)
pss = port_stats.stdout.decode('utf-8', 'ignore')
_status = None
_ip = None
for line in pss.splitlines():
m = re.search('Status:\\s+(.*)', line)
if (m is not None):
_status = m.group(1)
m = re.search('IP:\\s+(.*)', line)
if (m is not None):
_ip = m.group(1)
if (i % 3) == 0 :
logg.info("IP %s Status %s" % (_ip, _status))
if (_status == "Authorized"):
if ((_ip is not None) and (_ip != "0.0.0.0")):
logg.info("Station is associated with IP address.")
break
else:
if (not wait_ip_print):
logg.info("Waiting for station to get IP Address.")
wait_ip_print = True
else:
if (not wait_assoc_print):
logg.info("Waiting up to 180s for station to associate.")
wait_assoc_print = True
i += 1
# We wait a fairly long time since AP will take a long time to start on a CAC channel.
if (i > int(args.wait_time)):
err = "ERROR: Station did not connect within 180 seconds."
logg.info(err)
e_tot += err
e_tot += " "
if (args.wait_forever):
logg.info("Will continue waiting, you may wish to debug the system...")
i = 0
else:
break
time.sleep(1)
if args.series == "9800":
# being explicite as
if args.band == 'dual_band_6g':
pss = cs.show_ap_dot11_dual_band_6gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_dual_band_6ghz()
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.show_ap_dot11_dual_band_5gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_dual_band_5ghz()
logg.info(pss)
elif args.band == '6g':
pss = cs.show_ap_dot11_6gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_6ghz()
logg.info(pss)
elif args.band == '5g':
pss = cs.show_ap_dot11_5gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_5ghz()
logg.info(pss)
elif args.band == '24g':
pss = cs.show_ap_dot11_24gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_24ghz()
logg.info(pss)
# Start traffic
# subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
# "--cmd", "set_cx_state all c-udp-power RUNNING"], capture_output=True, check=False)
#
logg.info("Start Running traffic cx")
command = ["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "set_cx_state all c-udp-power RUNNING"]
logg.info("command: {command}".format(command=command))
summary_output = ''
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.debug(line)
summary_output += line
summary.wait()
logger.info(summary_output)
# Wait configured number of seconds more seconds
logg.info("Waiting {} seconds to let traffic run for a bit, Channel {} NSS {} BW {} TX-Power {}".format(args.duration, ch, n, bw, tx))
time.sleep(int(args.duration))
# gather information from ap
if(bool(ap_dict)):
logg.info("ap_dict {}".format(ap_dict))
logg.info("Read AP ap_scheme: {} ap_ip: {} ap_port: {} ap_user: {} ap_pw: {}".format(ap_dict['ap_scheme'], ap_dict['ap_ip'], ap_dict["ap_port"],
ap_dict['ap_user'], ap_dict['ap_pw']))
logg.info("####################################################################################################")
logg.info("# READ AP POWERREG")
logg.info("####################################################################################################")
summary_output = ''
try:
logg.info("ap_ctl.py: read AP power information")
# TODO use ap module
command = ["./ap_ctl.py", "--scheme", ap_dict['ap_scheme'], "--prompt", ap_dict['ap_prompt'], "--dest", ap_dict['ap_ip'], "--port", ap_dict["ap_port"],
"--user", ap_dict['ap_user'], "--passwd", ap_dict['ap_pw'], "--action", "powerreg"]
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.info(line)
if len(line) > 4:
summary_output += line
summary.wait()
logger.info(summary_output) # .decode('utf-8', 'ignore'))
except subprocess.CalledProcessError as process_error:
logg.info("####################################################################################################")
logg.info("# CHECK IF AP HAS TELNET CONNECTION ALREADY ACTIVE")
logg.info("####################################################################################################")
logg.info("####################################################################################################")
logg.info(
"# Unable to commicate to AP error code: {} output {}".format(
process_error.returncode, process_error.output))
logg.info("####################################################################################################")
summary = "empty_process_error"
summary_output = summary
ap_ant_gain = ''
ap_legal_ant_gain = ''
ap_total_power = ''
ap_total_power_found = False
ap_per_path_power = ''
ap_per_path_power_found = False
summary_output_split = summary_output.splitlines()
print(summary_output_split)
for line in summary_output.splitlines():
if 'Configured Antenna Gain(dBi):' in line:
pat = "Configured Antenna Gain\\(dBi\\)\\:\\s+(\\d+)"
match = re.search(pat, line)
if match is not None :
ap_ant_gain = match.group(1)
logger.info("AP antenna gain: {gain}".format(gain=ap_ant_gain))
if 'Legal Antenna Gain in use(dBi):' in line:
pat = "Legal Antenna Gain in use\\(dBi\\):\\s+(\\S+)"
match = re.search(pat, line)
if match is not None :
ap_legal_ant_gain = match.group(1)
logger.info("AP Legal antenna gain: {gain}".format(gain=ap_legal_ant_gain))
# /Allowed total powers\S+\Sn(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)
if "INFO" not in line and "Allowed total powers(dBm):" in line:
ap_total_power_found = True
continue
if ap_total_power_found is True:
ap_total_power_found = False
if args.band == '6g' or args.band == 'dual_band_6g':
if bw == '20':
pat = "(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
elif bw == '40':
pat = "(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
else:
pat = "(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
else:
pat = "(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
match = re.search(pat, line)
if match is not None:
ap_total_power = match.group(int(tx))
# /Allowed per-path powers\S+\Sn(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/gm
if "INFO" not in line and "Allowed per-path powers(dBm):" in line:
ap_per_path_power_found = True
continue
if ap_per_path_power_found is True:
ap_per_path_power_found = False
if args.band == '6g' or args.band == 'dual_band_6g':
if bw == '20':
# pat = "Allowed per-path powers\\S+\\Sn(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
pat = "(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
elif bw == '40':
# pat = "Allowed per-path powers\\S+\\Sn(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
pat = "(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
else:
# pat = "Allowed per-path powers\\S+\\Sn(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
pat = "(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
else:
pat = "(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)"
match = re.search(pat, line)
if match is not None:
ap_per_path_power = match.group(int(tx))
logg.info("ap_ant_gain: {ap_ant_gain}".format(ap_ant_gain=ap_ant_gain))
logg.info("ap_legal_ant_gain: {ap_legal_ant_gain}".format(ap_legal_ant_gain=ap_legal_ant_gain))
logg.info("ap_total_power: {ap_total_power}".format(ap_total_power=ap_total_power))
logg.info("ap_per_path_power: {ap_per_path_power}".format(ap_per_path_power=ap_per_path_power))
# Gather probe results and record data, verify NSS, BW, Channel
# note the probe will get the information from this command
# iw dev sta0000 station dump
i = 0
beacon_sig = None
sig = None
pf = 1
ants = []
while True:
time.sleep(1)
port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", lfresource, "--port_name", lfstation,
"--cli_cmd", "probe_port 1 %s %s" % (lfresource, lfstation)], capture_output=True, check=True)
pss = port_stats.stdout.decode('utf-8', 'ignore')
# for debug: print the output of lf_portmod.pl and the command used
logg.debug("######## lf_portmod ######### ")
logg.debug(pss)
logg.debug("######## lf_portmod END ######### ")
if (args.show_lf_portmod):
logg.info("./lf_portmod.pl --manager {} --card {} --port_name {} --cli_cmd probe_port 1 {} {}".format(lfmgr,
lfresource, lfstation, lfresource, lfstation))
logg.info(pss)
foundit = False
for line in pss.splitlines():
# logg.info("probe-line: %s"%(line))
# TODO switch to signal avg
m = re.search('signal avg:\\s+(\\S+)\\s+\\[(.*)\\]\\s+dBm', line)
# m = re.search('signal:\\s+(\\S+)\\s+\\[(.*)\\]\\s+dBm', line)
# print("m singal avg : {}".format(m))
# AX210 needs to look at signal
if (m is None):
m = re.search('signal:\\s+(\\S+)\\s+\\[(.*)\\]\\s+dBm', line)
# print("m signal: {}".format(m))
if (m is not None):
logg.info("search: signal: resulted in m = {}".format(m))
sig = m.group(1)
ants = m.group(2).split()
q = 0
for a in ants:
ants[q] = ants[q].replace(",", "", 1)
q += 1
logg.info("sig: %s ants: %s ants-len: %s n: %s" % (sig, m.group(2), len(ants), n))
if (len(ants) == int(n)):
foundit = True
else:
logg.info("Looking for %s spatial streams, signal avg reported fewer: %s" % (n, m.group(1)))
m = re.search('beacon signal avg:\\s+(\\S+)\\s+dBm', line)
if (m is not None):
logg.info("search: beacon signal avg: resulted in m = {}".format(m))
beacon_sig = m.group(1)
logg.info("beacon_sig: %s " % (beacon_sig))
if (foundit):
break
i += 1
if (i > 10):
err = "Tried and failed 10 times to find all probe values, continuing."
logg.info(err)
e_tot += err
e_tot += " "
while (len(ants) < int(n)):
ants.append("")
break
endp_stats = subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--endp_vals", "rx_bps",
"--cx_name", "c-udp-power"], capture_output=True, check=True)
pss = endp_stats.stdout.decode('utf-8', 'ignore')
logg.info(pss)
for line in pss.splitlines():
# logg.info("probe-line: %s"%(line))From Lanforge probe, command
# ./lf_portmod.pl with cli parameter probe_port 1 (about line 1150)
m = re.search('Rx Bytes:\\s+(\\d+)', line)
if (m is not None):
logg.info("Rx Bytes: result {}".format(m))
rx_bytes = int(m.group(1))
if (rx_bytes == 0):
err = "ERROR: No bytes received by data connection, test results may not be valid."
e_tot += err
e_tot += " "
# Stop traffic
# subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
# "--cmd", "set_cx_state all c-udp-power STOPPED"], capture_output=True, check=True)
command = ["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "set_cx_state all c-udp-power STOPPED"]
logg.info("command: {command}".format(command=command))
summary_output = ''
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.debug(line)
summary_output += line
summary.wait()
logger.info(summary_output)
antstr = ""
for x in range(4):
if (x < int(n)):
logg.info("x: %s n: %s len(ants): %s" % (x, n, len(ants)))
antstr += ants[x]
else:
antstr += " "
antstr += "\t"
logg.info("antenna dBm {antstr}".format(antstr=antstr))
port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", lfresource, "--port_name", lfstation,
"--show_port", "AP,IP,Mode,NSS,Bandwidth,Channel,Signal,Noise,Status,RX-Rate"], capture_output=True, check=True)
pss = port_stats.stdout.decode('utf-8', 'ignore')
logg.info(pss)
_ap = None
_bw = None
_ch = None
_mode = None
_nss = None
_noise = None
_rxrate = None
_noise_bare = None
for line in pss.splitlines():
m = re.search('AP:\\s+(.*)', line)
if (m is not None):
_ap = m.group(1)
logg.info("AP: {}".format(m))
m = re.search('Bandwidth:\\s+(.*)Mhz', line)
if (m is not None):
_bw = m.group(1)
logg.info("Bandwidth: {}".format(m))
m = re.search('Channel:\\s+(.*)', line)
if (m is not None):
_ch = m.group(1)
logg.info("Channel: {}".format(m))
m = re.search('Mode:\\s+(.*)', line)
if (m is not None):
_mode = m.group(1)
logg.info("Mode: {}".format(m))
m = re.search('NSS:\\s+(.*)', line)
if (m is not None):
_nss = m.group(1)
logg.info("NSS: {}".format(m))
m = re.search('Noise:\\s+(.*)', line)
if (m is not None):
_noise = m.group(1)
logg.info("Noise: {}".format(m))
m = re.search('Noise:\\s+(.*)dBm', line)
if (m is not None):
_noise_bare = m.group(1)
logg.info("Noise Bare: {}".format(m))
m = re.search('RX-Rate:\\s+(.*)', line)
if (m is not None):
_rxrate = m.group(1)
logg.info("RX-Rate: {}".format(m))
# ath10k radios now take noise-floor into account, so adjust_nf
# should remain set to false when using those radios. Possibly other
# radios would need this, so leave code in place.
rssi_adj = 0
if (args.adjust_nf and _noise_bare is not None):
_noise_i = int(_noise_bare)
if (_noise_i == 0):
# Guess we could not detect noise properly?
e_tot += "WARNING: Invalid noise-floor, calculations may be inaccurate. "
pf = 0
else:
rssi_adj = (_noise_i - nf_at_calibration)
if (sig is None):
e_tot += "ERROR: Could not detect signal level. "
sig = -100
pf = 0
if (beacon_sig is None):
e_tot += "ERROR: Could not detect beacon signal level. "
beacon_sig = -100
pf = 0
pi = int(pathloss)
ag = int(antenna_gain)
calc_dbm_beacon = int(beacon_sig) + pi + rssi_adj + ag
logg.info("calc_dbm_beacon {}".format(calc_dbm_beacon))
logg.info("sig: %s" % sig)
calc_dbm = int(sig) + pi + rssi_adj + ag
logg.info("calc_dbm %s" % (calc_dbm))
# Calculated per-antenna power is what we calculate the AP transmitted
# at (rssi + pathloss + antenna_gain ). Spatial stream rssi is -36 , with pathloss of 44 ,
# with antenna gain of 6
# -36 + 44 - 6
# then we calculate AP transmitted at +2
calc_ant1 = 0
if (ants[0] != ""):
calc_ant1 = int(ants[0]) + pi + rssi_adj + ag
logg.info("calc_ant1: {} = ants[0]: {} + pi: {} + rssi_adj: {} + ag: {}".format(calc_ant1, ants[0], pi, rssi_adj, ag))
calc_ant2 = 0
calc_ant3 = 0
calc_ant4 = 0
if (len(ants) > 1 and ants[1] != ""):
calc_ant2 = int(ants[1]) + pi + rssi_adj + ag
logg.info("calc_ant2: {} = ants[1]: {} + pi: {} + rssi_adj: {} + ag: {}".format(calc_ant2, ants[1], pi, rssi_adj, ag))
if (len(ants) > 2 and ants[2] != ""):
calc_ant3 = int(ants[2]) + pi + rssi_adj + ag
logg.info("calc_ant3: {} = ants[2]: {} + pi: {} + rssi_adj: {} + ag: {}".format(calc_ant3, ants[2], pi, rssi_adj, ag))
if (len(ants) > 3 and ants[3] != ""):
calc_ant4 = int(ants[3]) + pi + rssi_adj + ag
logg.info("calc_ant4: {} = ants[3]: {} + pi: {} + rssi_adj: {} + ag: {}".format(calc_ant4, ants[3], pi, rssi_adj, ag))
diff_a1 = ""
diff_a2 = ""
diff_a3 = ""
diff_a4 = ""
pfs = "PASS"
pfrange = pf_dbm
if (cc_dbm == ""):
cc_dbmi = 0
else:
cc_dbmi = int(cc_dbm)
diff_dbm = calc_dbm - cc_dbmi
if(int(abs(diff_dbm)) > pfrange):
w_tot = "Info: Controller dBm and Calculated dBm power different by greater than +/- {} dBm".format(
args.pf_dbm) # pass / fail dbm
logg.info("diff_dbm {} calc_dbm {} - cc_dbmi {}".format(diff_dbm, calc_dbm, cc_dbmi))
diff_dbm_beacon = calc_dbm_beacon - cc_dbmi
logg.info("diff_dbm_beacon {} calc_dbm_beacon {} - cc_dbmi {}".format(diff_dbm_beacon, calc_dbm_beacon, cc_dbmi))
if(int(abs(diff_dbm_beacon)) > int(args.beacon_dbm_diff)):
w_tot = "INFO: Controller dBm and Calculated dBm Beacon power different by greater than +/- {} dBm".format(
args.beacon_dbm_diff)
# Allowed per path is what we expect the AP should be transmitting at.
# calc_ant1 is what we calculated it actually transmitted at based on rssi
# pathloss and antenna gain. Allowed per-path is modified taking into account that multi
# NSS tranmission will mean that each chain should be decreased so that sum total
# of all chains is equal to the maximum allowed txpower.
allowed_per_path = cc_dbmi
logg.info("combined_power read from Controller: {} = cc_dbmi: {}".format(allowed_per_path, cc_dbmi))
if (int(_nss) == 1):
if args.nss_4x4_override:
allowed_per_path = cc_dbmi - 6
logg.info("allowed_per_path: {} = cc_dbmi: {} - 6 nss_4x4_override True".format(allowed_per_path, cc_dbmi))
logg.info("(Offset 1) diff_a1 (): {} = calc_ant1: {} - allowed_per_path: {} nss_4x4_override True".format(diff_a1, calc_ant1, allowed_per_path))
diff_a1 = calc_ant1 - cc_dbmi
logg.info("(Offset 1) diff_a1 (): {} = calc_ant1: {} - allowed_per_path: {}".format(diff_a1, calc_ant1, allowed_per_path))
# if args.per_ss:
if (abs(diff_a1) > pfrange):
pf = 0
if (int(_nss) == 2):
# NSS of 2 means each chain should transmit at 1/2 total power, thus the '- 3'
if args.nss_4x4_override:
allowed_per_path = cc_dbmi - 6
logg.info("allowed_per_path: {} = cc_dbmi: {} - 6 nss_4x4_override True".format(allowed_per_path, cc_dbmi))
else:
allowed_per_path = cc_dbmi - 3
logg.info("allowed_per_path: {} = cc_dbmi: {} - 3".format(allowed_per_path, cc_dbmi))
diff_a1 = calc_ant1 - allowed_per_path
logg.info("(Offset 1) diff_a1: {} = calc_ant1: {} - allowed_per_path: {}".format(diff_a1, calc_ant1, allowed_per_path))
diff_a2 = calc_ant2 - allowed_per_path
logg.info("(Offset 2) diff_a2: {} = calc_ant2: {} - allowed_per_path: {}".format(diff_a2, calc_ant2, allowed_per_path))
# if args.per_ss:
if ((abs(diff_a1) > pfrange) or (abs(diff_a2) > pfrange)):
pf = 0
if (int(_nss) == 3):
# NSS of 3 means each chain should transmit at 1/3 total power, thus the '- 5'
allowed_per_path = cc_dbmi - 5
logg.info("allowed_per_path: {} = cc_dbmi: {} - 5".format(allowed_per_path, cc_dbmi))
diff_a1 = calc_ant1 - allowed_per_path
logg.info("(Offset 1) diff_a1: {} = calc_ant1: {} - allowed_per_path: {}".format(diff_a1, calc_ant1, allowed_per_path))
diff_a2 = calc_ant2 - allowed_per_path
logg.info("(Offset 2) diff_a2: {} = calc_ant2: {} - allowed_per_path: {}".format(diff_a2, calc_ant2, allowed_per_path))
diff_a3 = calc_ant3 - allowed_per_path
logg.info("(Offset 3) diff_a3: {} = calc_ant3: {} - allowed_per_path: {}".format(diff_a3, calc_ant3, allowed_per_path))
# if args.per_ss:
if ((abs(diff_a1) > pfrange) or
(abs(diff_a2) > pfrange) or
(abs(diff_a3) > pfrange)):
pf = 0
if (int(_nss) == 4):
# NSS of 4 means each chain should transmit at 1/4 total power, thus the '- 6'
allowed_per_path = cc_dbmi - 6
logg.info("allowed_per_path: {} = cc_dbmi: {} - 6".format(allowed_per_path, cc_dbmi))
diff_a1 = calc_ant1 - allowed_per_path
logg.info("(Offset 1) diff_a1: {} = calc_ant1: {} - allowed_per_path: {}".format(diff_a1, calc_ant1, allowed_per_path))
diff_a2 = calc_ant2 - allowed_per_path
logg.info("(Offset 2) diff_a2: {} = calc_ant2: {} - allowed_per_path: {}".format(diff_a2, calc_ant2, allowed_per_path))
diff_a3 = calc_ant3 - allowed_per_path
logg.info("(Offset 3) diff_a3: {} = calc_ant3: {} - allowed_per_path: {}".format(diff_a3, calc_ant3, allowed_per_path))
diff_a4 = calc_ant4 - allowed_per_path
logg.info("(Offset 4) diff_a4: {} = calc_ant4: {} - allowed_per_path: {}".format(diff_a4, calc_ant4, allowed_per_path))
# Read AP to determine if there are less chains or spatial steams then expected
# Thus provide a passing result
failed_low = 0
# least = 0
if (abs(diff_a1) > pfrange):
failed_low += 1
# least = diff_a1 #leave in code if want to move to least
if (abs(diff_a2) > pfrange):
failed_low += 1
# least = min(least, diff_a2)
if (abs(diff_a3) > pfrange):
failed_low += 1
# least = min(least, diff_a3)
if (abs(diff_a4) > pfrange):
failed_low += 1
# least = min(least, diff_a4)
failed_low_threshold = 0
#
#
# If the ap dictionary is set the read the AP to see the number
# of spatial streams used. For tx power 1 the AP may determine to use
# fewer spatial streams
#
#
P1 = None
T1 = None
P2 = None
T2 = None
P3 = None
T3 = None
P4 = None
T4 = None
N_ANT = None
DAA_Pwr = None
DAA_N_TX = None
DAA_Total_pwr = None
if(bool(ap_dict) and args.nss_4x4_ap_adjust):
logg.info("ap_dict {}".format(ap_dict))
logg.info("Read AP ap_scheme: {} ap_ip: {} ap_port: {} ap_user: {} ap_pw: {}".format(ap_dict['ap_scheme'], ap_dict['ap_ip'], ap_dict["ap_port"],
ap_dict['ap_user'], ap_dict['ap_pw']))
logg.info("####################################################################################################")
logg.info("# READ AP POWERCFG")
logg.info("####################################################################################################")
try:
logg.info("ap_ctl.py: read AP power information")
# TODO use ap module
summary_output = ''
command = ["./ap_ctl.py", "--scheme", ap_dict['ap_scheme'], "--prompt", ap_dict['ap_prompt'], "--dest", ap_dict['ap_ip'], "--port", ap_dict["ap_port"],
"--user", ap_dict['ap_user'], "--passwd", ap_dict['ap_pw'], "--action", "powercfg"]
summary = subprocess.Popen(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(summary.stdout.readline, ''):
logger.debug(line)
if line:
summary_output += line
# sys.stdout.flush() # please see comments regarding the necessity of this line
summary.wait()
logger.info(summary_output) # .decode('utf-8', 'ignore'))
#
# ap_info = subprocess.run(["./ap_ctl.py", "--scheme", ap_dict['ap_scheme'], "--prompt", ap_dict['ap_prompt'], "--dest", ap_dict['ap_ip'], "--port", ap_dict["ap_port"],
# "--user", ap_dict['ap_user'], "--passwd", ap_dict['ap_pw'], "--action", "powercfg"], stdout=subprocess.PIPE)
# try:
# pss = ap_info.stdout.decode('utf-8', 'ignore')
# logg.info(pss)
# except BaseException:
# logg.info("ap_info was of type NoneType will set pss empty")
# pss = "empty"
# TODO print out the call stack
except subprocess.CalledProcessError as process_error:
logg.info("####################################################################################################")
logg.info("# CHECK IF AP HAS TELNET CONNECTION ALREADY ACTIVE")
logg.info("####################################################################################################")
logg.info("####################################################################################################")
logg.info(
"# Unable to commicate to AP error code: {} output {}".format(
process_error.returncode, process_error.output))
logg.info("####################################################################################################")
summary = "empty_process_error"
logg.info(summary_output)
for line in summary_output.splitlines():
logg.info("ap {}".format(line))
pat = '^\\s+1\\s+6\\s+\\S+\\s+\\S+\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)'
m = re.search(pat, line)
if (m is not None):
P1 = m.group(1)
T1 = m.group(2)
P2 = m.group(3)
T2 = m.group(4)
P3 = m.group(5)
T3 = m.group(6)
P4 = m.group(7)
T4 = m.group(8)
N_ANT = m.group(9)
DAA_Pwr = m.group(10)
DAA_N_TX = m.group(11) # number of spatial streams
DAA_Total_pwr = m.group(12)
# adjust the fail criterial based on the number of spatial streams
if DAA_N_TX == "4":
failed_low_threshold = 0
logg.info("4 failed_low_threshold {}".format(failed_low_threshold))
if DAA_N_TX == "3":
failed_low_threshold = 1
logg.info("3 failed_low_threshold {}".format(failed_low_threshold))
if DAA_N_TX == "2":
failed_low_threshold = 2
logg.info("2 failed_low_threshold {}".format(failed_low_threshold))
if DAA_N_TX == "1":
failed_low_threshold = 3
logg.info("1 failed_low_threshold {}".format(failed_low_threshold))
i_tot = "P1: {} T1: {} P2: {} T2: {} P3: {} T3: {} P4: {} T4: {} N_ANT: {} DAA_Pwr: {} DAA_N_TX: {} DAA_Total_pwr: {} ".format(
P1, T1, P2, T2, P3, T3, P4, T4, N_ANT, DAA_Pwr, DAA_N_TX, DAA_Total_pwr)
print(i_tot)
logg.info(i_tot)
else:
logg.info("AP Check using regular expressions")
#
# The controller may adjust the number of spatial streams to allow for the
# best power values
#
# for 4 spatial streams if the AP is read and the failed threshold is met then there is a failure
# the failure will be caugh below if the range is not correct.
# range check and reading the data from the AP may be used in conjunction thus it is coded to be non-exclusive
logg.info("failed_low: {} failed_low_threshold: {}".format(failed_low, failed_low_threshold))
if bool(ap_dict) and failed_low > failed_low_threshold:
logg.info("failed_low: {} > failed_low_threshold: {}".format(failed_low, failed_low_threshold))
pf = 0
# this allows for a larger offset for specific spatial streams
if(pf_ignore_offset != 0):
logg.info(
"diff_a1: {} diff_a2: {} diff_a3: {} diff_a4: {} pfrange: {} pf_ignore_offset: {}".format(
diff_a1, diff_a2, diff_a3, diff_a4, pfrange, pf_ignore_offset))
if (abs(diff_a1) > pfrange):
if(abs(diff_a1) < (pfrange + pf_ignore_offset)):
logg.info("abs(diff_a1): {} > (pfrange: {} + pf_ignore_offset: {})".format(abs(diff_a1), pfrange, pf_ignore_offset))
i_tot += "PASSED abs(diff_a1)({}) > pfrange({}) + pf_ignore_offset({}) ".format(abs(diff_a1), pfrange, pf_ignore_offset)
logg.info("i_tot {}".format(i_tot))
else:
logg.info("abs(diff_a1): {} failure".format(abs(diff_a1)))
pf = 0
if (abs(diff_a2) > pfrange):
if(abs(diff_a2) < (pfrange + pf_ignore_offset)):
logg.info("abs(diff_a2): {} > pfrange: {} + pf_ignore_offset: {}".format(abs(diff_a2), pfrange, pf_ignore_offset))
i_tot += "PASSED abs(diff_a2)({}) > pfrange({}) + pf_ignore_offset({}) ".format(abs(diff_a2), pfrange, pf_ignore_offset)
logg.info("i_tot {}".format(i_tot))
else:
logg.info("abs(diff_a2): {} failure".format(abs(diff_a2)))
pf = 0
if (abs(diff_a3) > pfrange):
if(abs(diff_a3) < (pfrange + pf_ignore_offset)):
logg.info("abs(diff_a3): {} > pfrange: {} + pf_ignore_offset: {}".format(abs(diff_a3), pfrange, pf_ignore_offset))
i_tot += "PASSED abs(diff_a3)({}) > pfrange({}) + pf_ignore_offset({}) ".format(abs(diff_a3), pfrange, pf_ignore_offset)
logg.info("i_tot {}".format(i_tot))
else:
logg.info("abs(diff_a3): {} failure".format(abs(diff_a3)))
pf = 0
if (abs(diff_a4) > pfrange):
if(abs(diff_a4) < (pfrange + pf_ignore_offset)):
logg.info("abs(diff_a4): {} > pfrange: {} + pf_ignore_offset: {}".format(abs(diff_a4), pfrange, pf_ignore_offset))
i_tot += "PASSED abs(diff_a4)({}) > pfrange({}) + pf_ignore_offset({}) ".format(abs(diff_a4), pfrange, pf_ignore_offset)
logg.info("i_tot {}".format(i_tot))
else:
logg.info("abs(diff_a4): {} failure".format(abs(diff_a4)))
pf = 0
# Did not read AP , did not have a adjusted offset
# use straight absolute value greater then the expected threshold
if failed_low_threshold == 0:
if (abs(diff_a1) > pfrange):
logg.info("abs(diff_a1): {} > (pfrange: {})".format(abs(diff_a1), pfrange))
pf = 0
if (abs(diff_a2) > pfrange):
logg.info("abs(diff_a2): {} > (pfrange: {})".format(abs(diff_a2), pfrange))
pf = 0
if (abs(diff_a3) > pfrange):
logg.info("abs(diff_a3): {} > (pfrange: {})".format(abs(diff_a3), pfrange))
pf = 0
if (abs(diff_a4) > pfrange):
logg.info("abs(diff_a4): {} > (pfrange: {})".format(abs(diff_a4), pfrange))
pf = 0
logg.info("_nss {} allowed_per_path (AP should be transmitting at) {}".format(_nss, allowed_per_path))
if (pf == 0 or e_tot != ""):
pfs = "FAIL"
run_end_time = datetime.datetime.now()
run_end_time_str = str(datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")).replace(':', '-')
logger.info("run_end_time : {run_end_time}".format(run_end_time=run_end_time_str))
run_time_delta = run_end_time - run_start_time
minutes, seconds = divmod(run_time_delta.seconds, 60)
hours, minutes = divmod(minutes, 60)
run_duration = "{day}d {hours}h {minutes}m {seconds}s {msec} ms".format(
day=run_time_delta.days, hours=hours, minutes=minutes, seconds=seconds, msec=run_time_delta.microseconds)
logger.info("Run Duration: {run_duration}".format(run_duration=run_duration))
total_run_duration += run_time_delta
minutes, seconds = divmod(total_run_duration.seconds, 60)
hours, minutes = divmod(minutes, 60)
total_run_duration_str = "{day}d {hours}h {minutes}m {seconds}s {msec} ms".format(
day=total_run_duration.days, hours=hours, minutes=minutes, seconds=seconds, msec=total_run_duration.microseconds)
logger.info("Total Run Duration: {total_run_duration}".format(total_run_duration=total_run_duration))
run_start_time = run_end_time
time_stamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "{:.3f}".format(time.time() - (math.floor(time.time())))[1:]
# This line writes the data to the CSV
ln = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (
myrd, pathloss, antenna_gain, ch, n, bw, tx, beacon_sig, sig,
antstr, _ap, _bw, _ch, _mode, _nss, _noise, _rxrate,
cc_mac, cc_ch, cc_power, cc_dbm,
calc_dbm, diff_dbm, calc_ant1, calc_ant2, calc_ant3, calc_ant4,
diff_a1, diff_a2, diff_a3, diff_a4, pfs, time_stamp, run_duration, total_run_duration_str
)
# logg.info("RESULT: %s"%(ln))
csv.write(ln)
csv.write("\t")
# TODO recorde the kpi.csv
# Controller dBm
# worksheet.write(row, col, cc_dbmi, center_blue)
results_dict = kpi_csv.kpi_csv_get_dict_update_time()
results_dict['Graph-Group'] = "Tx Power {ap} {band} {channel}".format(ap=args.ap, band=args.band, channel=cc_ch)
results_dict['pass/fail'] = pfs
# TODO kpi pass fail
# results_dict['Subtest-Pass'] = None
# results_dict['Subtest-Fail'] = None
results_dict['short-description'] = "CC dBm {ap} {band} {channel} {nss} {bw} {mode} {txpower}".format(
ap=args.ap, band=args.band, channel=_ch, nss=_nss, bw=_bw, mode=_mode, txpower=cc_power)
results_dict['numeric-score'] = "{cc_dbmi}".format(cc_dbmi=cc_dbmi)
results_dict['Units'] = "dBm"
kpi_csv.kpi_csv_write_dict(results_dict)
# Calculated beacon dBm
# worksheet.write(row, col, calc_dbm_beacon, center_blue)
results_dict = kpi_csv.kpi_csv_get_dict_update_time()
results_dict['Graph-Group'] = "Tx Power {ap} {band} {channel}".format(ap=args.ap, band=args.band, channel=cc_ch)
results_dict['pass/fail'] = pfs
# TODO kpi pass fail
# results_dict['Subtest-Pass'] = None
# results_dict['Subtest-Fail'] = None
results_dict['short-description'] = "Calc dBm Beacon {ap} {band} ch:{channel} nss:{nss} bw:{bw} {mode} tx:{txpower}".format(
ap=args.ap, band=args.band, channel=_ch, nss=_nss, bw=_bw, mode=_mode, txpower=cc_power)
results_dict['numeric-score'] = "{calc_dbm_beacon}".format(calc_dbm_beacon=calc_dbm_beacon)
results_dict['Units'] = "dBm"
kpi_csv.kpi_csv_write_dict(results_dict)
# Diff Controller dBm & Beacon dBM (+/- 7 dBm)
# worksheet.write(row, col, diff_dbm_beacon, center_blue)
results_dict = kpi_csv.kpi_csv_get_dict_update_time()
results_dict['Graph-Group'] = "Tx Power {ap} {band} {channel}".format(ap=args.ap, band=args.band, channel=cc_ch)
results_dict['pass/fail'] = pfs
# TODO kpi pass fail
# results_dict['Subtest-Pass'] = None
# results_dict['Subtest-Fail'] = None
results_dict['short-description'] = "Diff CC & Beacon dBm {ap} {band} ch:{channel} nss:{nss} bw:{bw} {mode} tx:{txpower}".format(
ap=args.ap, band=args.band, channel=_ch, nss=_nss, bw=_bw, mode=_mode, txpower=cc_power)
results_dict['numeric-score'] = "{diff_dbm_beacon}".format(diff_dbm_beacon=diff_dbm_beacon)
results_dict['Units'] = "dBm"
kpi_csv.kpi_csv_write_dict(results_dict)
# Calculated dBm Combined
# worksheet.write(row, col, calc_dbm, center_blue)
results_dict = kpi_csv.kpi_csv_get_dict_update_time()
results_dict['Graph-Group'] = "Tx Power {ap} {band} {channel}".format(ap=args.ap, band=args.band, channel=cc_ch)
results_dict['pass/fail'] = pfs
# TODO kpi pass fail
# results_dict['Subtest-Pass'] = None
# results_dict['Subtest-Fail'] = None
results_dict['short-description'] = "Calc dBm Combined {ap} {band} ch:{channel} nss:{nss} bw:{bw} {mode} tx:{txpower}".format(
ap=args.ap, band=args.band, channel=_ch, nss=_nss, bw=_bw, mode=_mode, txpower=cc_power)
results_dict['numeric-score'] = "{calc_dbm}".format(calc_dbm=calc_dbm)
results_dict['Units'] = "dBm"
kpi_csv.kpi_csv_write_dict(results_dict)
# Diff Controller dBm and Combined
# worksheet.write(row, col, diff_dbm, center_blue)
results_dict = kpi_csv.kpi_csv_get_dict_update_time()
results_dict['Graph-Group'] = "Tx Power {ap} {band} {channel}".format(ap=args.ap, band=args.band, channel=cc_ch)
results_dict['pass/fail'] = pfs
# TODO kpi pass fail
# results_dict['Subtest-Pass'] = None
# results_dict['Subtest-Fail'] = None
results_dict['short-description'] = "Diff CC dBm & Combined {ap} {band} ch:{channel} nss:{nss} bw:{bw} {mode} tx:{txpower}".format(
ap=args.ap, band=args.band, channel=_ch, nss=_nss, bw=_bw, mode=_mode, txpower=cc_power)
results_dict['numeric-score'] = "{diff_dbm}".format(diff_dbm=diff_dbm)
results_dict['Units'] = "dBm"
kpi_csv.kpi_csv_write_dict(results_dict)
# Start xlsx reporting - report as reported from ap summary
ln = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (
myrd, pathloss, antenna_gain, _ch, _nss, _bw, cc_power, cc_dbm, allowed_per_path,
antstr,
calc_ant1, calc_ant2, calc_ant3, calc_ant4,
diff_a1, diff_a2, diff_a3, diff_a4, pfs, time_stamp, run_duration, total_run_duration_str
)
csvs.write(ln)
csvs.write("\t")
col = 0
worksheet.write(row, col, myrd, center_blue)
col += 1
worksheet.write(row, col, args.series, center_blue)
col += 1
worksheet.write(row, col, cc_ch, center_blue)
col += 1
worksheet.write(row, col, _ch, center_blue)
col += 1
worksheet.write(row, col, _nss, center_blue)
col += 1
worksheet.write(row, col, cc_bw, center_blue)
col += 1
worksheet.write(row, col, _bw, center_blue)
col += 1
worksheet.write(row, col, cc_power, center_tan)
col += 1
worksheet.write(row, col, cc_dbm, center_tan)
if(bool(ap_dict)):
col += 1
worksheet.write(row, col, ap_total_power, center_tan)
col += 1
worksheet.write(row, col, allowed_per_path, center_tan)
if(bool(ap_dict)):
col += 1
worksheet.write(row, col, ap_per_path_power, center_tan)
col += 1
worksheet.write(row, col, pathloss, center_tan)
col += 1
worksheet.write(row, col, antenna_gain, center_tan)
col += 1
worksheet.write(row, col, _noise, center_tan)
col += 1
worksheet.write(row, col, rssi_adj, center_tan)
col += 1
if (args.adjust_nf):
worksheet.write(row, col, rssi_adj, center_tan)
col += 1
worksheet.write(row, col, _rxrate, center_tan)
col += 1
worksheet.write(row, col, beacon_sig, center_tan)
col += 1
worksheet.write(row, col, sig, center_tan)
col += 1
for x in range(4):
if (x < int(n)):
worksheet.write(row, col, ants[x], center_peach)
col += 1
else:
worksheet.write(row, col, " ", center_peach)
col += 1
worksheet.write(row, col, calc_ant1, center_pink)
col += 1
worksheet.write(row, col, calc_ant2, center_pink)
col += 1
worksheet.write(row, col, calc_ant3, center_pink)
col += 1
worksheet.write(row, col, calc_ant4, center_pink)
col += 1
if (diff_a1 != "" and abs(diff_a1) > pfrange):
worksheet.write(row, col, diff_a1, center_yel_red)
col += 1
else:
worksheet.write(row, col, diff_a1, center_yel)
col += 1
if (diff_a2 != "" and abs(diff_a2) > pfrange):
worksheet.write(row, col, diff_a2, center_yel_red)
col += 1
else:
worksheet.write(row, col, diff_a2, center_yel)
col += 1
if (diff_a3 != "" and abs(diff_a3) > pfrange):
worksheet.write(row, col, diff_a3, center_yel_red)
col += 1
else:
worksheet.write(row, col, diff_a3, center_yel)
col += 1
if (diff_a4 != "" and abs(diff_a4) > pfrange):
worksheet.write(row, col, diff_a4, center_yel_red)
col += 1
else:
worksheet.write(row, col, diff_a4, center_yel)
col += 1
worksheet.write(row, col, cc_dbmi, center_blue)
col += 1
worksheet.write(row, col, calc_dbm_beacon, center_blue)
col += 1
worksheet.write(row, col, diff_dbm_beacon, center_blue)
col += 1
worksheet.write(row, col, calc_dbm, center_blue)
col += 1
worksheet.write(row, col, diff_dbm, center_blue)
col += 1
if (pfs == "FAIL"):
worksheet.write(row, col, pfs, red)
col += 1
else:
worksheet.write(row, col, pfs, green)
col += 1
worksheet.write(row, col, time_stamp, green)
col += 1
worksheet.write(row, col, run_duration, green)
col += 1
worksheet.write(row, col, total_run_duration_str, green)
col += 1
if (_bw != bw):
err = "WARNING: Known Issue with AX210 Requested bandwidth: %s != station's reported bandwidth: %s. " % (bw, _bw)
e_tot += err
logg.info(err)
csv.write(err)
csvs.write(err)
if (_nss != n):
err = "ERROR: Station NSS: %s != configured: %s. " % (_nss, n)
logg.info(err)
csv.write(err)
csvs.write(err)
e_tot += err
if (e_tot == ""):
e_w_tot = e_tot + w_tot + i_tot
if(w_tot == ""):
worksheet.write(row, col, e_w_tot, green_left)
col += 1
else:
worksheet.write(row, col, e_w_tot, orange_left)
col += 1
else:
e_w_tot = e_tot + w_tot + i_tot
worksheet.write(row, col, e_w_tot, red_left)
col += 1
row += 1
csv.write("\n")
csv.flush()
csvs.write("\n")
csvs.flush()
# write out the data and exit on error : error takes presidence over failure
if (e_tot != ""):
if(args.exit_on_error):
logg.info("EXITING ON ERROR, exit_on_error err: {} ".format(e_tot))
close_workbook(workbook)
exit(1)
# write out the data and exit on failure
if (pf == 0):
if(args.exit_on_fail):
if(e_tot != ""):
logg.info("EXITING ON FAILURE as a result of err {}".format(e_tot))
else:
logg.info("EXITING ON FAILURE, exit_on_fail set there was no err ")
close_workbook(workbook)
exit(1)
workbook.close()
# check if keeping the existing state
# TODO add --no_cleanup
# Set things back to defaults
# if no_cleanup_station is False then clean up station
# TODO Have the station clean up be with
if(args.no_cleanup_station is False):
# Remove LANforge traffic connection
logg.info("Remove LANforge traffic connections")
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "set_cx_state all c-udp-power DELETED"], capture_output=False)
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "rm_endp c-udp-power-A"], capture_output=False)
subprocess.run(["./lf_firemod.pl", "--manager", lfmgr, "--resource", lfresource, "--action", "do_cmd",
"--cmd", "rm_endp c-udp-power-B"], capture_output=False)
logg.info("--no_cleanup_station set False, Deleting all stations on radio {}".format(args.radio))
subprocess.run(["./lf_associate_ap.pl", "--mgr", lfmgr, "--action", "del_all_phy", "--port_del", args.radio], timeout=20, capture_output=False)
# keep the state of the controller
if(args.keep_state):
# TODO may have to check the AP type or AP series
logg.info("9800/3504 flag --keep_state True thus leaving controller is last test configuration")
if args.band == 'dual_band_6g':
pss = cs.show_ap_dot11_dual_band_6gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_dual_band_6ghz()
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.show_ap_dot11_dual_band_5gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_dual_band_5ghz()
logg.info(pss)
elif args.band == '6g':
pss = cs.show_ap_dot11_6gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_6ghz()
logg.info(pss)
pss = cs.show_ap_dot11_5gz_summary()
logg.info(pss)
pss = cs.show_ap_dot11_24gz_summary()
logg.info(pss)
pss = cs.show_ap_summary()
logg.info(pss)
else:
logg.info("9800/3504 flag --keep_state False thus setting controller to known state ")
# TODO what is the known state
# Disable AP, retrun AP to known settings , enable AP
# TODO Choose fault settings
if args.series == "9800":
pss = cs.config_no_wlan()
logg.info(pss)
# Disable wlan networks to try to restore to original configuration
if args.band == 'dual_band_6g':
pss = cs.ap_dot11_dual_band_6ghz_shutdown()
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.ap_dot11_dual_band_5ghz_shutdown()
logg.info(pss)
else:
pss = cs.ap_dot11_6ghz_shutdown()
logg.info(pss)
pss = cs.ap_dot11_5ghz_shutdown()
logg.info(pss)
pss = cs.ap_dot11_24ghz_shutdown()
logg.info(pss)
if args.band == 'dual_band_6g':
pss = cs.config_dot11_dual_band_6ghz_tx_power()
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.config_dot11_dual_band_5ghz_tx_power()
logg.info(pss)
elif args.band == '6g':
pss = cs.config_dot11_6ghz_tx_power()
logg.info(pss)
elif args.band == '5g':
pss = cs.config_dot11_5ghz_tx_power()
logg.info(pss)
# NSS is set on the station earlier...
if (ch != "NA"):
if args.band == 'dual_band_6g':
pss = cs.config_dot11_dual_band_6ghz_channel()
elif args.band == 'dual_band_5g':
pss = cs.config_dot11_dual_band_5ghz_channel()
elif args.band == '6g':
pss = cs.config_dot11_6ghz_channel()
elif args.band == '5g':
pss = cs.config_dot11_5ghz_channel()
elif args.band == '24g':
pss = cs.config_dot11_24ghz_channel()
logg.info(pss)
if (bw != "NA"):
if args.band == 'dual_band_6g':
pss = cs.config_dot11_dual_band_6ghz_channel_width()
elif args.band == 'dual_band_5g':
pss = cs.config_dot11_dual_band_5ghz_channel_width()
elif args.band == '6g':
pss = cs.config_dot11_6ghz_channel_width()
elif args.band == '5g':
pss = cs.config_dot11_5ghz_channel_width()
logg.info(pss)
if args.series == "9800":
if args.band == 'dual_band_6g':
pss = cs.config_no_ap_dot11_dual_band_6ghz_shutdown()
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.config_no_ap_dot11_dual_band_5ghz_shutdown()
logg.info(pss)
elif args.band == '6g':
pss = cs.config_no_ap_dot11_6ghz_shutdown()
logg.info(pss)
pss = cs.config_no_ap_dot11_5ghz_shutdown()
logg.info(pss)
pss = cs.config_no_ap_dot11_24ghz_shutdown()
logg.info(pss)
if args.band == 'dual_band_6g':
pss = cs.ap_dot11_dual_band_6ghz_radio_role_auto()
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.ap_dot11_dual_band_5ghz_radio_role_auto()
logg.info(pss)
elif args.band == '6g':
pss = cs.ap_dot11_6ghz_radio_role_auto()
logg.info(pss)
pss = cs.ap_dot11_5ghz_radio_role_auto()
logg.info(pss)
else:
pss = cs.config_no_ap_dot11_5ghz_shutdown()
logg.info(pss)
pss = cs.config_no_ap_dot11_24ghz_shutdown()
logg.info(pss)
if args.band == 'dual_band_6g':
pss = cs.config_no_ap_dot11_dual_band_6ghz_shutdown() # enable_network dual_band 6ghz
logg.info(pss)
if args.band == 'dual_band_5g':
pss = cs.config_no_ap_dot11_dual_band_5ghz_shutdown() # enable_network dual_band 6ghz
logg.info(pss)
if args.band == '6g':
pss = cs.config_no_ap_dot11_6ghz_shutdown() # enable_network 6ghz
logg.info(pss)
if args.band == '5g':
pss = cs.config_no_ap_dot11_5ghz_shutdown() # enable_network 5ghz
logg.info(pss)
if args.band == '24g':
pss = cs.config_no_ap_dot11_24ghz_shutdown() # enable_network 24ghz
logg.info(pss)
# try enabling all bands
if args.enable_all_bands:
if args.band == 'dual_band_6g':
pss = cs.config_no_ap_dot11_dual_band_6ghz_shutdown() # enable_network dual_band 6ghz
logg.info(pss)
if args.band == 'dual_band_5g':
pss = cs.config_no_ap_dot11_dual_band_5ghz_shutdown() # enable_network dual_band 6ghz
logg.info(pss)
if args.band == '6g':
pss = cs.config_no_ap_dot11_6ghz_shutdown() # enable_network 6ghz
logg.info(pss)
pss = cs.config_no_ap_dot11_5ghz_shutdown() # enable_network 5ghz
logg.info(pss)
pss = cs.config_no_ap_dot11_24ghz_shutdown() # enable_network 5ghz
logg.info(pss)
# Show controller status
# Note
if args.band == 'dual_band_6g':
pss = cs.show_ap_dot11_dual_band_6gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_dual_band_6ghz()
logg.info(pss)
elif args.band == 'dual_band_5g':
pss = cs.show_ap_dot11_dual_band_5gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_dual_band_5ghz()
logg.info(pss)
elif args.band == '6g':
pss = cs.show_ap_dot11_6gz_summary()
logg.info(pss)
pss = cs.show_ap_bssid_6ghz()
logg.info(pss)
pss = cs.show_ap_dot11_5gz_summary()
logg.info(pss)
pss = cs.show_ap_dot11_24gz_summary()
logg.info(pss)
# Generate Report
report.set_title("Tx Power")
report.build_banner()
report.set_table_title("Tx Power")
# close the workbook
close_workbook(workbook)
if args.html_report:
# TODO fix csv output
report.set_table_dataframe_from_csv_sep_tab(full_outfile)
report.build_table()
# TODO the table looks off
try:
report.set_table_dataframe_from_xlsx(outfile_xlsx)
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')
# report.write_pdf_with_timestamp(_page_size='A4', _orientation='Landscape')
except BaseException:
traceback.print_exc()
# ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
if __name__ == '__main__':
main()
print("Summary results stored in %s, full results in %s, xlsx file in %s" % (outfile, full_outfile, outfile_xlsx))
####
####
####