From a6ce2896f77cb51271d126870c3c4d7a65a40dff Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Wed, 26 Jun 2024 21:17:33 +0200 Subject: [PATCH 1/3] CI: Create Iperf2 unidirectional function Add new function to run iperf v2 test. Supports testing with 1 UE, unidirectional traffic. Currently used in RFSim-4G, FeMBMS testcase. --- ci-scripts/cls_oaicitest.py | 40 +++++++++++++++++++ ci-scripts/main.py | 10 +++++ ci-scripts/xml_class_list.yml | 1 + .../xml_files/container_4g_rfsim_fembms.xml | 4 +- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index a9d3652f9d..0ff0f9e223 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -872,6 +872,46 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', messages) self.AutoTerminateUEandeNB(HTML,RAN,EPC,CONTAINERS) + def Iperf2_Unidir(self,HTML,RAN,EPC,CONTAINERS): + if self.ue_ids == [] or self.svr_id == None or len(self.ue_ids) != 1: + raise Exception("no module names in self.ue_ids or/and self.svr_id provided, multi UE scenario not supported") + ue = cls_module.Module_UE(self.ue_ids[0].strip()) + svr = cls_module.Module_UE(self.svr_id) + ueIP = ue.getIP() + if not ueIP: + return (False, f"UE {ue.getName()} has no IP address") + svrIP = svr.getIP() + if not svrIP: + return (False, f"Iperf server {ue.getName()} has no IP address") + server_filename = f'iperf_server_{self.testCase_id}_{ue.getName()}.log' + ymlPath = CONTAINERS.yamlPath[0].split('/') + logPath = f'../cmake_targets/log/{ymlPath[-1]}' + iperf_time = Iperf_ComputeTime(self.iperf_args) + target_bitrate, iperf_opt = Iperf_ComputeModifiedBW(0, 1, self.iperf_profile, self.iperf_args) + t = iperf_time*2.5 + with cls_cmd.getConnection('localhost') as local: + local.run(f'mkdir -p {logPath}') + with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(EPC.IPAddress) as cmd_svr: + cmd_ue.run(f'rm /tmp/{server_filename}', reportNonZero=False) + cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} iperf -B {ueIP} -s -u -i1 >> /tmp/{server_filename} &', timeout=t) + cmd_svr.run(f'{svr.getCmdPrefix()} timeout -vk3 {t} iperf -c {ueIP} -B {svrIP} {iperf_opt} -i1', timeout=t) + localPath = f'{os.getcwd()}' + # note: copy iperf2 log to the directory for log collection + cmd_ue.copyin(f'/tmp/{server_filename}', f'{localPath}/{logPath}/{server_filename}') + # note: copy iperf2 log to the current directory for log analysis and log collection + cmd_ue.copyin(f'/tmp/{server_filename}', f'{localPath}/{server_filename}') + cmd_ue.run(f'rm /tmp/{server_filename}', reportNonZero=False) + success, msg = Iperf_analyzeV2UDP(server_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate) + ue_header = f'UE {ue.getName()} ({ueIP})' + logging.info(f'\u001B[1;37;45m iperf result for {ue_header}\u001B[0m') + for l in msg.split('\n'): + logging.info(f'\u001B[1;35m {l} \u001B[0m') + if success: + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'OK', [f'{ue_header}\n{msg}']) + else: + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', [f'{ue_header}\n{msg}']) + self.AutoTerminateUEandeNB(HTML,RAN,EPC,CONTAINERS) + def AnalyzeLogFile_UE(self, UElogFile,HTML,RAN): if (not os.path.isfile(f'./{UElogFile}')): return -1 diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 03c99d9741..8f1235d8b4 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -281,6 +281,14 @@ def GetParametersFromXML(action): logging.error('test-case has wrong option ' + CiTestObj.iperf_options) CiTestObj.iperf_options = 'check' + elif action == 'Iperf2_Unidir': + CiTestObj.iperf_args = test.findtext('iperf_args') + CiTestObj.ue_ids = test.findtext('id').split(' ') + CiTestObj.svr_id = test.findtext('svr_id') or None + CiTestObj.iperf_packetloss_threshold = test.findtext('iperf_packetloss_threshold') + CiTestObj.iperf_bitrate_threshold = test.findtext('iperf_bitrate_threshold') or '90' + CiTestObj.iperf_profile = test.findtext('iperf_profile') or 'balanced' + elif action == 'IdleSleep': string_field = test.findtext('idle_sleep_time_in_sec') if (string_field is None): @@ -770,6 +778,8 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re CiTestObj.Ping(HTML,RAN,EPC,CONTAINERS) elif action == 'Iperf': CiTestObj.Iperf(HTML,RAN,EPC,CONTAINERS) + elif action == 'Iperf2_Unidir': + CiTestObj.Iperf2_Unidir(HTML,RAN,EPC,CONTAINERS) elif action == 'Initialize_HSS': EPC.InitializeHSS(HTML) elif action == 'Terminate_HSS': diff --git a/ci-scripts/xml_class_list.yml b/ci-scripts/xml_class_list.yml index 1b6b88147d..6a3908004d 100755 --- a/ci-scripts/xml_class_list.yml +++ b/ci-scripts/xml_class_list.yml @@ -21,6 +21,7 @@ - CheckStatusUE - Ping - Iperf + - Iperf2_Unidir - Deploy_EPC - Undeploy_EPC - Initialize_HSS diff --git a/ci-scripts/xml_files/container_4g_rfsim_fembms.xml b/ci-scripts/xml_files/container_4g_rfsim_fembms.xml index 941b59a040..bd15657c23 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fembms.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fembms.xml @@ -72,8 +72,8 @@ - Iperf - Iperf UDP DL + Iperf2_Unidir + Iperf2 UDP DL rfsim4g_ue_fembms rfsim4g_enb_fembms -u -t 30 -b 2M From b790150e5465cd567346b3be29bdf8638ed11862 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Wed, 26 Jun 2024 21:20:06 +0200 Subject: [PATCH 2/3] CI: Simplify Iperf_Module() Remove code related to "rfsim4g_enb_fembms" testcase from Iperf_Module() - replaced by Iperf2_Unidir(). Use copyin() for both remote and local commands - no need to check if running on localhost anymore. Create directory for log collection on executor node - used for log collection in pipelines running on localhost. Copy the iperf3 report to the executor machine for log collection. The report needs to be copied to two different directories to ensure proper log collection across all pipelines. Using absolute paths ensures compatibility with both remote and local commands. --- ci-scripts/cls_oaicitest.py | 53 +++++++++++++++---------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 0ff0f9e223..81bd035733 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -790,10 +790,8 @@ class OaiCiTest(): udpIperf = re.search('-u', iperf_opt) is not None bidirIperf = re.search('--bidir', iperf_opt) is not None client_filename = f'iperf_client_{self.testCase_id}_{ue.getName()}.log' - server_filename = f'iperf_server_{self.testCase_id}_{ue.getName()}.log' ymlPath = CONTAINERS.yamlPath[0].split('/') logPath = f'../cmake_targets/log/{ymlPath[1]}' - if udpIperf: target_bitrate, iperf_opt = Iperf_ComputeModifiedBW(idx, ue_num, self.iperf_profile, self.iperf_args) # note: for UDP testing we don't want to use json report - reports 0 Mbps received bitrate @@ -805,36 +803,29 @@ class OaiCiTest(): # hack: the ADB UEs don't have iperf in $PATH, so we need to hardcode for the moment iperf_ue = '/data/local/tmp/iperf3' if re.search('adb', ue.getName()) else 'iperf3' ue_header = f'UE {ue.getName()} ({ueIP})' - - if svr.getName() == "rfsim4g_enb_fembms": - with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(EPC.IPAddress) as cmd_svr: - port = 5002 + idx - cmd_ue.run(f'{ue.getCmdPrefix()} iperf -B {ueIP} -s -u -i1 >> {server_filename} &', timeout=iperf_time*1.5) - cmd_svr.run(f'{svr.getCmdPrefix()} iperf -c {ueIP} -B {svrIP} {iperf_opt} -i1 2>&1 | tee {client_filename}', timeout=iperf_time*2.5) - cmd_ue.run(f'cp {client_filename} {logPath}/{client_filename}') - cmd_ue.run(f'cp {server_filename} {logPath}/{server_filename}') - status, msg = Iperf_analyzeV2UDP(server_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate) + # create log directory on executor node + with cls_cmd.getConnection('localhost') as local: + local.run(f'mkdir -p {logPath}') + with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(EPC.IPAddress) as cmd_svr: + port = 5002 + idx + # note: some core setups start an iperf3 server automatically, indicated in ci_infra by runIperf3Server: False` + t = iperf_time * 2.5 + cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False) + if runIperf3Server: + cmd_svr.run(f'{svr.getCmdPrefix()} nohup timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} &', timeout=t) + cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> /tmp/{client_filename}', timeout=t) + localPath = f'{os.getcwd()}' + # note: copy iperf3 log to the directory for log collection, used by pipelines running on executor machine + cmd_ue.copyin(f'/tmp/{client_filename}', f'{localPath}/{logPath}/{client_filename}') + # note: copy iperf3 log to the current directory for log analysis and log collection + cmd_ue.copyin(f'/tmp/{client_filename}', f'{localPath}/{client_filename}') + cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False) + if udpIperf: + status, msg = Iperf_analyzeV3UDP(client_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate) + elif bidirIperf: + status, msg = Iperf_analyzeV3BIDIRJson(client_filename) else: - with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(EPC.IPAddress) as cmd_svr: - port = 5002 + idx - # note: some core setups start an iperf3 server automatically, indicated in ci_infra by runIperf3Server: False` - t = iperf_time * 2.5 - if runIperf3Server: - cmd_svr.run(f'{svr.getCmdPrefix()} nohup timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} &', timeout=t) - cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False) - cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> /tmp/{client_filename}', timeout=t) - if svr.getHost() == 'localhost': - cmd_ue.run(f'mkdir -p {logPath}') - cmd_ue.run(f'cp /tmp/{client_filename} {logPath}/{client_filename}') - cmd_ue.run(f'cp /tmp/{client_filename} {client_filename}') - else: - cmd_ue.copyin(f'/tmp/{client_filename}', client_filename) - if udpIperf: - status, msg = Iperf_analyzeV3UDP(client_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate) - elif bidirIperf: - status, msg = Iperf_analyzeV3BIDIRJson(client_filename) - else: - status, msg = Iperf_analyzeV3TCPJson(client_filename, self.iperf_tcp_rate_target) + status, msg = Iperf_analyzeV3TCPJson(client_filename, self.iperf_tcp_rate_target) logging.info(f'\u001B[1;37;45m iperf result for {ue_header}\u001B[0m') for l in msg.split('\n'): From c9a40c464eb113123ee4e5426c354b125d4bbb68 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Tue, 16 Jul 2024 15:09:47 +0200 Subject: [PATCH 3/3] Minor fix of Iperf_analyzeV2UDP() Don't break after first occurence of status template (interval "0.0- 1.0 sec"). With this modification we get final result, which is at the last line of log (for interval "0.0-30.2 sec"). --- ci-scripts/cls_oaicitest.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 81bd035733..b7e61c06cd 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -202,8 +202,6 @@ def Iperf_analyzeV2UDP(server_filename, iperf_bitrate_threshold, iperf_packetlos with open(server_filename, 'r') as server_file: for line in server_file.readlines(): result = re.search(statusTemplate, str(line)) - if result is not None: - break if result is None: return (False, 'Could not parse server report!') bitrate = float(result.group('bitrate'))