From 2a7331730237545548fd02ed1ee50f3194a18b3c Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 31 Jul 2023 09:41:48 +0200 Subject: [PATCH 1/9] Jenkins: don't send mail Jenkins is programmed to send e-mails, but most people don't use it: - the mail has all results in the attachments, but it is clumsy to navigate between mail reader and browser, and CSS is not correct - there is no link to the actual results - everybody gets a notification from Gitlab, since we also post it there Also, everybody who I asked told me they don't like this mail. Let's get rid of it. --- ci-scripts/Jenkinsfile-GitLab-Container | 19 ------------------- ci-scripts/doGitLabMerge.sh | 2 -- 2 files changed, 21 deletions(-) diff --git a/ci-scripts/Jenkinsfile-GitLab-Container b/ci-scripts/Jenkinsfile-GitLab-Container index 61b3774d0f..44789e12b0 100644 --- a/ci-scripts/Jenkinsfile-GitLab-Container +++ b/ci-scripts/Jenkinsfile-GitLab-Container @@ -103,7 +103,6 @@ pipeline { gitCommitAuthorEmailAddr = sh returnStdout: true, script: 'git log -n1 --pretty=format:%ae ${GIT_COMMIT}' gitCommitAuthorEmailAddr = gitCommitAuthorEmailAddr.trim() echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}" - sh "git log -n1 --pretty=format:\"%s\" > .git/CI_COMMIT_MSG" } } } @@ -504,24 +503,6 @@ pipeline { } } post { - always { - script { - def eSubject = JOB_NAME + ' - Build # ' + BUILD_ID + ' - ' + currentBuild.result + '!' - def eBody = "Hi,\n\n" - eBody += "Here are attached HTML report files for " + JOB_NAME + " - Build # " + BUILD_ID + " - " + currentBuild.result + "!\n\n" - eBody += "Regards,\n" - eBody += "OAI CI Team" - emailext attachmentsPattern: '*results*.html', - body: eBody, - replyTo: 'no-reply@openairinterface.org', - subject: eSubject, - to: gitCommitAuthorEmailAddr - - if (fileExists('.git/CI_COMMIT_MSG')) { - sh "rm -f .git/CI_COMMIT_MSG" - } - } - } success { script { def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): passed (" + BUILD_URL + ")" diff --git a/ci-scripts/doGitLabMerge.sh b/ci-scripts/doGitLabMerge.sh index c4aa9157de..c0d6121314 100755 --- a/ci-scripts/doGitLabMerge.sh +++ b/ci-scripts/doGitLabMerge.sh @@ -135,8 +135,6 @@ then exit $STATUS fi -git log -n1 --pretty=format:\"%s\" > .git/CI_COMMIT_MSG - git merge --ff $TARGET_COMMIT_ID -m "Temporary merge for CI" STATUS=`git status | egrep -c "You have unmerged paths.|fix conflicts"` From 003dd281c391be5b33315838bafa897181ab321d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 31 Jul 2023 13:19:44 +0200 Subject: [PATCH 2/9] CI: refactor Iperf(): no queues or locks, use ThreadPoolExecutor --- ci-scripts/cls_oaicitest.py | 395 +++++++----------------------------- 1 file changed, 73 insertions(+), 322 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 06ae655f37..b0fa28d76c 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -43,7 +43,7 @@ import logging import datetime import signal import statistics as stat -from multiprocessing import Process, Lock, SimpleQueue +from multiprocessing import SimpleQueue import concurrent.futures #import our libs @@ -494,15 +494,6 @@ class OaiCiTest(): messages = [f.result() for f in futures] HTML.CreateHtmlTestRowQueue('NA', 'OK', messages) - def ping_iperf_wrong_exit(self, lock, UE_IPAddress, device_id, statusQueue, message): - logging.error(f"ue {device_id} {UE_IPAddress}: {message}") - lock.acquire() - statusQueue.put(-1) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - statusQueue.put(message) - lock.release() - def Ping_common(self, EPC, ue, RAN): # Launch ping on the EPC side (true for ltebox and old open-air-cn) ping_status = 0 @@ -617,7 +608,7 @@ class OaiCiTest(): def Iperf_ComputeModifiedBW(self, idx, ue_num): result = re.search('-b (?P[0-9\.]+)[KMG]', str(self.iperf_args)) if result is None: - logging.debug('\u001B[1;37;41m Iperf bandwidth Not Found! \u001B[0m') + logging.error('\u001B[1;37;41m Iperf bandwidth Not Found! \u001B[0m') sys.exit(1) iperf_bandwidth = result.group('iperf_bandwidth') if self.iperf_profile == 'balanced': @@ -635,11 +626,11 @@ class OaiCiTest(): iperf_bandwidth_str_new = f"-b {'%.2f' % iperf_bandwidth_new}" result = re.sub(iperf_bandwidth_str, iperf_bandwidth_str_new, str(self.iperf_args)) if result is None: - logging.debug('\u001B[1;37;41m Calculate Iperf bandwidth Failed! \u001B[0m') + logging.error('\u001B[1;37;41m Calculate Iperf bandwidth Failed! \u001B[0m') sys.exit(1) return result - def Iperf_analyzeV2TCPOutput(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options,EPC,SSH, filename): + def Iperf_analyzeV2TCPOutput(self, SSH, filename): SSH.command(f'awk -f /tmp/tcp_iperf_stats.awk {filename}', '\$', 5) result = re.search('Avg Bitrate : (?P[0-9\.]+ Mbits\/sec) Max Bitrate : (?P[0-9\.]+ Mbits\/sec) Min Bitrate : (?P[0-9\.]+ Mbits\/sec)', SSH.getBefore()) @@ -647,44 +638,34 @@ class OaiCiTest(): avgbitrate = result.group('average') maxbitrate = result.group('maximum') minbitrate = result.group('minimum') - lock.acquire() - logging.debug(f'\u001B[1;37;44m TCP iperf result ({UE_IPAddress}) \u001B[0m') msg = 'TCP Stats :\n' if avgbitrate is not None: - logging.debug(f'\u001B[1;34m Avg Bitrate : {avgbitrate} \u001B[0m') msg += f'Avg Bitrate : {avgbitrate} \n' if maxbitrate is not None: - logging.debug(f'\u001B[1;34m Max Bitrate : {maxbitrate} \u001B[0m') msg += f'Max Bitrate : {maxbitrate} \n' if minbitrate is not None: - logging.debug(f'\u001B[1;34m Min Bitrate : {minbitrate} \u001B[0m') msg += f'Min Bitrate : {minbitrate} \n' - statusQueue.put(0) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - statusQueue.put(msg) - lock.release() + return (True, msg) - return 0 + return (False, "could not analyze log file") - def Iperf_analyzeV2Output(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options,EPC,SSH): + def Iperf_analyzeV2Output(self, iperf_real_options, EPC, SSH): result = re.search('-u', str(iperf_real_options)) if result is None: - logging.debug('Into Iperf_analyzeV2TCPOutput client') filename = f'{EPC.SourceCodePath}/scripts/iperf_{self.testCase_id}_{device_id}.log' - response = self.Iperf_analyzeV2TCPOutput(lock, UE_IPAddress, device_id, statusQueue, iperf_real_options, EPC, SSH, filename) - logging.debug(f'Iperf_analyzeV2TCPOutput response returned value = {response}') + response = self.Iperf_analyzeV2TCPOutput(SSH, filename) return response result = re.search('Server Report:', SSH.getBefore()) if result is None: result = re.search('read failed: Connection refused', SSH.getBefore()) if result is not None: - logging.debug('\u001B[1;37;41m Could not connect to iperf server! \u001B[0m') + msg = 'Could not connect to iperf server!' + return (False, msg) else: - logging.debug('\u001B[1;37;41m Server Report and Connection refused Not Found! \u001B[0m') - return -1 + msg = 'Server Report and Connection refused Not Found!' + return (False, msg) # Computing the requested bandwidth in float result = re.search('-b (?P[0-9\.]+)[KMG]', str(iperf_real_options)) if result is not None: @@ -708,14 +689,11 @@ class OaiCiTest(): bitrate = result.group('bitrate') packetloss = result.group('packetloss') jitter = result.group('jitter') - lock.acquire() - logging.debug(f'\u001B[1;37;44m iperf result {UE_IPAddress} \u001B[0m') + iperfStatus = True msg = f'Req Bitrate : {req_bandwidth} \n' - logging.debug(f'\u001B[1;34m Req Bitrate : {req_bandwidth} \u001B[0m') if bitrate is not None: msg += f'Bitrate : {bitrate} \n' - logging.debug(f'\u001B[1;34m Bitrate : {bitrate} \u001B[0m') result = re.search('(?P[0-9\.]+) [KMG]bits/sec', str(bitrate)) if result is not None: actual_bw = float(str(result.group('real_bw'))) @@ -731,50 +709,37 @@ class OaiCiTest(): br_loss = 100 * actual_bw / req_bw bitperf = '%.2f ' % br_loss msg += f'Bitrate Perf: {bitperf} %\n' - logging.debug(f'\u001B[1;34m Bitrate Perf: {bitperf} %\u001B[0m') if packetloss is not None: msg += f'Packet Loss : {packetloss} %\n' - logging.debug(f'\u001B[1;34m Packet Loss : {packetloss} %\u001B[0m') if float(packetloss) > float(self.iperf_packetloss_threshold): msg += 'Packet Loss too high!\n' - logging.debug('\u001B[1;37;41m Packet Loss too high \u001B[0m') iperfStatus = False if jitter is not None: msg += f'Jitter : {jitter} \n' - logging.debug(f'\u001B[1;34m Jitter : {jitter} \u001B[0m') - if (iperfStatus): - statusQueue.put(0) - else: - statusQueue.put(-1) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - statusQueue.put(msg) - lock.release() - return 0 + + return (iperfStatus, msg) else: - return -2 + return (False, "could not analyze server log") - def Iperf_analyzeV2BIDIR(self, lock, UE_IPAddress, device_id, statusQueue,server_filename,client_filename): + def Iperf_analyzeV2BIDIR(self, server_filename, client_filename): #check the 2 files are here if (not os.path.isfile(client_filename)) or (not os.path.isfile(server_filename)): - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Bidir TCP : Client or Server Log File not present') - return + return (False, 'Bidir TCP: Client or Server Log File not present') #check the 2 files size if (os.path.getsize(client_filename)==0) and (os.path.getsize(server_filename)==0): - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Bidir TCP : Client and Server Log File are empty') - return + return (False, 'Bidir TCP: Client and Server Log File are empty') - report_msg='TCP BIDIR Report:\n' + report_msg = '' #if client is not empty, all the info is in, otherwise we ll use the server file to get some partial info client_filesize = os.path.getsize(client_filename) if client_filesize == 0: report_msg+="Client file (UE) present but !!! EMPTY !!!\n" - report_msg+="Partial report from server file\n" + report_msg+="Partial report from server file" filename = server_filename else : - report_msg+="Report from client file (UE)\n" + report_msg+="Report from client file (UE)" filename = client_filename report=[] #used to check if relevant lines were found @@ -784,32 +749,19 @@ class OaiCiTest(): result = re.search(rf'^\[\s+\d+\](?P\[.+\]).*\s+(?P[0-9\.]+ [KMG]bits\/sec).*\s+(?P\bsender|receiver\b)', str(line)) if result is not None: report.append(str(line)) - report_msg+=result.group('role') + ' ' + result.group('direction')+ '\t = ' +result.group('bitrate')+'\n' - if len(report)>0: - lock.acquire() - statusQueue.put(0) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - statusQueue.put(report_msg) - logging.debug(f'\u001B[1;37;45m TCP Bidir Iperf Result ({UE_IPAddress}) \u001B[0m') - for rLine in report_msg.split('\n'): - logging.debug(f'\u001B[1;35m {rLine} \u001B[0m') - lock.release() - else: - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Bidir TCP : Could not analyze from Log file') + report_msg += f"\n{result.group('role')} {result.group('direction')}\t: {result.group('bitrate')}" + if len(report) == 0: + return (False, 'Bidir TCP: Could not analyze from Log file') + return (True, report_msg) - - def Iperf_analyzeV2Server(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options, filename,type): + def Iperf_analyzeV2Server(self, iperf_real_options, filename, type): if (not os.path.isfile(filename)): - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Could not analyze from server log') - return + return (False, 'Could not analyze from server log') # Computing the requested bandwidth in float result = re.search('-b (?P[0-9\.]+)[KMG]', str(iperf_real_options)) if result is None: - logging.debug('Iperf bandwidth Not Found!') - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Could not compute Iperf bandwidth!') - return + return (False, 'Could not compute Iperf bandwidth!') else: req_bandwidth = result.group('iperf_bandwidth') req_bw = float(req_bandwidth) @@ -856,6 +808,8 @@ class OaiCiTest(): br_sum = curr_br + br_sum ji_sum = float(ji[0]) + ji_sum + server_file.close() + if (row_idx > 0): br_sum = br_sum / row_idx ji_sum = ji_sum / row_idx @@ -876,87 +830,25 @@ class OaiCiTest(): pl = float(100 * pl_sum / ps_sum) packetloss = '%2.1f ' % (pl) packetloss += '%' - #checking packet loss compliance - if float(pl) > float(self.iperf_packetloss_threshold): - pal_too_high_msg = 'Packet Loss too high : tested = '+packetloss+', target = '+self.iperf_packetloss_threshold+'%' - else: - pal_too_high_msg='Packet Loss value is within acceptance range' - #checking bitrate perf compliance - if float(br_loss) < float(self.iperf_bitrate_threshold): - bit_too_low_msg = 'Bitrate too low : tested = '+bitperf+', target = '+self.iperf_bitrate_threshold+'%' - else: - bit_too_low_msg='Bitrate perf value is within acceptance range' - lock.acquire() - if (float(br_loss) < float(self.iperf_bitrate_threshold)) and (float(pl) > float(self.iperf_packetloss_threshold)): - statusQueue.put(-1) - elif (float(br_loss) < float(self.iperf_bitrate_threshold)) or (float(pl) > float(self.iperf_packetloss_threshold)): - statusQueue.put(1) - else: - statusQueue.put(0) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) + + result = float(br_loss) >= float(self.iperf_bitrate_threshold) and float(pl) <= float(self.iperf_packetloss_threshold) req_msg = f'Req Bitrate : {req_bandwidth}' bir_msg = f'Bitrate : {bitrate}' brl_msg = f'Bitrate Perf: {bitperf}' + if float(br_loss) < float(self.iperf_bitrate_threshold): + brl_msg += f' (too low! <{self.iperf_bitrate_threshold}%)' jit_msg = f'Jitter : {jitter}' pal_msg = f'Packet Loss : {packetloss}' - statusQueue.put(f'{req_msg}\n{bir_msg}\n{brl_msg}\n{jit_msg}\n{pal_msg}\n{pal_too_high_msg}\n{bit_too_low_msg}\n') - logging.debug(f'\u001B[1;37;45m iperf result ({UE_IPAddress}) \u001B[0m') - logging.debug(f'\u001B[1;35m {req_msg} \u001B[0m') - logging.debug(f'\u001B[1;35m {bir_msg} \u001B[0m') - logging.debug(f'\u001B[1;35m {brl_msg} \u001B[0m') - logging.debug(f'\u001B[1;35m {jit_msg} \u001B[0m') - logging.debug(f'\u001B[1;35m {pal_msg} \u001B[0m') - logging.debug(f'\u001B[1;35m {pal_too_high_msg} \u001B[0m') - logging.debug(f'\u001B[1;35m {bit_too_low_msg} \u001B[0m') - lock.release() + if float(pl) > float(self.iperf_packetloss_threshold): + pal_msg += f' (too high! >{self.iperf_packetloss_threshold}%)' + return (result, f'{req_msg}\n{bir_msg}\n{brl_msg}\n{jit_msg}\n{pal_msg}') else: - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Could not analyze from server log') + return (False, 'Could not analyze from server log') - server_file.close() - - - def Iperf_analyzeV3Output(self, lock, UE_IPAddress, device_id, statusQueue,SSH): - - result = re.search('(?P[0-9\.]+ [KMG]bits\/sec) +(?:|[0-9\.]+ ms +\d+\/\d+ \((?P[0-9\.]+)%\)) +(?:|receiver)\r\n(?:|\[ *\d+\] Sent \d+ datagrams)\r\niperf Done\.', SSH.getBefore()) - if result is None: - result = re.search('(?Piperf: error - [a-zA-Z0-9 :]+)', SSH.getBefore()) - lock.acquire() - statusQueue.put(-1) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - if result is not None: - logging.debug(f"\u001B[1;37;41m {result.group('error')} \u001B[0m") - statusQueue.put(result.group('error')) - else: - logging.debug('\u001B[1;37;41m Bitrate and/or Packet Loss Not Found! \u001B[0m') - statusQueue.put('Bitrate and/or Packet Loss Not Found!') - lock.release() - - bitrate = result.group('bitrate') - packetloss = result.group('packetloss') - lock.acquire() - logging.debug(f'\u001B[1;37;44m iperf result ({UE_IPAddress}) \u001B[0m') - logging.debug(f'\u001B[1;34m Bitrate : {bitrate} \u001B[0m') - msg = f'Bitrate : {bitrate} \n' - iperfStatus = True - if packetloss is not None: - logging.debug(f'\u001B[1;34m Packet Loss : {packetloss} %\u001B[0m') - msg += f'Packet Loss : {packetloss} %\n' - if float(packetloss) > float(self.iperf_packetloss_threshold): - logging.debug('\u001B[1;37;41m Packet Loss too high \u001B[0m') - msg += 'Packet Loss too high!\n' - iperfStatus = False - if (iperfStatus): - statusQueue.put(0) - else: - statusQueue.put(-1) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - statusQueue.put(msg) - lock.release() - - def Iperf_Module(self, lock, statusQueue, EPC, ue, RAN, idx, ue_num): + def Iperf_Module(self, EPC, ue, RAN, idx, ue_num): + ueIP = ue.getIP() + if not ueIP: + return (False, f"UE {ue.getName()} has no IP address") SSH = sshconnection.SSHConnection() server_filename = f'iperf_server_{self.testCase_id}_{ue.getName()}.log' client_filename = f'iperf_client_{self.testCase_id}_{ue.getName()}.log' @@ -987,16 +879,18 @@ 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/iperf' if re.search('adb', ue.getName()) else 'iperf' + ue_header = f'UE {ue.getName()} ({ueIP})' + if self.iperf_direction == "DL": logging.debug("Iperf in DL requested") cmd = cls_cmd.getConnection(ue.getHost()) cmd.run(f'rm {server_filename}') - cmd.run(f'{ue.getCmdPrefix()} {iperf_ue} -s -B {ue.getIP()} {udpSwitch} -i 1 -t {iperf_time * 1.5} {port} &> /tmp/{server_filename} &') + cmd.run(f'{ue.getCmdPrefix()} {iperf_ue} -s -B {ueIP} {udpSwitch} -i 1 -t {iperf_time * 1.5} {port} &> /tmp/{server_filename} &') cmd.close() cmd = cls_cmd.getConnection(EPC.IPAddress) cmd.run(f'rm {EPC.SourceCodePath}/{client_filename}') - cmd.run(f'{cn_iperf_prefix} iperf -c {ue.getIP()} {iperf_opt} {port} &> {EPC.SourceCodePath}/{client_filename}', timeout=iperf_time * 1.5) + cmd.run(f'{cn_iperf_prefix} iperf -c {ueIP} {iperf_opt} {port} &> {EPC.SourceCodePath}/{client_filename}', timeout=iperf_time * 1.5) cmd.copyin(f'{EPC.SourceCodePath}/{client_filename}', client_filename) cmd.close() @@ -1005,10 +899,10 @@ class OaiCiTest(): cmd.close() if udpIperf: - self.Iperf_analyzeV2Server(lock, ue.getIP(), ue.getName(), statusQueue, iperf_opt, server_filename, 1) + status, msg = self.Iperf_analyzeV2Server(iperf_opt, server_filename, 1) else: cmd = cls_cmd.getConnection(EPC.IPAddress) - self.Iperf_analyzeV2TCPOutput(lock, ue.getIP(), ue.getName(), statusQueue, iperf_opt, EPC, cmd, f"{EPC.SourceCodePath}/{client_filename}") + status, msg = self.Iperf_analyzeV2TCPOutput(cmd, f"{EPC.SourceCodePath}/{client_filename}") cmd.close() elif self.iperf_direction == "UL": @@ -1020,7 +914,7 @@ class OaiCiTest(): cmd = cls_cmd.getConnection(ue.getHost()) cmd.run(f'rm /tmp/{client_filename}') - cmd.run(f'{ue.getCmdPrefix()} {iperf_ue} -B {ue.getIP()} -c {cn_target_ip} {iperf_opt} {port} &> /tmp/{client_filename}', timeout=iperf_time*1.5) + cmd.run(f'{ue.getCmdPrefix()} {iperf_ue} -B {ueIP} -c {cn_target_ip} {iperf_opt} {port} &> /tmp/{client_filename}', timeout=iperf_time*1.5) cmd.copyin(f'/tmp/{client_filename}', client_filename) cmd.close() @@ -1029,10 +923,10 @@ class OaiCiTest(): cmd.close() if udpIperf: - self.Iperf_analyzeV2Server(lock, ue.getIP(), ue.getName(), statusQueue, iperf_opt, server_filename, 1) + status, msg = self.Iperf_analyzeV2Server(iperf_opt, server_filename, 1) else: cmd = cls_cmd.getConnection(ue.getHost()) - self.Iperf_analyzeV2TCPOutput(lock, ue.getIP(), ue.getName(), statusQueue, iperf_opt, EPC, cmd, f"/tmp/{client_filename}") + status, msg = self.Iperf_analyzeV2TCPOutput(cmd, f"/tmp/{client_filename}") cmd.close() elif self.iperf_direction=="BIDIR": @@ -1044,7 +938,7 @@ class OaiCiTest(): cmd = cls_cmd.getConnection(ue.getHost()) cmd.run(f'rm /tmp/{client_filename}') - cmd.run(f'iperf3 -B {ue.getIP()} -c {cn_target_ip} {iperf_opt} {port} &> /tmp/{client_filename}', timeout=iperf_time*1.5) + cmd.run(f'iperf3 -B {ueIP} -c {cn_target_ip} {iperf_opt} {port} &> /tmp/{client_filename}', timeout=iperf_time*1.5) cmd.copyin(f'/tmp/{client_filename}', client_filename) cmd.close() @@ -1052,129 +946,32 @@ class OaiCiTest(): cmd.copyin(f'{EPC.SourceCodePath}/{server_filename}', server_filename) cmd.close() - self.Iperf_analyzeV2BIDIR(lock, ue.getHost(), ue.getName(), statusQueue, server_filename, client_filename) + status, msg = self.Iperf_analyzeV2BIDIR(server_filename, client_filename) elif self.iperf_direction == "IPERF3": cmd = cls_cmd.getConnection(ue.getHost()) cmd.run(f'rm /tmp/{server_filename}', reportNonZero=False) port = f'{5002+idx}' - cmd.run(f'{ue.getCmdPrefix()} iperf3 -B {ue.getIP()} -c {cn_target_ip} -p {port} {iperf_opt} --get-server-output &> /tmp/{server_filename}', timeout=iperf_time*1.5) + cmd.run(f'{ue.getCmdPrefix()} iperf3 -B {ueIP} -c {cn_target_ip} -p {port} {iperf_opt} --get-server-output &> /tmp/{server_filename}', timeout=iperf_time*1.5) cmd.copyin(f'/tmp/{server_filename}', server_filename) cmd.close() if udpIperf: - self.Iperf_analyzeV2Server(lock, ue.getIP(), ue.getName(), statusQueue, iperf_opt, server_filename, 1) + status, msg = self.Iperf_analyzeV2Server(iperf_opt, server_filename, 1) else: cmd = cls_cmd.getConnection(EPC.IPAddress) - self.Iperf_analyzeV2TCPOutput(lock, ue.getIP(), ue.getName(), statusQueue, iperf_opt, EPC, cmd, f'/tmp/{server_filename}') + status, msg = self.Iperf_analyzeV2TCPOutput(cmd, f'/tmp/{server_filename}') cmd.close() else : raise Exception("Incorrect or missing IPERF direction in XML") + 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') + return (status, f'{ue_header}\n{msg}') + def IperfNoS1(self,HTML,RAN,EPC,CONTAINERS): - SSH = sshconnection.SSHConnection() - if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or self.UEIPAddress == '' or self.UEUserName == '' or self.UEPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - check_eNB = True - check_OAI_UE = True - server_on_enb = re.search('-R', str(self.iperf_args)) - if server_on_enb is not None: - iServerIPAddr = RAN.eNBIPAddress - iServerUser = RAN.eNBUserName - iServerPasswd = RAN.eNBPassword - iClientIPAddr = self.UEIPAddress - iClientUser = self.UEUserName - iClientPasswd = self.UEPassword - else: - iServerIPAddr = self.UEIPAddress - iServerUser = self.UEUserName - iServerPasswd = self.UEPassword - iClientIPAddr = RAN.eNBIPAddress - iClientUser = RAN.eNBUserName - iClientPasswd = RAN.eNBPassword - if self.iperf_options != 'sink': - # Starting the iperf server - SSH.open(iServerIPAddr, iServerUser, iServerPasswd) - # args SHALL be "-c client -u any" - # -c 10.0.1.2 -u -b 1M -t 30 -i 1 -fm -B 10.0.1.1 - # -B 10.0.1.1 -u -s -i 1 -fm - server_options = re.sub('-u.*$', '-u -s -i 1 -fm', str(self.iperf_args)) - server_options = server_options.replace('-c','-B') - SSH.command(f'rm -f /tmp/tmp_iperf_server_{self.testCase_id}.log', '\$', 5) - SSH.command(f'echo $USER; nohup iperf {server_options} > /tmp/tmp_iperf_server_{self.testCase_id}.log 2>&1 &', iServerUser, 5) - time.sleep(0.5) - SSH.close() - - # Starting the iperf client - modified_options = self.Iperf_ComputeModifiedBW(0, 1) - modified_options = modified_options.replace('-R','') - iperf_time = self.Iperf_ComputeTime() - SSH.open(iClientIPAddr, iClientUser, iClientPasswd) - SSH.command(f'rm -f /tmp/tmp_iperf_{self.testCase_id}.log', '\$', 5) - iperf_status = SSH.command(f'stdbuf -o0 iperf {modified_options} 2>&1 | stdbuf -o0 tee /tmp/tmp_iperf_{self.testCase_id}.log', '\$', int(iperf_time)*5.0) - status_queue = SimpleQueue() - lock = Lock() - if iperf_status < 0: - message = 'iperf on OAI UE crashed due to TIMEOUT !' - logging.debug(f'\u001B[1;37;41m {message} \u001B[0m') - clientStatus = -2 - else: - if self.iperf_options == 'sink': - clientStatus = 0 - status_queue.put(0) - status_queue.put('OAI-UE') - status_queue.put('10.0.1.2') - status_queue.put('Sink Test : no check') - else: - clientStatus = self.Iperf_analyzeV2Output(lock, '10.0.1.2', 'OAI-UE', status_queue, modified_options, EPC,SSH) - SSH.close() - - # Stopping the iperf server - if self.iperf_options != 'sink': - SSH.open(iServerIPAddr, iServerUser, iServerPasswd) - SSH.command('killall --signal SIGKILL iperf', '\$', 5) - time.sleep(0.5) - SSH.close() - - if (clientStatus == -1): - if (os.path.isfile(f'iperf_server_{self.testCase_id}.log')): - os.remove(f'iperf_server_{self.testCase_id}.log') - SSH.copyin(iServerIPAddr, iServerUser, iServerPasswd, f'/tmp/tmp_iperf_server_{self.testCase_id}.log', f'iperf_server_{self.testCase_id}_OAI-UE.log') - filename=f'iperf_server_{self.testCase_id}_OAI-UE.log' - self.Iperf_analyzeV2Server(lock, '10.0.1.2', 'OAI-UE', status_queue, modified_options,filename,0) - - # copying on the EPC server for logCollection - if (clientStatus == -1): - copyin_res = SSH.copyin(iServerIPAddr, iServerUser, iServerPasswd, f'/tmp/tmp_iperf_server_{self.testCase_id}.log', f'iperf_server_{self.testCase_id}_OAI-UE.log') - if (copyin_res == 0): - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, f'iperf_server_{self.testCase_id}_OAI-UE.log', f'{EPC.SourceCodePath}/scripts') - copyin_res = SSH.copyin(iClientIPAddr, iClientUser, iClientPasswd, f'/tmp/tmp_iperf_{self.testCase_id}.log', f'iperf_{self.testCase_id}_OAI-UE.log') - if (copyin_res == 0): - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, f'iperf_{self.testCase_id}_OAI-UE.log', f'{EPC.SourceCodePath}/scripts') - iperf_noperf = False - if status_queue.empty(): - iperf_status = False - else: - iperf_status = True - messages = [] - while (not status_queue.empty()): - count = status_queue.get() - if (count < 0): - iperf_status = False - if (count > 0): - iperf_noperf = True - device_id = status_queue.get() - ip_addr = status_queue.get() - message = status_queue.get() - messages.append(f'UE ({device_id})\nIP Address : {ip_addr}\n{message}') - if (iperf_noperf and iperf_status): - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'PERF NOT MET', messages) - elif (iperf_status): - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'OK', messages) - else: - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', messages) - self.AutoTerminateUEandeNB(HTML,RAN,EPC,CONTAINERS) + raise 'IperfNoS1 not implemented' def Iperf(self,HTML,RAN,EPC,CONTAINERS): result = re.search('noS1', str(RAN.Initialize_eNB_args)) @@ -1187,64 +984,18 @@ class OaiCiTest(): logging.debug(f'Iperf: iperf_args "{self.iperf_args}" iperf_direction "{self.iperf_direction}" iperf_packetloss_threshold "{self.iperf_packetloss_threshold}" iperf_bitrate_threshold "{self.iperf_bitrate_threshold}" iperf_profile "{self.iperf_profile}" iperf_options "{self.iperf_options}"') - ues = [] - for ue_name in self.ue_ids: - ue = cls_module_ue.Module_UE(ue_name) - if not ue.getIP(): - logging.error("no IP addresses returned") - HTML.CreateHtmlTestRow(self.ping_args, 'KO', CONST.UE_IP_ADDRESS_ISSUE) - self.AutoTerminateUEandeNB(HTML,RAN,EPC,CONTAINERS) - ues.append(ue) + ues = [cls_module_ue.Module_UE(n.strip()) for n in self.ue_ids] logging.debug(ues) - - self.dummyIperfVersion = '2.0.10' - #cmd = 'iperf --version' - #logging.debug(cmd + '\n') - #iperfStdout = subprocess.check_output(cmd, shell=True, universal_newlines=True) - #result = re.search('iperf version 2.0.5', str(iperfStdout.strip())) - #if result is not None: - # dummyIperfVersion = '2.0.5' - #result = re.search('iperf version 2.0.10', str(iperfStdout.strip())) - #if result is not None: - # dummyIperfVersion = '2.0.10' - multi_jobs = [] - ue_num = len(ues) - i = 0 - lock = Lock() - status_queue = SimpleQueue() - for ue in ues: - p = Process(target = self.Iperf_Module ,args = (lock, status_queue, EPC, ue, RAN, i, ue_num)) - p.daemon = True - p.start() - multi_jobs.append(p) - i = i + 1 - for job in multi_jobs: - job.join() - - if (status_queue.empty()): - HTML.CreateHtmlTestRow(self.iperf_args, 'KO', CONST.ALL_PROCESSES_OK) - self.AutoTerminateUEandeNB(HTML,RAN,EPC,CONTAINERS) + with concurrent.futures.ThreadPoolExecutor() as executor: + futures = [executor.submit(self.Iperf_Module, EPC, ue, RAN, i, len(ues)) for i, ue in enumerate(ues)] + results = [f.result() for f in futures] + # each result in results is a tuple, first member goes to successes, second to messages + successes, messages = map(list, zip(*results)) + if len(successes) == len(ues) and all(successes): + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'OK', messages) else: - iperf_status = True - iperf_noperf = False - messages = [] - while (not status_queue.empty()): - count = status_queue.get() - if (count < 0): - iperf_status = False - if (count > 0): - iperf_noperf = True - device_id = status_queue.get() - ip_addr = status_queue.get() - msg = status_queue.get() - messages.append(f'UE ({device_id})\nIP Address : {ip_addr}\n{msg}') - if (iperf_noperf and iperf_status): - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'PERF NOT MET', messages) - elif (iperf_status): - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'OK', messages) - else: - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', messages) - self.AutoTerminateUEandeNB(HTML,RAN,EPC,CONTAINERS) + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', messages) + self.AutoTerminateUEandeNB(HTML,RAN,EPC,CONTAINERS) def AnalyzeLogFile_UE(self, UElogFile,HTML,RAN): if (not os.path.isfile(f'./{UElogFile}')): From 5d66ae56d0082c282674e5106fc2b8e66cc7b2a3 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Aug 2023 13:30:32 +0200 Subject: [PATCH 3/9] Specify realistic LTE iperf bitrate thresholds A side effect of the previous commit's refactoring is that we mark the pipeline as failed if a (packet loss/bitrate) threshold is violated. Previously, the HTML would contain a "Perf not met" hint, but be marked as successful. This commit introduces more realistic thresholds for various pipeline runs. The values are arbitary, following the performances in pipelines that were marked as successful, but actually had performance problems. They allow, though, a pipeline to pass following the normal performance we saw the last weeks. --- ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1.xml | 2 ++ .../xml_files/container_lte_b200_fdd_05Mhz_tm1_if4_5.xml | 3 +++ ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1.xml | 1 + ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1_cdrx.xml | 1 + ci-scripts/xml_files/container_lte_b200_fdd_20Mhz_tm1.xml | 1 + ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1.xml | 1 + .../xml_files/container_lte_b200_tdd_05Mhz_tm1_if4_5.xml | 1 + ci-scripts/xml_files/container_lte_b200_tdd_10Mhz_tm1.xml | 1 + .../container_lte_b200_tdd_20Mhz_tm1_default_scheduler.xml | 3 +++ 9 files changed, 14 insertions(+) diff --git a/ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1.xml b/ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1.xml index 7d43346bd6..3c4a0d006e 100644 --- a/ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1.xml +++ b/ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1.xml @@ -120,6 +120,7 @@ Replaces xml_files/enb_usrp210_band7_test_05mhz_tm1.xml iperf (5MHz - DL/15Mbps/UDP)(30 sec)(balanced profile) -u -b 15M -t 30 -i 1 50 + 50 DL adb_ue_1 adb_ue_2 @@ -178,6 +179,7 @@ Replaces xml_files/enb_usrp210_band7_test_05mhz_tm1.xml iperf (5MHz - UL/9Mbps/UDP)(30 sec)(single-ue profile) -u -b 9M -t 30 -i 1 50 + 50 single-ue UL adb_ue_1 diff --git a/ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1_if4_5.xml b/ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1_if4_5.xml index df544ab529..0f4b6535ef 100644 --- a/ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1_if4_5.xml +++ b/ci-scripts/xml_files/container_lte_b200_fdd_05Mhz_tm1_if4_5.xml @@ -128,6 +128,7 @@ iperf (5MHz - DL/15Mbps/UDP)(30 sec)(balanced profile) -u -b 15M -t 30 -i 1 50 + 50 DL adb_ue_1 adb_ue_2 @@ -156,6 +157,7 @@ iperf (5MHz - UL/9Mbps/UDP)(30 sec)(balanced profile) -u -b 9M -t 30 -i 1 50 + 50 balanced UL adb_ue_1 adb_ue_2 @@ -166,6 +168,7 @@ iperf (5MHz - UL/9Mbps/UDP)(30 sec)(single-ue profile) -u -b 9M -t 30 -i 1 50 + 50 single-ue UL adb_ue_1 diff --git a/ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1.xml b/ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1.xml index f8e7d5ab12..6fd083a2e8 100644 --- a/ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1.xml +++ b/ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1.xml @@ -114,6 +114,7 @@ Replaces xml_files/enb_usrp210_band7_test_10mhz_tm1.xml iperf (10MHz - DL/30Mbps/UDP)(30 sec)(balanced profile) -u -b 30M -t 30 -i 1 50 + 50 balanced DL adb_ue_1 adb_ue_2 diff --git a/ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1_cdrx.xml b/ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1_cdrx.xml index 66ee0ffb42..01be9676be 100644 --- a/ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1_cdrx.xml +++ b/ci-scripts/xml_files/container_lte_b200_fdd_10Mhz_tm1_cdrx.xml @@ -107,6 +107,7 @@ Replaces xml_files/enb_usrp210_band7_test_10mhz_tm1.xml iperf (10MHz - DL/30Mbps/UDP)(30 sec)(balanced profile) -u -b 30M -t 30 -i 1 50 + 50 balanced DL adb_ue_1 adb_ue_2 diff --git a/ci-scripts/xml_files/container_lte_b200_fdd_20Mhz_tm1.xml b/ci-scripts/xml_files/container_lte_b200_fdd_20Mhz_tm1.xml index 1c6cf4b647..8bb2dfafc9 100644 --- a/ci-scripts/xml_files/container_lte_b200_fdd_20Mhz_tm1.xml +++ b/ci-scripts/xml_files/container_lte_b200_fdd_20Mhz_tm1.xml @@ -113,6 +113,7 @@ Replaces xml_files/enb_usrp210_band7_test_10mhz_tm1.xml iperf (20MHz - DL/70Mbps/UDP)(30 sec)(balanced profile) -u -b 70M -t 30 -i 1 50 + 50 balanced DL adb_ue_1 adb_ue_2 diff --git a/ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1.xml b/ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1.xml index f926d1a056..d181fd1a0d 100644 --- a/ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1.xml +++ b/ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1.xml @@ -121,6 +121,7 @@ Replaces xml_files/enb_usrp210_band40_test_05mhz_tm1.xml iperf (5MHz - DL/6.5Mbps/UDP)(30 sec)(balanced) -u -b 6.5M -t 30 -i 1 50 + 50 balanced DL adb_ue_1 adb_ue_2 diff --git a/ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1_if4_5.xml b/ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1_if4_5.xml index b71cc555e6..1c87cefe82 100644 --- a/ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1_if4_5.xml +++ b/ci-scripts/xml_files/container_lte_b200_tdd_05Mhz_tm1_if4_5.xml @@ -129,6 +129,7 @@ iperf (5MHz - DL/6.5Mbps/UDP)(30 sec)(balanced) -u -b 6.5M -t 30 -i 1 50 + 50 balanced DL adb_ue_1 adb_ue_2 diff --git a/ci-scripts/xml_files/container_lte_b200_tdd_10Mhz_tm1.xml b/ci-scripts/xml_files/container_lte_b200_tdd_10Mhz_tm1.xml index 93e51242fc..ef9239fe86 100644 --- a/ci-scripts/xml_files/container_lte_b200_tdd_10Mhz_tm1.xml +++ b/ci-scripts/xml_files/container_lte_b200_tdd_10Mhz_tm1.xml @@ -111,6 +111,7 @@ Replaces xml_files/enb_usrp210_band40_test_10mhz_tm1.xml iperf (10MHz - DL/13.5Mbps/UDP)(30 sec)(balanced) -u -b 13.5M -t 30 -i 1 50 + 50 balanced DL adb_ue_1 adb_ue_2 diff --git a/ci-scripts/xml_files/container_lte_b200_tdd_20Mhz_tm1_default_scheduler.xml b/ci-scripts/xml_files/container_lte_b200_tdd_20Mhz_tm1_default_scheduler.xml index d229be48b5..0fae91af5f 100644 --- a/ci-scripts/xml_files/container_lte_b200_tdd_20Mhz_tm1_default_scheduler.xml +++ b/ci-scripts/xml_files/container_lte_b200_tdd_20Mhz_tm1_default_scheduler.xml @@ -113,6 +113,7 @@ Replaces xml_files/enb_usrp210_band40_test_20mhz_tm1_default_scheduler.xml iperf (20MHz - DL/27.5Mbps/UDP)(30 sec)(balanced) -u -b 27.5M -t 30 -i 1 50 + 50 balanced DL adb_ue_1 adb_ue_2 @@ -123,6 +124,7 @@ Replaces xml_files/enb_usrp210_band40_test_20mhz_tm1_default_scheduler.xml iperf (20MHz - DL/27.5Mbps/UDP)(30 sec)(single-ue) -u -b 27.5M -t 30 -i 1 50 + 50 single-ue DL adb_ue_1 @@ -133,6 +135,7 @@ Replaces xml_files/enb_usrp210_band40_test_20mhz_tm1_default_scheduler.xml iperf (20MHz - DL/27.5Mbps/UDP)(30 sec)(unbalanced) -u -b 27.5M -t 30 -i 1 50 + 50 unbalanced DL adb_ue_1 adb_ue_2 From 5ddb9f683f6172e0afb31b88a84e22509aaed050 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 31 Jul 2023 13:30:08 +0200 Subject: [PATCH 4/9] CI: cell content's
 style to stack horizontally

The results of individual UEs (e.g., ping) is listed vertically, like
so:

| ping results | UE1 |
|              | UE2 |
|              | UE3 |

Where UE1, UE2, ... represents an entire box with results for UE 1, 2,
... For many UEs, this results in considerable need for vertical space. This
commit changes to something like the following to save space:

| ping results | UE1 UE2 UE3 |

For a single UE, this commit has no major impact (the boxes are not
stretched to width anymore).
---
 ci-scripts/cls_oai_html.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ci-scripts/cls_oai_html.py b/ci-scripts/cls_oai_html.py
index 48a88a8590..81b01cd530 100644
--- a/ci-scripts/cls_oai_html.py
+++ b/ci-scripts/cls_oai_html.py
@@ -469,8 +469,8 @@ class HTMLManagement():
 				self.htmlFile.write('        ')
 			else:
 				self.htmlFile.write('        ')
-			for i in infoList:
-				self.htmlFile.write(f'         
{i}
') + for i in infoList: # add custom style to have elements side-by-side to reduce need for vertical space + self.htmlFile.write(f'
{i}
') self.htmlFile.write(' ') self.htmlFile.write(' \n') From 728649d61486b9c4e82befdb2ceaa5167831ed64 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 31 Jul 2023 14:59:25 +0200 Subject: [PATCH 5/9] Add iperf throughput test to Quectel F1 test - throughput test in DL - UL throughput: 30s is enough - increase bidir test time for more reliable measurement - increase retx thresholds: 1% for first round is too low --- .../container_sa_f1_b200_quectel.xml | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ci-scripts/xml_files/container_sa_f1_b200_quectel.xml b/ci-scripts/xml_files/container_sa_f1_b200_quectel.xml index 532d9eaef4..a20ea8845e 100644 --- a/ci-scripts/xml_files/container_sa_f1_b200_quectel.xml +++ b/ci-scripts/xml_files/container_sa_f1_b200_quectel.xml @@ -42,19 +42,15 @@ 110011 110001 150000 + 170001 + 170000 + 170002 110011 110002 100001 130201 200000 - @@ -159,19 +155,19 @@ Iperf - iperf (DL/125Mbps/UDP)(60 sec)(single-ue profile) - -u -b 125M -t 60 -i 1 -fm + iperf (DL/80Mbps/UDP)(30 sec) + -u -b 80M -t 30 -i 1 -fm DL idefix - 25 - 80 + 10 + 95 single-ue Iperf - iperf (UL/8Mbps/UDP)(60 sec)(single-ue profile) - -u -b 8M -t 60 -i 1 -fm + iperf (UL/8Mbps/UDP)(30 sec) + -u -b 8M -t 30 -i 1 -fm UL idefix 1 @@ -181,8 +177,8 @@ Iperf - iperf (BIDIR TCP)(10 sec)(single-ue profile) - -t 10 --bidir + iperf (BIDIR TCP)(20 sec)(single-ue profile) + -t 20 --bidir BIDIR idefix single-ue @@ -194,8 +190,8 @@ ci-scripts/yaml_files/sa_f1_b200_gnb 0 0 - 1,100,100,100 - 1,100,100,100 + 10,100,100,100 + 10,100,100,100 From 0c9c071e121e26263bf4edd1ec63f2aa009e1723 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 2 Aug 2023 10:21:00 +0200 Subject: [PATCH 6/9] Quectel F1 test: no radio required for CU --- ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml index d5396e4933..e40dc686d7 100644 --- a/ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml @@ -6,11 +6,9 @@ services: privileged: true container_name: sa-cu-gnb environment: - USE_B2XX: 'yes' USE_ADDITIONAL_OPTIONS: --sa --log_config.global_log_options level,nocolor,time,line_num,function volumes: - ../../conf_files/gnb-cu.sa.band78.106prb.usrpb200.conf:/opt/oai-gnb/etc/gnb.conf - - /dev:/dev networks: public_net: ipv4_address: 192.168.68.194 From 86e596b032191364d1b5e0f8a1b2a620da09becb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 2 Aug 2023 10:35:36 +0200 Subject: [PATCH 7/9] Reduce useless log (config) in Quectel F1 DU config --- .../gnb-du.sa.band78.106prb.usrpb200.conf | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf index 26b2a4f111..b30bf92370 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf @@ -218,15 +218,6 @@ THREAD_STRUCT = ( } ); - log_config : - { - global_log_level ="info"; - hw_log_level ="info"; - phy_log_level ="info"; - mac_log_level ="info"; - rlc_log_level ="info"; - pdcp_log_level ="info"; - rrc_log_level ="info"; - f1ap_log_level ="debug"; - ngap_log_level ="debug"; - }; +log_config : { + global_log_level = "info"; +}; From 7c9c3db6f1bc637f441c329666f22da9cc1f9e9d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Aug 2023 13:08:54 +0200 Subject: [PATCH 8/9] Add Quectel/F1+E1 test --- .../conf_files/gnb-cucp.sa.f1.quectel.conf | 219 ++++++++++++++++++ .../conf_files/gnb-cuup.sa.f1.quectel.conf | 78 +++++++ .../sa_e1_b200_gnb/docker-compose.yml | 68 ++++++ 3 files changed, 365 insertions(+) create mode 100644 ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf create mode 100644 ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf create mode 100644 ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml diff --git a/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf b/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf new file mode 100644 index 0000000000..c403de1b64 --- /dev/null +++ b/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf @@ -0,0 +1,219 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; +Num_Threads_PUSCH = 8; +sa = 1; + +gNBs = +( + { + ////////// Identification parameters: + gNB_CU_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 222; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); + + + nr_cellid = 12345678L; + force_256qam_off = 1; + + tr_s_preference = "f1"; + + local_s_if_name = "eth0"; + local_s_address = "192.168.68.194"; + remote_s_address = "192.168.68.195"; + local_s_portc = 501; + local_s_portd = 2153; + remote_s_portc = 500; + remote_s_portd = 2153; + ssb_SubcarrierOffset = 0; + min_rxtxtime = 6; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641280; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640008; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ( { ipv4 = "172.21.16.137"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + E1_INTERFACE = + ( + { + type = "cp"; + ipv4_cucp = "192.168.68.194"; + port_cucp = 38462; + ipv4_cuup = "192.168.68.196"; + port_cuup = 38462; + } + ) + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "eth0"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.68.194"; + }; + } +); + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : { + global_log_level = "info"; +}; diff --git a/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf b/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf new file mode 100644 index 0000000000..535ef68bd0 --- /dev/null +++ b/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf @@ -0,0 +1,78 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; +sa = 1; + +gNBs = +( + { + ////////// Identification parameters: + gNB_CU_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 222; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); + + + tr_s_preference = "f1"; + + local_s_if_name = "lo"; + local_s_address = "192.168.68.196"; + remote_s_address = "192.168.68.195"; + local_s_portc = 501; + local_s_portd = 2153; + remote_s_portc = 500; + remote_s_portd = 2153; + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + E1_INTERFACE = + ( + { + type = "up"; + ipv4_cucp = "192.168.68.194"; + ipv4_cuup = "192.168.68.196"; + } + ) + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.68.196"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.68.196"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config: { + global_log_level = "info"; +}; diff --git a/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml new file mode 100644 index 0000000000..5b6e03db16 --- /dev/null +++ b/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml @@ -0,0 +1,68 @@ +version: '3.8' + +services: + gnb_cucp: + image: oai-gnb:latest + privileged: true + container_name: sa-cucp-gnb + environment: + USE_ADDITIONAL_OPTIONS: --sa --log_config.global_log_options level,nocolor,time,line_num,function + volumes: + - ../../conf_files/gnb-cucp.sa.f1.quectel.conf:/opt/oai-gnb/etc/gnb.conf + networks: + public_net: + ipv4_address: 192.168.68.194 + healthcheck: + # pgrep does NOT work + test: /bin/bash -c "ps aux | grep -v grep | grep -c softmodem" + interval: 10s + timeout: 5s + retries: 5 + + gnb_cuup: + image: oai-nr-cuup:latest + privileged: true + container_name: sa-cuup-gnb + environment: + USE_ADDITIONAL_OPTIONS: --sa --log_config.global_log_options level,nocolor,time,line_num,function + volumes: + - ../../conf_files/gnb-cuup.sa.f1.quectel.conf:/opt/oai-gnb/etc/gnb.conf + networks: + public_net: + ipv4_address: 192.168.68.196 + healthcheck: + # pgrep does NOT work + test: /bin/bash -c "ps aux | grep -v grep | grep -c nr-cuup" + interval: 10s + timeout: 5s + retries: 5 + + gnb_du_tdd: + image: oai-gnb:latest + privileged: true + container_name: sa-du-b200-gnb + environment: + USE_B2XX: 'yes' + USE_ADDITIONAL_OPTIONS: --sa --RUs.[0].sdr_addrs serial=30C51D4 --continuous-tx -E --log_config.global_log_options level,nocolor,time,line_num,function --gNBs.[0].min_rxtxtime 2 --gNBs.[0].do_CSIRS 1 --gNBs.[0].do_SRS 1 --RUs.[0].att_rx 14 --RUs.[0].att_tx 14 + volumes: + - ../../conf_files/gnb-du.sa.band78.106prb.usrpb200.conf:/opt/oai-gnb/etc/gnb.conf + - /dev:/dev + networks: + public_net: + ipv4_address: 192.168.68.195 + #entrypoint: /bin/bash -c "sleep infinity" + healthcheck: + # pgrep does NOT work + test: /bin/bash -c "ps aux | grep -v grep | grep -c softmodem" + interval: 10s + timeout: 5s + retries: 5 + +networks: + public_net: + name: sa-b200-gnb-net + ipam: + config: + - subnet: 192.168.68.192/26 + driver_opts: + com.docker.network.bridge.name: "sa-gnb-net" From 7b297eb27a68521c36d64b7a27107621f55047b6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 2 Aug 2023 10:59:07 +0200 Subject: [PATCH 9/9] Add XML for Quectel F1+E1 test, don't clean after F1 --- ci-scripts/cls_containerize.py | 2 +- .../container_sa_e1_b200_quectel.xml | 166 ++++++++++++++++++ .../container_sa_e1_b200_terminate.xml | 48 +++++ .../container_sa_f1_b200_terminate.xml | 7 - 4 files changed, 215 insertions(+), 8 deletions(-) create mode 100644 ci-scripts/xml_files/container_sa_e1_b200_quectel.xml create mode 100644 ci-scripts/xml_files/container_sa_e1_b200_terminate.xml diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 9a54f258eb..85de2f65a9 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -903,7 +903,7 @@ class Containerize(): cnt = 0 while (cnt < 20): mySSH.command('docker logs ' + containerName + ' | egrep --text --color=never -i "wait|sync|Starting"', '\$', 30) - result = re.search('got sync|Starting F1AP at CU|Got sync|Waiting for RUs to be configured', mySSH.getBefore()) + result = re.search('got sync|Starting E1AP at CU UP|Starting F1AP at CU|Got sync|Waiting for RUs to be configured', mySSH.getBefore()) if result is None: time.sleep(6) cnt += 1 diff --git a/ci-scripts/xml_files/container_sa_e1_b200_quectel.xml b/ci-scripts/xml_files/container_sa_e1_b200_quectel.xml new file mode 100644 index 0000000000..41358c1022 --- /dev/null +++ b/ci-scripts/xml_files/container_sa_e1_b200_quectel.xml @@ -0,0 +1,166 @@ + + + TEST-SA-FR1-F1-E1-B200 + 40 MHz TDD F1+E1 SA + tasks + 1 + + 333333 + 300000 + 310000 + 330101 + 330102 + 330103 + 310001 + 350000 + 370001 + 370000 + 370002 + 310011 + 310002 + 330201 + 200000 + + + + + Pull_Local_Registry + Pull Images from Local Registry + 0 + oai-gnb oai-nr-cuup + + + + Initialize_UE + Initialize Quectel + idefix + + + Terminate_UE + Terminate Quectel + idefix + + + Attach_UE + Attach Quectel + idefix + + + Detach_UE + Detach Quectel + idefix + + + + Deploy_Object + Deploy gNB-CU-CP in a container + ci-scripts/yaml_files/sa_e1_b200_gnb + 0 + 0 + gnb_cucp + + + + Deploy_Object + Deploy gNB-CU-UP in a container + ci-scripts/yaml_files/sa_e1_b200_gnb + 0 + 0 + gnb_cuup + + + + Deploy_Object + Deploy gNB-DU (TDD/Band78/40MHz/B200) in a container + ci-scripts/yaml_files/sa_e1_b200_gnb + 0 + 0 + gnb_du_tdd + + + + Custom_Command + Disable Sleep States + ofqot + sudo cpupower idle-set -D 0 + + + Custom_Command + Enable Sleep States + ofqot + sudo cpupower idle-set -E + + + + Ping + Ping: 20pings in 20sec + idefix + -c 20 %cn_ip% + 1 + 25 + + + + Iperf + iperf (DL/80Mbps/UDP)(30 sec) + -u -b 80M -t 30 -i 1 -fm + DL + idefix + 10 + 95 + single-ue + + + + Iperf + iperf (UL/8Mbps/UDP)(30 sec) + -u -b 8M -t 30 -i 1 -fm + UL + idefix + 1 + 95 + single-ue + + + + Iperf + iperf (BIDIR TCP)(10 sec)(single-ue profile) + -t 20 --bidir + BIDIR + idefix + single-ue + + + + Undeploy_Object + Undeploy CU-DU + ci-scripts/yaml_files/sa_e1_b200_gnb + 0 + 0 + 10,100,100,100 + 10,100,100,100 + + + + diff --git a/ci-scripts/xml_files/container_sa_e1_b200_terminate.xml b/ci-scripts/xml_files/container_sa_e1_b200_terminate.xml new file mode 100644 index 0000000000..ad902e838a --- /dev/null +++ b/ci-scripts/xml_files/container_sa_e1_b200_terminate.xml @@ -0,0 +1,48 @@ + + + TEST-SA-FR1-F1-E1-B200-terminate + Tear-down + tasks + 1 + + 330201 + 222222 + + + + + Undeploy_Object + Undeploy CU-CP/CU-UP/DU + ci-scripts/yaml_files/sa_e1_b200_gnb + 0 + 0 + + + + Clean_Test_Server_Images + Clean Test Images on Test Server + 0 + + + diff --git a/ci-scripts/xml_files/container_sa_f1_b200_terminate.xml b/ci-scripts/xml_files/container_sa_f1_b200_terminate.xml index cd8445f9ce..f5b13beaed 100644 --- a/ci-scripts/xml_files/container_sa_f1_b200_terminate.xml +++ b/ci-scripts/xml_files/container_sa_f1_b200_terminate.xml @@ -27,7 +27,6 @@ 1 230201 - 222222 @@ -39,10 +38,4 @@ 0 - - Clean_Test_Server_Images - Clean Test Images on Test Server - 0 - -