diff --git a/ci-scripts/Jenkinsfile-GitLab-Container b/ci-scripts/Jenkinsfile-GitLab-Container index 8d52bc9f8e..c066333eaa 100644 --- a/ci-scripts/Jenkinsfile-GitLab-Container +++ b/ci-scripts/Jenkinsfile-GitLab-Container @@ -464,6 +464,26 @@ pipeline { } } } + stage ("SA-OAIUE-CN5G") { + when { expression {do5Gtest} } + steps { + script { + triggerCN5GSlaveJob ('RAN-SA-OAIUE-CN5G', 'RAN-SA-OAIUE-CN5G') + } + } + post { + always { + script { + finalizeSlaveJob('RAN-SA-OAIUE-CN5G') + } + } + failure { + script { + currentBuild.result = 'FAILURE' + } + } + } + } } } stage ("DockerHub-Push") { diff --git a/ci-scripts/Jenkinsfile-nrue b/ci-scripts/Jenkinsfile-nrue new file mode 100644 index 0000000000..ab5fe0747d --- /dev/null +++ b/ci-scripts/Jenkinsfile-nrue @@ -0,0 +1,242 @@ +#!/bin/groovy +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +// Location of the executor node +def pythonExecutor = params.pythonExecutor + +// Location of the test XML file to be run +def testXMLFile = params.pythonTestXmlFile +def mainPythonAllXmlFiles = "" +def buildStageStatus = true + +// Name of the test stage +def testStageName = params.pipelineTestStageName + +// Name of the resource +def lockResources = [] +if (params.LockResources != null && params.LockResources.trim().length() > 0) + params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} + +// Global Parameters. Normally they should be populated when the master job +// triggers the slave job with parameters +def eNB_Repository +def eNB_Branch +def eNB_CommitID +def eNB_AllowMergeRequestProcess = false +def eNB_TargetBranch + +// Flags +def scmEvent = false +def upstreamEvent = false + +//------------------------------------------------------------------------------- +// Pipeline start +pipeline { + agent { + label pythonExecutor + } + options { + timestamps() + ansiColor('xterm') + lock(extra: lockResources) + } + + stages { + stage ('Verify Parameters') { + steps { + script { + echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' + + JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"' + JOB_TIMESTAMP = JOB_TIMESTAMP.trim() + + def allParametersPresent = true + + // It is already to late to check it + if (params.pythonExecutor != null) { + echo "eNB CI executor node : ${pythonExecutor}" + } + // If not present picking a default Stage Name + if (params.pipelineTestStageName == null) { + // picking default + testStageName = 'Template Test Stage' + } + if (params.LockResources == null) { + echo "no LockResources given" + allParametersPresent = false + } + if (params.eNB_IPAddress == null) { + allParametersPresent = false + } + if (params.eNB_SourceCodePath == null) { + allParametersPresent = false + } + if (params.eNB_Credentials == null) { + allParametersPresent = false + } + if (params.eNB1_IPAddress == null) { + allParametersPresent = false + } + if (params.eNB1_SourceCodePath == null) { + allParametersPresent = false + } + if (params.eNB1_Credentials == null) { + allParametersPresent = false + } + // the following 4 parameters should be pushed by the master trigger + // if not present, take the job GIT variables (used for developing) + if (params.eNB_Repository == null) { + eNB_Repository = env.GIT_URL + } else { + eNB_Repository = params.eNB_Repository + } + echo "eNB_Repository : ${eNB_Repository}" + if (params.eNB_Branch == null) { + eNB_Branch = env.GIT_BRANCH + } else { + eNB_Branch = params.eNB_Branch + } + echo "eNB_Branch : ${eNB_Branch}" + if (params.eNB_CommitID == null) { + eNB_CommitID = env.GIT_COMMIT + } else { + eNB_CommitID = params.eNB_CommitID + } + echo "eNB_CommitID : ${eNB_CommitID}" + if (params.eNB_mergeRequest != null) { + eNB_AllowMergeRequestProcess = params.eNB_mergeRequest + if (eNB_AllowMergeRequestProcess) { + if (params.eNB_TargetBranch != null) { + eNB_TargetBranch = params.eNB_TargetBranch + } else { + eNB_TargetBranch = 'develop' + } + echo "eNB_TargetBranch : ${eNB_TargetBranch}" + } + } + + if (params.EPC_IPAddress == null) { + allParametersPresent = false + } + if (params.EPC_Type == null) { + allParametersPresent = false + } + if (params.EPC_SourceCodePath == null) { + allParametersPresent = false + } + if (params.EPC_Credentials == null) { + allParametersPresent = false + } + if (params.OC_Credentials == null) { + allParametersPresent = false + } + if (params.OC_ProjectName == null) { + allParametersPresent = false + } + if (allParametersPresent) { + echo "All parameters are present" + if (eNB_AllowMergeRequestProcess) { + sh "git fetch" + sh "./ci-scripts/doGitLabMerge.sh --src-branch ${eNB_Branch} --src-commit ${eNB_CommitID} --target-branch ${eNB_TargetBranch} --target-commit latest" + } else { + sh "git fetch" + sh "git checkout -f ${eNB_CommitID}" + } + } else { + echo "Some parameters are missing" + sh "./ci-scripts/fail.sh" + } + } + } + } + + stage ("Deploy and Test") { + steps { + script { + dir ('ci-scripts') { + echo "\u2705 \u001B[32m${testStageName}\u001B[0m" + // If not present picking a default XML file + if (params.pythonTestXmlFile == null) { + // picking default + testXMLFile = 'xml_files/enb_usrpB210_band7_50PRB.xml' + echo "Test XML file(default): ${testXMLFile}" + mainPythonAllXmlFiles += "--XMLTestFile=" + testXMLFile + " " + } else { + String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") + for (xmlFile in myXmlTestSuite) { + if (fileExists(xmlFile)) { + mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " + echo "Test XML file : ${xmlFile}" + } + } + } + withCredentials([ + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'], + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB1_Credentials}", usernameVariable: 'eNB1_Username', passwordVariable: 'eNB1_Password'], + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.EPC_Credentials}", usernameVariable: 'EPC_Username', passwordVariable: 'EPC_Password'], + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password'] + ]) { + sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}" + String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") + for (xmlFile in myXmlTestSuite) { + if (fileExists(xmlFile)) { + try { + sh "python3 main.py --mode=TesteNB --eNBIPAddress=${params.eNB_IPAddress} --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --EPCIPAddress=${params.EPC_IPAddress} --EPCType=${params.EPC_Type} --EPCUserName=${EPC_Username} --EPCPassword=${EPC_Password} --EPCSourceCodePath=${params.EPC_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName} --eNB1IPAddress=${params.eNB1_IPAddress} --eNB1UserName=${eNB1_Username} --eNB1Password=${eNB1_Password} --eNB1SourceCodePath=${params.eNB1_SourceCodePath}" + } catch (Exception e) { + currentBuild.result = 'FAILURE' + buildStageStatus = false + } + } + } + sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}" + } + } + } + } + } + stage ("Log Collection") { + steps { + script { + dir ('ci-scripts') { + // Zipping all archived log files + sh "mkdir test_logs" + sh "mv *.log* test_logs || true" + sh "zip -r -qq test_logs_${env.BUILD_ID}.zip *test_log*/" + sh "rm -rf *test_log*/" + if (fileExists("test_logs_${env.BUILD_ID}.zip")) { + archiveArtifacts artifacts: "test_logs_${env.BUILD_ID}.zip" + } + if (fileExists("test_logs_CN.zip")){ + sh "mv test_logs_CN.zip test_logs_CN_${env.BUILD_ID}.zip" + archiveArtifacts artifacts: "test_logs_CN_${env.BUILD_ID}.zip" + } + if (fileExists("test_results.html")) { + sh "mv test_results.html test_results-${env.JOB_NAME}.html" + sh "sed -i -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' test_results-${JOB_NAME}.html" + archiveArtifacts artifacts: "test_results-${env.JOB_NAME}.html" + } + } + } + } + } + } +} diff --git a/ci-scripts/ci_ueinfra.yaml b/ci-scripts/ci_ueinfra.yaml index 2aa3952fcb..daedf496f7 100644 --- a/ci-scripts/ci_ueinfra.yaml +++ b/ci-scripts/ci_ueinfra.yaml @@ -122,10 +122,13 @@ amarisoft_ue_10: CmdPrefix: ip netns exec ue10 MTU: 1500 -oai_ue_obelix: - Host: obelix +oai_ue_caracal: + Host: caracal + AttachScript: 'docker start oai-nr-ue' + DetachScript: 'docker stop oai-nr-ue' NetworkScript: ip a show dev oaitun_ue1 IF: oaitun_ue1 + MTU: 1500 rfsim5g_gnb_nos1: Host: localhost diff --git a/ci-scripts/cls_cluster.py b/ci-scripts/cls_cluster.py index 56c60f7551..24b0c8902e 100644 --- a/ci-scripts/cls_cluster.py +++ b/ci-scripts/cls_cluster.py @@ -82,6 +82,7 @@ class Cluster: self.ranTargetBranch = "" self.cmd = None self.imageToPull = '' + self.testSvrId = None def _recreate_entitlements(self): # recreating entitlements, don't care if deletion fails @@ -195,12 +196,13 @@ class Cluster: self.cmd.run(f'oc delete -f {filename}') def PullClusterImage(self, HTML, RAN): + if self.testSvrId == None: self.testSvrId = self.eNBIPAddress if self.imageToPull == '': HELP.GenericHelp(CONST.Version) - HELP.EPCSrvHelp(self.imageToPull) - sys.exit('Insufficient eNB Parameters') + sys.exit('Insufficient Parameter') + logging.debug(f'Pull OC image {self.imageToPull} to server {self.testSvrId}') self.testCase_id = HTML.testCase_id - cmd = cls_cmd.getConnection(self.eNBIPAddress) + cmd = cls_cmd.getConnection(self.testSvrId) succeeded = OC_login(cmd, self.OCUserName, self.OCPassword, CI_OC_RAN_NAMESPACE) if not succeeded: logging.error('\u001B[1m OC Cluster Login Failed\u001B[0m') diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 813b14ce22..3f034e9b73 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -52,7 +52,7 @@ import cls_cmd import sshconnection as SSH import helpreadme as HELP import constants as CONST - +import cls_oaicitest #----------------------------------------------------------- # Helper functions used here and in other classes # (e.g., cls_cluster.py) @@ -838,7 +838,7 @@ class Containerize(): mySSH.command('cd ' + lSourcePath + '/' + self.yamlPath[self.eNB_instance], '\$', 5) mySSH.command('cp docker-compose.y*ml ci-docker-compose.yml', '\$', 5) - imagesList = ['oai-enb', 'oai-gnb', 'oai-nr-cuup', 'oai-gnb-aw2s'] + imagesList = ['oai-enb', 'oai-gnb', 'oai-nr-cuup', 'oai-gnb-aw2s', 'oai-nr-ue'] for image in imagesList: imageTag = ImageTagToUse(image, self.ranCommitID, self.ranBranch, self.ranAllowMerge) mySSH.command(f'sed -i -e "s#image: {image}:latest#image: oai-ci/{imageTag}#" ci-docker-compose.yml', '\$', 2) @@ -877,6 +877,7 @@ class Containerize(): else: time.sleep(10) cnt += 1 + mySSH.command('docker inspect --format="ImageUsed: {{.Config.Image}}" ' + containerName, '\$', 5) for stdoutLine in mySSH.getBefore().split('\n'): if stdoutLine.count('ImageUsed: oai-ci'): @@ -903,7 +904,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', mySSH.getBefore()) + result = re.search('got sync|Starting F1AP at CU|Got sync', mySSH.getBefore()) if result is None: time.sleep(6) cnt += 1 @@ -993,7 +994,6 @@ class Containerize(): mySSH.command('docker volume prune --force', '\$', 20) mySSH.close() - # Analyzing log file! files = ','.join([f'{s}-{HTML.testCase_id}' for s in services]) if len(services) > 1: @@ -1005,6 +1005,16 @@ class Containerize(): HTML.htmleNBFailureMsg='Could not copy logfile to analyze it!' HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE) self.exitStatus = 1 + # use function for UE log analysis, when oai-nr-ue container is used + elif 'oai-nr-ue' in services: + self.exitStatus == 0 + logging.debug('\u001B[1m Analyzing UE logfile ' + filename + ' \u001B[0m') + logStatus = cls_oaicitest.OaiCiTest().AnalyzeLogFile_UE(f'{filename}', HTML, RAN) + if (logStatus < 0): + fullStatus = False + HTML.CreateHtmlTestRow('UE log Analysis', 'KO', logStatus) + else: + HTML.CreateHtmlTestRow('UE log Analysis', 'OK', CONST.ALL_PROCESSES_OK) else: for svcName in services: filename = f'{svcName}-{HTML.testCase_id}.log' diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 97eaed8ba5..4d0d4d8f17 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -1059,7 +1059,7 @@ class OaiCiTest(): 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 -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 {ue.getIP()} -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: diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.n310.7ds2u.conf b/ci-scripts/conf_files/gnb.sa.band78.106prb.n310.7ds2u.conf new file mode 100644 index 0000000000..a1e66c7ade --- /dev/null +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.n310.7ds2u.conf @@ -0,0 +1,261 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_CU_ID = 0xe00; + + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + + plmn_list = ({ mcc = 208; mnc = 97; mnc_length = 2; snssaiList = ({sst = 1;}); }); + + nr_cellid = 12345678L + + ////////// Physical parameters: + + min_rxtxtime = 5; + do_SRS = 0; + force_256qam_off = 1; + pdsch_AntennaPorts_N1 = 1; + pdsch_AntennaPorts_XP = 1; + pusch_AntennaPorts = 1; + + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 0; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + dl_frequencyBand = 78; + absoluteFrequencySSB = 634080; + dl_absoluteFrequencyPointA = 632808; + #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=0,L=106 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + 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 = 12; + 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 = 3; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#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 = -70; +# 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=spar e1 + 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.6.100"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + NETWORK_INTERFACES : + { + + GNB_INTERFACE_NAME_FOR_NG_AMF = "eno33np0"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.124/22"; + GNB_INTERFACE_NAME_FOR_NGU = "eno33np0"; + GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.124/22"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 180; + pucch_TargetSNRx10 = 200; + ulsch_max_frame_inactivity = 1; + ul_max_mcs = 16; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + #pucch0_dtx_threshold = 150; +} +); + +RUs = ( +{ + local_rf = "yes"; + nb_tx = 1; + nb_rx = 1; + att_tx = 0; + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + eNB_instances = [0]; + ##beamforming 1x2 matrix: 1 layer x 2 antennas + bf_weights = [0x00007fff, 0x0000]; + sf_extension = 0 + sdr_addrs = "addr=192.168.80.52,clock_source=internal,time_source=internal" +} +); + +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"; + 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"; + gtpu_log_level ="info"; +}; + diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 898dfca6d8..613b6238f5 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -420,10 +420,12 @@ def GetParametersFromXML(action): RAN.command = test.findtext('command') RAN.command_fail = test.findtext('command_fail') in ['True', 'true', 'Yes', 'yes'] elif action == 'Pull_Cluster_Image': - # CLUSTER.imageToPull.clear() string_field = test.findtext('images_to_pull') if (string_field is not None): CLUSTER.imageToPull = string_field.split() + string_field = test.findtext('test_svr_id') + if (string_field is not None): + CLUSTER.testSvrId = string_field else: logging.warning(f"unknown action {action} from option-parsing point-of-view") diff --git a/ci-scripts/xml_files/container_sa_n310_nrue.xml b/ci-scripts/xml_files/container_sa_n310_nrue.xml new file mode 100644 index 0000000000..309b2860f7 --- /dev/null +++ b/ci-scripts/xml_files/container_sa_n310_nrue.xml @@ -0,0 +1,146 @@ + + + TEST-5G-NRUE + 40 MHz TDD SA + tasks + 1 + + 010000 010001 + 020000 020001 + 000001 + 001000 + 002000 + 000100 + 000200 + 040001 040000 + + + + + Pull_Cluster_Image + Pull Images from Cluster + oai-gnb + avra + + + + Pull_Cluster_Image + Pull Images from Cluster + oai-nr-ue + caracal + + + + Custom_Command + Reboot USRP N310 + avra + ssh root@192.168.80.52 reboot ; sleep 45 + + + + Custom_Command + Reboot USRP N310 + caracal + ssh root@192.168.20.2 reboot ; sleep 45 + + + + Deploy_Object + Deploy gNB (TDD/Band78/40MHz/N310) in a container + ci-scripts/yaml_files/5g_sa_n310_gnb + 0 + 0 + + + + Deploy_Object + Deploy nrUE (TDD/Band78/40MHz/N310) in a container + ci-scripts/yaml_files/5g_sa_n310_nrue + 1 + 1 + + + + Attach_UE + Attach OAIUE + oai_ue_caracal + + + + Iperf + iperf (DL/20Mbps/UDP)(30 sec)(single-ue profile) + -u -b 20M -t 30 -i 1 -R + IPERF3 + oai_ue_caracal + 25 + 80 + + + + Iperf + iperf (UL/5Mbps/UDP)(30 sec)(single-ue profile) + -u -b 5M -t 30 -i 1 + IPERF3 + oai_ue_caracal + 25 + 80 + + + + Ping + Ping: 20pings in 20sec + oai_ue_caracal + -c 20 + 1 + 25 + + + + Ping + Ping: 100pings in 20sec + oai_ue_caracal + -c 100 -i 0.2 + 1 + 30 + + + + Undeploy_Object + Undeploy gNB + ci-scripts/yaml_files/5g_sa_n310_gnb + 0 + 0 + 20,100,100,100 + 20,100,100,100 + + + + Undeploy_Object + Undeploy nr UE + ci-scripts/yaml_files/5g_sa_n310_nrue + 1 + 1 + + + diff --git a/ci-scripts/xml_files/container_sa_n310_nrue_cleanup.xml b/ci-scripts/xml_files/container_sa_n310_nrue_cleanup.xml new file mode 100644 index 0000000000..0bac1e010b --- /dev/null +++ b/ci-scripts/xml_files/container_sa_n310_nrue_cleanup.xml @@ -0,0 +1,46 @@ + + + test-aw2s-cleanup + CleanUp of test servers + trash + + 111111 + 222222 + + + + + Clean_Test_Server_Images + Clean Test Images on Test Server + 0 + + + + Clean_Test_Server_Images + Clean Test Images on Test Server + 1 + + + + diff --git a/ci-scripts/yaml_files/5g_sa_n310_gnb/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n310_gnb/docker-compose.yml new file mode 100644 index 0000000000..30cef6b62b --- /dev/null +++ b/ci-scripts/yaml_files/5g_sa_n310_gnb/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3.8' +services: + oai-gnb: + image: oai-gnb:latest + privileged: true + network_mode: "host" + container_name: oai-gnb + environment: + TZ: Europe/Paris + USE_ADDITIONAL_OPTIONS: --sa --tune-offset 20000000 --log_config.global_log_options level,nocolor,time + volumes: + - ../../conf_files/gnb.sa.band78.106prb.n310.7ds2u.conf:/opt/oai-gnb/etc/gnb.conf + healthcheck: + test: /bin/bash -c "pgrep nr-softmodem" + interval: 10s + timeout: 5s + retries: 5 diff --git a/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml new file mode 100644 index 0000000000..038ea51532 --- /dev/null +++ b/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.8' +services: + oai-nr-ue: + image: oai-nr-ue:latest + privileged: true + network_mode: host + container_name: oai-nr-ue + #entrypoint: /bin/bash -c "sleep infinity" + environment: + TZ: Europe/Paris + USE_ADDITIONAL_OPTIONS: --usrp-args type=n3xx,name=ni-n3xx-31641B9,addr=192.168.20.2 -r 106 --numerology 1 --band 78 -C 3511200000 --sa --ue-fo-compensation --ue-rxgain 65 --tune-offset 20000000 --log_config.global_log_options level,nocolor,time --uicc0.imsi 208970000000111 --uicc0.nssai_sd 16777215 + volumes: + - ../../conf_files/ue.sa.conf:/opt/oai-nr-ue/etc/nr-ue.conf + healthcheck: + test: /bin/bash -c "pgrep nr-uesoftmodem" + interval: 10s + timeout: 5s + retries: 5 diff --git a/doc/TESTBenches.md b/doc/TESTBenches.md index 300d9cd56a..35aebfe1aa 100644 --- a/doc/TESTBenches.md +++ b/doc/TESTBenches.md @@ -61,6 +61,14 @@ Note: The available resources, and their current usage, is indicated here: [PDF version](testbenches_doc_resources/5g-aw2s-bench.pdf) | [LaTeX/TikZ version](testbenches_doc_resources/5g-aw2s-bench.tex) if you want to modify to reflect your setup +### 5G UE OTA Testbench + +**Purpose**: Over-the-air 5G tests with OAI UE + +![OAI UE Testbench](testbenches_doc_resources/5g-nrue-bench.png) + +[PDF version](testbenches_doc_resources/5g-nrue-bench.pdf) | [LaTeX/TikZ version](testbenches_doc_resources/5g-nrue-bench.tex) if you want to modify to reflect your setup + ### 4G Testbench(es) **Purpose**: 4G/LTE testbenches @@ -121,6 +129,9 @@ Webhook - [RAN-SA-B200-Module-SABOX-Container](https://jenkins-oai.eurecom.fr/job/RAN-SA-B200-Module-SABOX-Container/) - ofqot + B200, idefix + Quectel, nepes w/ sabox - basic SA test (20 MHz TDD), F1, reestablishment, ... +- [RAN-SA-OAIUE-CN5G](https://jenkins-oai.eurecom.fr/job/RAN-SA-OAIUE-CN5G/) + - 5G-NR SA test setup: gNB on avra(RHEL9.2) + N310, OAIUE on caracal(RHEL9.1) + N310, OAI CN5G + - OpenShift cluster for CN deployment and container images for gNB and UE deployment - [RAN-Ubuntu18-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu18-Image-Builder/) - obelix: Ubuntu 20 image build using docker (Note: builds U20 images while pipeline is named U18!) @@ -134,9 +145,6 @@ Webhook - [RAN-SA-Module-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-Module-CN5G/) - asterix + N310 (gNB), nrmodule2 + Quectel, porcepix w/ OAI 5GC - NR 2x2 (standalone) -- [RAN-SA-OAIUE-N310-X300-CN5G](https://jenkins-oai.eurecom.fr/job/RAN-SA-OAIUE-N310-X300-CN5G/) - - asterix + N310 (gNB), obelix + N310 or X300 (5G UE), porcepix w/ OAI 5GC - - OTA test with OAIUE using both N310 and X300 - [RAN-SA-AmariS-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-AmariS-CN5G/) - asterix + N310, amariue (1x UE), porcepix w/ OAI 5GC - Amarisoft UE simulator: expected to be increased to more UEs diff --git a/doc/testbenches_doc_resources/5g-nrue-bench.pdf b/doc/testbenches_doc_resources/5g-nrue-bench.pdf new file mode 100644 index 0000000000..7e461807c1 Binary files /dev/null and b/doc/testbenches_doc_resources/5g-nrue-bench.pdf differ diff --git a/doc/testbenches_doc_resources/5g-nrue-bench.png b/doc/testbenches_doc_resources/5g-nrue-bench.png new file mode 100644 index 0000000000..c167591592 Binary files /dev/null and b/doc/testbenches_doc_resources/5g-nrue-bench.png differ diff --git a/doc/testbenches_doc_resources/5g-nrue-bench.tex b/doc/testbenches_doc_resources/5g-nrue-bench.tex new file mode 100644 index 0000000000..c180929767 --- /dev/null +++ b/doc/testbenches_doc_resources/5g-nrue-bench.tex @@ -0,0 +1,36 @@ +\documentclass{standalone} + +\usepackage{tikz} +\usetikzlibrary{backgrounds, positioning, shapes.symbols} +\usepackage{helvet} +\renewcommand*{\rmdefault}{\sfdefault} + +\begin{document} +\begin{tikzpicture} + [ + font=\footnotesize, + faraday/.style={minimum size=3cm, draw, dashed}, + duplexer/.style={draw,fill=white}, + ] + + \node[label=above:avra] (avra) + {\includegraphics[width=1.2cm]{server}}; + \node[right=0.5cm of avra, label=above:USRP N310] (n310gnb) + {\includegraphics[width=1.2cm]{n310}} edge (avra); + \node[right=.2cm of n310gnb, duplexer] (b78o) {n78} edge (n310gnb); + \node[right=0.5cm of b78o] (ant1) + {\includegraphics[width=0.3cm]{antenna}} edge (b78o); + \node[left=0.5cm of avra] (oc) + {\includegraphics[width=1.2cm]{openshift}} edge (avra); + + \node[right=7cm of n310gnb, label=above:caracal] (caracal) + {\includegraphics[width=1.2cm]{server}}; + \node[left=0.5cm of caracal, label=above:USRP N310] (n310ue) + {\includegraphics[width=1.2cm]{n310}} edge (caracal); + \node[left=.2cm of n310ue, duplexer] (b78a) {n78} edge (n310ue); + \node[left=0.5cm of b78a] (ant2) + {\includegraphics[width=0.3cm]{antenna}} edge (b78a); + + +\end{tikzpicture} +\end{document} diff --git a/doc/testbenches_doc_resources/openshift.png b/doc/testbenches_doc_resources/openshift.png new file mode 100644 index 0000000000..4be8884b9d Binary files /dev/null and b/doc/testbenches_doc_resources/openshift.png differ