diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py new file mode 100644 index 0000000000..4b6f5ad667 --- /dev/null +++ b/ci-scripts/cls_oaicitest.py @@ -0,0 +1,3155 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- +# +# +# Required Python Version +# Python 3.x +# +# Required Python Package +# pexpect +#--------------------------------------------------------------------- + + +#----------------------------------------------------------- +# Import Libs +#----------------------------------------------------------- +import sys # arg +import re # reg +import pexpect # pexpect +import time # sleep +import os +import subprocess +import xml.etree.ElementTree as ET +import logging +import datetime +import signal +from multiprocessing import Process, Lock, SimpleQueue +logging.basicConfig( + level=logging.DEBUG, + format="[%(asctime)s] %(name)s:%(levelname)s: %(message)s" +) + +#import our libs +import helpreadme as HELP +import constants as CONST +import sshconnection + +#----------------------------------------------------------- +# OaiCiTest Class Definition +#----------------------------------------------------------- +class OaiCiTest(): + + def __init__(self): + self.ranRepository = '' + self.ranBranch = '' + self.ranCommitID = '' + self.ranAllowMerge = False + self.ranTargetBranch = '' + + self.FailReportCnt = 0 + self.ADBIPAddress = '' + self.ADBUserName = '' + self.ADBPassword = '' + self.ADBCentralized = True + self.testCase_id = '' + self.testXMLfiles = [] + self.desc = '' + self.ping_args = '' + self.ping_packetloss_threshold = '' + self.iperf_args = '' + self.iperf_packetloss_threshold = '' + self.iperf_profile = '' + self.iperf_options = '' + self.nbMaxUEtoAttach = -1 + self.UEDevices = [] + self.UEDevicesStatus = [] + self.UEDevicesRemoteServer = [] + self.UEDevicesRemoteUser = [] + self.UEDevicesOffCmd = [] + self.UEDevicesOnCmd = [] + self.UEDevicesRebootCmd = [] + self.CatMDevices = [] + self.UEIPAddresses = [] + self.idle_sleep_time = 0 + self.x2_ho_options = 'network' + self.x2NbENBs = 0 + self.x2ENBBsIds = [] + self.x2ENBConnectedUEs = [] + self.repeatCounts = [] + self.finalStatus = False + self.UEIPAddress = '' + self.UEUserName = '' + self.UEPassword = '' + self.UE_instance = 0 + self.UESourceCodePath = '' + self.UELogFile = '' + self.Build_OAI_UE_args = '' + self.Initialize_OAI_UE_args = '' + self.clean_repository = True + self.air_interface='' + self.expectedNbOfConnectedUEs = 0 + + + def BuildOAIUE(self,HTML): + if self.UEIPAddress == '' or self.ranRepository == '' or self.ranBranch == '' or self.UEUserName == '' or self.UEPassword == '' or self.UESourceCodePath == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + SSH = sshconnection.SSHConnection() + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + result = re.search('--nrUE', self.Build_OAI_UE_args) + if result is not None: + self.air_interface='nr-uesoftmodem' + ue_prefix = 'NR ' + else: + self.air_interface='lte-uesoftmodem' + ue_prefix = '' + result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository) + if result is not None: + full_ran_repo_name = self.ranRepository + else: + full_ran_repo_name = self.ranRepository + '.git' + SSH.command('mkdir -p ' + self.UESourceCodePath, '\$', 5) + SSH.command('cd ' + self.UESourceCodePath, '\$', 5) + SSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone ' + full_ran_repo_name + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600) + # here add a check if git clone or git fetch went smoothly + SSH.command('git config user.email "jenkins@openairinterface.org"', '\$', 5) + SSH.command('git config user.name "OAI Jenkins"', '\$', 5) + if self.clean_repository: + SSH.command('ls *.txt', '\$', 5) + result = re.search('LAST_BUILD_INFO', SSH.getBefore()) + if result is not None: + mismatch = False + SSH.command('grep SRC_COMMIT LAST_BUILD_INFO.txt', '\$', 2) + result = re.search(self.ranCommitID, SSH.getBefore()) + if result is None: + mismatch = True + SSH.command('grep MERGED_W_TGT_BRANCH LAST_BUILD_INFO.txt', '\$', 2) + if self.ranAllowMerge: + result = re.search('YES', SSH.getBefore()) + if result is None: + mismatch = True + SSH.command('grep TGT_BRANCH LAST_BUILD_INFO.txt', '\$', 2) + if self.ranTargetBranch == '': + result = re.search('develop', SSH.getBefore()) + else: + result = re.search(self.ranTargetBranch, SSH.getBefore()) + if result is None: + mismatch = True + else: + result = re.search('NO', SSH.getBefore()) + if result is None: + mismatch = True + if not mismatch: + SSH.close() + HTML.CreateHtmlTestRow(self.Build_OAI_UE_args, 'OK', CONST.ALL_PROCESSES_OK) + return + + SSH.command('echo ' + self.UEPassword + ' | sudo -S git clean -x -d -ff', '\$', 30) + + # if the commit ID is provided use it to point to it + if self.ranCommitID != '': + SSH.command('git checkout -f ' + self.ranCommitID, '\$', 5) + # if the branch is not develop, then it is a merge request and we need to do + # the potential merge. Note that merge conflicts should already been checked earlier + if self.ranAllowMerge: + if self.ranTargetBranch == '': + if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'): + SSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5) + else: + logging.debug('Merging with the target branch: ' + self.ranTargetBranch) + SSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5) + SSH.command('source oaienv', '\$', 5) + SSH.command('cd cmake_targets', '\$', 5) + SSH.command('mkdir -p log', '\$', 5) + SSH.command('chmod 777 log', '\$', 5) + # no need to remove in log (git clean did the trick) + SSH.command('stdbuf -o0 ./build_oai ' + self.Build_OAI_UE_args + ' 2>&1 | stdbuf -o0 tee compile_oai_ue.log', 'Bypassing the Tests|build have failed', 900) + SSH.command('ls ran_build/build', '\$', 3) + SSH.command('ls ran_build/build', '\$', 3) + buildStatus = True + result = re.search(self.air_interface, SSH.getBefore()) + if result is None: + buildStatus = False + SSH.command('mkdir -p build_log_' + self.testCase_id, '\$', 5) + SSH.command('mv log/* ' + 'build_log_' + self.testCase_id, '\$', 5) + SSH.command('mv compile_oai_ue.log ' + 'build_log_' + self.testCase_id, '\$', 5) + if buildStatus: + # Generating a BUILD INFO file + SSH.command('echo "SRC_BRANCH: ' + self.ranBranch + '" > ../LAST_BUILD_INFO.txt', '\$', 2) + SSH.command('echo "SRC_COMMIT: ' + self.ranCommitID + '" >> ../LAST_BUILD_INFO.txt', '\$', 2) + if self.ranAllowMerge: + SSH.command('echo "MERGED_W_TGT_BRANCH: YES" >> ../LAST_BUILD_INFO.txt', '\$', 2) + if self.ranTargetBranch == '': + SSH.command('echo "TGT_BRANCH: develop" >> ../LAST_BUILD_INFO.txt', '\$', 2) + else: + SSH.command('echo "TGT_BRANCH: ' + self.ranTargetBranch + '" >> ../LAST_BUILD_INFO.txt', '\$', 2) + else: + SSH.command('echo "MERGED_W_TGT_BRANCH: NO" >> ../LAST_BUILD_INFO.txt', '\$', 2) + SSH.close() + HTML.CreateHtmlTestRow(self.Build_OAI_UE_args, 'OK', CONST.ALL_PROCESSES_OK, 'OAI UE') + else: + SSH.close() + logging.error('\u001B[1m Building OAI UE Failed\u001B[0m') + HTML.CreateHtmlTestRow(self.Build_OAI_UE_args, 'KO', CONST.ALL_PROCESSES_OK, 'OAI UE') + HTML.CreateHtmlTabFooter(False) + sys.exit(1) + + def CheckFlexranCtrlInstallation(self,RAN,EPC): + if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '': + return + SSH = sshconnection.SSHConnection() + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('ls -ls /opt/flexran_rtc/*/rt_controller', '\$', 5) + result = re.search('/opt/flexran_rtc/build/rt_controller', SSH.getBefore()) + if result is not None: + RAN.flexranCtrlInstalled=True + logging.debug('Flexran Controller is installed') + SSH.close() + + def InitializeFlexranCtrl(self, HTML,RAN,EPC): + if RAN.flexranCtrlInstalled == False: + return + if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + SSH = sshconnection.SSHConnection() + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cd /opt/flexran_rtc', '\$', 5) + SSH.command('echo ' + EPC.Password + ' | sudo -S rm -f log/*.log', '\$', 5) + SSH.command('echo ' + EPC.Password + ' | sudo -S echo "build/rt_controller -c log_config/basic_log" > ./my-flexran-ctl.sh', '\$', 5) + SSH.command('echo ' + EPC.Password + ' | sudo -S chmod 755 ./my-flexran-ctl.sh', '\$', 5) + SSH.command('echo ' + EPC.Password + ' | sudo -S daemon --unsafe --name=flexran_rtc_daemon --chdir=/opt/flexran_rtc -o /opt/flexran_rtc/log/flexranctl_' + self.testCase_id + '.log ././my-flexran-ctl.sh', '\$', 5) + SSH.command('ps -aux | grep --color=never rt_controller', '\$', 5) + result = re.search('rt_controller -c ', SSH.getBefore()) + if result is not None: + logging.debug('\u001B[1m Initialize FlexRan Controller Completed\u001B[0m') + RAN.flexranCtrlStarted=True + SSH.close() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + + def InitializeUE_common(self, device_id, idx,COTS_UE): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + if not self.ADBCentralized: + # Reboot UE + #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesRebootCmd[idx], '\$', 60) + # Wait + #time.sleep(60) + # Put in LTE-Mode only + #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "settings put global preferred_network_mode 11"\'', '\$', 60) + #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "settings put global preferred_network_mode1 11"\'', '\$', 60) + #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "settings put global preferred_network_mode2 11"\'', '\$', 60) + #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "settings put global preferred_network_mode3 11"\'', '\$', 60) + # enable data service + #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "svc data enable"\'', '\$', 60) + # we need to do radio on/off cycle to make sure of above changes + # airplane mode off // radio on + #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOnCmd[idx], '\$', 60) + #time.sleep(10) + # airplane mode on // radio off + #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) + + # normal procedure without reboot + # enable data service + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "svc data enable"\'', '\$', 60) + # airplane mode on // radio off + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) + SSH.close() + return + + #RH quick add-on to integrate cots control defined by yaml + #if device_id exists in yaml dictionary, we execute the new procedure defined in cots_ue class + #otherwise we use the legacy procedure + logging.debug('Device id ' + str(device_id) + ', in COTS UE dict : ' + str(COTS_UE.Check_Exists(device_id))) + if COTS_UE.Check_Exists(device_id): + #switch device to Airplane mode ON (ie Radio OFF) + COTS_UE.Set_Airplane(device_id, 'ON') + else: + # enable data service + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "svc data enable"', '\$', 60) + + # The following commands are deprecated since we no longer work on Android 7+ + # SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell settings put global airplane_mode_on 1', '\$', 10) + # SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true', '\$', 60) + # a dedicated script has to be installed inside the UE + # airplane mode on means call /data/local/tmp/off + if device_id == '84B7N16418004022': + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/off"', '\$', 60) + else: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/off', '\$', 60) + #airplane mode off means call /data/local/tmp/on + logging.debug('\u001B[1mUE (' + device_id + ') Initialize Completed\u001B[0m') + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def InitializeUE(self,HTML,COTS_UE): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + multi_jobs = [] + i = 0 + for device_id in self.UEDevices: + p = Process(target = self.InitializeUE_common, args = (device_id,i,COTS_UE,)) + p.daemon = True + p.start() + multi_jobs.append(p) + i += 1 + for job in multi_jobs: + job.join() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + + + + + def InitializeOAIUE(self,HTML,RAN,EPC,COTS_UE): + if self.UEIPAddress == '' or self.UEUserName == '' or self.UEPassword == '' or self.UESourceCodePath == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + if self.air_interface == 'lte-uesoftmodem': + result = re.search('--no-L2-connect', str(self.Initialize_OAI_UE_args)) + if result is None: + check_eNB = True + check_OAI_UE = False + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow(self.air_interface + ' ' + self.Initialize_OAI_UE_args, 'KO', pStatus) + HTML.CreateHtmlTabFooter(False) + sys.exit(1) + UE_prefix = '' + else: + UE_prefix = 'NR ' + SSH = sshconnection.SSHConnection() + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + # b2xx_fx3_utils reset procedure + SSH.command('echo ' + self.UEPassword + ' | sudo -S uhd_find_devices', '\$', 60) + result = re.search('type: b200', SSH.getBefore()) + if result is not None: + logging.debug('Found a B2xx device --> resetting it') + SSH.command('echo ' + self.UEPassword + ' | sudo -S b2xx_fx3_utils --reset-device', '\$', 10) + # Reloading FGPA bin firmware + SSH.command('echo ' + self.UEPassword + ' | sudo -S uhd_find_devices', '\$', 60) + result = re.search('type: n3xx', str(SSH.getBefore())) + if result is not None: + logging.debug('Found a N3xx device --> resetting it') + SSH.command('cd ' + self.UESourceCodePath, '\$', 5) + # Initialize_OAI_UE_args usually start with -C and followed by the location in repository + SSH.command('source oaienv', '\$', 5) + SSH.command('cd cmake_targets/ran_build/build', '\$', 5) + if self.air_interface == 'lte-uesoftmodem': + result = re.search('--no-L2-connect', str(self.Initialize_OAI_UE_args)) + # We may have to regenerate the .u* files + if result is None: + SSH.command('ls /tmp/*.sed', '\$', 5) + result = re.search('adapt_usim_parameters', SSH.getBefore()) + if result is not None: + SSH.command('sed -f /tmp/adapt_usim_parameters.sed ../../../openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf > ../../../openair3/NAS/TOOLS/ci-ue_eurecom_test_sfr.conf', '\$', 5) + else: + SSH.command('sed -e "s#93#92#" -e "s#8baf473f2f8fd09487cccbd7097c6862#fec86ba6eb707ed08905757b1bb44b8f#" -e "s#e734f8734007d6c5ce7a0508809e7e9c#C42449363BBAD02B66D16BC975D77CC1#" ../../../openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf > ../../../openair3/NAS/TOOLS/ci-ue_eurecom_test_sfr.conf', '\$', 5) + SSH.command('echo ' + self.UEPassword + ' | sudo -S rm -Rf .u*', '\$', 5) + SSH.command('echo ' + self.UEPassword + ' | sudo -S ../../../targets/bin/conf2uedata -c ../../../openair3/NAS/TOOLS/ci-ue_eurecom_test_sfr.conf -o .', '\$', 5) + else: + SSH.command('if [ -e rbconfig.raw ]; then echo ' + self.UEPassword + ' | sudo -S rm rbconfig.raw; fi', '\$', 5) + SSH.command('if [ -e reconfig.raw ]; then echo ' + self.UEPassword + ' | sudo -S rm reconfig.raw; fi', '\$', 5) + # Copy the RAW files from gNB running directory (maybe on another machine) + copyin_res = SSH.copyin(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath + '/cmake_targets/rbconfig.raw', '.') + if (copyin_res == 0): + SSH.copyout(self.UEIPAddress, self.UEUserName, self.UEPassword, './rbconfig.raw', self.UESourceCodePath + '/cmake_targets/ran_build/build') + copyin_res = SSH.copyin(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath + '/cmake_targets/reconfig.raw', '.') + if (copyin_res == 0): + SSH.copyout(self.UEIPAddress, self.UEUserName, self.UEPassword, './reconfig.raw', self.UESourceCodePath + '/cmake_targets/ran_build/build') + SSH.command('echo "ulimit -c unlimited && ./'+ self.air_interface +' ' + self.Initialize_OAI_UE_args + '" > ./my-lte-uesoftmodem-run' + str(self.UE_instance) + '.sh', '\$', 5) + SSH.command('chmod 775 ./my-lte-uesoftmodem-run' + str(self.UE_instance) + '.sh', '\$', 5) + SSH.command('echo ' + self.UEPassword + ' | sudo -S rm -Rf ' + self.UESourceCodePath + '/cmake_targets/ue_' + self.testCase_id + '.log', '\$', 5) + self.UELogFile = 'ue_' + self.testCase_id + '.log' + + # We are now looping several times to hope we really sync w/ an eNB + doOutterLoop = True + outterLoopCounter = 5 + gotSyncStatus = True + fullSyncStatus = True + while (doOutterLoop): + SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets/ran_build/build', '\$', 5) + SSH.command('echo ' + self.UEPassword + ' | sudo -S rm -Rf ' + self.UESourceCodePath + '/cmake_targets/ue_' + self.testCase_id + '.log', '\$', 5) + SSH.command('echo $USER; nohup sudo -E ./my-lte-uesoftmodem-run' + str(self.UE_instance) + '.sh' + ' > ' + self.UESourceCodePath + '/cmake_targets/ue_' + self.testCase_id + '.log ' + ' 2>&1 &', self.UEUserName, 5) + time.sleep(6) + SSH.command('cd ../..', '\$', 5) + doLoop = True + loopCounter = 10 + gotSyncStatus = True + # the 'got sync' message is for the UE threads synchronization + while (doLoop): + loopCounter = loopCounter - 1 + if (loopCounter == 0): + # Here should never occur + logging.error('"got sync" message never showed!') + gotSyncStatus = False + doLoop = False + continue + SSH.command('stdbuf -o0 cat ue_' + self.testCase_id + '.log | egrep --text --color=never -i "wait|sync"', '\$', 4) + if self.air_interface == 'nr-uesoftmodem': + result = re.search('Starting sync detection', SSH.getBefore()) + else: + result = re.search('got sync', SSH.getBefore()) + if result is None: + time.sleep(10) + else: + doLoop = False + logging.debug('Found "got sync" message!') + if gotSyncStatus == False: + # we certainly need to stop the lte-uesoftmodem process if it is still running! + SSH.command('ps -aux | grep --text --color=never softmodem | grep -v grep', '\$', 4) + result = re.search('-uesoftmodem', SSH.getBefore()) + if result is not None: + SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal=SIGINT -r *-uesoftmodem', '\$', 4) + time.sleep(3) + outterLoopCounter = outterLoopCounter - 1 + if (outterLoopCounter == 0): + doOutterLoop = False + continue + # We are now checking if sync w/ eNB DOES NOT OCCUR + # Usually during the cell synchronization stage, the UE returns with No cell synchronization message + # That is the case for LTE + # In NR case, it's a positive message that will show if synchronization occurs + doLoop = True + if self.air_interface == 'nr-uesoftmodem': + loopCounter = 10 + else: + # We are now checking if sync w/ eNB DOES NOT OCCUR + # Usually during the cell synchronization stage, the UE returns with No cell synchronization message + loopCounter = 10 + while (doLoop): + loopCounter = loopCounter - 1 + if (loopCounter == 0): + if self.air_interface == 'nr-uesoftmodem': + # Here we do have great chances that UE did NOT cell-sync w/ gNB + doLoop = False + fullSyncStatus = False + logging.debug('Never seen the NR-Sync message (Measured Carrier Frequency) --> try again') + time.sleep(6) + # Stopping the NR-UE + SSH.command('ps -aux | grep --text --color=never softmodem | grep -v grep', '\$', 4) + result = re.search('nr-uesoftmodem', SSH.getBefore()) + if result is not None: + SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal=SIGINT nr-uesoftmodem', '\$', 4) + time.sleep(6) + else: + # Here we do have a great chance that the UE did cell-sync w/ eNB + doLoop = False + doOutterLoop = False + fullSyncStatus = True + continue + SSH.command('stdbuf -o0 cat ue_' + self.testCase_id + '.log | egrep --text --color=never -i "wait|sync|Frequency"', '\$', 4) + if self.air_interface == 'nr-uesoftmodem': + # Positive messaging --> + result = re.search('Measured Carrier Frequency', SSH.getBefore()) + if result is not None: + doLoop = False + doOutterLoop = False + fullSyncStatus = True + else: + time.sleep(6) + else: + # Negative messaging --> + result = re.search('No cell synchronization found', SSH.getBefore()) + if result is None: + time.sleep(6) + else: + doLoop = False + fullSyncStatus = False + logging.debug('Found: "No cell synchronization" message! --> try again') + time.sleep(6) + SSH.command('ps -aux | grep --text --color=never softmodem | grep -v grep', '\$', 4) + result = re.search('lte-uesoftmodem', SSH.getBefore()) + if result is not None: + SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal=SIGINT lte-uesoftmodem', '\$', 4) + outterLoopCounter = outterLoopCounter - 1 + if (outterLoopCounter == 0): + doOutterLoop = False + + if fullSyncStatus and gotSyncStatus: + doInterfaceCheck = False + if self.air_interface == 'lte-uesoftmodem': + result = re.search('--no-L2-connect', str(self.Initialize_OAI_UE_args)) + if result is None: + doInterfaceCheck = True + # For the moment, only in explicit noS1 without kernel module (ie w/ tunnel interface) + if self.air_interface == 'nr-uesoftmodem': + result = re.search('--noS1 --nokrnmod 1', str(self.Initialize_OAI_UE_args)) + if result is not None: + doInterfaceCheck = True + if doInterfaceCheck: + SSH.command('ifconfig oaitun_ue1', '\$', 4) + SSH.command('ifconfig oaitun_ue1', '\$', 4) + # ifconfig output is different between ubuntu 16 and ubuntu 18 + result = re.search('inet addr:1|inet 1', SSH.getBefore()) + if result is not None: + logging.debug('\u001B[1m oaitun_ue1 interface is mounted and configured\u001B[0m') + tunnelInterfaceStatus = True + else: + logging.debug(SSH.getBefore()) + logging.error('\u001B[1m oaitun_ue1 interface is either NOT mounted or NOT configured\u001B[0m') + tunnelInterfaceStatus = False + if RAN.eNBmbmsEnables[0]: + SSH.command('ifconfig oaitun_uem1', '\$', 4) + result = re.search('inet addr', SSH.getBefore()) + if result is not None: + logging.debug('\u001B[1m oaitun_uem1 interface is mounted and configured\u001B[0m') + tunnelInterfaceStatus = tunnelInterfaceStatus and True + else: + logging.error('\u001B[1m oaitun_uem1 interface is either NOT mounted or NOT configured\u001B[0m') + tunnelInterfaceStatus = False + else: + tunnelInterfaceStatus = True + else: + tunnelInterfaceStatus = True + + SSH.close() + if fullSyncStatus and gotSyncStatus and tunnelInterfaceStatus: + HTML.CreateHtmlTestRow(self.air_interface + ' ' + self.Initialize_OAI_UE_args, 'OK', CONST.ALL_PROCESSES_OK, 'OAI UE') + logging.debug('\u001B[1m Initialize OAI UE Completed\u001B[0m') + if (self.ADBIPAddress != 'none'): + self.UEDevices = [] + self.UEDevices.append('OAI-UE') + self.UEDevicesStatus = [] + self.UEDevicesStatus.append(CONST.UE_STATUS_DETACHED) + else: + if self.air_interface == 'lte-uesoftmodem': + if RAN.eNBmbmsEnables[0]: + HTML.htmlUEFailureMsg='oaitun_ue1/oaitun_uem1 interfaces are either NOT mounted or NOT configured' + else: + HTML.htmlUEFailureMsg='oaitun_ue1 interface is either NOT mounted or NOT configured' + HTML.CreateHtmlTestRow(self.air_interface + ' ' + self.Initialize_OAI_UE_args, 'KO', CONST.OAI_UE_PROCESS_NO_TUNNEL_INTERFACE, 'OAI UE') + else: + HTML.htmlUEFailureMsg='nr-uesoftmodem did NOT synced' + HTML.CreateHtmlTestRow(self.air_interface + ' ' + self.Initialize_OAI_UE_args, 'KO', CONST.OAI_UE_PROCESS_COULD_NOT_SYNC, 'OAI UE') + logging.error('\033[91mInitialize OAI UE Failed! \033[0m') + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + + def checkDevTTYisUnlocked(self): + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + count = 0 + while count < 5: + SSH.command('echo ' + self.ADBPassword + ' | sudo -S lsof | grep ttyUSB0', '\$', 10) + result = re.search('picocom', SSH.getBefore()) + if result is None: + count = 10 + else: + time.sleep(5) + count = count + 1 + SSH.close() + + def InitializeCatM(self,HTML): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + SSH = sshconnection.SSHConnection() + SSH.enablePicocomClosure() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + # dummy call to start a sudo session. The picocom command does NOT handle well the `sudo -S` + SSH.command('echo ' + self.ADBPassword + ' | sudo -S ls', '\$', 10) + SSH.command('sudo picocom --baud 921600 --flow n --databits 8 /dev/ttyUSB0', 'Terminal ready', 10) + time.sleep(1) + # Calling twice AT to clear all buffers + SSH.command('AT', 'OK|ERROR', 5) + SSH.command('AT', 'OK', 5) + # Doing a power cycle + SSH.command('AT^RESET', 'SIMSTORE,READY', 15) + SSH.command('AT', 'OK|ERROR', 5) + SSH.command('AT', 'OK', 5) + SSH.command('ATE1', 'OK', 5) + # Disabling the Radio + SSH.command('AT+CFUN=0', 'OK', 5) + logging.debug('\u001B[1m Cellular Functionality disabled\u001B[0m') + # Checking if auto-attach is enabled + SSH.command('AT^AUTOATT?', 'OK', 5) + result = re.search('AUTOATT: (?P[0-9\-]+)', SSH.getBefore()) + if result is not None: + if result.group('state') is not None: + autoAttachState = int(result.group('state')) + if autoAttachState is not None: + if autoAttachState == 0: + SSH.command('AT^AUTOATT=1', 'OK', 5) + logging.debug('\u001B[1m Auto-Attach enabled\u001B[0m') + else: + logging.debug('\u001B[1;37;41m Could not check Auto-Attach! \u001B[0m') + # Force closure of picocom but device might still be locked + SSH.close() + SSH.disablePicocomClosure() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + self.checkDevTTYisUnlocked() + + def TerminateCatM(self,HTML): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + SSH = sshconnection.SSHConnection() + SSH.enablePicocomClosure() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + # dummy call to start a sudo session. The picocom command does NOT handle well the `sudo -S` + SSH.command('echo ' + self.ADBPassword + ' | sudo -S ls', '\$', 10) + SSH.command('sudo picocom --baud 921600 --flow n --databits 8 /dev/ttyUSB0', 'Terminal ready', 10) + time.sleep(1) + # Calling twice AT to clear all buffers + SSH.command('AT', 'OK|ERROR', 5) + SSH.command('AT', 'OK', 5) + # Disabling the Radio + SSH.command('AT+CFUN=0', 'OK', 5) + logging.debug('\u001B[1m Cellular Functionality disabled\u001B[0m') + SSH.close() + SSH.disablePicocomClosure() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + self.checkDevTTYisUnlocked() + + def AttachCatM(self,HTML,RAN,COTS_UE): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + SSH = sshconnection.SSHConnection() + SSH.enablePicocomClosure() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + # dummy call to start a sudo session. The picocom command does NOT handle well the `sudo -S` + SSH.command('echo ' + self.ADBPassword + ' | sudo -S ls', '\$', 10) + SSH.command('sudo picocom --baud 921600 --flow n --databits 8 /dev/ttyUSB0', 'Terminal ready', 10) + time.sleep(1) + # Calling twice AT to clear all buffers + SSH.command('AT', 'OK|ERROR', 5) + SSH.command('AT', 'OK', 5) + # Enabling the Radio + SSH.command('AT+CFUN=1', 'SIMSTORE,READY', 5) + logging.debug('\u001B[1m Cellular Functionality enabled\u001B[0m') + time.sleep(4) + # We should check if we register + count = 0 + attach_cnt = 0 + attach_status = False + while count < 5: + SSH.command('AT+CEREG?', 'OK', 5) + result = re.search('CEREG: 2,(?P[0-9\-]+),', SSH.getBefore()) + if result is not None: + mDataConnectionState = int(result.group('state')) + if mDataConnectionState is not None: + if mDataConnectionState == 1: + count = 10 + attach_status = True + result = re.search('CEREG: 2,1,"(?P[0-9A-Z]+)","(?P[0-9A-Z]+)"', SSH.getBefore()) + if result is not None: + networky = result.group('networky') + networkz = result.group('networkz') + logging.debug('\u001B[1m CAT-M module attached to eNB (' + str(networky) + '/' + str(networkz) + ')\u001B[0m') + else: + logging.debug('\u001B[1m CAT-M module attached to eNB\u001B[0m') + else: + logging.debug('+CEREG: 2,' + str(mDataConnectionState)) + attach_cnt = attach_cnt + 1 + else: + logging.debug(SSH.getBefore()) + attach_cnt = attach_cnt + 1 + count = count + 1 + time.sleep(1) + if attach_status: + SSH.command('AT+CESQ', 'OK', 5) + result = re.search('CESQ: 99,99,255,255,(?P[0-9]+),(?P[0-9]+)', SSH.getBefore()) + if result is not None: + nRSRQ = int(result.group('rsrq')) + nRSRP = int(result.group('rsrp')) + if (nRSRQ is not None) and (nRSRP is not None): + logging.debug(' RSRQ = ' + str(-20+(nRSRQ/2)) + ' dB') + logging.debug(' RSRP = ' + str(-140+nRSRP) + ' dBm') + SSH.close() + SSH.disablePicocomClosure() + html_queue = SimpleQueue() + self.checkDevTTYisUnlocked() + if attach_status: + html_cell = '
CAT-M module Attachment Completed in ' + str(attach_cnt+4) + ' seconds'
+			if (nRSRQ is not None) and (nRSRP is not None):
+				html_cell += '\n   RSRQ = ' + str(-20+(nRSRQ/2)) + ' dB'
+				html_cell += '\n   RSRP = ' + str(-140+nRSRP) + ' dBm
' + else: + html_cell += '' + html_queue.put(html_cell) + HTML.CreateHtmlTestRowQueue('N/A', 'OK', 1, html_queue) + else: + logging.error('\u001B[1m CAT-M module Attachment Failed\u001B[0m') + html_cell = '
CAT-M module Attachment Failed
' + html_queue.put(html_cell) + HTML.CreateHtmlTestRowQueue('N/A', 'KO', 1, html_queue) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + + def PingCatM(self,HTML,RAN,EPC,COTS_UE): + if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.SourceCodePath == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + check_eNB = True + check_OAI_UE = False + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + try: + statusQueue = SimpleQueue() + lock = Lock() + SSH = sshconnection.SSHConnection() + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cd ' + EPC.SourceCodePath, '\$', 5) + SSH.command('cd scripts', '\$', 5) + if re.match('OAI', EPC.Type, re.IGNORECASE): + logging.debug('Using the OAI EPC HSS: not implemented yet') + HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) + HTML.CreateHtmlTabFooter(False) + sys.exit(1) + else: + SSH.command('egrep --color=never "Allocated ipv4 addr" /opt/ltebox/var/log/xGwLog.0', '\$', 5) + result = re.search('Allocated ipv4 addr: (?P[0-9\.]+) from Pool', SSH.getBefore()) + if result is not None: + moduleIPAddr = result.group('ipaddr') + else: + HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + ping_time = re.findall("-c (\d+)",str(self.ping_args)) + device_id = 'catm' + ping_status = SSH.command('stdbuf -o0 ping ' + self.ping_args + ' ' + str(moduleIPAddr) + ' 2>&1 | stdbuf -o0 tee ping_' + self.testCase_id + '_' + device_id + '.log', '\$', int(ping_time[0])*1.5) + # TIMEOUT CASE + if ping_status < 0: + message = 'Ping with UE (' + str(moduleIPAddr) + ') crashed due to TIMEOUT!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, moduleIPAddr, device_id, statusQueue, message) + return + result = re.search(', (?P[0-9\.]+)% packet loss, time [0-9\.]+ms', SSH.getBefore()) + if result is None: + message = 'Packet Loss Not Found!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, moduleIPAddr, device_id, statusQueue, message) + return + packetloss = result.group('packetloss') + if float(packetloss) == 100: + message = 'Packet Loss is 100%' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, moduleIPAddr, device_id, statusQueue, message) + return + result = re.search('rtt min\/avg\/max\/mdev = (?P[0-9\.]+)\/(?P[0-9\.]+)\/(?P[0-9\.]+)\/[0-9\.]+ ms', SSH.getBefore()) + if result is None: + message = 'Ping RTT_Min RTT_Avg RTT_Max Not Found!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, moduleIPAddr, device_id, statusQueue, message) + return + rtt_min = result.group('rtt_min') + rtt_avg = result.group('rtt_avg') + rtt_max = result.group('rtt_max') + pal_msg = 'Packet Loss : ' + packetloss + '%' + min_msg = 'RTT(Min) : ' + rtt_min + ' ms' + avg_msg = 'RTT(Avg) : ' + rtt_avg + ' ms' + max_msg = 'RTT(Max) : ' + rtt_max + ' ms' + lock.acquire() + logging.debug('\u001B[1;37;44m ping result (' + moduleIPAddr + ') \u001B[0m') + logging.debug('\u001B[1;34m ' + pal_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + min_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + avg_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + max_msg + '\u001B[0m') + qMsg = pal_msg + '\n' + min_msg + '\n' + avg_msg + '\n' + max_msg + packetLossOK = True + if packetloss is not None: + if float(packetloss) > float(self.ping_packetloss_threshold): + qMsg += '\nPacket Loss too high' + logging.debug('\u001B[1;37;41m Packet Loss too high \u001B[0m') + packetLossOK = False + elif float(packetloss) > 0: + qMsg += '\nPacket Loss is not 0%' + logging.debug('\u001B[1;30;43m Packet Loss is not 0% \u001B[0m') + lock.release() + SSH.close() + html_cell = '
CAT-M module\nIP Address  : ' + moduleIPAddr + '\n' + qMsg + '
' + statusQueue.put(html_cell) + if (packetLossOK): + HTML.CreateHtmlTestRowQueue(self.ping_args, 'OK', 1, statusQueue) + else: + HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', 1, statusQueue) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def AttachUE_common(self, device_id, statusQueue, lock, idx,COTS_UE): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + if self.ADBCentralized: + #RH quick add on to integrate cots control defined by yaml + #if device Id exists in yaml dictionary, we execute the new procedure defined in cots_ue class + #otherwise we use the legacy procedure + if COTS_UE.Check_Exists(device_id): + #switch device to Airplane mode OFF (ie Radio ON) + COTS_UE.Set_Airplane(device_id, 'OFF') + elif device_id == '84B7N16418004022': + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/on"', '\$', 60) + else: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/on', '\$', 60) + else: + # airplane mode off // radio on + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOnCmd[idx], '\$', 60) + time.sleep(2) + max_count = 45 + count = max_count + while count > 0: + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "dumpsys telephony.registry" | grep -m 1 mDataConnectionState', '\$', 15) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "dumpsys telephony.registry"\' | grep -m 1 mDataConnectionState', '\$', 60) + result = re.search('mDataConnectionState.*=(?P[0-9\-]+)', SSH.getBefore()) + if result is None: + logging.debug('\u001B[1;37;41m mDataConnectionState Not Found! \u001B[0m') + lock.acquire() + statusQueue.put(-1) + statusQueue.put(device_id) + statusQueue.put('mDataConnectionState Not Found!') + lock.release() + break + mDataConnectionState = int(result.group('state')) + if mDataConnectionState == 2: + logging.debug('\u001B[1mUE (' + device_id + ') Attach Completed\u001B[0m') + lock.acquire() + statusQueue.put(max_count - count) + statusQueue.put(device_id) + statusQueue.put('Attach Completed') + lock.release() + break + count = count - 1 + if count == 15 or count == 30: + logging.debug('\u001B[1;30;43m Retry UE (' + device_id + ') Flight Mode Off \u001B[0m') + if self.ADBCentralized: + #RH quick add on to intgrate cots control defined by yaml + #if device id exists in yaml dictionary, we execute the new procedure defined in cots_ue class + #otherwise we use the legacy procedure + if COTS_UE.Check_Exists(device_id): + #switch device to Airplane mode ON (ie Radio OFF) + COTS_UE.Set_Airplane(device_id, 'ON') + elif device_id == '84B7N16418004022': + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/off"', '\$', 60) + else: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/off', '\$', 60) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) + time.sleep(0.5) + if self.ADBCentralized: + #RH quick add on to integrate cots control defined by yaml + #if device id exists in yaml dictionary, we execute the new procedre defined incots_ue class + #otherwise we use the legacy procedure + if COTS_UE.Check_Exists(device_id): + #switch device to Airplane mode OFF (ie Radio ON) + COTS_UE.Set_Airplane(device_id, 'OFF') + elif device_id == '84B7N16418004022': + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/on"', '\$', 60) + else: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/on', '\$', 60) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOnCmd[idx], '\$', 60) + time.sleep(0.5) + logging.debug('\u001B[1mWait UE (' + device_id + ') a second until mDataConnectionState=2 (' + str(max_count-count) + ' times)\u001B[0m') + time.sleep(1) + if count == 0: + logging.debug('\u001B[1;37;41m UE (' + device_id + ') Attach Failed \u001B[0m') + lock.acquire() + statusQueue.put(-1) + statusQueue.put(device_id) + statusQueue.put('Attach Failed') + lock.release() + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def AttachUE(self,HTML,RAN,EPC,COTS_UE): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + check_eNB = True + check_OAI_UE = False + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow('N/A', 'KO', pStatus) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + multi_jobs = [] + status_queue = SimpleQueue() + lock = Lock() + nb_ue_to_connect = 0 + for device_id in self.UEDevices: + if (self.nbMaxUEtoAttach == -1) or (nb_ue_to_connect < self.nbMaxUEtoAttach): + self.UEDevicesStatus[nb_ue_to_connect] = CONST.UE_STATUS_ATTACHING + p = Process(target = self.AttachUE_common, args = (device_id, status_queue, lock,nb_ue_to_connect,COTS_UE,)) + p.daemon = True + p.start() + multi_jobs.append(p) + nb_ue_to_connect = nb_ue_to_connect + 1 + for job in multi_jobs: + job.join() + + if (status_queue.empty()): + HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ALL_PROCESSES_OK) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + else: + attach_status = True + html_queue = SimpleQueue() + while (not status_queue.empty()): + count = status_queue.get() + if (count < 0): + attach_status = False + device_id = status_queue.get() + message = status_queue.get() + if (count < 0): + html_cell = '
UE (' + device_id + ')\n' + message + '
' + else: + html_cell = '
UE (' + device_id + ')\n' + message + ' in ' + str(count + 2) + ' seconds
' + html_queue.put(html_cell) + if (attach_status): + cnt = 0 + while cnt < len(self.UEDevices): + if self.UEDevicesStatus[cnt] == CONST.UE_STATUS_ATTACHING: + self.UEDevicesStatus[cnt] = CONST.UE_STATUS_ATTACHED + cnt += 1 + HTML.CreateHtmlTestRowQueue('N/A', 'OK', len(self.UEDevices), html_queue) + result = re.search('T_stdout', str(RAN.Initialize_eNB_args)) + if result is not None: + logging.debug('Waiting 5 seconds to fill up record file') + time.sleep(5) + else: + HTML.CreateHtmlTestRowQueue('N/A', 'KO', len(self.UEDevices), html_queue) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + + def DetachUE_common(self, device_id, idx,COTS_UE): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + if self.ADBCentralized: + #RH quick add on to integrate cots control defined by yaml + #if device id exists in yaml dictionary, we execute the new procedure defined in cots_ue class + #otherwise we use the legacy procedure + if COTS_UE.Check_Exists(device_id): + #switch device to Airplane mode ON (ie Radio OFF) + COTS_UE.Set_Airplane(device_id,'ON') + elif device_id == '84B7N16418004022': + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/off"', '\$', 60) + else: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/off', '\$', 60) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) + logging.debug('\u001B[1mUE (' + device_id + ') Detach Completed\u001B[0m') + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def DetachUE(self,HTML,RAN,EPC,COTS_UE): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + check_eNB = True + check_OAI_UE = False + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow('N/A', 'KO', pStatus) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + multi_jobs = [] + cnt = 0 + for device_id in self.UEDevices: + self.UEDevicesStatus[cnt] = CONST.UE_STATUS_DETACHING + p = Process(target = self.DetachUE_common, args = (device_id,cnt,COTS_UE,)) + p.daemon = True + p.start() + multi_jobs.append(p) + cnt += 1 + for job in multi_jobs: + job.join() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + result = re.search('T_stdout', str(RAN.Initialize_eNB_args)) + if result is not None: + logging.debug('Waiting 5 seconds to fill up record file') + time.sleep(5) + cnt = 0 + while cnt < len(self.UEDevices): + self.UEDevicesStatus[cnt] = CONST.UE_STATUS_DETACHED + cnt += 1 + + def RebootUE_common(self, device_id): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + previousmDataConnectionStates = [] + # Save mDataConnectionState + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell dumpsys telephony.registry | grep mDataConnectionState', '\$', 15) + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell dumpsys telephony.registry | grep mDataConnectionState', '\$', 15) + result = re.search('mDataConnectionState.*=(?P[0-9\-]+)', SSH.getBefore()) + if result is None: + logging.debug('\u001B[1;37;41m mDataConnectionState Not Found! \u001B[0m') + sys.exit(1) + previousmDataConnectionStates.append(int(result.group('state'))) + # Reboot UE + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell reboot', '\$', 10) + time.sleep(60) + previousmDataConnectionState = previousmDataConnectionStates.pop(0) + count = 180 + while count > 0: + count = count - 1 + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell dumpsys telephony.registry | grep mDataConnectionState', '\$', 15) + result = re.search('mDataConnectionState.*=(?P[0-9\-]+)', SSH.getBefore()) + if result is None: + mDataConnectionState = None + else: + mDataConnectionState = int(result.group('state')) + logging.debug('mDataConnectionState = ' + result.group('state')) + if mDataConnectionState is None or (previousmDataConnectionState == 2 and mDataConnectionState != 2): + logging.debug('\u001B[1mWait UE (' + device_id + ') a second until reboot completion (' + str(180-count) + ' times)\u001B[0m') + time.sleep(1) + else: + logging.debug('\u001B[1mUE (' + device_id + ') Reboot Completed\u001B[0m') + break + if count == 0: + logging.debug('\u001B[1;37;41m UE (' + device_id + ') Reboot Failed \u001B[0m') + sys.exit(1) + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def RebootUE(self,HTML,RAN,EPC): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + check_eNB = True + check_OAI_UE = False + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow('N/A', 'KO', pStatus) + HTML.CreateHtmlTabFooter(False) + sys.exit(1) + multi_jobs = [] + for device_id in self.UEDevices: + p = Process(target = self.RebootUE_common, args = (device_id,)) + p.daemon = True + p.start() + multi_jobs.append(p) + for job in multi_jobs: + job.join() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + + def DataDisableUE_common(self, device_id, idx): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + # disable data service + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "svc data disable"', '\$', 60) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "svc data disable"\'', '\$', 60) + logging.debug('\u001B[1mUE (' + device_id + ') Disabled Data Service\u001B[0m') + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def DataDisableUE(self,HTML): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + multi_jobs = [] + i = 0 + for device_id in self.UEDevices: + p = Process(target = self.DataDisableUE_common, args = (device_id,i,)) + p.daemon = True + p.start() + multi_jobs.append(p) + i += 1 + for job in multi_jobs: + job.join() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + + def DataEnableUE_common(self, device_id, idx): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + # enable data service + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "svc data enable"', '\$', 60) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "svc data enable"\'', '\$', 60) + logging.debug('\u001B[1mUE (' + device_id + ') Enabled Data Service\u001B[0m') + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def DataEnableUE(self,HTML): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + multi_jobs = [] + i = 0 + for device_id in self.UEDevices: + p = Process(target = self.DataEnableUE_common, args = (device_id,i,)) + p.daemon = True + p.start() + multi_jobs.append(p) + i += 1 + for job in multi_jobs: + job.join() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + + def GetAllUEDevices(self, terminate_ue_flag): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + if self.ADBCentralized: + SSH.command('adb devices', '\$', 15) + self.UEDevices = re.findall("\\\\r\\\\n([A-Za-z0-9]+)\\\\tdevice",SSH.getBefore()) + #report number and id of devices found + msg = "UEDevices found by GetAllUEDevices : " + " ".join(self.UEDevices) + logging.debug(msg) + SSH.close() + else: + if (os.path.isfile('./phones_list.txt')): + os.remove('./phones_list.txt') + SSH.command('ls /etc/*/phones*.txt', '\$', 5) + result = re.search('/etc/ci/phones_list.txt', SSH.getBefore()) + SSH.close() + if (result is not None) and (len(self.UEDevices) == 0): + SSH.copyin(self.ADBIPAddress, self.ADBUserName, self.ADBPassword, '/etc/ci/phones_list.txt', '.') + if (os.path.isfile('./phones_list.txt')): + phone_list_file = open('./phones_list.txt', 'r') + for line in phone_list_file.readlines(): + line = line.strip() + result = re.search('^#', line) + if result is not None: + continue + comma_split = line.split(",") + self.UEDevices.append(comma_split[0]) + self.UEDevicesRemoteServer.append(comma_split[1]) + self.UEDevicesRemoteUser.append(comma_split[2]) + self.UEDevicesOffCmd.append(comma_split[3]) + self.UEDevicesOnCmd.append(comma_split[4]) + self.UEDevicesRebootCmd.append(comma_split[5]) + phone_list_file.close() + + if terminate_ue_flag == True: + if len(self.UEDevices) == 0: + logging.debug('\u001B[1;37;41m UE Not Found! \u001B[0m') + sys.exit(1) + if len(self.UEDevicesStatus) == 0: + cnt = 0 + while cnt < len(self.UEDevices): + self.UEDevicesStatus.append(CONST.UE_STATUS_DETACHED) + cnt += 1 + + def GetAllCatMDevices(self, terminate_ue_flag): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + if self.ADBCentralized: + SSH.command('lsusb | egrep "Future Technology Devices International, Ltd FT2232C" | sed -e "s#:.*##" -e "s# #_#g"', '\$', 15) + #self.CatMDevices = re.findall("\\\\r\\\\n([A-Za-z0-9_]+)",SSH.getBefore()) + self.CatMDevices = re.findall("\\\\r\\\\n([A-Za-z0-9_]+)",SSH.getBefore()) + else: + if (os.path.isfile('./modules_list.txt')): + os.remove('./modules_list.txt') + SSH.command('ls /etc/*/modules*.txt', '\$', 5) + result = re.search('/etc/ci/modules_list.txt', SSH.getBefore()) + SSH.close() + if result is not None: + logging.debug('Found a module list file on ADB server') + if terminate_ue_flag == True: + if len(self.CatMDevices) == 0: + logging.debug('\u001B[1;37;41m CAT-M UE Not Found! \u001B[0m') + sys.exit(1) + SSH.close() + + def CheckUEStatus_common(self, lock, device_id, statusQueue, idx): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "dumpsys telephony.registry"', '\$', 15) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "dumpsys telephony.registry"\'', '\$', 60) + result = re.search('mServiceState=(?P[0-9]+)', SSH.getBefore()) + serviceState = 'Service State: UNKNOWN' + if result is not None: + lServiceState = int(result.group('serviceState')) + if lServiceState == 3: + serviceState = 'Service State: RADIO_POWERED_OFF' + if lServiceState == 1: + serviceState = 'Service State: OUT_OF_SERVICE' + if lServiceState == 0: + serviceState = 'Service State: IN_SERVICE' + if lServiceState == 2: + serviceState = 'Service State: EMERGENCY_ONLY' + result = re.search('mDataConnectionState=(?P[0-9]+)', SSH.getBefore()) + dataConnectionState = 'Data State: UNKNOWN' + if result is not None: + lDataConnectionState = int(result.group('dataConnectionState')) + if lDataConnectionState == 0: + dataConnectionState = 'Data State: DISCONNECTED' + if lDataConnectionState == 1: + dataConnectionState = 'Data State: CONNECTING' + if lDataConnectionState == 2: + dataConnectionState = 'Data State: CONNECTED' + if lDataConnectionState == 3: + dataConnectionState = 'Data State: SUSPENDED' + result = re.search('mDataConnectionReason=(?P[0-9a-zA-Z_]+)', SSH.getBefore()) + dataConnectionReason = 'Data Reason: UNKNOWN' + if result is not None: + dataConnectionReason = 'Data Reason: ' + result.group('dataConnectionReason') + lock.acquire() + logging.debug('\u001B[1;37;44m Status Check (' + str(device_id) + ') \u001B[0m') + logging.debug('\u001B[1;34m ' + serviceState + '\u001B[0m') + logging.debug('\u001B[1;34m ' + dataConnectionState + '\u001B[0m') + logging.debug('\u001B[1;34m ' + dataConnectionReason + '\u001B[0m') + statusQueue.put(0) + statusQueue.put(device_id) + qMsg = serviceState + '\n' + dataConnectionState + '\n' + dataConnectionReason + statusQueue.put(qMsg) + lock.release() + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def CheckStatusUE(self,HTML,RAN,EPC,COTS_UE): + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + check_eNB = True + check_OAI_UE = False + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow('N/A', 'KO', pStatus) + HTML.CreateHtmlTabFooter(False) + sys.exit(1) + multi_jobs = [] + lock = Lock() + status_queue = SimpleQueue() + i = 0 + for device_id in self.UEDevices: + p = Process(target = self.CheckUEStatus_common, args = (lock,device_id,status_queue,i,)) + p.daemon = True + p.start() + multi_jobs.append(p) + i += 1 + for job in multi_jobs: + job.join() + if RAN.flexranCtrlInstalled and RAN.flexranCtrlStarted: + SSH = sshconnection.SSHConnection() + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cd /opt/flexran_rtc', '\$', 5) + SSH.command('curl http://localhost:9999/stats | jq \'.\' > log/check_status_' + self.testCase_id + '.log 2>&1', '\$', 5) + SSH.command('cat log/check_status_' + self.testCase_id + '.log | jq \'.eNB_config[0].UE\' | grep -c rnti | sed -e "s#^#Nb Connected UE = #"', '\$', 5) + result = re.search('Nb Connected UE = (?P[0-9]+)', SSH.getBefore()) + passStatus = True + if result is not None: + nb_ues = int(result.group('nb_ues')) + htmlOptions = 'Nb Connected UE(s) to eNB = ' + str(nb_ues) + logging.debug('\u001B[1;37;44m ' + htmlOptions + ' \u001B[0m') + if self.expectedNbOfConnectedUEs > -1: + if nb_ues != self.expectedNbOfConnectedUEs: + passStatus = False + else: + htmlOptions = 'N/A' + SSH.close() + else: + passStatus = True + htmlOptions = 'N/A' + + if (status_queue.empty()): + HTML.CreateHtmlTestRow(htmlOptions, 'KO', CONST.ALL_PROCESSES_OK) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + else: + check_status = True + html_queue = SimpleQueue() + while (not status_queue.empty()): + count = status_queue.get() + if (count < 0): + check_status = False + device_id = status_queue.get() + message = status_queue.get() + html_cell = '
UE (' + device_id + ')\n' + message + '
' + html_queue.put(html_cell) + if check_status and passStatus: + HTML.CreateHtmlTestRowQueue(htmlOptions, 'OK', len(self.UEDevices), html_queue) + else: + HTML.CreateHtmlTestRowQueue(htmlOptions, 'KO', len(self.UEDevices), html_queue) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + + def GetAllUEIPAddresses(self): + SSH = sshconnection.SSHConnection() + if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + ue_ip_status = 0 + self.UEIPAddresses = [] + if (len(self.UEDevices) == 1) and (self.UEDevices[0] == 'OAI-UE'): + if self.UEIPAddress == '' or self.UEUserName == '' or self.UEPassword == '' or self.UESourceCodePath == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('ifconfig oaitun_ue1', '\$', 4) + result = re.search('inet addr:(?P[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)|inet (?P[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)', SSH.getBefore()) + if result is not None: + if result.group('ueipaddress') is not None: + UE_IPAddress = result.group('ueipaddress') + else: + UE_IPAddress = result.group('ueipaddress2') + logging.debug('\u001B[1mUE (' + self.UEDevices[0] + ') IP Address is ' + UE_IPAddress + '\u001B[0m') + self.UEIPAddresses.append(UE_IPAddress) + else: + logging.debug('\u001B[1;37;41m UE IP Address Not Found! \u001B[0m') + ue_ip_status -= 1 + SSH.close() + return ue_ip_status + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + idx = 0 + for device_id in self.UEDevices: + if self.UEDevicesStatus[idx] != CONST.UE_STATUS_ATTACHED: + idx += 1 + continue + count = 0 + while count < 4: + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "ip addr show | grep rmnet"', '\$', 15) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "ip addr show | grep rmnet"\'', '\$', 60) + result = re.search('inet (?P[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\/[0-9]+[0-9a-zA-Z\.\s]+', SSH.getBefore()) + if result is None: + logging.debug('\u001B[1;37;41m UE IP Address Not Found! \u001B[0m') + time.sleep(1) + count += 1 + else: + count = 10 + if count < 9: + ue_ip_status -= 1 + continue + UE_IPAddress = result.group('ueipaddress') + logging.debug('\u001B[1mUE (' + device_id + ') IP Address is ' + UE_IPAddress + '\u001B[0m') + for ueipaddress in self.UEIPAddresses: + if ueipaddress == UE_IPAddress: + logging.debug('\u001B[1mUE (' + device_id + ') IP Address ' + UE_IPAddress + ': has already been allocated to another device !' + '\u001B[0m') + ue_ip_status -= 1 + continue + self.UEIPAddresses.append(UE_IPAddress) + idx += 1 + SSH.close() + return ue_ip_status + + def ping_iperf_wrong_exit(self, lock, UE_IPAddress, device_id, statusQueue, message): + lock.acquire() + statusQueue.put(-1) + statusQueue.put(device_id) + statusQueue.put(UE_IPAddress) + statusQueue.put(message) + lock.release() + + def Ping_common(self, lock, UE_IPAddress, device_id, statusQueue,EPC): + try: + SSH = sshconnection.SSHConnection() + # Launch ping on the EPC side (true for ltebox and old open-air-cn) + # But for OAI-Rel14-CUPS, we launch from python executor + launchFromEpc = True + if re.match('OAI-Rel14-CUPS', EPC.Type, re.IGNORECASE): + launchFromEpc = False + ping_time = re.findall("-c (\d+)",str(self.ping_args)) + + if launchFromEpc: + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cd ' + EPC.SourceCodePath, '\$', 5) + SSH.command('cd scripts', '\$', 5) + ping_status = SSH.command('stdbuf -o0 ping ' + self.ping_args + ' ' + UE_IPAddress + ' 2>&1 | stdbuf -o0 tee ping_' + self.testCase_id + '_' + device_id + '.log', '\$', int(ping_time[0])*1.5) + else: + cmd = 'ping ' + self.ping_args + ' ' + UE_IPAddress + ' 2>&1 > ping_' + self.testCase_id + '_' + device_id + '.log' + message = cmd + '\n' + logging.debug(cmd) + ret = subprocess.run(cmd, shell=True) + ping_status = ret.returncode + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'ping_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cat ' + EPC.SourceCodePath + '/scripts/ping_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + # TIMEOUT CASE + if ping_status < 0: + message = 'Ping with UE (' + str(UE_IPAddress) + ') crashed due to TIMEOUT!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) + return + result = re.search(', (?P[0-9\.]+)% packet loss, time [0-9\.]+ms', SSH.getBefore()) + if result is None: + message = 'Packet Loss Not Found!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) + return + packetloss = result.group('packetloss') + if float(packetloss) == 100: + message = 'Packet Loss is 100%' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) + return + result = re.search('rtt min\/avg\/max\/mdev = (?P[0-9\.]+)\/(?P[0-9\.]+)\/(?P[0-9\.]+)\/[0-9\.]+ ms', SSH.getBefore()) + if result is None: + message = 'Ping RTT_Min RTT_Avg RTT_Max Not Found!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) + return + rtt_min = result.group('rtt_min') + rtt_avg = result.group('rtt_avg') + rtt_max = result.group('rtt_max') + pal_msg = 'Packet Loss : ' + packetloss + '%' + min_msg = 'RTT(Min) : ' + rtt_min + ' ms' + avg_msg = 'RTT(Avg) : ' + rtt_avg + ' ms' + max_msg = 'RTT(Max) : ' + rtt_max + ' ms' + lock.acquire() + logging.debug('\u001B[1;37;44m ping result (' + UE_IPAddress + ') \u001B[0m') + logging.debug('\u001B[1;34m ' + pal_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + min_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + avg_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + max_msg + '\u001B[0m') + qMsg = pal_msg + '\n' + min_msg + '\n' + avg_msg + '\n' + max_msg + packetLossOK = True + if packetloss is not None: + if float(packetloss) > float(self.ping_packetloss_threshold): + qMsg += '\nPacket Loss too high' + logging.debug('\u001B[1;37;41m Packet Loss too high \u001B[0m') + packetLossOK = False + elif float(packetloss) > 0: + qMsg += '\nPacket Loss is not 0%' + logging.debug('\u001B[1;30;43m Packet Loss is not 0% \u001B[0m') + if (packetLossOK): + statusQueue.put(0) + else: + statusQueue.put(-1) + statusQueue.put(device_id) + statusQueue.put(UE_IPAddress) + statusQueue.put(qMsg) + lock.release() + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def PingNoS1_wrong_exit(self, qMsg,HTML): + html_queue = SimpleQueue() + html_cell = '
OAI UE ping result\n' + qMsg + '
' + html_queue.put(html_cell) + HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', len(self.UEDevices), html_queue) + + def PingNoS1(self,HTML,RAN,EPC,COTS_UE): + SSH=sshconnection.SSHConnection() + check_eNB = True + check_OAI_UE = True + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + ping_from_eNB = re.search('oaitun_enb1', str(self.ping_args)) + if ping_from_eNB is not None: + if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + else: + if self.UEIPAddress == '' or self.UEUserName == '' or self.UEPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + try: + if ping_from_eNB is not None: + SSH.open(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword) + SSH.command('cd ' + RAN.eNBSourceCodePath + '/cmake_targets/', '\$', 5) + else: + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets/', '\$', 5) + ping_time = re.findall("-c (\d+)",str(self.ping_args)) + ping_status = SSH.command('stdbuf -o0 ping ' + self.ping_args + ' 2>&1 | stdbuf -o0 tee ping_' + self.testCase_id + '.log', '\$', int(ping_time[0])*1.5) + # TIMEOUT CASE + if ping_status < 0: + message = 'Ping with OAI UE crashed due to TIMEOUT!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + self.PingNoS1_wrong_exit(message,HTML) + return + result = re.search(', (?P[0-9\.]+)% packet loss, time [0-9\.]+ms', SSH.getBefore()) + if result is None: + message = 'Packet Loss Not Found!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + self.PingNoS1_wrong_exit(message,HTML) + return + packetloss = result.group('packetloss') + if float(packetloss) == 100: + message = 'Packet Loss is 100%' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + self.PingNoS1_wrong_exit(message,HTML) + return + result = re.search('rtt min\/avg\/max\/mdev = (?P[0-9\.]+)\/(?P[0-9\.]+)\/(?P[0-9\.]+)\/[0-9\.]+ ms', SSH.getBefore()) + if result is None: + message = 'Ping RTT_Min RTT_Avg RTT_Max Not Found!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + self.PingNoS1_wrong_exit(message,HTML) + return + rtt_min = result.group('rtt_min') + rtt_avg = result.group('rtt_avg') + rtt_max = result.group('rtt_max') + pal_msg = 'Packet Loss : ' + packetloss + '%' + min_msg = 'RTT(Min) : ' + rtt_min + ' ms' + avg_msg = 'RTT(Avg) : ' + rtt_avg + ' ms' + max_msg = 'RTT(Max) : ' + rtt_max + ' ms' + logging.debug('\u001B[1;37;44m OAI UE ping result \u001B[0m') + logging.debug('\u001B[1;34m ' + pal_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + min_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + avg_msg + '\u001B[0m') + logging.debug('\u001B[1;34m ' + max_msg + '\u001B[0m') + qMsg = pal_msg + '\n' + min_msg + '\n' + avg_msg + '\n' + max_msg + packetLossOK = True + if packetloss is not None: + if float(packetloss) > float(self.ping_packetloss_threshold): + qMsg += '\nPacket Loss too high' + logging.debug('\u001B[1;37;41m Packet Loss too high \u001B[0m') + packetLossOK = False + elif float(packetloss) > 0: + qMsg += '\nPacket Loss is not 0%' + logging.debug('\u001B[1;30;43m Packet Loss is not 0% \u001B[0m') + SSH.close() + html_queue = SimpleQueue() + ip_addr = 'TBD' + html_cell = '
OAI UE ping result\n' + qMsg + '
' + html_queue.put(html_cell) + if packetLossOK: + HTML.CreateHtmlTestRowQueue(self.ping_args, 'OK', len(self.UEDevices), html_queue) + else: + HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', len(self.UEDevices), html_queue) + + # copying on the EPC server for logCollection + if ping_from_eNB is not None: + copyin_res = SSH.copyin(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath + '/cmake_targets/ping_' + self.testCase_id + '.log', '.') + else: + copyin_res = SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/ping_' + self.testCase_id + '.log', '.') + if (copyin_res == 0): + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'ping_' + self.testCase_id + '.log', EPC.SourceCodePath + '/scripts') + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def Ping(self,HTML,RAN,EPC,COTS_UE): + result = re.search('noS1', str(RAN.Initialize_eNB_args)) + if result is not None: + self.PingNoS1(HTML,RAN,EPC,COTS_UE) + return + if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.SourceCodePath == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + check_eNB = True + if (len(self.UEDevices) == 1) and (self.UEDevices[0] == 'OAI-UE'): + check_OAI_UE = True + else: + check_OAI_UE = False + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + ueIpStatus = self.GetAllUEIPAddresses() + if (ueIpStatus < 0): + HTML.CreateHtmlTestRow(self.ping_args, 'KO', CONST.UE_IP_ADDRESS_ISSUE) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + multi_jobs = [] + i = 0 + lock = Lock() + status_queue = SimpleQueue() + for UE_IPAddress in self.UEIPAddresses: + device_id = self.UEDevices[i] + p = Process(target = self.Ping_common, args = (lock,UE_IPAddress,device_id,status_queue,EPC,)) + 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.ping_args, 'KO', CONST.ALL_PROCESSES_OK) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + else: + ping_status = True + html_queue = SimpleQueue() + while (not status_queue.empty()): + count = status_queue.get() + if (count < 0): + ping_status = False + device_id = status_queue.get() + ip_addr = status_queue.get() + message = status_queue.get() + html_cell = '
UE (' + device_id + ')\nIP Address  : ' + ip_addr + '\n' + message + '
' + html_queue.put(html_cell) + if (ping_status): + HTML.CreateHtmlTestRowQueue(self.ping_args, 'OK', len(self.UEDevices), html_queue) + else: + HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', len(self.UEDevices), html_queue) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + + def Iperf_ComputeTime(self): + result = re.search('-t (?P\d+)', str(self.iperf_args)) + if result is None: + logging.debug('\u001B[1;37;41m Iperf time Not Found! \u001B[0m') + sys.exit(1) + return result.group('iperf_time') + + 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') + sys.exit(1) + iperf_bandwidth = result.group('iperf_bandwidth') + if self.iperf_profile == 'balanced': + iperf_bandwidth_new = float(iperf_bandwidth)/ue_num + if self.iperf_profile == 'single-ue': + iperf_bandwidth_new = float(iperf_bandwidth) + if self.iperf_profile == 'unbalanced': + # residual is 2% of max bw + residualBW = float(iperf_bandwidth) / 50 + if idx == 0: + iperf_bandwidth_new = float(iperf_bandwidth) - ((ue_num - 1) * residualBW) + else: + iperf_bandwidth_new = residualBW + iperf_bandwidth_str = '-b ' + iperf_bandwidth + iperf_bandwidth_str_new = '-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') + sys.exit(1) + return result + + def Iperf_analyzeV2TCPOutput(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options,EPC,SSH): + + SSH.command('awk -f /tmp/tcp_iperf_stats.awk ' + EPC.SourceCodePath + '/scripts/iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 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()) + if result is not None: + avgbitrate = result.group('average') + maxbitrate = result.group('maximum') + minbitrate = result.group('minimum') + lock.acquire() + logging.debug('\u001B[1;37;44m TCP iperf result (' + UE_IPAddress + ') \u001B[0m') + msg = 'TCP Stats :\n' + if avgbitrate is not None: + logging.debug('\u001B[1;34m Avg Bitrate : ' + avgbitrate + '\u001B[0m') + msg += 'Avg Bitrate : ' + avgbitrate + '\n' + if maxbitrate is not None: + logging.debug('\u001B[1;34m Max Bitrate : ' + maxbitrate + '\u001B[0m') + msg += 'Max Bitrate : ' + maxbitrate + '\n' + if minbitrate is not None: + logging.debug('\u001B[1;34m Min Bitrate : ' + minbitrate + '\u001B[0m') + msg += 'Min Bitrate : ' + minbitrate + '\n' + statusQueue.put(0) + statusQueue.put(device_id) + statusQueue.put(UE_IPAddress) + statusQueue.put(msg) + lock.release() + + return 0 + + def Iperf_analyzeV2Output(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options,EPC,SSH): + + result = re.search('-u', str(iperf_real_options)) + if result is None: + logging.debug('Into Iperf_analyzeV2TCPOutput client') + response = self.Iperf_analyzeV2TCPOutput(lock, UE_IPAddress, device_id, statusQueue, iperf_real_options,EPC,SSH) + logging.debug('Iperf_analyzeV2TCPOutput response returned value = ' + str(response)) + 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') + else: + logging.debug('\u001B[1;37;41m Server Report and Connection refused Not Found! \u001B[0m') + return -1 + # Computing the requested bandwidth in float + result = re.search('-b (?P[0-9\.]+)[KMG]', str(iperf_real_options)) + if result is not None: + req_bandwidth = result.group('iperf_bandwidth') + req_bw = float(req_bandwidth) + result = re.search('-b [0-9\.]+K', str(iperf_real_options)) + if result is not None: + req_bandwidth = '%.1f Kbits/sec' % req_bw + req_bw = req_bw * 1000 + result = re.search('-b [0-9\.]+M', str(iperf_real_options)) + if result is not None: + req_bandwidth = '%.1f Mbits/sec' % req_bw + req_bw = req_bw * 1000000 + result = re.search('-b [0-9\.]+G', str(iperf_real_options)) + if result is not None: + req_bandwidth = '%.1f Gbits/sec' % req_bw + req_bw = req_bw * 1000000000 + + result = re.search('Server Report:\\\\r\\\\n(?:|\[ *\d+\].*) (?P[0-9\.]+ [KMG]bits\/sec) +(?P[0-9\.]+ ms) +(\d+\/..\d+) +(\((?P[0-9\.]+)%\))', SSH.getBefore()) + if result is not None: + bitrate = result.group('bitrate') + packetloss = result.group('packetloss') + jitter = result.group('jitter') + lock.acquire() + logging.debug('\u001B[1;37;44m iperf result (' + UE_IPAddress + ') \u001B[0m') + iperfStatus = True + msg = 'Req Bitrate : ' + req_bandwidth + '\n' + logging.debug('\u001B[1;34m Req Bitrate : ' + req_bandwidth + '\u001B[0m') + if bitrate is not None: + msg += 'Bitrate : ' + bitrate + '\n' + logging.debug('\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'))) + result = re.search('[0-9\.]+ K', bitrate) + if result is not None: + actual_bw = actual_bw * 1000 + result = re.search('[0-9\.]+ M', bitrate) + if result is not None: + actual_bw = actual_bw * 1000000 + result = re.search('[0-9\.]+ G', bitrate) + if result is not None: + actual_bw = actual_bw * 1000000000 + br_loss = 100 * actual_bw / req_bw + bitperf = '%.2f ' % br_loss + msg += 'Bitrate Perf: ' + bitperf + '%\n' + logging.debug('\u001B[1;34m Bitrate Perf: ' + bitperf + '%\u001B[0m') + if packetloss is not None: + msg += 'Packet Loss : ' + packetloss + '%\n' + logging.debug('\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 += 'Jitter : ' + jitter + '\n' + logging.debug('\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 + else: + return -2 + + def Iperf_analyzeV2Server(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options): + if (not os.path.isfile('iperf_server_' + self.testCase_id + '_' + device_id + '.log')): + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Could not analyze from server log') + return + # 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 + else: + req_bandwidth = result.group('iperf_bandwidth') + req_bw = float(req_bandwidth) + result = re.search('-b [0-9\.]+K', str(iperf_real_options)) + if result is not None: + req_bandwidth = '%.1f Kbits/sec' % req_bw + req_bw = req_bw * 1000 + result = re.search('-b [0-9\.]+M', str(iperf_real_options)) + if result is not None: + req_bandwidth = '%.1f Mbits/sec' % req_bw + req_bw = req_bw * 1000000 + result = re.search('-b [0-9\.]+G', str(iperf_real_options)) + if result is not None: + req_bandwidth = '%.1f Gbits/sec' % req_bw + req_bw = req_bw * 1000000000 + + server_file = open('iperf_server_' + self.testCase_id + '_' + device_id + '.log', 'r') + br_sum = 0.0 + ji_sum = 0.0 + pl_sum = 0 + ps_sum = 0 + row_idx = 0 + for line in server_file.readlines(): + result = re.search('(?P[0-9\.]+ [KMG]bits\/sec) +(?P[0-9\.]+ ms) +(?P[0-9]+)/ +(?P[0-9]+)', str(line)) + if result is not None: + bitrate = result.group('bitrate') + jitter = result.group('jitter') + packetlost = result.group('lostPack') + packetsent = result.group('sentPack') + br = bitrate.split(' ') + ji = jitter.split(' ') + row_idx = row_idx + 1 + curr_br = float(br[0]) + pl_sum = pl_sum + int(packetlost) + ps_sum = ps_sum + int(packetsent) + if (br[1] == 'Kbits/sec'): + curr_br = curr_br * 1000 + if (br[1] == 'Mbits/sec'): + curr_br = curr_br * 1000 * 1000 + br_sum = curr_br + br_sum + ji_sum = float(ji[0]) + ji_sum + if (row_idx > 0): + br_sum = br_sum / row_idx + ji_sum = ji_sum / row_idx + br_loss = 100 * br_sum / req_bw + if (br_sum > 1000): + br_sum = br_sum / 1000 + if (br_sum > 1000): + br_sum = br_sum / 1000 + bitrate = '%.2f Mbits/sec' % br_sum + else: + bitrate = '%.2f Kbits/sec' % br_sum + else: + bitrate = '%.2f bits/sec' % br_sum + bitperf = '%.2f ' % br_loss + bitperf += '%' + jitter = '%.2f ms' % (ji_sum) + if (ps_sum > 0): + pl = float(100 * pl_sum / ps_sum) + packetloss = '%2.1f ' % (pl) + packetloss += '%' + else: + packetloss = 'unknown' + lock.acquire() + if (br_loss < 90): + statusQueue.put(1) + else: + statusQueue.put(0) + statusQueue.put(device_id) + statusQueue.put(UE_IPAddress) + req_msg = 'Req Bitrate : ' + req_bandwidth + bir_msg = 'Bitrate : ' + bitrate + brl_msg = 'Bitrate Perf: ' + bitperf + jit_msg = 'Jitter : ' + jitter + pal_msg = 'Packet Loss : ' + packetloss + statusQueue.put(req_msg + '\n' + bir_msg + '\n' + brl_msg + '\n' + jit_msg + '\n' + pal_msg + '\n') + logging.debug('\u001B[1;37;45m iperf result (' + UE_IPAddress + ') \u001B[0m') + logging.debug('\u001B[1;35m ' + req_msg + '\u001B[0m') + logging.debug('\u001B[1;35m ' + bir_msg + '\u001B[0m') + logging.debug('\u001B[1;35m ' + brl_msg + '\u001B[0m') + logging.debug('\u001B[1;35m ' + jit_msg + '\u001B[0m') + logging.debug('\u001B[1;35m ' + pal_msg + '\u001B[0m') + lock.release() + else: + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, '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('\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('\u001B[1;37;44m iperf result (' + UE_IPAddress + ') \u001B[0m') + logging.debug('\u001B[1;34m Bitrate : ' + bitrate + '\u001B[0m') + msg = 'Bitrate : ' + bitrate + '\n' + iperfStatus = True + if packetloss is not None: + logging.debug('\u001B[1;34m Packet Loss : ' + packetloss + '%\u001B[0m') + msg += '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_UL_common(self, lock, UE_IPAddress, device_id, idx, ue_num, statusQueue,EPC): + SSH = sshconnection.SSHConnection() + udpIperf = True + result = re.search('-u', str(self.iperf_args)) + if result is None: + udpIperf = False + ipnumbers = UE_IPAddress.split('.') + if (len(ipnumbers) == 4): + ipnumbers[3] = '1' + EPC_Iperf_UE_IPAddress = ipnumbers[0] + '.' + ipnumbers[1] + '.' + ipnumbers[2] + '.' + ipnumbers[3] + + # Launch iperf server on EPC side (true for ltebox and old open-air-cn0 + # But for OAI-Rel14-CUPS, we launch from python executor and we are using its IP address as iperf client address + launchFromEpc = True + if re.match('OAI-Rel14-CUPS', EPC.Type, re.IGNORECASE): + launchFromEpc = False + cmd = 'hostname -I' + ret = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') + if ret.stdout is not None: + EPC_Iperf_UE_IPAddress = ret.stdout.strip() + port = 5001 + idx + if launchFromEpc: + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cd ' + EPC.SourceCodePath + '/scripts', '\$', 5) + SSH.command('rm -f iperf_server_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + if udpIperf: + SSH.command('echo $USER; nohup iperf -u -s -i 1 -p ' + str(port) + ' > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', EPC.UserName, 5) + else: + SSH.command('echo $USER; nohup iperf -s -i 1 -p ' + str(port) + ' > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', EPC.UserName, 5) + SSH.close() + else: + if self.ueIperfVersion == self.dummyIperfVersion: + prefix = '' + else: + prefix = '' + if self.ueIperfVersion == '2.0.5': + prefix = '/opt/iperf-2.0.5/bin/' + if udpIperf: + cmd = 'nohup ' + prefix + 'iperf -u -s -i 1 -p ' + str(port) + ' > iperf_server_' + self.testCase_id + '_' + device_id + '.log 2>&1 &' + else: + cmd = 'nohup ' + prefix + 'iperf -s -i 1 -p ' + str(port) + ' > iperf_server_' + self.testCase_id + '_' + device_id + '.log 2>&1 &' + logging.debug(cmd) + subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') + time.sleep(0.5) + + # Launch iperf client on UE + if (device_id == 'OAI-UE'): + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets', '\$', 5) + else: + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + SSH.command('cd ' + EPC.SourceCodePath+ '/scripts', '\$', 5) + iperf_time = self.Iperf_ComputeTime() + time.sleep(0.5) + + if udpIperf: + modified_options = self.Iperf_ComputeModifiedBW(idx, ue_num) + else: + modified_options = str(self.iperf_args) + modified_options = modified_options.replace('-R','') + time.sleep(0.5) + + SSH.command('rm -f iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + if (device_id == 'OAI-UE'): + iperf_status = SSH.command('iperf -c ' + EPC_Iperf_UE_IPAddress + ' ' + modified_options + ' -p ' + str(port) + ' -B ' + UE_IPAddress + ' 2>&1 | stdbuf -o0 tee iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) + else: + if self.ADBCentralized: + iperf_status = SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "/data/local/tmp/iperf -c ' + EPC_Iperf_UE_IPAddress + ' ' + modified_options + ' -p ' + str(port) + '" 2>&1 | stdbuf -o0 tee iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) + else: + iperf_status = SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "/data/local/tmp/iperf -c ' + EPC_Iperf_UE_IPAddress + ' ' + modified_options + ' -p ' + str(port) + '"\' 2>&1 > iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) + SSH.command('fromdos -o iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + SSH.command('cat iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + # TIMEOUT Case + if iperf_status < 0: + SSH.close() + message = 'iperf on UE (' + str(UE_IPAddress) + ') crashed due to TIMEOUT !' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) + return + clientStatus = self.Iperf_analyzeV2Output(lock, UE_IPAddress, device_id, statusQueue, modified_options,EPC,SSH) + SSH.close() + + # Kill iperf server on EPC side + if launchFromEpc: + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('killall --signal SIGKILL iperf', EPC.UserName, 5) + SSH.close() + else: + cmd = 'killall --signal SIGKILL iperf' + logging.debug(cmd) + subprocess.run(cmd, shell=True) + time.sleep(1) + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') + # in case of failure, retrieve server log + if (clientStatus == -1) or (clientStatus == -2): + if launchFromEpc: + time.sleep(1) + if (os.path.isfile('iperf_server_' + self.testCase_id + '_' + device_id + '.log')): + os.remove('iperf_server_' + self.testCase_id + '_' + device_id + '.log') + SSH.copyin(EPC.IPAddress, EPC.UserName, EPC.Password, EPC.SourceCodePath+ '/scripts/iperf_server_' + self.testCase_id + '_' + device_id + '.log', '.') + self.Iperf_analyzeV2Server(lock, UE_IPAddress, device_id, statusQueue, modified_options) + # in case of OAI-UE + if (device_id == 'OAI-UE'): + SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/iperf_' + self.testCase_id + '_' + device_id + '.log', '.') + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') + + def Iperf_common(self, lock, UE_IPAddress, device_id, idx, ue_num, statusQueue,EPC): + try: + SSH = sshconnection.SSHConnection() + # Single-UE profile -- iperf only on one UE + if self.iperf_profile == 'single-ue' and idx != 0: + return + useIperf3 = False + udpIperf = True + + self.ueIperfVersion = '2.0.5' + if (device_id != 'OAI-UE'): + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + # if by chance ADB server and EPC are on the same remote host, at least log collection will take care of it + SSH.command('if [ ! -d ' + EPC.SourceCodePath + '/scripts ]; then mkdir -p ' + EPC.SourceCodePath + '/scripts ; fi', '\$', 5) + SSH.command('cd ' + EPC.SourceCodePath + '/scripts', '\$', 5) + # Checking if iperf / iperf3 are installed + if self.ADBCentralized: + SSH.command('adb -s ' + device_id + ' shell "ls /data/local/tmp"', '\$', 5) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "ls /data/local/tmp"\'', '\$', 60) + result = re.search('iperf3', SSH.getBefore()) + if result is None: + result = re.search('iperf', SSH.getBefore()) + if result is None: + message = 'Neither iperf nor iperf3 installed on UE!' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + SSH.close() + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) + return + else: + if self.ADBCentralized: + SSH.command('adb -s ' + device_id + ' shell "/data/local/tmp/iperf --version"', '\$', 5) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "/data/local/tmp/iperf --version"\'', '\$', 60) + result = re.search('iperf version 2.0.5', SSH.getBefore()) + if result is not None: + self.ueIperfVersion = '2.0.5' + result = re.search('iperf version 2.0.10', SSH.getBefore()) + if result is not None: + self.ueIperfVersion = '2.0.10' + else: + useIperf3 = True + SSH.close() + else: + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('iperf --version', '\$', 5) + result = re.search('iperf version 2.0.5', SSH.getBefore()) + if result is not None: + self.ueIperfVersion = '2.0.5' + result = re.search('iperf version 2.0.10', SSH.getBefore()) + if result is not None: + self.ueIperfVersion = '2.0.10' + SSH.close() + # in case of iperf, UL has its own function + if (not useIperf3): + result = re.search('-R', str(self.iperf_args)) + if result is not None: + self.Iperf_UL_common(lock, UE_IPAddress, device_id, idx, ue_num, statusQueue,EPC) + return + + # Launch the IPERF server on the UE side for DL + if (device_id == 'OAI-UE'): + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets', '\$', 5) + SSH.command('rm -f iperf_server_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + result = re.search('-u', str(self.iperf_args)) + if result is None: + SSH.command('echo $USER; nohup iperf -B ' + UE_IPAddress + ' -s -i 1 > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.UEUserName, 5) + udpIperf = False + else: + SSH.command('echo $USER; nohup iperf -B ' + UE_IPAddress + ' -u -s -i 1 > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.UEUserName, 5) + else: + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + SSH.command('cd ' + EPC.SourceCodePath + '/scripts', '\$', 5) + if self.ADBCentralized: + if (useIperf3): + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/iperf3 -s &', '\$', 5) + else: + SSH.command('rm -f iperf_server_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + result = re.search('-u', str(self.iperf_args)) + if result is None: + SSH.command('echo $USER; nohup adb -s ' + device_id + ' shell "/data/local/tmp/iperf -s -i 1" > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.ADBUserName, 5) + udpIperf = False + else: + SSH.command('echo $USER; nohup adb -s ' + device_id + ' shell "/data/local/tmp/iperf -u -s -i 1" > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.ADBUserName, 5) + else: + SSH.command('rm -f iperf_server_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + SSH.command('echo $USER; nohup ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "/data/local/tmp/iperf -u -s -i 1" \' 2>&1 > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.ADBUserName, 60) + + time.sleep(0.5) + SSH.close() + + # Launch the IPERF client on the EPC side for DL (true for ltebox and old open-air-cn + # But for OAI-Rel14-CUPS, we launch from python executor + launchFromEpc = True + if re.match('OAI-Rel14-CUPS', EPC.Type, re.IGNORECASE): + launchFromEpc = False + if launchFromEpc: + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cd ' + EPC.SourceCodePath + '/scripts', '\$', 5) + iperf_time = self.Iperf_ComputeTime() + time.sleep(0.5) + + if udpIperf: + modified_options = self.Iperf_ComputeModifiedBW(idx, ue_num) + else: + modified_options = str(self.iperf_args) + time.sleep(0.5) + + if launchFromEpc: + SSH.command('rm -f iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + else: + if (os.path.isfile('iperf_' + self.testCase_id + '_' + device_id + '.log')): + os.remove('iperf_' + self.testCase_id + '_' + device_id + '.log') + if (useIperf3): + SSH.command('stdbuf -o0 iperf3 -c ' + UE_IPAddress + ' ' + modified_options + ' 2>&1 | stdbuf -o0 tee iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) + + clientStatus = 0 + self.Iperf_analyzeV3Output(lock, UE_IPAddress, device_id, statusQueue,SSH) + else: + if launchFromEpc: + iperf_status = SSH.command('stdbuf -o0 iperf -c ' + UE_IPAddress + ' ' + modified_options + ' 2>&1 | stdbuf -o0 tee iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) + else: + if self.ueIperfVersion == self.dummyIperfVersion: + prefix = '' + else: + prefix = '' + if self.ueIperfVersion == '2.0.5': + prefix = '/opt/iperf-2.0.5/bin/' + cmd = prefix + 'iperf -c ' + UE_IPAddress + ' ' + modified_options + ' 2>&1 > iperf_' + self.testCase_id + '_' + device_id + '.log' + message = cmd + '\n' + logging.debug(cmd) + ret = subprocess.run(cmd, shell=True) + iperf_status = ret.returncode + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cat ' + EPC.SourceCodePath + '/scripts/iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) + if iperf_status < 0: + if launchFromEpc: + SSH.close() + message = 'iperf on UE (' + str(UE_IPAddress) + ') crashed due to TIMEOUT !' + logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') + self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) + return + logging.debug('Into Iperf_analyzeV2Output client') + clientStatus = self.Iperf_analyzeV2Output(lock, UE_IPAddress, device_id, statusQueue, modified_options, EPC,SSH) + logging.debug('Iperf_analyzeV2Output clientStatus returned value = ' + str(clientStatus)) + SSH.close() + + # Kill the IPERF server that runs in background + if (device_id == 'OAI-UE'): + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('killall iperf', '\$', 5) + else: + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell ps | grep --color=never iperf | grep -v grep', '\$', 5) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "ps" | grep --color=never iperf | grep -v grep\'', '\$', 60) + result = re.search('shell +(?P\d+)', SSH.getBefore()) + if result is not None: + pid_iperf = result.group('pid') + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell kill -KILL ' + pid_iperf, '\$', 5) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "kill -KILL ' + pid_iperf + '"\'', '\$', 60) + SSH.close() + # if the client report is absent, try to analyze the server log file + if (clientStatus == -1): + time.sleep(1) + if (os.path.isfile('iperf_server_' + self.testCase_id + '_' + device_id + '.log')): + os.remove('iperf_server_' + self.testCase_id + '_' + device_id + '.log') + if (device_id == 'OAI-UE'): + SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/iperf_server_' + self.testCase_id + '_' + device_id + '.log', '.') + else: + SSH.copyin(self.ADBIPAddress, self.ADBUserName, self.ADBPassword, EPC.SourceCodePath + '/scripts/iperf_server_' + self.testCase_id + '_' + device_id + '.log', '.') + # fromdos has to be called on the python executor not on ADB server + cmd = 'fromdos -o iperf_server_' + self.testCase_id + '_' + device_id + '.log' + subprocess.run(cmd, shell=True) + self.Iperf_analyzeV2Server(lock, UE_IPAddress, device_id, statusQueue, modified_options) + + # in case of OAI UE: + if (device_id == 'OAI-UE'): + if (os.path.isfile('iperf_server_' + self.testCase_id + '_' + device_id + '.log')): + if not launchFromEpc: + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') + else: + SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/iperf_server_' + self.testCase_id + '_' + device_id + '.log', '.') + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def IperfNoS1(self,HTML,RAN,EPC,COTS_UE): + 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 + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow(self.iperf_args, 'KO', pStatus) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + 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('rm -f /tmp/tmp_iperf_server_' + self.testCase_id + '.log', '\$', 5) + SSH.command('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('rm -f /tmp/tmp_iperf_' + self.testCase_id + '.log', '\$', 5) + iperf_status = SSH.command('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('\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('iperf_server_' + self.testCase_id + '.log')): + os.remove('iperf_server_' + self.testCase_id + '.log') + SSH.copyin(iServerIPAddr, iServerUser, iServerPasswd, '/tmp/tmp_iperf_server_' + self.testCase_id + '.log', 'iperf_server_' + self.testCase_id + '_OAI-UE.log') + self.Iperf_analyzeV2Server(lock, '10.0.1.2', 'OAI-UE', status_queue, modified_options) + + # copying on the EPC server for logCollection + if (clientStatus == -1): + copyin_res = SSH.copyin(iServerIPAddr, iServerUser, iServerPasswd, '/tmp/tmp_iperf_server_' + self.testCase_id + '.log', 'iperf_server_' + self.testCase_id + '_OAI-UE.log') + if (copyin_res == 0): + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_OAI-UE.log', EPC.SourceCodePath + '/scripts') + copyin_res = SSH.copyin(iClientIPAddr, iClientUser, iClientPasswd, '/tmp/tmp_iperf_' + self.testCase_id + '.log', 'iperf_' + self.testCase_id + '_OAI-UE.log') + if (copyin_res == 0): + SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_' + self.testCase_id + '_OAI-UE.log', EPC.SourceCodePath + '/scripts') + iperf_noperf = False + if status_queue.empty(): + iperf_status = False + else: + iperf_status = True + html_queue = SimpleQueue() + 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() + html_cell = '
UE (' + device_id + ')\nIP Address  : ' + ip_addr + '\n' + message + '
' + html_queue.put(html_cell) + if (iperf_noperf and iperf_status): + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'PERF NOT MET', len(self.UEDevices), html_queue) + elif (iperf_status): + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'OK', len(self.UEDevices), html_queue) + else: + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', len(self.UEDevices), html_queue) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + + def Iperf(self,HTML,RAN,EPC,COTS_UE): + result = re.search('noS1', str(RAN.Initialize_eNB_args)) + if result is not None: + self.IperfNoS1(HTML,RAN,EPC,COTS_UE) + return + if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.SourceCodePath == '' or self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + check_eNB = True + if (len(self.UEDevices) == 1) and (self.UEDevices[0] == 'OAI-UE'): + check_OAI_UE = True + else: + check_OAI_UE = False + pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) + if (pStatus < 0): + HTML.CreateHtmlTestRow(self.iperf_args, 'KO', pStatus) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + ueIpStatus = self.GetAllUEIPAddresses() + if (ueIpStatus < 0): + logging.debug('going here') + HTML.CreateHtmlTestRow(self.iperf_args, 'KO', CONST.UE_IP_ADDRESS_ISSUE) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + return + + 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 = [] + i = 0 + ue_num = len(self.UEIPAddresses) + lock = Lock() + status_queue = SimpleQueue() + for UE_IPAddress in self.UEIPAddresses: + device_id = self.UEDevices[i] + p = Process(target = self.Iperf_common, args = (lock,UE_IPAddress,device_id,i,ue_num,status_queue,EPC,)) + 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,COTS_UE) + else: + iperf_status = True + iperf_noperf = False + html_queue = SimpleQueue() + 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() + html_cell = '
UE (' + device_id + ')\nIP Address  : ' + ip_addr + '\n' + message + '
' + html_queue.put(html_cell) + if (iperf_noperf and iperf_status): + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'PERF NOT MET', len(self.UEDevices), html_queue) + elif (iperf_status): + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'OK', len(self.UEDevices), html_queue) + else: + HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', len(self.UEDevices), html_queue) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + + def CheckProcessExist(self, check_eNB, check_OAI_UE,RAN,EPC): + multi_jobs = [] + status_queue = SimpleQueue() + # in noS1 config, no need to check status from EPC + # in gNB also currently no need to check + result = re.search('noS1|band78', str(RAN.Initialize_eNB_args)) + if result is None: + p = Process(target = EPC.CheckHSSProcess, args = (status_queue,)) + p.daemon = True + p.start() + multi_jobs.append(p) + p = Process(target = EPC.CheckMMEProcess, args = (status_queue,)) + p.daemon = True + p.start() + multi_jobs.append(p) + p = Process(target = EPC.CheckSPGWProcess, args = (status_queue,)) + p.daemon = True + p.start() + multi_jobs.append(p) + else: + if (check_eNB == False) and (check_OAI_UE == False): + return 0 + if check_eNB: + p = Process(target = RAN.CheckeNBProcess, args = (status_queue,)) + p.daemon = True + p.start() + multi_jobs.append(p) + if check_OAI_UE: + p = Process(target = self.CheckOAIUEProcess, args = (status_queue,)) + p.daemon = True + p.start() + multi_jobs.append(p) + for job in multi_jobs: + job.join() + + if (status_queue.empty()): + return -15 + else: + result = 0 + while (not status_queue.empty()): + status = status_queue.get() + if (status < 0): + result = status + if result == CONST.ENB_PROCESS_FAILED: + fileCheck = re.search('enb_', str(RAN.eNBLogFiles[0])) + if fileCheck is not None: + SSH.copyin(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath + '/cmake_targets/' + RAN.eNBLogFiles[0], '.') + logStatus = RAN.AnalyzeLogFile_eNB(RAN.eNBLogFiles[0]) + if logStatus < 0: + result = logStatus + RAN.eNBLogFiles[0]='' + if RAN.flexranCtrlInstalled and RAN.flexranCtrlStarted: + self.TerminateFlexranCtrl() + return result + + def CheckOAIUEProcessExist(self, initialize_OAI_UE_flag,HTML,RAN): + multi_jobs = [] + status_queue = SimpleQueue() + if initialize_OAI_UE_flag == False: + p = Process(target = self.CheckOAIUEProcess, args = (status_queue,)) + p.daemon = True + p.start() + multi_jobs.append(p) + for job in multi_jobs: + job.join() + + if (status_queue.empty()): + return -15 + else: + result = 0 + while (not status_queue.empty()): + status = status_queue.get() + if (status < 0): + result = status + if result == CONST.OAI_UE_PROCESS_FAILED: + fileCheck = re.search('ue_', str(self.UELogFile)) + if fileCheck is not None: + SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/' + self.UELogFile, '.') + logStatus = self.AnalyzeLogFile_UE(self.UELogFile,HTML,RAN) + if logStatus < 0: + result = logStatus + return result + + def CheckOAIUEProcess(self, status_queue): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('stdbuf -o0 ps -aux | grep --color=never ' + self.air_interface + ' | grep -v grep', '\$', 5) + result = re.search(self.air_interface, SSH.getBefore()) + if result is None: + logging.debug('\u001B[1;37;41m OAI UE Process Not Found! \u001B[0m') + status_queue.put(CONST.OAI_UE_PROCESS_FAILED) + else: + status_queue.put(CONST.OAI_UE_PROCESS_OK) + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + + def AnalyzeLogFile_UE(self, UElogFile,HTML,RAN): + if (not os.path.isfile('./' + UElogFile)): + return -1 + ue_log_file = open('./' + UElogFile, 'r') + exitSignalReceived = False + foundAssertion = False + msgAssertion = '' + msgLine = 0 + foundSegFault = False + foundRealTimeIssue = False + uciStatMsgCount = 0 + pdcpDataReqFailedCount = 0 + badDciCount = 0 + f1aRetransmissionCount = 0 + fatalErrorCount = 0 + macBsrTimerExpiredCount = 0 + rrcConnectionRecfgComplete = 0 + no_cell_sync_found = False + mib_found = False + frequency_found = False + plmn_found = False + nrUEFlag = False + nrDecodeMib = 0 + nrFoundDCI = 0 + nrCRCOK = 0 + mbms_messages = 0 + HTML.htmlUEFailureMsg='' + global_status = CONST.ALL_PROCESSES_OK + for line in ue_log_file.readlines(): + result = re.search('nr_synchro_time', str(line)) + if result is not None: + nrUEFlag = True + if nrUEFlag: + result = re.search('decode mib', str(line)) + if result is not None: + nrDecodeMib += 1 + result = re.search('found 1 DCIs', str(line)) + if result is not None: + nrFoundDCI += 1 + result = re.search('CRC OK', str(line)) + if result is not None: + nrCRCOK += 1 + result = re.search('Exiting OAI softmodem', str(line)) + if result is not None: + exitSignalReceived = True + result = re.search('System error|[Ss]egmentation [Ff]ault|======= Backtrace: =========|======= Memory map: ========', str(line)) + if result is not None and not exitSignalReceived: + foundSegFault = True + result = re.search('[Cc]ore [dD]ump', str(line)) + if result is not None and not exitSignalReceived: + foundSegFault = True + result = re.search('./lte-uesoftmodem', str(line)) + if result is not None and not exitSignalReceived: + foundSegFault = True + result = re.search('[Aa]ssertion', str(line)) + if result is not None and not exitSignalReceived: + foundAssertion = True + result = re.search('LLL', str(line)) + if result is not None and not exitSignalReceived: + foundRealTimeIssue = True + if foundAssertion and (msgLine < 3): + msgLine += 1 + msgAssertion += str(line) + result = re.search('uci->stat', str(line)) + if result is not None and not exitSignalReceived: + uciStatMsgCount += 1 + result = re.search('PDCP data request failed', str(line)) + if result is not None and not exitSignalReceived: + pdcpDataReqFailedCount += 1 + result = re.search('bad DCI 1', str(line)) + if result is not None and not exitSignalReceived: + badDciCount += 1 + result = re.search('Format1A Retransmission but TBS are different', str(line)) + if result is not None and not exitSignalReceived: + f1aRetransmissionCount += 1 + result = re.search('FATAL ERROR', str(line)) + if result is not None and not exitSignalReceived: + fatalErrorCount += 1 + result = re.search('MAC BSR Triggered ReTxBSR Timer expiry', str(line)) + if result is not None and not exitSignalReceived: + macBsrTimerExpiredCount += 1 + result = re.search('Generating RRCConnectionReconfigurationComplete', str(line)) + if result is not None: + rrcConnectionRecfgComplete += 1 + # No cell synchronization found, abandoning + result = re.search('No cell synchronization found, abandoning', str(line)) + if result is not None: + no_cell_sync_found = True + if RAN.eNBmbmsEnables[0]: + result = re.search('TRIED TO PUSH MBMS DATA', str(line)) + if result is not None: + mbms_messages += 1 + result = re.search("MIB Information => ([a-zA-Z]{1,10}), ([a-zA-Z]{1,10}), NidCell (?P\d{1,3}), N_RB_DL (?P\d{1,3}), PHICH DURATION (?P\d), PHICH RESOURCE (?P.{1,4}), TX_ANT (?P\d)", str(line)) + if result is not None and (not mib_found): + try: + mibMsg = "MIB Information: " + result.group(1) + ', ' + result.group(2) + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' + logging.debug('\033[94m' + mibMsg + '\033[0m') + mibMsg = " nidcell = " + result.group('nidcell') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + logging.debug('\033[94m' + mibMsg + '\033[0m') + mibMsg = " n_rb_dl = " + result.group('n_rb_dl') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' + logging.debug('\033[94m' + mibMsg + '\033[0m') + mibMsg = " phich_duration = " + result.group('phich_duration') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + logging.debug('\033[94m' + mibMsg + '\033[0m') + mibMsg = " phich_resource = " + result.group('phich_resource') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' + logging.debug('\033[94m' + mibMsg + '\033[0m') + mibMsg = " tx_ant = " + result.group('tx_ant') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' + logging.debug('\033[94m' + mibMsg + '\033[0m') + mib_found = True + except Exception as e: + logging.error('\033[91m' + "MIB marker was not found" + '\033[0m') + result = re.search("Measured Carrier Frequency (?P\d{1,15}) Hz", str(line)) + if result is not None and (not frequency_found): + try: + mibMsg = "Measured Carrier Frequency = " + result.group('measured_carrier_frequency') + ' Hz' + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' + logging.debug('\033[94m' + mibMsg + '\033[0m') + frequency_found = True + except Exception as e: + logging.error('\033[91m' + "Measured Carrier Frequency not found" + '\033[0m') + result = re.search("PLMN MCC (?P\d{1,3}), MNC (?P\d{1,3}), TAC", str(line)) + if result is not None and (not plmn_found): + try: + mibMsg = 'PLMN MCC = ' + result.group('mcc') + ' MNC = ' + result.group('mnc') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' + logging.debug('\033[94m' + mibMsg + '\033[0m') + plmn_found = True + except Exception as e: + logging.error('\033[91m' + "PLMN not found" + '\033[0m') + result = re.search("Found (?P[\w,\s]{1,15}) \(name from internal table\)", str(line)) + if result is not None: + try: + mibMsg = "The operator is: " + result.group('operator') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' + logging.debug('\033[94m' + mibMsg + '\033[0m') + except Exception as e: + logging.error('\033[91m' + "Operator name not found" + '\033[0m') + result = re.search("SIB5 InterFreqCarrierFreq element (.{1,4})/(.{1,4})", str(line)) + if result is not None: + try: + mibMsg = "SIB5 InterFreqCarrierFreq element " + result.group(1) + '/' + result.group(2) + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + ' -> ' + logging.debug('\033[94m' + mibMsg + '\033[0m') + except Exception as e: + logging.error('\033[91m' + "SIB5 InterFreqCarrierFreq element not found" + '\033[0m') + result = re.search("DL Carrier Frequency/ARFCN : \-*(?P\d{1,15}/\d{1,4})", str(line)) + if result is not None: + try: + freq = result.group('carrier_frequency') + new_freq = re.sub('/[0-9]+','',freq) + float_freq = float(new_freq) / 1000000 + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + 'DL Freq: ' + ('%.1f' % float_freq) + ' MHz' + logging.debug('\033[94m' + " DL Carrier Frequency is: " + str(freq) + '\033[0m') + except Exception as e: + logging.error('\033[91m' + " DL Carrier Frequency not found" + '\033[0m') + result = re.search("AllowedMeasBandwidth : (?P\d{1,7})", str(line)) + if result is not None: + try: + prb = result.group('allowed_bandwidth') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + ' -- PRB: ' + prb + '\n' + logging.debug('\033[94m' + " AllowedMeasBandwidth: " + prb + '\033[0m') + except Exception as e: + logging.error('\033[91m' + " AllowedMeasBandwidth not found" + '\033[0m') + ue_log_file.close() + if rrcConnectionRecfgComplete > 0: + statMsg = 'UE connected to eNB (' + str(rrcConnectionRecfgComplete) + ' RRCConnectionReconfigurationComplete message(s) generated)' + logging.debug('\033[94m' + statMsg + '\033[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if nrUEFlag: + if nrDecodeMib > 0: + statMsg = 'UE showed ' + str(nrDecodeMib) + ' MIB decode message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if nrFoundDCI > 0: + statMsg = 'UE showed ' + str(nrFoundDCI) + ' DCI found message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if nrCRCOK > 0: + statMsg = 'UE showed ' + str(nrCRCOK) + ' PDSCH decoding message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if not frequency_found: + statMsg = 'NR-UE could NOT synch!' + logging.error('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if uciStatMsgCount > 0: + statMsg = 'UE showed ' + str(uciStatMsgCount) + ' "uci->stat" message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if pdcpDataReqFailedCount > 0: + statMsg = 'UE showed ' + str(pdcpDataReqFailedCount) + ' "PDCP data request failed" message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if badDciCount > 0: + statMsg = 'UE showed ' + str(badDciCount) + ' "bad DCI 1(A)" message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if f1aRetransmissionCount > 0: + statMsg = 'UE showed ' + str(f1aRetransmissionCount) + ' "Format1A Retransmission but TBS are different" message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if fatalErrorCount > 0: + statMsg = 'UE showed ' + str(fatalErrorCount) + ' "FATAL ERROR:" message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if macBsrTimerExpiredCount > 0: + statMsg = 'UE showed ' + str(fatalErrorCount) + ' "MAC BSR Triggered ReTxBSR Timer expiry" message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if RAN.eNBmbmsEnables[0]: + if mbms_messages > 0: + statMsg = 'UE showed ' + str(mbms_messages) + ' "TRIED TO PUSH MBMS DATA" message(s)' + logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') + else: + statMsg = 'UE did NOT SHOW "TRIED TO PUSH MBMS DATA" message(s)' + logging.debug('\u001B[1;30;41m ' + statMsg + ' \u001B[0m') + global_status = CONST.OAI_UE_PROCESS_NO_MBMS_MSGS + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' + if foundSegFault: + logging.debug('\u001B[1;37;41m UE ended with a Segmentation Fault! \u001B[0m') + if not nrUEFlag: + global_status = CONST.OAI_UE_PROCESS_SEG_FAULT + else: + if not frequency_found: + global_status = CONST.OAI_UE_PROCESS_SEG_FAULT + if foundAssertion: + logging.debug('\u001B[1;30;43m UE showed an assertion! \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + 'UE showed an assertion!\n' + if not nrUEFlag: + if not mib_found or not frequency_found: + global_status = CONST.OAI_UE_PROCESS_ASSERTION + else: + if not frequency_found: + global_status = CONST.OAI_UE_PROCESS_ASSERTION + if foundRealTimeIssue: + logging.debug('\u001B[1;37;41m UE faced real time issues! \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + 'UE faced real time issues!\n' + if nrUEFlag: + if not frequency_found: + global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC + else: + if no_cell_sync_found and not mib_found: + logging.debug('\u001B[1;37;41m UE could not synchronize ! \u001B[0m') + HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + 'UE could not synchronize!\n' + global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC + return global_status + + + def TerminateFlexranCtrl(self,HTML,RAN,EPC): + if RAN.flexranCtrlInstalled == False or RAN.flexranCtrlStarted == False: + return + if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + SSH = sshconnection.SSHConnection() + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('echo ' + EPC.Password + ' | sudo -S daemon --name=flexran_rtc_daemon --stop', '\$', 5) + time.sleep(1) + SSH.command('echo ' + EPC.Password + ' | sudo -S killall --signal SIGKILL rt_controller', '\$', 5) + time.sleep(1) + SSH.close() + RAN.flexranCtrlStarted=False + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + + def TerminateUE_common(self, device_id, idx,COTS_UE): + try: + SSH = sshconnection.SSHConnection() + SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) + # back in airplane mode on (ie radio off) + if self.ADBCentralized: + #RH quick add on to intgrate cots control defined by yaml + #if device Id exists in yaml dictionary, we execute the new procedure defined in cots_ue class + #otherwise we use the legacy procedure + if COTS_UE.Check_Exists(device_id): + #switch device to Airplane mode ON (ie Radio OFF) + COTS_UE.Set_Airplane(device_id, 'ON') + elif device_id == '84B7N16418004022': + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/off"', '\$', 60) + else: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/off', '\$', 60) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) + logging.debug('\u001B[1mUE (' + device_id + ') Detach Completed\u001B[0m') + + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "ps | grep --color=never iperf | grep -v grep"', '\$', 5) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "ps | grep --color=never iperf | grep -v grep"\'', '\$', 60) + result = re.search('shell +(?P\d+)', SSH.getBefore()) + if result is not None: + pid_iperf = result.group('pid') + if self.ADBCentralized: + SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "kill -KILL ' + pid_iperf + '"', '\$', 5) + else: + SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "kill -KILL ' + pid_iperf + '"\'', '\$', 60) + SSH.close() + except: + os.kill(os.getppid(),signal.SIGUSR1) + + def TerminateUE(self,HTML,COTS_UE): + terminate_ue_flag = False + self.GetAllUEDevices(terminate_ue_flag) + multi_jobs = [] + i = 0 + for device_id in self.UEDevices: + p = Process(target= self.TerminateUE_common, args = (device_id,i,COTS_UE,)) + p.daemon = True + p.start() + multi_jobs.append(p) + i += 1 + for job in multi_jobs: + job.join() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + + def TerminateOAIUE(self,HTML,RAN,COTS_UE): + SSH = sshconnection.SSHConnection() + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets', '\$', 5) + SSH.command('ps -aux | grep --color=never softmodem | grep -v grep', '\$', 5) + result = re.search('-uesoftmodem', SSH.getBefore()) + if result is not None: + SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal SIGINT -r .*-uesoftmodem || true', '\$', 5) + time.sleep(10) + SSH.command('ps -aux | grep --color=never softmodem | grep -v grep', '\$', 5) + result = re.search('-uesoftmodem', SSH.getBefore()) + if result is not None: + SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal SIGKILL -r .*-uesoftmodem || true', '\$', 5) + time.sleep(5) + SSH.command('rm -f my-lte-uesoftmodem-run' + str(self.UE_instance) + '.sh', '\$', 5) + SSH.close() + result = re.search('ue_', str(self.UELogFile)) + if result is not None: + copyin_res = SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/' + self.UELogFile, '.') + if (copyin_res == -1): + logging.debug('\u001B[1;37;41m Could not copy UE logfile to analyze it! \u001B[0m') + HTML.htmlUEFailureMsg='Could not copy UE logfile to analyze it!' + HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OAI_UE_PROCESS_NOLOGFILE_TO_ANALYZE, 'UE') + self.UELogFile = '' + return + logging.debug('\u001B[1m Analyzing UE logfile \u001B[0m') + logStatus = self.AnalyzeLogFile_UE(self.UELogFile,HTML,RAN) + result = re.search('--no-L2-connect', str(self.Initialize_OAI_UE_args)) + if result is not None: + ueAction = 'Sniffing' + else: + ueAction = 'Connection' + if (logStatus < 0): + logging.debug('\u001B[1m' + ueAction + ' Failed \u001B[0m') + HTML.htmlUEFailureMsg='' + ueAction + ' Failed\n' + HTML.htmlUEFailureMsg + HTML.CreateHtmlTestRow('N/A', 'KO', logStatus, 'UE') + if self.air_interface == 'lte-uesoftmodem': + # In case of sniffing on commercial eNBs we have random results + # Not an error then + if (logStatus != CONST.OAI_UE_PROCESS_COULD_NOT_SYNC) or (ueAction != 'Sniffing'): + self.Initialize_OAI_UE_args = '' + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + else: + if (logStatus == CONST.OAI_UE_PROCESS_COULD_NOT_SYNC): + self.Initialize_OAI_UE_args = '' + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE) + else: + logging.debug('\u001B[1m' + ueAction + ' Completed \u001B[0m') + HTML.htmlUEFailureMsg='' + ueAction + ' Completed\n' + HTML.htmlUEFailureMsg + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + self.UELogFile = '' + else: + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + + def AutoTerminateUEandeNB(self,HTML,RAN,COTS_UE): + if (self.ADBIPAddress != 'none'): + self.testCase_id = 'AUTO-KILL-UE' + HTML.testCase_id=self.testCase_id + self.desc = 'Automatic Termination of UE' + HTML.desc='Automatic Termination of UE' + self.ShowTestID() + self.TerminateUE(HTML,COTS_UE) + if (self.Initialize_OAI_UE_args != ''): + self.testCase_id = 'AUTO-KILL-OAI-UE' + HTML.testCase_id=self.testCase_id + self.desc = 'Automatic Termination of OAI-UE' + HTML.desc='Automatic Termination of OAI-UE' + self.ShowTestID() + self.TerminateOAIUE(HTML,RAN,COTS_UE) + if (RAN.Initialize_eNB_args != ''): + self.testCase_id = 'AUTO-KILL-eNB' + HTML.testCase_id=self.testCase_id + self.desc = 'Automatic Termination of eNB' + HTML.desc='Automatic Termination of eNB' + self.ShowTestID() + RAN.eNB_instance=0 + RAN.TerminateeNB() + if RAN.flexranCtrlInstalled and RAN.flexranCtrlStarted: + self.testCase_id = 'AUTO-KILL-flexran-ctl' + HTML.testCase_id=self.testCase_id + self.desc = 'Automatic Termination of FlexRan CTL' + HTML.desc='Automatic Termination of FlexRan CTL' + self.ShowTestID() + self.TerminateFlexranCtrl(HTML,RAN,EPC) + RAN.prematureExit=True + + def IdleSleep(self,HTML): + time.sleep(self.idle_sleep_time) + HTML.CreateHtmlTestRow(str(self.idle_sleep_time) + ' sec', 'OK', CONST.ALL_PROCESSES_OK) + + def X2_Status(self, idx, fileName): + cmd = "curl --silent http://" + EPC.IPAddress + ":9999/stats | jq '.' > " + fileName + message = cmd + '\n' + logging.debug(cmd) + subprocess.run(cmd, shell=True) + if idx == 0: + cmd = "jq '.mac_stats | length' " + fileName + strNbEnbs = subprocess.check_output(cmd, shell=True, universal_newlines=True) + self.x2NbENBs = int(strNbEnbs.strip()) + cnt = 0 + while cnt < self.x2NbENBs: + cmd = "jq '.mac_stats[" + str(cnt) + "].bs_id' " + fileName + bs_id = subprocess.check_output(cmd, shell=True, universal_newlines=True) + self.x2ENBBsIds[idx].append(bs_id.strip()) + cmd = "jq '.mac_stats[" + str(cnt) + "].ue_mac_stats | length' " + fileName + stNbUEs = subprocess.check_output(cmd, shell=True, universal_newlines=True) + nbUEs = int(stNbUEs.strip()) + ueIdx = 0 + self.x2ENBConnectedUEs[idx].append([]) + while ueIdx < nbUEs: + cmd = "jq '.mac_stats[" + str(cnt) + "].ue_mac_stats[" + str(ueIdx) + "].rnti' " + fileName + rnti = subprocess.check_output(cmd, shell=True, universal_newlines=True) + self.x2ENBConnectedUEs[idx][cnt].append(rnti.strip()) + ueIdx += 1 + cnt += 1 + + msg = "FlexRan Controller is connected to " + str(self.x2NbENBs) + " eNB(s)" + logging.debug(msg) + message += msg + '\n' + cnt = 0 + while cnt < self.x2NbENBs: + msg = " -- eNB: " + str(self.x2ENBBsIds[idx][cnt]) + " is connected to " + str(len(self.x2ENBConnectedUEs[idx][cnt])) + " UE(s)" + logging.debug(msg) + message += msg + '\n' + ueIdx = 0 + while ueIdx < len(self.x2ENBConnectedUEs[idx][cnt]): + msg = " -- UE rnti: " + str(self.x2ENBConnectedUEs[idx][cnt][ueIdx]) + logging.debug(msg) + message += msg + '\n' + ueIdx += 1 + cnt += 1 + return message + + def Perform_X2_Handover(self,HTML,RAN,EPC): + html_queue = SimpleQueue() + fullMessage = '
'
+		msg = 'Doing X2 Handover w/ option ' + self.x2_ho_options
+		logging.debug(msg)
+		fullMessage += msg + '\n'
+		if self.x2_ho_options == 'network':
+			if RAN.flexranCtrlInstalled and RAN.flexranCtrlStarted:
+				self.x2ENBBsIds = []
+				self.x2ENBConnectedUEs = []
+				self.x2ENBBsIds.append([])
+				self.x2ENBBsIds.append([])
+				self.x2ENBConnectedUEs.append([])
+				self.x2ENBConnectedUEs.append([])
+				fullMessage += self.X2_Status(0, self.testCase_id + '_pre_ho.json') 
+
+				msg = "Activating the X2 Net control on each eNB"
+				logging.debug(msg)
+				fullMessage += msg + '\n'
+				eNB_cnt = self.x2NbENBs
+				cnt = 0
+				while cnt < eNB_cnt:
+					cmd = "curl -XPOST http://" + EPC.IPAddress + ":9999/rrc/x2_ho_net_control/enb/" + str(self.x2ENBBsIds[0][cnt]) + "/1"
+					logging.debug(cmd)
+					fullMessage += cmd + '\n'
+					subprocess.run(cmd, shell=True)
+					cnt += 1
+				# Waiting for the activation to be active
+				time.sleep(10)
+				msg = "Switching UE(s) from eNB to eNB"
+				logging.debug(msg)
+				fullMessage += msg + '\n'
+				cnt = 0
+				while cnt < eNB_cnt:
+					ueIdx = 0
+					while ueIdx < len(self.x2ENBConnectedUEs[0][cnt]):
+						cmd = "curl -XPOST http://" + EPC.IPAddress() + ":9999/rrc/ho/senb/" + str(self.x2ENBBsIds[0][cnt]) + "/ue/" + str(self.x2ENBConnectedUEs[0][cnt][ueIdx]) + "/tenb/" + str(self.x2ENBBsIds[0][eNB_cnt - cnt - 1])
+						logging.debug(cmd)
+						fullMessage += cmd + '\n'
+						subprocess.run(cmd, shell=True)
+						ueIdx += 1
+					cnt += 1
+				time.sleep(10)
+				# check
+				logging.debug("Checking the Status after X2 Handover")
+				fullMessage += self.X2_Status(1, self.testCase_id + '_post_ho.json') 
+				cnt = 0
+				x2Status = True
+				while cnt < eNB_cnt:
+					if len(self.x2ENBConnectedUEs[0][cnt]) == len(self.x2ENBConnectedUEs[1][cnt]):
+						x2Status = False
+					cnt += 1
+				if x2Status:
+					msg = "X2 Handover was successful"
+					logging.debug(msg)
+					fullMessage += msg + '
' + html_queue.put(fullMessage) + HTML.CreateHtmlTestRowQueue('N/A', 'OK', len(self.UEDevices), html_queue) + else: + msg = "X2 Handover FAILED" + logging.error(msg) + fullMessage += msg + '' + html_queue.put(fullMessage) + HTML.CreateHtmlTestRowQueue('N/A', 'OK', len(self.UEDevices), html_queue) + else: + HTML.CreateHtmlTestRow('Cannot perform requested X2 Handover', 'KO', CONST.ALL_PROCESSES_OK) + + def LogCollectBuild(self,RAN): + SSH = sshconnection.SSHConnection() + if (RAN.eNBIPAddress != '' and RAN.eNBUserName != '' and RAN.eNBPassword != ''): + IPAddress = RAN.eNBIPAddress + UserName = RAN.eNBUserName + Password = RAN.eNBPassword + SourceCodePath = RAN.eNBSourceCodePath + elif (self.UEIPAddress != '' and self.UEUserName != '' and self.UEPassword != ''): + IPAddress = self.UEIPAddress + UserName = self.UEUserName + Password = self.UEPassword + SourceCodePath = self.UESourceCodePath + else: + sys.exit('Insufficient Parameter') + SSH.open(IPAddress, UserName, Password) + SSH.command('cd ' + SourceCodePath, '\$', 5) + SSH.command('cd cmake_targets', '\$', 5) + SSH.command('rm -f build.log.zip', '\$', 5) + SSH.command('zip build.log.zip build_log_*/*', '\$', 60) + SSH.close() + + def LogCollectPing(self,EPC): + SSH = sshconnection.SSHConnection() + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cd ' + EPC.SourceCodePath, '\$', 5) + SSH.command('cd scripts', '\$', 5) + SSH.command('rm -f ping.log.zip', '\$', 5) + SSH.command('zip ping.log.zip ping*.log', '\$', 60) + SSH.command('rm ping*.log', '\$', 5) + SSH.close() + + def LogCollectIperf(self,EPC): + SSH = sshconnection.SSHConnection() + SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) + SSH.command('cd ' + EPC.SourceCodePath, '\$', 5) + SSH.command('cd scripts', '\$', 5) + SSH.command('rm -f iperf.log.zip', '\$', 5) + SSH.command('zip iperf.log.zip iperf*.log', '\$', 60) + SSH.command('rm iperf*.log', '\$', 5) + SSH.close() + + def LogCollectOAIUE(self): + SSH = sshconnection.SSHConnection() + SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) + SSH.command('cd ' + self.UESourceCodePath, '\$', 5) + SSH.command('cd cmake_targets', '\$', 5) + SSH.command('echo ' + self.UEPassword + ' | sudo -S rm -f ue.log.zip', '\$', 5) + SSH.command('echo ' + self.UEPassword + ' | sudo -S zip ue.log.zip ue*.log core* ue_*record.raw ue_*.pcap ue_*txt', '\$', 60) + SSH.command('echo ' + self.UEPassword + ' | sudo -S rm ue*.log core* ue_*record.raw ue_*.pcap ue_*txt', '\$', 5) + SSH.close() + + def RetrieveSystemVersion(self, machine,HTML,RAN): + if RAN.eNBIPAddress == 'none' or self.UEIPAddress == 'none': + HTML.OsVersion[0]='Ubuntu 16.04.5 LTS' + HTML.KernelVersion[0]='4.15.0-45-generic' + HTML.UhdVersion[0]='3.13.0.1-0' + HTML.UsrpBoard[0]='B210' + HTML.CpuNb[0]='4' + HTML.CpuModel[0]='Intel(R) Core(TM) i5-6200U' + HTML.CpuMHz[0]='2399.996 MHz' + return 0 + if machine == 'eNB': + if RAN.eNBIPAddress != '' and RAN.eNBUserName != '' and RAN.eNBPassword != '': + IPAddress = RAN.eNBIPAddress + UserName = RAN.eNBUserName + Password = RAN.eNBPassword + idx = 0 + else: + return -1 + if machine == 'UE': + if self.UEIPAddress != '' and self.UEUserName != '' and self.UEPassword != '': + IPAddress = self.UEIPAddress + UserName = self.UEUserName + Password = self.UEPassword + idx = 1 + else: + return -1 + + SSH = sshconnection.SSHConnection() + SSH.open(IPAddress, UserName, Password) + SSH.command('lsb_release -a', '\$', 5) + result = re.search('Description:\\\\t(?P[a-zA-Z0-9\-\_\.\ ]+)', SSH.getBefore()) + if result is not None: + OsVersion = result.group('os_type') + logging.debug('OS is: ' + OsVersion) + HTML.OsVersion[idx]=OsVersion + else: + SSH.command('hostnamectl', '\$', 5) + result = re.search('Operating System: (?P[a-zA-Z0-9\-\_\.\ ]+)', SSH.getBefore()) + if result is not None: + OsVersion = result.group('os_type') + if OsVersion == 'CentOS Linux 7 ': + SSH.command('cat /etc/redhat-release', '\$', 5) + result = re.search('CentOS Linux release (?P[0-9\.]+)', SSH.getBefore()) + if result is not None: + OsVersion = OsVersion.replace('7 ', result.group('os_version')) + logging.debug('OS is: ' + OsVersion) + HTML.OsVersion[idx]=OsVersion + SSH.command('uname -r', '\$', 5) + result = re.search('uname -r\\\\r\\\\n(?P[a-zA-Z0-9\-\_\.]+)', SSH.getBefore()) + if result is not None: + KernelVersion = result.group('kernel_version') + logging.debug('Kernel Version is: ' + KernelVersion) + HTML.KernelVersion[idx]=KernelVersion + SSH.command('dpkg --list | egrep --color=never libuhd003', '\$', 5) + result = re.search('libuhd003:amd64 *(?P[0-9\.]+)', SSH.getBefore()) + if result is not None: + UhdVersion = result.group('uhd_version') + logging.debug('UHD Version is: ' + UhdVersion) + HTML.UhdVersion[idx]=UhdVersion + else: + SSH.command('uhd_config_info --version', '\$', 5) + result = re.search('UHD (?P[a-zA-Z0-9\.\-]+)', SSH.getBefore()) + if result is not None: + UhdVersion = result.group('uhd_version') + logging.debug('UHD Version is: ' + UhdVersion) + HTML.UhdVersion[idx]=UhdVersion + SSH.command('echo ' + Password + ' | sudo -S uhd_find_devices', '\$', 60) + usrp_boards = re.findall('product: ([0-9A-Za-z]+)\\\\r\\\\n', SSH.getBefore()) + count = 0 + for board in usrp_boards: + if count == 0: + UsrpBoard = board + else: + UsrpBoard += ',' + board + count += 1 + if count > 0: + logging.debug('USRP Board(s) : ' + UsrpBoard) + HTML.UsrpBoard[idx]=UsrpBoard + SSH.command('lscpu', '\$', 5) + result = re.search('CPU\(s\): *(?P[0-9]+).*Model name: *(?P[a-zA-Z0-9\-\_\.\ \(\)]+).*CPU MHz: *(?P[0-9\.]+)', SSH.getBefore()) + if result is not None: + CpuNb = result.group('nb_cpus') + logging.debug('nb_cpus: ' + CpuNb) + HTML.CpuNb[idx]=CpuNb + CpuModel = result.group('model') + logging.debug('model: ' + CpuModel) + HTML.CpuModel[idx]=CpuModel + CpuMHz = result.group('cpu_mhz') + ' MHz' + logging.debug('cpu_mhz: ' + CpuMHz) + HTML.CpuMHz[idx]=CpuMHz + SSH.close() + + def ShowTestID(self): + logging.debug('\u001B[1m----------------------------------------\u001B[0m') + logging.debug('\u001B[1mTest ID:' + self.testCase_id + '\u001B[0m') + logging.debug('\u001B[1m' + self.desc + '\u001B[0m') + logging.debug('\u001B[1m----------------------------------------\u001B[0m') diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 86fd9d84df..ca29538713 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -37,9 +37,12 @@ import helpreadme as HELP import constants as CONST + +import cls_oaicitest #main class for OAI CI test framework import cls_physim #class PhySim for physical simulators build and test import cls_cots_ue #class CotsUe for Airplane mode control + import sshconnection import epc import ran @@ -67,3055 +70,6 @@ logging.basicConfig( -#----------------------------------------------------------- -# OaiCiTest Class Definition -#----------------------------------------------------------- -class OaiCiTest(): - - def __init__(self): - self.ranRepository = '' - self.ranBranch = '' - self.ranCommitID = '' - self.ranAllowMerge = False - self.ranTargetBranch = '' - - self.FailReportCnt = 0 - self.ADBIPAddress = '' - self.ADBUserName = '' - self.ADBPassword = '' - self.ADBCentralized = True - self.testCase_id = '' - self.testXMLfiles = [] - self.desc = '' - self.ping_args = '' - self.ping_packetloss_threshold = '' - self.iperf_args = '' - self.iperf_packetloss_threshold = '' - self.iperf_profile = '' - self.iperf_options = '' - self.nbMaxUEtoAttach = -1 - self.UEDevices = [] - self.UEDevicesStatus = [] - self.UEDevicesRemoteServer = [] - self.UEDevicesRemoteUser = [] - self.UEDevicesOffCmd = [] - self.UEDevicesOnCmd = [] - self.UEDevicesRebootCmd = [] - self.CatMDevices = [] - self.UEIPAddresses = [] - self.idle_sleep_time = 0 - self.x2_ho_options = 'network' - self.x2NbENBs = 0 - self.x2ENBBsIds = [] - self.x2ENBConnectedUEs = [] - self.repeatCounts = [] - self.finalStatus = False - self.UEIPAddress = '' - self.UEUserName = '' - self.UEPassword = '' - self.UE_instance = 0 - self.UESourceCodePath = '' - self.UELogFile = '' - self.Build_OAI_UE_args = '' - self.Initialize_OAI_UE_args = '' - self.clean_repository = True - self.air_interface='' - self.expectedNbOfConnectedUEs = 0 - - def BuildOAIUE(self): - if self.UEIPAddress == '' or self.ranRepository == '' or self.ranBranch == '' or self.UEUserName == '' or self.UEPassword == '' or self.UESourceCodePath == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - result = re.search('--nrUE', self.Build_OAI_UE_args) - if result is not None: - self.air_interface='nr-uesoftmodem' - ue_prefix = 'NR ' - else: - self.air_interface='lte-uesoftmodem' - ue_prefix = '' - result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository) - if result is not None: - full_ran_repo_name = self.ranRepository - else: - full_ran_repo_name = self.ranRepository + '.git' - SSH.command('mkdir -p ' + self.UESourceCodePath, '\$', 5) - SSH.command('cd ' + self.UESourceCodePath, '\$', 5) - SSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone ' + full_ran_repo_name + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600) - # here add a check if git clone or git fetch went smoothly - SSH.command('git config user.email "jenkins@openairinterface.org"', '\$', 5) - SSH.command('git config user.name "OAI Jenkins"', '\$', 5) - if self.clean_repository: - SSH.command('ls *.txt', '\$', 5) - result = re.search('LAST_BUILD_INFO', SSH.getBefore()) - if result is not None: - mismatch = False - SSH.command('grep SRC_COMMIT LAST_BUILD_INFO.txt', '\$', 2) - result = re.search(self.ranCommitID, SSH.getBefore()) - if result is None: - mismatch = True - SSH.command('grep MERGED_W_TGT_BRANCH LAST_BUILD_INFO.txt', '\$', 2) - if self.ranAllowMerge: - result = re.search('YES', SSH.getBefore()) - if result is None: - mismatch = True - SSH.command('grep TGT_BRANCH LAST_BUILD_INFO.txt', '\$', 2) - if self.ranTargetBranch == '': - result = re.search('develop', SSH.getBefore()) - else: - result = re.search(self.ranTargetBranch, SSH.getBefore()) - if result is None: - mismatch = True - else: - result = re.search('NO', SSH.getBefore()) - if result is None: - mismatch = True - if not mismatch: - SSH.close() - HTML.CreateHtmlTestRow(self.Build_OAI_UE_args, 'OK', CONST.ALL_PROCESSES_OK) - return - - SSH.command('echo ' + self.UEPassword + ' | sudo -S git clean -x -d -ff', '\$', 30) - - # if the commit ID is provided use it to point to it - if self.ranCommitID != '': - SSH.command('git checkout -f ' + self.ranCommitID, '\$', 5) - # if the branch is not develop, then it is a merge request and we need to do - # the potential merge. Note that merge conflicts should already been checked earlier - if self.ranAllowMerge: - if self.ranTargetBranch == '': - if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'): - SSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5) - else: - logging.debug('Merging with the target branch: ' + self.ranTargetBranch) - SSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5) - SSH.command('source oaienv', '\$', 5) - SSH.command('cd cmake_targets', '\$', 5) - SSH.command('mkdir -p log', '\$', 5) - SSH.command('chmod 777 log', '\$', 5) - # no need to remove in log (git clean did the trick) - SSH.command('stdbuf -o0 ./build_oai ' + self.Build_OAI_UE_args + ' 2>&1 | stdbuf -o0 tee compile_oai_ue.log', 'Bypassing the Tests|build have failed', 900) - SSH.command('ls ran_build/build', '\$', 3) - SSH.command('ls ran_build/build', '\$', 3) - buildStatus = True - result = re.search(self.air_interface, SSH.getBefore()) - if result is None: - buildStatus = False - SSH.command('mkdir -p build_log_' + self.testCase_id, '\$', 5) - SSH.command('mv log/* ' + 'build_log_' + self.testCase_id, '\$', 5) - SSH.command('mv compile_oai_ue.log ' + 'build_log_' + self.testCase_id, '\$', 5) - if buildStatus: - # Generating a BUILD INFO file - SSH.command('echo "SRC_BRANCH: ' + self.ranBranch + '" > ../LAST_BUILD_INFO.txt', '\$', 2) - SSH.command('echo "SRC_COMMIT: ' + self.ranCommitID + '" >> ../LAST_BUILD_INFO.txt', '\$', 2) - if self.ranAllowMerge: - SSH.command('echo "MERGED_W_TGT_BRANCH: YES" >> ../LAST_BUILD_INFO.txt', '\$', 2) - if self.ranTargetBranch == '': - SSH.command('echo "TGT_BRANCH: develop" >> ../LAST_BUILD_INFO.txt', '\$', 2) - else: - SSH.command('echo "TGT_BRANCH: ' + self.ranTargetBranch + '" >> ../LAST_BUILD_INFO.txt', '\$', 2) - else: - SSH.command('echo "MERGED_W_TGT_BRANCH: NO" >> ../LAST_BUILD_INFO.txt', '\$', 2) - SSH.close() - HTML.CreateHtmlTestRow(self.Build_OAI_UE_args, 'OK', CONST.ALL_PROCESSES_OK, 'OAI UE') - else: - SSH.close() - logging.error('\u001B[1m Building OAI UE Failed\u001B[0m') - HTML.CreateHtmlTestRow(self.Build_OAI_UE_args, 'KO', CONST.ALL_PROCESSES_OK, 'OAI UE') - HTML.CreateHtmlTabFooter(False) - sys.exit(1) - - def CheckFlexranCtrlInstallation(self): - if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '': - return - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('ls -ls /opt/flexran_rtc/*/rt_controller', '\$', 5) - result = re.search('/opt/flexran_rtc/build/rt_controller', SSH.getBefore()) - if result is not None: - RAN.flexranCtrlInstalled=True - logging.debug('Flexran Controller is installed') - SSH.close() - - def InitializeFlexranCtrl(self): - if RAN.flexranCtrlInstalled == False: - return - if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cd /opt/flexran_rtc', '\$', 5) - SSH.command('echo ' + EPC.Password + ' | sudo -S rm -f log/*.log', '\$', 5) - SSH.command('echo ' + EPC.Password + ' | sudo -S echo "build/rt_controller -c log_config/basic_log" > ./my-flexran-ctl.sh', '\$', 5) - SSH.command('echo ' + EPC.Password + ' | sudo -S chmod 755 ./my-flexran-ctl.sh', '\$', 5) - SSH.command('echo ' + EPC.Password + ' | sudo -S daemon --unsafe --name=flexran_rtc_daemon --chdir=/opt/flexran_rtc -o /opt/flexran_rtc/log/flexranctl_' + self.testCase_id + '.log ././my-flexran-ctl.sh', '\$', 5) - SSH.command('ps -aux | grep --color=never rt_controller', '\$', 5) - result = re.search('rt_controller -c ', SSH.getBefore()) - if result is not None: - logging.debug('\u001B[1m Initialize FlexRan Controller Completed\u001B[0m') - RAN.flexranCtrlStarted=True - SSH.close() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - - def InitializeUE_common(self, device_id, idx): - try: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - if not self.ADBCentralized: - # Reboot UE - #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesRebootCmd[idx], '\$', 60) - # Wait - #time.sleep(60) - # Put in LTE-Mode only - #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "settings put global preferred_network_mode 11"\'', '\$', 60) - #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "settings put global preferred_network_mode1 11"\'', '\$', 60) - #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "settings put global preferred_network_mode2 11"\'', '\$', 60) - #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "settings put global preferred_network_mode3 11"\'', '\$', 60) - # enable data service - #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "svc data enable"\'', '\$', 60) - # we need to do radio on/off cycle to make sure of above changes - # airplane mode off // radio on - #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOnCmd[idx], '\$', 60) - #time.sleep(10) - # airplane mode on // radio off - #SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) - - # normal procedure without reboot - # enable data service - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "svc data enable"\'', '\$', 60) - # airplane mode on // radio off - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) - SSH.close() - return - - #RH quick add-on to integrate cots control defined by yaml - #if device_id exists in yaml dictionary, we execute the new procedure defined in cots_ue class - #otherwise we use the legacy procedure - if COTS_UE.Check_Exists(device_id): - #switch device to Airplane mode ON (ie Radio OFF) - COTS_UE.Set_Airplane(device_id, 'ON') - else: - # enable data service - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "svc data enable"', '\$', 60) - - # The following commands are deprecated since we no longer work on Android 7+ - # SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell settings put global airplane_mode_on 1', '\$', 10) - # SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true', '\$', 60) - # a dedicated script has to be installed inside the UE - # airplane mode on means call /data/local/tmp/off - if device_id == '84B7N16418004022': - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/off"', '\$', 60) - else: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/off', '\$', 60) - #airplane mode off means call /data/local/tmp/on - logging.debug('\u001B[1mUE (' + device_id + ') Initialize Completed\u001B[0m') - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def InitializeUE(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - multi_jobs = [] - i = 0 - for device_id in self.UEDevices: - p = Process(target = self.InitializeUE_common, args = (device_id,i,)) - p.daemon = True - p.start() - multi_jobs.append(p) - i += 1 - for job in multi_jobs: - job.join() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - - def InitializeOAIUE(self): - if self.UEIPAddress == '' or self.UEUserName == '' or self.UEPassword == '' or self.UESourceCodePath == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - if self.air_interface == 'lte-uesoftmodem': - result = re.search('--no-L2-connect', str(self.Initialize_OAI_UE_args)) - if result is None: - check_eNB = True - check_OAI_UE = False - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow(self.air_interface + ' ' + self.Initialize_OAI_UE_args, 'KO', pStatus) - HTML.CreateHtmlTabFooter(False) - sys.exit(1) - UE_prefix = '' - else: - UE_prefix = 'NR ' - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - # b2xx_fx3_utils reset procedure - SSH.command('echo ' + self.UEPassword + ' | sudo -S uhd_find_devices', '\$', 60) - result = re.search('type: b200', SSH.getBefore()) - if result is not None: - logging.debug('Found a B2xx device --> resetting it') - SSH.command('echo ' + self.UEPassword + ' | sudo -S b2xx_fx3_utils --reset-device', '\$', 10) - # Reloading FGPA bin firmware - SSH.command('echo ' + self.UEPassword + ' | sudo -S uhd_find_devices', '\$', 60) - result = re.search('type: n3xx', str(SSH.getBefore())) - if result is not None: - logging.debug('Found a N3xx device --> resetting it') - SSH.command('cd ' + self.UESourceCodePath, '\$', 5) - # Initialize_OAI_UE_args usually start with -C and followed by the location in repository - SSH.command('source oaienv', '\$', 5) - SSH.command('cd cmake_targets/ran_build/build', '\$', 5) - if self.air_interface == 'lte-uesoftmodem': - result = re.search('--no-L2-connect', str(self.Initialize_OAI_UE_args)) - # We may have to regenerate the .u* files - if result is None: - SSH.command('ls /tmp/*.sed', '\$', 5) - result = re.search('adapt_usim_parameters', SSH.getBefore()) - if result is not None: - SSH.command('sed -f /tmp/adapt_usim_parameters.sed ../../../openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf > ../../../openair3/NAS/TOOLS/ci-ue_eurecom_test_sfr.conf', '\$', 5) - else: - SSH.command('sed -e "s#93#92#" -e "s#8baf473f2f8fd09487cccbd7097c6862#fec86ba6eb707ed08905757b1bb44b8f#" -e "s#e734f8734007d6c5ce7a0508809e7e9c#C42449363BBAD02B66D16BC975D77CC1#" ../../../openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf > ../../../openair3/NAS/TOOLS/ci-ue_eurecom_test_sfr.conf', '\$', 5) - SSH.command('echo ' + self.UEPassword + ' | sudo -S rm -Rf .u*', '\$', 5) - SSH.command('echo ' + self.UEPassword + ' | sudo -S ../../../targets/bin/conf2uedata -c ../../../openair3/NAS/TOOLS/ci-ue_eurecom_test_sfr.conf -o .', '\$', 5) - else: - SSH.command('if [ -e rbconfig.raw ]; then echo ' + self.UEPassword + ' | sudo -S rm rbconfig.raw; fi', '\$', 5) - SSH.command('if [ -e reconfig.raw ]; then echo ' + self.UEPassword + ' | sudo -S rm reconfig.raw; fi', '\$', 5) - # Copy the RAW files from gNB running directory (maybe on another machine) - copyin_res = SSH.copyin(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath + '/cmake_targets/rbconfig.raw', '.') - if (copyin_res == 0): - SSH.copyout(self.UEIPAddress, self.UEUserName, self.UEPassword, './rbconfig.raw', self.UESourceCodePath + '/cmake_targets/ran_build/build') - copyin_res = SSH.copyin(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath + '/cmake_targets/reconfig.raw', '.') - if (copyin_res == 0): - SSH.copyout(self.UEIPAddress, self.UEUserName, self.UEPassword, './reconfig.raw', self.UESourceCodePath + '/cmake_targets/ran_build/build') - SSH.command('echo "ulimit -c unlimited && ./'+ self.air_interface +' ' + self.Initialize_OAI_UE_args + '" > ./my-lte-uesoftmodem-run' + str(self.UE_instance) + '.sh', '\$', 5) - SSH.command('chmod 775 ./my-lte-uesoftmodem-run' + str(self.UE_instance) + '.sh', '\$', 5) - SSH.command('echo ' + self.UEPassword + ' | sudo -S rm -Rf ' + self.UESourceCodePath + '/cmake_targets/ue_' + self.testCase_id + '.log', '\$', 5) - self.UELogFile = 'ue_' + self.testCase_id + '.log' - - # We are now looping several times to hope we really sync w/ an eNB - doOutterLoop = True - outterLoopCounter = 5 - gotSyncStatus = True - fullSyncStatus = True - while (doOutterLoop): - SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets/ran_build/build', '\$', 5) - SSH.command('echo ' + self.UEPassword + ' | sudo -S rm -Rf ' + self.UESourceCodePath + '/cmake_targets/ue_' + self.testCase_id + '.log', '\$', 5) - SSH.command('echo $USER; nohup sudo -E ./my-lte-uesoftmodem-run' + str(self.UE_instance) + '.sh' + ' > ' + self.UESourceCodePath + '/cmake_targets/ue_' + self.testCase_id + '.log ' + ' 2>&1 &', self.UEUserName, 5) - time.sleep(6) - SSH.command('cd ../..', '\$', 5) - doLoop = True - loopCounter = 10 - gotSyncStatus = True - # the 'got sync' message is for the UE threads synchronization - while (doLoop): - loopCounter = loopCounter - 1 - if (loopCounter == 0): - # Here should never occur - logging.error('"got sync" message never showed!') - gotSyncStatus = False - doLoop = False - continue - SSH.command('stdbuf -o0 cat ue_' + self.testCase_id + '.log | egrep --text --color=never -i "wait|sync"', '\$', 4) - if self.air_interface == 'nr-uesoftmodem': - result = re.search('Starting sync detection', SSH.getBefore()) - else: - result = re.search('got sync', SSH.getBefore()) - if result is None: - time.sleep(10) - else: - doLoop = False - logging.debug('Found "got sync" message!') - if gotSyncStatus == False: - # we certainly need to stop the lte-uesoftmodem process if it is still running! - SSH.command('ps -aux | grep --text --color=never softmodem | grep -v grep', '\$', 4) - result = re.search('-uesoftmodem', SSH.getBefore()) - if result is not None: - SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal=SIGINT -r *-uesoftmodem', '\$', 4) - time.sleep(3) - outterLoopCounter = outterLoopCounter - 1 - if (outterLoopCounter == 0): - doOutterLoop = False - continue - # We are now checking if sync w/ eNB DOES NOT OCCUR - # Usually during the cell synchronization stage, the UE returns with No cell synchronization message - # That is the case for LTE - # In NR case, it's a positive message that will show if synchronization occurs - doLoop = True - if self.air_interface == 'nr-uesoftmodem': - loopCounter = 10 - else: - # We are now checking if sync w/ eNB DOES NOT OCCUR - # Usually during the cell synchronization stage, the UE returns with No cell synchronization message - loopCounter = 10 - while (doLoop): - loopCounter = loopCounter - 1 - if (loopCounter == 0): - if self.air_interface == 'nr-uesoftmodem': - # Here we do have great chances that UE did NOT cell-sync w/ gNB - doLoop = False - fullSyncStatus = False - logging.debug('Never seen the NR-Sync message (Measured Carrier Frequency) --> try again') - time.sleep(6) - # Stopping the NR-UE - SSH.command('ps -aux | grep --text --color=never softmodem | grep -v grep', '\$', 4) - result = re.search('nr-uesoftmodem', SSH.getBefore()) - if result is not None: - SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal=SIGINT nr-uesoftmodem', '\$', 4) - time.sleep(6) - else: - # Here we do have a great chance that the UE did cell-sync w/ eNB - doLoop = False - doOutterLoop = False - fullSyncStatus = True - continue - SSH.command('stdbuf -o0 cat ue_' + self.testCase_id + '.log | egrep --text --color=never -i "wait|sync|Frequency"', '\$', 4) - if self.air_interface == 'nr-uesoftmodem': - # Positive messaging --> - result = re.search('Measured Carrier Frequency', SSH.getBefore()) - if result is not None: - doLoop = False - doOutterLoop = False - fullSyncStatus = True - else: - time.sleep(6) - else: - # Negative messaging --> - result = re.search('No cell synchronization found', SSH.getBefore()) - if result is None: - time.sleep(6) - else: - doLoop = False - fullSyncStatus = False - logging.debug('Found: "No cell synchronization" message! --> try again') - time.sleep(6) - SSH.command('ps -aux | grep --text --color=never softmodem | grep -v grep', '\$', 4) - result = re.search('lte-uesoftmodem', SSH.getBefore()) - if result is not None: - SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal=SIGINT lte-uesoftmodem', '\$', 4) - outterLoopCounter = outterLoopCounter - 1 - if (outterLoopCounter == 0): - doOutterLoop = False - - if fullSyncStatus and gotSyncStatus: - doInterfaceCheck = False - if self.air_interface == 'lte-uesoftmodem': - result = re.search('--no-L2-connect', str(self.Initialize_OAI_UE_args)) - if result is None: - doInterfaceCheck = True - # For the moment, only in explicit noS1 without kernel module (ie w/ tunnel interface) - if self.air_interface == 'nr-uesoftmodem': - result = re.search('--noS1 --nokrnmod 1', str(self.Initialize_OAI_UE_args)) - if result is not None: - doInterfaceCheck = True - if doInterfaceCheck: - SSH.command('ifconfig oaitun_ue1', '\$', 4) - SSH.command('ifconfig oaitun_ue1', '\$', 4) - # ifconfig output is different between ubuntu 16 and ubuntu 18 - result = re.search('inet addr:1|inet 1', SSH.getBefore()) - if result is not None: - logging.debug('\u001B[1m oaitun_ue1 interface is mounted and configured\u001B[0m') - tunnelInterfaceStatus = True - else: - logging.debug(SSH.getBefore()) - logging.error('\u001B[1m oaitun_ue1 interface is either NOT mounted or NOT configured\u001B[0m') - tunnelInterfaceStatus = False - if RAN.eNBmbmsEnables[0]: - SSH.command('ifconfig oaitun_uem1', '\$', 4) - result = re.search('inet addr', SSH.getBefore()) - if result is not None: - logging.debug('\u001B[1m oaitun_uem1 interface is mounted and configured\u001B[0m') - tunnelInterfaceStatus = tunnelInterfaceStatus and True - else: - logging.error('\u001B[1m oaitun_uem1 interface is either NOT mounted or NOT configured\u001B[0m') - tunnelInterfaceStatus = False - else: - tunnelInterfaceStatus = True - else: - tunnelInterfaceStatus = True - - SSH.close() - if fullSyncStatus and gotSyncStatus and tunnelInterfaceStatus: - HTML.CreateHtmlTestRow(self.air_interface + ' ' + self.Initialize_OAI_UE_args, 'OK', CONST.ALL_PROCESSES_OK, 'OAI UE') - logging.debug('\u001B[1m Initialize OAI UE Completed\u001B[0m') - if (self.ADBIPAddress != 'none'): - self.UEDevices = [] - self.UEDevices.append('OAI-UE') - self.UEDevicesStatus = [] - self.UEDevicesStatus.append(CONST.UE_STATUS_DETACHED) - else: - if self.air_interface == 'lte-uesoftmodem': - if RAN.eNBmbmsEnables[0]: - HTML.htmlUEFailureMsg='oaitun_ue1/oaitun_uem1 interfaces are either NOT mounted or NOT configured' - else: - HTML.htmlUEFailureMsg='oaitun_ue1 interface is either NOT mounted or NOT configured' - HTML.CreateHtmlTestRow(self.air_interface + ' ' + self.Initialize_OAI_UE_args, 'KO', CONST.OAI_UE_PROCESS_NO_TUNNEL_INTERFACE, 'OAI UE') - else: - HTML.htmlUEFailureMsg='nr-uesoftmodem did NOT synced' - HTML.CreateHtmlTestRow(self.air_interface + ' ' + self.Initialize_OAI_UE_args, 'KO', CONST.OAI_UE_PROCESS_COULD_NOT_SYNC, 'OAI UE') - logging.error('\033[91mInitialize OAI UE Failed! \033[0m') - self.AutoTerminateUEandeNB() - - def checkDevTTYisUnlocked(self): - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - count = 0 - while count < 5: - SSH.command('echo ' + self.ADBPassword + ' | sudo -S lsof | grep ttyUSB0', '\$', 10) - result = re.search('picocom', SSH.getBefore()) - if result is None: - count = 10 - else: - time.sleep(5) - count = count + 1 - SSH.close() - - def InitializeCatM(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.enablePicocomClosure() - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - # dummy call to start a sudo session. The picocom command does NOT handle well the `sudo -S` - SSH.command('echo ' + self.ADBPassword + ' | sudo -S ls', '\$', 10) - SSH.command('sudo picocom --baud 921600 --flow n --databits 8 /dev/ttyUSB0', 'Terminal ready', 10) - time.sleep(1) - # Calling twice AT to clear all buffers - SSH.command('AT', 'OK|ERROR', 5) - SSH.command('AT', 'OK', 5) - # Doing a power cycle - SSH.command('AT^RESET', 'SIMSTORE,READY', 15) - SSH.command('AT', 'OK|ERROR', 5) - SSH.command('AT', 'OK', 5) - SSH.command('ATE1', 'OK', 5) - # Disabling the Radio - SSH.command('AT+CFUN=0', 'OK', 5) - logging.debug('\u001B[1m Cellular Functionality disabled\u001B[0m') - # Checking if auto-attach is enabled - SSH.command('AT^AUTOATT?', 'OK', 5) - result = re.search('AUTOATT: (?P[0-9\-]+)', SSH.getBefore()) - if result is not None: - if result.group('state') is not None: - autoAttachState = int(result.group('state')) - if autoAttachState is not None: - if autoAttachState == 0: - SSH.command('AT^AUTOATT=1', 'OK', 5) - logging.debug('\u001B[1m Auto-Attach enabled\u001B[0m') - else: - logging.debug('\u001B[1;37;41m Could not check Auto-Attach! \u001B[0m') - # Force closure of picocom but device might still be locked - SSH.close() - SSH.disablePicocomClosure() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - self.checkDevTTYisUnlocked() - - def TerminateCatM(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.enablePicocomClosure() - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - # dummy call to start a sudo session. The picocom command does NOT handle well the `sudo -S` - SSH.command('echo ' + self.ADBPassword + ' | sudo -S ls', '\$', 10) - SSH.command('sudo picocom --baud 921600 --flow n --databits 8 /dev/ttyUSB0', 'Terminal ready', 10) - time.sleep(1) - # Calling twice AT to clear all buffers - SSH.command('AT', 'OK|ERROR', 5) - SSH.command('AT', 'OK', 5) - # Disabling the Radio - SSH.command('AT+CFUN=0', 'OK', 5) - logging.debug('\u001B[1m Cellular Functionality disabled\u001B[0m') - SSH.close() - SSH.disablePicocomClosure() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - self.checkDevTTYisUnlocked() - - def AttachCatM(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.enablePicocomClosure() - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - # dummy call to start a sudo session. The picocom command does NOT handle well the `sudo -S` - SSH.command('echo ' + self.ADBPassword + ' | sudo -S ls', '\$', 10) - SSH.command('sudo picocom --baud 921600 --flow n --databits 8 /dev/ttyUSB0', 'Terminal ready', 10) - time.sleep(1) - # Calling twice AT to clear all buffers - SSH.command('AT', 'OK|ERROR', 5) - SSH.command('AT', 'OK', 5) - # Enabling the Radio - SSH.command('AT+CFUN=1', 'SIMSTORE,READY', 5) - logging.debug('\u001B[1m Cellular Functionality enabled\u001B[0m') - time.sleep(4) - # We should check if we register - count = 0 - attach_cnt = 0 - attach_status = False - while count < 5: - SSH.command('AT+CEREG?', 'OK', 5) - result = re.search('CEREG: 2,(?P[0-9\-]+),', SSH.getBefore()) - if result is not None: - mDataConnectionState = int(result.group('state')) - if mDataConnectionState is not None: - if mDataConnectionState == 1: - count = 10 - attach_status = True - result = re.search('CEREG: 2,1,"(?P[0-9A-Z]+)","(?P[0-9A-Z]+)"', SSH.getBefore()) - if result is not None: - networky = result.group('networky') - networkz = result.group('networkz') - logging.debug('\u001B[1m CAT-M module attached to eNB (' + str(networky) + '/' + str(networkz) + ')\u001B[0m') - else: - logging.debug('\u001B[1m CAT-M module attached to eNB\u001B[0m') - else: - logging.debug('+CEREG: 2,' + str(mDataConnectionState)) - attach_cnt = attach_cnt + 1 - else: - logging.debug(SSH.getBefore()) - attach_cnt = attach_cnt + 1 - count = count + 1 - time.sleep(1) - if attach_status: - SSH.command('AT+CESQ', 'OK', 5) - result = re.search('CESQ: 99,99,255,255,(?P[0-9]+),(?P[0-9]+)', SSH.getBefore()) - if result is not None: - nRSRQ = int(result.group('rsrq')) - nRSRP = int(result.group('rsrp')) - if (nRSRQ is not None) and (nRSRP is not None): - logging.debug(' RSRQ = ' + str(-20+(nRSRQ/2)) + ' dB') - logging.debug(' RSRP = ' + str(-140+nRSRP) + ' dBm') - SSH.close() - SSH.disablePicocomClosure() - html_queue = SimpleQueue() - self.checkDevTTYisUnlocked() - if attach_status: - html_cell = '
CAT-M module Attachment Completed in ' + str(attach_cnt+4) + ' seconds'
-			if (nRSRQ is not None) and (nRSRP is not None):
-				html_cell += '\n   RSRQ = ' + str(-20+(nRSRQ/2)) + ' dB'
-				html_cell += '\n   RSRP = ' + str(-140+nRSRP) + ' dBm
' - else: - html_cell += '' - html_queue.put(html_cell) - HTML.CreateHtmlTestRowQueue('N/A', 'OK', 1, html_queue) - else: - logging.error('\u001B[1m CAT-M module Attachment Failed\u001B[0m') - html_cell = '
CAT-M module Attachment Failed
' - html_queue.put(html_cell) - HTML.CreateHtmlTestRowQueue('N/A', 'KO', 1, html_queue) - self.AutoTerminateUEandeNB() - - def PingCatM(self): - if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.SourceCodePath == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - check_eNB = True - check_OAI_UE = False - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) - self.AutoTerminateUEandeNB() - return - try: - statusQueue = SimpleQueue() - lock = Lock() - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cd ' + EPC.SourceCodePath, '\$', 5) - SSH.command('cd scripts', '\$', 5) - if re.match('OAI', EPC.Type, re.IGNORECASE): - logging.debug('Using the OAI EPC HSS: not implemented yet') - HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) - HTML.CreateHtmlTabFooter(False) - sys.exit(1) - else: - SSH.command('egrep --color=never "Allocated ipv4 addr" /opt/ltebox/var/log/xGwLog.0', '\$', 5) - result = re.search('Allocated ipv4 addr: (?P[0-9\.]+) from Pool', SSH.getBefore()) - if result is not None: - moduleIPAddr = result.group('ipaddr') - else: - HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) - self.AutoTerminateUEandeNB() - return - ping_time = re.findall("-c (\d+)",str(self.ping_args)) - device_id = 'catm' - ping_status = SSH.command('stdbuf -o0 ping ' + self.ping_args + ' ' + str(moduleIPAddr) + ' 2>&1 | stdbuf -o0 tee ping_' + self.testCase_id + '_' + device_id + '.log', '\$', int(ping_time[0])*1.5) - # TIMEOUT CASE - if ping_status < 0: - message = 'Ping with UE (' + str(moduleIPAddr) + ') crashed due to TIMEOUT!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, moduleIPAddr, device_id, statusQueue, message) - return - result = re.search(', (?P[0-9\.]+)% packet loss, time [0-9\.]+ms', SSH.getBefore()) - if result is None: - message = 'Packet Loss Not Found!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, moduleIPAddr, device_id, statusQueue, message) - return - packetloss = result.group('packetloss') - if float(packetloss) == 100: - message = 'Packet Loss is 100%' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, moduleIPAddr, device_id, statusQueue, message) - return - result = re.search('rtt min\/avg\/max\/mdev = (?P[0-9\.]+)\/(?P[0-9\.]+)\/(?P[0-9\.]+)\/[0-9\.]+ ms', SSH.getBefore()) - if result is None: - message = 'Ping RTT_Min RTT_Avg RTT_Max Not Found!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, moduleIPAddr, device_id, statusQueue, message) - return - rtt_min = result.group('rtt_min') - rtt_avg = result.group('rtt_avg') - rtt_max = result.group('rtt_max') - pal_msg = 'Packet Loss : ' + packetloss + '%' - min_msg = 'RTT(Min) : ' + rtt_min + ' ms' - avg_msg = 'RTT(Avg) : ' + rtt_avg + ' ms' - max_msg = 'RTT(Max) : ' + rtt_max + ' ms' - lock.acquire() - logging.debug('\u001B[1;37;44m ping result (' + moduleIPAddr + ') \u001B[0m') - logging.debug('\u001B[1;34m ' + pal_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + min_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + avg_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + max_msg + '\u001B[0m') - qMsg = pal_msg + '\n' + min_msg + '\n' + avg_msg + '\n' + max_msg - packetLossOK = True - if packetloss is not None: - if float(packetloss) > float(self.ping_packetloss_threshold): - qMsg += '\nPacket Loss too high' - logging.debug('\u001B[1;37;41m Packet Loss too high \u001B[0m') - packetLossOK = False - elif float(packetloss) > 0: - qMsg += '\nPacket Loss is not 0%' - logging.debug('\u001B[1;30;43m Packet Loss is not 0% \u001B[0m') - lock.release() - SSH.close() - html_cell = '
CAT-M module\nIP Address  : ' + moduleIPAddr + '\n' + qMsg + '
' - statusQueue.put(html_cell) - if (packetLossOK): - HTML.CreateHtmlTestRowQueue(self.ping_args, 'OK', 1, statusQueue) - else: - HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', 1, statusQueue) - self.AutoTerminateUEandeNB() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def AttachUE_common(self, device_id, statusQueue, lock, idx): - try: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - if self.ADBCentralized: - #RH quick add on to integrate cots control defined by yaml - #if device Id exists in yaml dictionary, we execute the new procedure defined in cots_ue class - #otherwise we use the legacy procedure - if COTS_UE.Check_Exists(device_id): - #switch device to Airplane mode OFF (ie Radio ON) - COTS_UE.Set_Airplane(device_id, 'OFF') - elif device_id == '84B7N16418004022': - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/on"', '\$', 60) - else: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/on', '\$', 60) - else: - # airplane mode off // radio on - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOnCmd[idx], '\$', 60) - time.sleep(2) - max_count = 45 - count = max_count - while count > 0: - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "dumpsys telephony.registry" | grep -m 1 mDataConnectionState', '\$', 15) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "dumpsys telephony.registry"\' | grep -m 1 mDataConnectionState', '\$', 60) - result = re.search('mDataConnectionState.*=(?P[0-9\-]+)', SSH.getBefore()) - if result is None: - logging.debug('\u001B[1;37;41m mDataConnectionState Not Found! \u001B[0m') - lock.acquire() - statusQueue.put(-1) - statusQueue.put(device_id) - statusQueue.put('mDataConnectionState Not Found!') - lock.release() - break - mDataConnectionState = int(result.group('state')) - if mDataConnectionState == 2: - logging.debug('\u001B[1mUE (' + device_id + ') Attach Completed\u001B[0m') - lock.acquire() - statusQueue.put(max_count - count) - statusQueue.put(device_id) - statusQueue.put('Attach Completed') - lock.release() - break - count = count - 1 - if count == 15 or count == 30: - logging.debug('\u001B[1;30;43m Retry UE (' + device_id + ') Flight Mode Off \u001B[0m') - if self.ADBCentralized: - #RH quick add on to intgrate cots control defined by yaml - #if device id exists in yaml dictionary, we execute the new procedure defined in cots_ue class - #otherwise we use the legacy procedure - if COTS_UE.Check_Exists(device_id): - #switch device to Airplane mode ON (ie Radio OFF) - COTS_UE.Set_Airplane(device_id, 'ON') - elif device_id == '84B7N16418004022': - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/off"', '\$', 60) - else: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/off', '\$', 60) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) - time.sleep(0.5) - if self.ADBCentralized: - #RH quick add on to integrate cots control defined by yaml - #if device id exists in yaml dictionary, we execute the new procedre defined incots_ue class - #otherwise we use the legacy procedure - if COTS_UE.Check_Exists(device_id): - #switch device to Airplane mode OFF (ie Radio ON) - COTS_UE.Set_Airplane(device_id, 'OFF') - elif device_id == '84B7N16418004022': - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/on"', '\$', 60) - else: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/on', '\$', 60) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOnCmd[idx], '\$', 60) - time.sleep(0.5) - logging.debug('\u001B[1mWait UE (' + device_id + ') a second until mDataConnectionState=2 (' + str(max_count-count) + ' times)\u001B[0m') - time.sleep(1) - if count == 0: - logging.debug('\u001B[1;37;41m UE (' + device_id + ') Attach Failed \u001B[0m') - lock.acquire() - statusQueue.put(-1) - statusQueue.put(device_id) - statusQueue.put('Attach Failed') - lock.release() - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def AttachUE(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - check_eNB = True - check_OAI_UE = False - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow('N/A', 'KO', pStatus) - self.AutoTerminateUEandeNB() - return - multi_jobs = [] - status_queue = SimpleQueue() - lock = Lock() - nb_ue_to_connect = 0 - for device_id in self.UEDevices: - if (self.nbMaxUEtoAttach == -1) or (nb_ue_to_connect < self.nbMaxUEtoAttach): - self.UEDevicesStatus[nb_ue_to_connect] = CONST.UE_STATUS_ATTACHING - p = Process(target = self.AttachUE_common, args = (device_id, status_queue, lock,nb_ue_to_connect,)) - p.daemon = True - p.start() - multi_jobs.append(p) - nb_ue_to_connect = nb_ue_to_connect + 1 - for job in multi_jobs: - job.join() - - if (status_queue.empty()): - HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ALL_PROCESSES_OK) - self.AutoTerminateUEandeNB() - return - else: - attach_status = True - html_queue = SimpleQueue() - while (not status_queue.empty()): - count = status_queue.get() - if (count < 0): - attach_status = False - device_id = status_queue.get() - message = status_queue.get() - if (count < 0): - html_cell = '
UE (' + device_id + ')\n' + message + '
' - else: - html_cell = '
UE (' + device_id + ')\n' + message + ' in ' + str(count + 2) + ' seconds
' - html_queue.put(html_cell) - if (attach_status): - cnt = 0 - while cnt < len(self.UEDevices): - if self.UEDevicesStatus[cnt] == CONST.UE_STATUS_ATTACHING: - self.UEDevicesStatus[cnt] = CONST.UE_STATUS_ATTACHED - cnt += 1 - HTML.CreateHtmlTestRowQueue('N/A', 'OK', len(self.UEDevices), html_queue) - result = re.search('T_stdout', str(RAN.Initialize_eNB_args)) - if result is not None: - logging.debug('Waiting 5 seconds to fill up record file') - time.sleep(5) - else: - HTML.CreateHtmlTestRowQueue('N/A', 'KO', len(self.UEDevices), html_queue) - self.AutoTerminateUEandeNB() - - def DetachUE_common(self, device_id, idx): - try: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - if self.ADBCentralized: - #RH quick add on to integrate cots control defined by yaml - #if device id exists in yaml dictionary, we execute the new procedure defined in cots_ue class - #otherwise we use the legacy procedure - if COTS_UE.Check_Exists(device_id): - #switch device to Airplane mode ON (ie Radio OFF) - COTS_UE.Set_Airplane(device_id,'ON') - elif device_id == '84B7N16418004022': - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/off"', '\$', 60) - else: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/off', '\$', 60) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) - logging.debug('\u001B[1mUE (' + device_id + ') Detach Completed\u001B[0m') - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def DetachUE(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - check_eNB = True - check_OAI_UE = False - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow('N/A', 'KO', pStatus) - self.AutoTerminateUEandeNB() - return - multi_jobs = [] - cnt = 0 - for device_id in self.UEDevices: - self.UEDevicesStatus[cnt] = CONST.UE_STATUS_DETACHING - p = Process(target = self.DetachUE_common, args = (device_id,cnt,)) - p.daemon = True - p.start() - multi_jobs.append(p) - cnt += 1 - for job in multi_jobs: - job.join() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - result = re.search('T_stdout', str(RAN.Initialize_eNB_args)) - if result is not None: - logging.debug('Waiting 5 seconds to fill up record file') - time.sleep(5) - cnt = 0 - while cnt < len(self.UEDevices): - self.UEDevicesStatus[cnt] = CONST.UE_STATUS_DETACHED - cnt += 1 - - def RebootUE_common(self, device_id): - try: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - previousmDataConnectionStates = [] - # Save mDataConnectionState - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell dumpsys telephony.registry | grep mDataConnectionState', '\$', 15) - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell dumpsys telephony.registry | grep mDataConnectionState', '\$', 15) - result = re.search('mDataConnectionState.*=(?P[0-9\-]+)', SSH.getBefore()) - if result is None: - logging.debug('\u001B[1;37;41m mDataConnectionState Not Found! \u001B[0m') - sys.exit(1) - previousmDataConnectionStates.append(int(result.group('state'))) - # Reboot UE - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell reboot', '\$', 10) - time.sleep(60) - previousmDataConnectionState = previousmDataConnectionStates.pop(0) - count = 180 - while count > 0: - count = count - 1 - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell dumpsys telephony.registry | grep mDataConnectionState', '\$', 15) - result = re.search('mDataConnectionState.*=(?P[0-9\-]+)', SSH.getBefore()) - if result is None: - mDataConnectionState = None - else: - mDataConnectionState = int(result.group('state')) - logging.debug('mDataConnectionState = ' + result.group('state')) - if mDataConnectionState is None or (previousmDataConnectionState == 2 and mDataConnectionState != 2): - logging.debug('\u001B[1mWait UE (' + device_id + ') a second until reboot completion (' + str(180-count) + ' times)\u001B[0m') - time.sleep(1) - else: - logging.debug('\u001B[1mUE (' + device_id + ') Reboot Completed\u001B[0m') - break - if count == 0: - logging.debug('\u001B[1;37;41m UE (' + device_id + ') Reboot Failed \u001B[0m') - sys.exit(1) - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def RebootUE(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - check_eNB = True - check_OAI_UE = False - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow('N/A', 'KO', pStatus) - HTML.CreateHtmlTabFooter(False) - sys.exit(1) - multi_jobs = [] - for device_id in self.UEDevices: - p = Process(target = self.RebootUE_common, args = (device_id,)) - p.daemon = True - p.start() - multi_jobs.append(p) - for job in multi_jobs: - job.join() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - - def DataDisableUE_common(self, device_id, idx): - try: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - # disable data service - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "svc data disable"', '\$', 60) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "svc data disable"\'', '\$', 60) - logging.debug('\u001B[1mUE (' + device_id + ') Disabled Data Service\u001B[0m') - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def DataDisableUE(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - multi_jobs = [] - i = 0 - for device_id in self.UEDevices: - p = Process(target = self.DataDisableUE_common, args = (device_id,i,)) - p.daemon = True - p.start() - multi_jobs.append(p) - i += 1 - for job in multi_jobs: - job.join() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - - def DataEnableUE_common(self, device_id, idx): - try: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - # enable data service - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "svc data enable"', '\$', 60) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "svc data enable"\'', '\$', 60) - logging.debug('\u001B[1mUE (' + device_id + ') Enabled Data Service\u001B[0m') - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def DataEnableUE(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - multi_jobs = [] - i = 0 - for device_id in self.UEDevices: - p = Process(target = self.DataEnableUE_common, args = (device_id,i,)) - p.daemon = True - p.start() - multi_jobs.append(p) - i += 1 - for job in multi_jobs: - job.join() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - - def GetAllUEDevices(self, terminate_ue_flag): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - if self.ADBCentralized: - SSH.command('adb devices', '\$', 15) - self.UEDevices = re.findall("\\\\r\\\\n([A-Za-z0-9]+)\\\\tdevice",SSH.getBefore()) - SSH.close() - else: - if (os.path.isfile('./phones_list.txt')): - os.remove('./phones_list.txt') - SSH.command('ls /etc/*/phones*.txt', '\$', 5) - result = re.search('/etc/ci/phones_list.txt', SSH.getBefore()) - SSH.close() - if (result is not None) and (len(self.UEDevices) == 0): - SSH.copyin(self.ADBIPAddress, self.ADBUserName, self.ADBPassword, '/etc/ci/phones_list.txt', '.') - if (os.path.isfile('./phones_list.txt')): - phone_list_file = open('./phones_list.txt', 'r') - for line in phone_list_file.readlines(): - line = line.strip() - result = re.search('^#', line) - if result is not None: - continue - comma_split = line.split(",") - self.UEDevices.append(comma_split[0]) - self.UEDevicesRemoteServer.append(comma_split[1]) - self.UEDevicesRemoteUser.append(comma_split[2]) - self.UEDevicesOffCmd.append(comma_split[3]) - self.UEDevicesOnCmd.append(comma_split[4]) - self.UEDevicesRebootCmd.append(comma_split[5]) - phone_list_file.close() - - if terminate_ue_flag == True: - if len(self.UEDevices) == 0: - logging.debug('\u001B[1;37;41m UE Not Found! \u001B[0m') - sys.exit(1) - if len(self.UEDevicesStatus) == 0: - cnt = 0 - while cnt < len(self.UEDevices): - self.UEDevicesStatus.append(CONST.UE_STATUS_DETACHED) - cnt += 1 - - def GetAllCatMDevices(self, terminate_ue_flag): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - if self.ADBCentralized: - SSH.command('lsusb | egrep "Future Technology Devices International, Ltd FT2232C" | sed -e "s#:.*##" -e "s# #_#g"', '\$', 15) - #self.CatMDevices = re.findall("\\\\r\\\\n([A-Za-z0-9_]+)",SSH.getBefore()) - self.CatMDevices = re.findall("\\\\r\\\\n([A-Za-z0-9_]+)",SSH.getBefore()) - else: - if (os.path.isfile('./modules_list.txt')): - os.remove('./modules_list.txt') - SSH.command('ls /etc/*/modules*.txt', '\$', 5) - result = re.search('/etc/ci/modules_list.txt', SSH.getBefore()) - SSH.close() - if result is not None: - logging.debug('Found a module list file on ADB server') - if terminate_ue_flag == True: - if len(self.CatMDevices) == 0: - logging.debug('\u001B[1;37;41m CAT-M UE Not Found! \u001B[0m') - sys.exit(1) - SSH.close() - - def CheckUEStatus_common(self, lock, device_id, statusQueue, idx): - try: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "dumpsys telephony.registry"', '\$', 15) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "dumpsys telephony.registry"\'', '\$', 60) - result = re.search('mServiceState=(?P[0-9]+)', SSH.getBefore()) - serviceState = 'Service State: UNKNOWN' - if result is not None: - lServiceState = int(result.group('serviceState')) - if lServiceState == 3: - serviceState = 'Service State: RADIO_POWERED_OFF' - if lServiceState == 1: - serviceState = 'Service State: OUT_OF_SERVICE' - if lServiceState == 0: - serviceState = 'Service State: IN_SERVICE' - if lServiceState == 2: - serviceState = 'Service State: EMERGENCY_ONLY' - result = re.search('mDataConnectionState=(?P[0-9]+)', SSH.getBefore()) - dataConnectionState = 'Data State: UNKNOWN' - if result is not None: - lDataConnectionState = int(result.group('dataConnectionState')) - if lDataConnectionState == 0: - dataConnectionState = 'Data State: DISCONNECTED' - if lDataConnectionState == 1: - dataConnectionState = 'Data State: CONNECTING' - if lDataConnectionState == 2: - dataConnectionState = 'Data State: CONNECTED' - if lDataConnectionState == 3: - dataConnectionState = 'Data State: SUSPENDED' - result = re.search('mDataConnectionReason=(?P[0-9a-zA-Z_]+)', SSH.getBefore()) - dataConnectionReason = 'Data Reason: UNKNOWN' - if result is not None: - dataConnectionReason = 'Data Reason: ' + result.group('dataConnectionReason') - lock.acquire() - logging.debug('\u001B[1;37;44m Status Check (' + str(device_id) + ') \u001B[0m') - logging.debug('\u001B[1;34m ' + serviceState + '\u001B[0m') - logging.debug('\u001B[1;34m ' + dataConnectionState + '\u001B[0m') - logging.debug('\u001B[1;34m ' + dataConnectionReason + '\u001B[0m') - statusQueue.put(0) - statusQueue.put(device_id) - qMsg = serviceState + '\n' + dataConnectionState + '\n' + dataConnectionReason - statusQueue.put(qMsg) - lock.release() - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def CheckStatusUE(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - check_eNB = True - check_OAI_UE = False - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow('N/A', 'KO', pStatus) - HTML.CreateHtmlTabFooter(False) - sys.exit(1) - multi_jobs = [] - lock = Lock() - status_queue = SimpleQueue() - i = 0 - for device_id in self.UEDevices: - p = Process(target = self.CheckUEStatus_common, args = (lock,device_id,status_queue,i,)) - p.daemon = True - p.start() - multi_jobs.append(p) - i += 1 - for job in multi_jobs: - job.join() - if RAN.flexranCtrlInstalled and RAN.flexranCtrlStarted: - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cd /opt/flexran_rtc', '\$', 5) - SSH.command('curl http://localhost:9999/stats | jq \'.\' > log/check_status_' + self.testCase_id + '.log 2>&1', '\$', 5) - SSH.command('cat log/check_status_' + self.testCase_id + '.log | jq \'.eNB_config[0].UE\' | grep -c rnti | sed -e "s#^#Nb Connected UE = #"', '\$', 5) - result = re.search('Nb Connected UE = (?P[0-9]+)', SSH.getBefore()) - passStatus = True - if result is not None: - nb_ues = int(result.group('nb_ues')) - htmlOptions = 'Nb Connected UE(s) to eNB = ' + str(nb_ues) - logging.debug('\u001B[1;37;44m ' + htmlOptions + ' \u001B[0m') - if self.expectedNbOfConnectedUEs > -1: - if nb_ues != self.expectedNbOfConnectedUEs: - passStatus = False - else: - htmlOptions = 'N/A' - SSH.close() - else: - passStatus = True - htmlOptions = 'N/A' - - if (status_queue.empty()): - HTML.CreateHtmlTestRow(htmlOptions, 'KO', CONST.ALL_PROCESSES_OK) - self.AutoTerminateUEandeNB() - else: - check_status = True - html_queue = SimpleQueue() - while (not status_queue.empty()): - count = status_queue.get() - if (count < 0): - check_status = False - device_id = status_queue.get() - message = status_queue.get() - html_cell = '
UE (' + device_id + ')\n' + message + '
' - html_queue.put(html_cell) - if check_status and passStatus: - HTML.CreateHtmlTestRowQueue(htmlOptions, 'OK', len(self.UEDevices), html_queue) - else: - HTML.CreateHtmlTestRowQueue(htmlOptions, 'KO', len(self.UEDevices), html_queue) - self.AutoTerminateUEandeNB() - - def GetAllUEIPAddresses(self): - if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - ue_ip_status = 0 - self.UEIPAddresses = [] - if (len(self.UEDevices) == 1) and (self.UEDevices[0] == 'OAI-UE'): - if self.UEIPAddress == '' or self.UEUserName == '' or self.UEPassword == '' or self.UESourceCodePath == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('ifconfig oaitun_ue1', '\$', 4) - result = re.search('inet addr:(?P[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)|inet (?P[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)', SSH.getBefore()) - if result is not None: - if result.group('ueipaddress') is not None: - UE_IPAddress = result.group('ueipaddress') - else: - UE_IPAddress = result.group('ueipaddress2') - logging.debug('\u001B[1mUE (' + self.UEDevices[0] + ') IP Address is ' + UE_IPAddress + '\u001B[0m') - self.UEIPAddresses.append(UE_IPAddress) - else: - logging.debug('\u001B[1;37;41m UE IP Address Not Found! \u001B[0m') - ue_ip_status -= 1 - SSH.close() - return ue_ip_status - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - idx = 0 - for device_id in self.UEDevices: - if self.UEDevicesStatus[idx] != CONST.UE_STATUS_ATTACHED: - idx += 1 - continue - count = 0 - while count < 4: - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "ip addr show | grep rmnet"', '\$', 15) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "ip addr show | grep rmnet"\'', '\$', 60) - result = re.search('inet (?P[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\/[0-9]+[0-9a-zA-Z\.\s]+', SSH.getBefore()) - if result is None: - logging.debug('\u001B[1;37;41m UE IP Address Not Found! \u001B[0m') - time.sleep(1) - count += 1 - else: - count = 10 - if count < 9: - ue_ip_status -= 1 - continue - UE_IPAddress = result.group('ueipaddress') - logging.debug('\u001B[1mUE (' + device_id + ') IP Address is ' + UE_IPAddress + '\u001B[0m') - for ueipaddress in self.UEIPAddresses: - if ueipaddress == UE_IPAddress: - logging.debug('\u001B[1mUE (' + device_id + ') IP Address ' + UE_IPAddress + ': has already been allocated to another device !' + '\u001B[0m') - ue_ip_status -= 1 - continue - self.UEIPAddresses.append(UE_IPAddress) - idx += 1 - SSH.close() - return ue_ip_status - - def ping_iperf_wrong_exit(self, lock, UE_IPAddress, device_id, statusQueue, message): - lock.acquire() - statusQueue.put(-1) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - statusQueue.put(message) - lock.release() - - def Ping_common(self, lock, UE_IPAddress, device_id, statusQueue): - try: - # Launch ping on the EPC side (true for ltebox and old open-air-cn) - # But for OAI-Rel14-CUPS, we launch from python executor - launchFromEpc = True - if re.match('OAI-Rel14-CUPS', EPC.Type, re.IGNORECASE): - launchFromEpc = False - ping_time = re.findall("-c (\d+)",str(self.ping_args)) - - if launchFromEpc: - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cd ' + EPC.SourceCodePath, '\$', 5) - SSH.command('cd scripts', '\$', 5) - ping_status = SSH.command('stdbuf -o0 ping ' + self.ping_args + ' ' + UE_IPAddress + ' 2>&1 | stdbuf -o0 tee ping_' + self.testCase_id + '_' + device_id + '.log', '\$', int(ping_time[0])*1.5) - else: - cmd = 'ping ' + self.ping_args + ' ' + UE_IPAddress + ' 2>&1 > ping_' + self.testCase_id + '_' + device_id + '.log' - message = cmd + '\n' - logging.debug(cmd) - ret = subprocess.run(cmd, shell=True) - ping_status = ret.returncode - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'ping_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cat ' + EPC.SourceCodePath + '/scripts/ping_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - # TIMEOUT CASE - if ping_status < 0: - message = 'Ping with UE (' + str(UE_IPAddress) + ') crashed due to TIMEOUT!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) - return - result = re.search(', (?P[0-9\.]+)% packet loss, time [0-9\.]+ms', SSH.getBefore()) - if result is None: - message = 'Packet Loss Not Found!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) - return - packetloss = result.group('packetloss') - if float(packetloss) == 100: - message = 'Packet Loss is 100%' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) - return - result = re.search('rtt min\/avg\/max\/mdev = (?P[0-9\.]+)\/(?P[0-9\.]+)\/(?P[0-9\.]+)\/[0-9\.]+ ms', SSH.getBefore()) - if result is None: - message = 'Ping RTT_Min RTT_Avg RTT_Max Not Found!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) - return - rtt_min = result.group('rtt_min') - rtt_avg = result.group('rtt_avg') - rtt_max = result.group('rtt_max') - pal_msg = 'Packet Loss : ' + packetloss + '%' - min_msg = 'RTT(Min) : ' + rtt_min + ' ms' - avg_msg = 'RTT(Avg) : ' + rtt_avg + ' ms' - max_msg = 'RTT(Max) : ' + rtt_max + ' ms' - lock.acquire() - logging.debug('\u001B[1;37;44m ping result (' + UE_IPAddress + ') \u001B[0m') - logging.debug('\u001B[1;34m ' + pal_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + min_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + avg_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + max_msg + '\u001B[0m') - qMsg = pal_msg + '\n' + min_msg + '\n' + avg_msg + '\n' + max_msg - packetLossOK = True - if packetloss is not None: - if float(packetloss) > float(self.ping_packetloss_threshold): - qMsg += '\nPacket Loss too high' - logging.debug('\u001B[1;37;41m Packet Loss too high \u001B[0m') - packetLossOK = False - elif float(packetloss) > 0: - qMsg += '\nPacket Loss is not 0%' - logging.debug('\u001B[1;30;43m Packet Loss is not 0% \u001B[0m') - if (packetLossOK): - statusQueue.put(0) - else: - statusQueue.put(-1) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - statusQueue.put(qMsg) - lock.release() - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def PingNoS1_wrong_exit(self, qMsg): - html_queue = SimpleQueue() - html_cell = '
OAI UE ping result\n' + qMsg + '
' - html_queue.put(html_cell) - HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', len(self.UEDevices), html_queue) - - def PingNoS1(self): - check_eNB = True - check_OAI_UE = True - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) - self.AutoTerminateUEandeNB() - return - ping_from_eNB = re.search('oaitun_enb1', str(self.ping_args)) - if ping_from_eNB is not None: - if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - else: - if self.UEIPAddress == '' or self.UEUserName == '' or self.UEPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - try: - if ping_from_eNB is not None: - SSH.open(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword) - SSH.command('cd ' + RAN.eNBSourceCodePath + '/cmake_targets/', '\$', 5) - else: - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets/', '\$', 5) - ping_time = re.findall("-c (\d+)",str(self.ping_args)) - ping_status = SSH.command('stdbuf -o0 ping ' + self.ping_args + ' 2>&1 | stdbuf -o0 tee ping_' + self.testCase_id + '.log', '\$', int(ping_time[0])*1.5) - # TIMEOUT CASE - if ping_status < 0: - message = 'Ping with OAI UE crashed due to TIMEOUT!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - self.PingNoS1_wrong_exit(message) - return - result = re.search(', (?P[0-9\.]+)% packet loss, time [0-9\.]+ms', SSH.getBefore()) - if result is None: - message = 'Packet Loss Not Found!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - self.PingNoS1_wrong_exit(message) - return - packetloss = result.group('packetloss') - if float(packetloss) == 100: - message = 'Packet Loss is 100%' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - self.PingNoS1_wrong_exit(message) - return - result = re.search('rtt min\/avg\/max\/mdev = (?P[0-9\.]+)\/(?P[0-9\.]+)\/(?P[0-9\.]+)\/[0-9\.]+ ms', SSH.getBefore()) - if result is None: - message = 'Ping RTT_Min RTT_Avg RTT_Max Not Found!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - self.PingNoS1_wrong_exit(message) - return - rtt_min = result.group('rtt_min') - rtt_avg = result.group('rtt_avg') - rtt_max = result.group('rtt_max') - pal_msg = 'Packet Loss : ' + packetloss + '%' - min_msg = 'RTT(Min) : ' + rtt_min + ' ms' - avg_msg = 'RTT(Avg) : ' + rtt_avg + ' ms' - max_msg = 'RTT(Max) : ' + rtt_max + ' ms' - logging.debug('\u001B[1;37;44m OAI UE ping result \u001B[0m') - logging.debug('\u001B[1;34m ' + pal_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + min_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + avg_msg + '\u001B[0m') - logging.debug('\u001B[1;34m ' + max_msg + '\u001B[0m') - qMsg = pal_msg + '\n' + min_msg + '\n' + avg_msg + '\n' + max_msg - packetLossOK = True - if packetloss is not None: - if float(packetloss) > float(self.ping_packetloss_threshold): - qMsg += '\nPacket Loss too high' - logging.debug('\u001B[1;37;41m Packet Loss too high \u001B[0m') - packetLossOK = False - elif float(packetloss) > 0: - qMsg += '\nPacket Loss is not 0%' - logging.debug('\u001B[1;30;43m Packet Loss is not 0% \u001B[0m') - SSH.close() - html_queue = SimpleQueue() - ip_addr = 'TBD' - html_cell = '
OAI UE ping result\n' + qMsg + '
' - html_queue.put(html_cell) - if packetLossOK: - HTML.CreateHtmlTestRowQueue(self.ping_args, 'OK', len(self.UEDevices), html_queue) - else: - HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', len(self.UEDevices), html_queue) - - # copying on the EPC server for logCollection - if ping_from_eNB is not None: - copyin_res = SSH.copyin(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath + '/cmake_targets/ping_' + self.testCase_id + '.log', '.') - else: - copyin_res = SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/ping_' + self.testCase_id + '.log', '.') - if (copyin_res == 0): - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'ping_' + self.testCase_id + '.log', EPC.SourceCodePath + '/scripts') - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def Ping(self): - result = re.search('noS1', str(RAN.Initialize_eNB_args)) - if result is not None: - self.PingNoS1() - return - if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.SourceCodePath == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - check_eNB = True - if (len(self.UEDevices) == 1) and (self.UEDevices[0] == 'OAI-UE'): - check_OAI_UE = True - else: - check_OAI_UE = False - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow(self.ping_args, 'KO', pStatus) - self.AutoTerminateUEandeNB() - return - ueIpStatus = self.GetAllUEIPAddresses() - if (ueIpStatus < 0): - HTML.CreateHtmlTestRow(self.ping_args, 'KO', CONST.UE_IP_ADDRESS_ISSUE) - self.AutoTerminateUEandeNB() - return - multi_jobs = [] - i = 0 - lock = Lock() - status_queue = SimpleQueue() - for UE_IPAddress in self.UEIPAddresses: - device_id = self.UEDevices[i] - p = Process(target = self.Ping_common, args = (lock,UE_IPAddress,device_id,status_queue,)) - 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.ping_args, 'KO', CONST.ALL_PROCESSES_OK) - self.AutoTerminateUEandeNB() - else: - ping_status = True - html_queue = SimpleQueue() - while (not status_queue.empty()): - count = status_queue.get() - if (count < 0): - ping_status = False - device_id = status_queue.get() - ip_addr = status_queue.get() - message = status_queue.get() - html_cell = '
UE (' + device_id + ')\nIP Address  : ' + ip_addr + '\n' + message + '
' - html_queue.put(html_cell) - if (ping_status): - HTML.CreateHtmlTestRowQueue(self.ping_args, 'OK', len(self.UEDevices), html_queue) - else: - HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', len(self.UEDevices), html_queue) - self.AutoTerminateUEandeNB() - - def Iperf_ComputeTime(self): - result = re.search('-t (?P\d+)', str(self.iperf_args)) - if result is None: - logging.debug('\u001B[1;37;41m Iperf time Not Found! \u001B[0m') - sys.exit(1) - return result.group('iperf_time') - - 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') - sys.exit(1) - iperf_bandwidth = result.group('iperf_bandwidth') - if self.iperf_profile == 'balanced': - iperf_bandwidth_new = float(iperf_bandwidth)/ue_num - if self.iperf_profile == 'single-ue': - iperf_bandwidth_new = float(iperf_bandwidth) - if self.iperf_profile == 'unbalanced': - # residual is 2% of max bw - residualBW = float(iperf_bandwidth) / 50 - if idx == 0: - iperf_bandwidth_new = float(iperf_bandwidth) - ((ue_num - 1) * residualBW) - else: - iperf_bandwidth_new = residualBW - iperf_bandwidth_str = '-b ' + iperf_bandwidth - iperf_bandwidth_str_new = '-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') - sys.exit(1) - return result - - def Iperf_analyzeV2TCPOutput(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options): - SSH.command('awk -f /tmp/tcp_iperf_stats.awk ' + EPC.SourceCodePath + '/scripts/iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 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()) - if result is not None: - avgbitrate = result.group('average') - maxbitrate = result.group('maximum') - minbitrate = result.group('minimum') - lock.acquire() - logging.debug('\u001B[1;37;44m TCP iperf result (' + UE_IPAddress + ') \u001B[0m') - msg = 'TCP Stats :\n' - if avgbitrate is not None: - logging.debug('\u001B[1;34m Avg Bitrate : ' + avgbitrate + '\u001B[0m') - msg += 'Avg Bitrate : ' + avgbitrate + '\n' - if maxbitrate is not None: - logging.debug('\u001B[1;34m Max Bitrate : ' + maxbitrate + '\u001B[0m') - msg += 'Max Bitrate : ' + maxbitrate + '\n' - if minbitrate is not None: - logging.debug('\u001B[1;34m Min Bitrate : ' + minbitrate + '\u001B[0m') - msg += 'Min Bitrate : ' + minbitrate + '\n' - statusQueue.put(0) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - statusQueue.put(msg) - lock.release() - return 0 - - def Iperf_analyzeV2Output(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options): - result = re.search('-u', str(iperf_real_options)) - if result is None: - return self.Iperf_analyzeV2TCPOutput(lock, UE_IPAddress, device_id, statusQueue, iperf_real_options) - - 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') - else: - logging.debug('\u001B[1;37;41m Server Report and Connection refused Not Found! \u001B[0m') - return -1 - # Computing the requested bandwidth in float - result = re.search('-b (?P[0-9\.]+)[KMG]', str(iperf_real_options)) - if result is not None: - req_bandwidth = result.group('iperf_bandwidth') - req_bw = float(req_bandwidth) - result = re.search('-b [0-9\.]+K', str(iperf_real_options)) - if result is not None: - req_bandwidth = '%.1f Kbits/sec' % req_bw - req_bw = req_bw * 1000 - result = re.search('-b [0-9\.]+M', str(iperf_real_options)) - if result is not None: - req_bandwidth = '%.1f Mbits/sec' % req_bw - req_bw = req_bw * 1000000 - result = re.search('-b [0-9\.]+G', str(iperf_real_options)) - if result is not None: - req_bandwidth = '%.1f Gbits/sec' % req_bw - req_bw = req_bw * 1000000000 - - result = re.search('Server Report:\\\\r\\\\n(?:|\[ *\d+\].*) (?P[0-9\.]+ [KMG]bits\/sec) +(?P[0-9\.]+ ms) +(\d+\/..\d+) +(\((?P[0-9\.]+)%\))', SSH.getBefore()) - if result is not None: - bitrate = result.group('bitrate') - packetloss = result.group('packetloss') - jitter = result.group('jitter') - lock.acquire() - logging.debug('\u001B[1;37;44m iperf result (' + UE_IPAddress + ') \u001B[0m') - iperfStatus = True - msg = 'Req Bitrate : ' + req_bandwidth + '\n' - logging.debug('\u001B[1;34m Req Bitrate : ' + req_bandwidth + '\u001B[0m') - if bitrate is not None: - msg += 'Bitrate : ' + bitrate + '\n' - logging.debug('\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'))) - result = re.search('[0-9\.]+ K', bitrate) - if result is not None: - actual_bw = actual_bw * 1000 - result = re.search('[0-9\.]+ M', bitrate) - if result is not None: - actual_bw = actual_bw * 1000000 - result = re.search('[0-9\.]+ G', bitrate) - if result is not None: - actual_bw = actual_bw * 1000000000 - br_loss = 100 * actual_bw / req_bw - bitperf = '%.2f ' % br_loss - msg += 'Bitrate Perf: ' + bitperf + '%\n' - logging.debug('\u001B[1;34m Bitrate Perf: ' + bitperf + '%\u001B[0m') - if packetloss is not None: - msg += 'Packet Loss : ' + packetloss + '%\n' - logging.debug('\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 += 'Jitter : ' + jitter + '\n' - logging.debug('\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 - else: - return -2 - - def Iperf_analyzeV2Server(self, lock, UE_IPAddress, device_id, statusQueue, iperf_real_options): - if (not os.path.isfile('iperf_server_' + self.testCase_id + '_' + device_id + '.log')): - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Could not analyze from server log') - return - # 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 - else: - req_bandwidth = result.group('iperf_bandwidth') - req_bw = float(req_bandwidth) - result = re.search('-b [0-9\.]+K', str(iperf_real_options)) - if result is not None: - req_bandwidth = '%.1f Kbits/sec' % req_bw - req_bw = req_bw * 1000 - result = re.search('-b [0-9\.]+M', str(iperf_real_options)) - if result is not None: - req_bandwidth = '%.1f Mbits/sec' % req_bw - req_bw = req_bw * 1000000 - result = re.search('-b [0-9\.]+G', str(iperf_real_options)) - if result is not None: - req_bandwidth = '%.1f Gbits/sec' % req_bw - req_bw = req_bw * 1000000000 - - server_file = open('iperf_server_' + self.testCase_id + '_' + device_id + '.log', 'r') - br_sum = 0.0 - ji_sum = 0.0 - pl_sum = 0 - ps_sum = 0 - row_idx = 0 - for line in server_file.readlines(): - result = re.search('(?P[0-9\.]+ [KMG]bits\/sec) +(?P[0-9\.]+ ms) +(?P[0-9]+)/ +(?P[0-9]+)', str(line)) - if result is not None: - bitrate = result.group('bitrate') - jitter = result.group('jitter') - packetlost = result.group('lostPack') - packetsent = result.group('sentPack') - br = bitrate.split(' ') - ji = jitter.split(' ') - row_idx = row_idx + 1 - curr_br = float(br[0]) - pl_sum = pl_sum + int(packetlost) - ps_sum = ps_sum + int(packetsent) - if (br[1] == 'Kbits/sec'): - curr_br = curr_br * 1000 - if (br[1] == 'Mbits/sec'): - curr_br = curr_br * 1000 * 1000 - br_sum = curr_br + br_sum - ji_sum = float(ji[0]) + ji_sum - if (row_idx > 0): - br_sum = br_sum / row_idx - ji_sum = ji_sum / row_idx - br_loss = 100 * br_sum / req_bw - if (br_sum > 1000): - br_sum = br_sum / 1000 - if (br_sum > 1000): - br_sum = br_sum / 1000 - bitrate = '%.2f Mbits/sec' % br_sum - else: - bitrate = '%.2f Kbits/sec' % br_sum - else: - bitrate = '%.2f bits/sec' % br_sum - bitperf = '%.2f ' % br_loss - bitperf += '%' - jitter = '%.2f ms' % (ji_sum) - if (ps_sum > 0): - pl = float(100 * pl_sum / ps_sum) - packetloss = '%2.1f ' % (pl) - packetloss += '%' - else: - packetloss = 'unknown' - lock.acquire() - if (br_loss < 90): - statusQueue.put(1) - else: - statusQueue.put(0) - statusQueue.put(device_id) - statusQueue.put(UE_IPAddress) - req_msg = 'Req Bitrate : ' + req_bandwidth - bir_msg = 'Bitrate : ' + bitrate - brl_msg = 'Bitrate Perf: ' + bitperf - jit_msg = 'Jitter : ' + jitter - pal_msg = 'Packet Loss : ' + packetloss - statusQueue.put(req_msg + '\n' + bir_msg + '\n' + brl_msg + '\n' + jit_msg + '\n' + pal_msg + '\n') - logging.debug('\u001B[1;37;45m iperf result (' + UE_IPAddress + ') \u001B[0m') - logging.debug('\u001B[1;35m ' + req_msg + '\u001B[0m') - logging.debug('\u001B[1;35m ' + bir_msg + '\u001B[0m') - logging.debug('\u001B[1;35m ' + brl_msg + '\u001B[0m') - logging.debug('\u001B[1;35m ' + jit_msg + '\u001B[0m') - logging.debug('\u001B[1;35m ' + pal_msg + '\u001B[0m') - lock.release() - else: - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, 'Could not analyze from server log') - - server_file.close() - - - def Iperf_analyzeV3Output(self, lock, UE_IPAddress, device_id, statusQueue): - 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('\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('\u001B[1;37;44m iperf result (' + UE_IPAddress + ') \u001B[0m') - logging.debug('\u001B[1;34m Bitrate : ' + bitrate + '\u001B[0m') - msg = 'Bitrate : ' + bitrate + '\n' - iperfStatus = True - if packetloss is not None: - logging.debug('\u001B[1;34m Packet Loss : ' + packetloss + '%\u001B[0m') - msg += '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_UL_common(self, lock, UE_IPAddress, device_id, idx, ue_num, statusQueue): - udpIperf = True - result = re.search('-u', str(self.iperf_args)) - if result is None: - udpIperf = False - ipnumbers = UE_IPAddress.split('.') - if (len(ipnumbers) == 4): - ipnumbers[3] = '1' - EPC_Iperf_UE_IPAddress = ipnumbers[0] + '.' + ipnumbers[1] + '.' + ipnumbers[2] + '.' + ipnumbers[3] - - # Launch iperf server on EPC side (true for ltebox and old open-air-cn0 - # But for OAI-Rel14-CUPS, we launch from python executor and we are using its IP address as iperf client address - launchFromEpc = True - if re.match('OAI-Rel14-CUPS', EPC.Type, re.IGNORECASE): - launchFromEpc = False - cmd = 'hostname -I' - ret = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - if ret.stdout is not None: - EPC_Iperf_UE_IPAddress = ret.stdout.strip() - port = 5001 + idx - if launchFromEpc: - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cd ' + EPC.SourceCodePath + '/scripts', '\$', 5) - SSH.command('rm -f iperf_server_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - if udpIperf: - SSH.command('echo $USER; nohup iperf -u -s -i 1 -p ' + str(port) + ' > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', EPC.UserName, 5) - else: - SSH.command('echo $USER; nohup iperf -s -i 1 -p ' + str(port) + ' > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', EPC.UserName, 5) - SSH.close() - else: - if self.ueIperfVersion == self.dummyIperfVersion: - prefix = '' - else: - prefix = '' - if self.ueIperfVersion == '2.0.5': - prefix = '/opt/iperf-2.0.5/bin/' - if udpIperf: - cmd = 'nohup ' + prefix + 'iperf -u -s -i 1 -p ' + str(port) + ' > iperf_server_' + self.testCase_id + '_' + device_id + '.log 2>&1 &' - else: - cmd = 'nohup ' + prefix + 'iperf -s -i 1 -p ' + str(port) + ' > iperf_server_' + self.testCase_id + '_' + device_id + '.log 2>&1 &' - logging.debug(cmd) - subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - time.sleep(0.5) - - # Launch iperf client on UE - if (device_id == 'OAI-UE'): - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets', '\$', 5) - else: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - SSH.command('cd ' + EPC.SourceCodePath+ '/scripts', '\$', 5) - iperf_time = self.Iperf_ComputeTime() - time.sleep(0.5) - - if udpIperf: - modified_options = self.Iperf_ComputeModifiedBW(idx, ue_num) - else: - modified_options = str(self.iperf_args) - modified_options = modified_options.replace('-R','') - time.sleep(0.5) - - SSH.command('rm -f iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - if (device_id == 'OAI-UE'): - iperf_status = SSH.command('iperf -c ' + EPC_Iperf_UE_IPAddress + ' ' + modified_options + ' -p ' + str(port) + ' -B ' + UE_IPAddress + ' 2>&1 | stdbuf -o0 tee iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) - else: - if self.ADBCentralized: - iperf_status = SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "/data/local/tmp/iperf -c ' + EPC_Iperf_UE_IPAddress + ' ' + modified_options + ' -p ' + str(port) + '" 2>&1 | stdbuf -o0 tee iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) - else: - iperf_status = SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "/data/local/tmp/iperf -c ' + EPC_Iperf_UE_IPAddress + ' ' + modified_options + ' -p ' + str(port) + '"\' 2>&1 > iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) - SSH.command('fromdos -o iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - SSH.command('cat iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - # TIMEOUT Case - if iperf_status < 0: - SSH.close() - message = 'iperf on UE (' + str(UE_IPAddress) + ') crashed due to TIMEOUT !' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) - return - clientStatus = self.Iperf_analyzeV2Output(lock, UE_IPAddress, device_id, statusQueue, modified_options) - SSH.close() - - # Kill iperf server on EPC side - if launchFromEpc: - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('killall --signal SIGKILL iperf', EPC.UserName, 5) - SSH.close() - else: - cmd = 'killall --signal SIGKILL iperf' - logging.debug(cmd) - subprocess.run(cmd, shell=True) - time.sleep(1) - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') - # in case of failure, retrieve server log - if (clientStatus == -1) or (clientStatus == -2): - if launchFromEpc: - time.sleep(1) - if (os.path.isfile('iperf_server_' + self.testCase_id + '_' + device_id + '.log')): - os.remove('iperf_server_' + self.testCase_id + '_' + device_id + '.log') - SSH.copyin(EPC.IPAddress, EPC.UserName, EPC.Password, EPC.SourceCodePath+ '/scripts/iperf_server_' + self.testCase_id + '_' + device_id + '.log', '.') - self.Iperf_analyzeV2Server(lock, UE_IPAddress, device_id, statusQueue, modified_options) - # in case of OAI-UE - if (device_id == 'OAI-UE'): - SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/iperf_' + self.testCase_id + '_' + device_id + '.log', '.') - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') - - def Iperf_common(self, lock, UE_IPAddress, device_id, idx, ue_num, statusQueue): - try: - # Single-UE profile -- iperf only on one UE - if self.iperf_profile == 'single-ue' and idx != 0: - return - useIperf3 = False - udpIperf = True - - self.ueIperfVersion = '2.0.5' - if (device_id != 'OAI-UE'): - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - # if by chance ADB server and EPC are on the same remote host, at least log collection will take care of it - SSH.command('if [ ! -d ' + EPC.SourceCodePath + '/scripts ]; then mkdir -p ' + EPC.SourceCodePath + '/scripts ; fi', '\$', 5) - SSH.command('cd ' + EPC.SourceCodePath + '/scripts', '\$', 5) - # Checking if iperf / iperf3 are installed - if self.ADBCentralized: - SSH.command('adb -s ' + device_id + ' shell "ls /data/local/tmp"', '\$', 5) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "ls /data/local/tmp"\'', '\$', 60) - result = re.search('iperf3', SSH.getBefore()) - if result is None: - result = re.search('iperf', SSH.getBefore()) - if result is None: - message = 'Neither iperf nor iperf3 installed on UE!' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - SSH.close() - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) - return - else: - if self.ADBCentralized: - SSH.command('adb -s ' + device_id + ' shell "/data/local/tmp/iperf --version"', '\$', 5) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "/data/local/tmp/iperf --version"\'', '\$', 60) - result = re.search('iperf version 2.0.5', SSH.getBefore()) - if result is not None: - self.ueIperfVersion = '2.0.5' - result = re.search('iperf version 2.0.10', SSH.getBefore()) - if result is not None: - self.ueIperfVersion = '2.0.10' - else: - useIperf3 = True - SSH.close() - else: - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('iperf --version', '\$', 5) - result = re.search('iperf version 2.0.5', SSH.getBefore()) - if result is not None: - self.ueIperfVersion = '2.0.5' - result = re.search('iperf version 2.0.10', SSH.getBefore()) - if result is not None: - self.ueIperfVersion = '2.0.10' - SSH.close() - # in case of iperf, UL has its own function - if (not useIperf3): - result = re.search('-R', str(self.iperf_args)) - if result is not None: - self.Iperf_UL_common(lock, UE_IPAddress, device_id, idx, ue_num, statusQueue) - return - - # Launch the IPERF server on the UE side for DL - if (device_id == 'OAI-UE'): - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets', '\$', 5) - SSH.command('rm -f iperf_server_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - result = re.search('-u', str(self.iperf_args)) - if result is None: - SSH.command('echo $USER; nohup iperf -B ' + UE_IPAddress + ' -s -i 1 > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.UEUserName, 5) - udpIperf = False - else: - SSH.command('echo $USER; nohup iperf -B ' + UE_IPAddress + ' -u -s -i 1 > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.UEUserName, 5) - else: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - SSH.command('cd ' + EPC.SourceCodePath + '/scripts', '\$', 5) - if self.ADBCentralized: - if (useIperf3): - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/iperf3 -s &', '\$', 5) - else: - SSH.command('rm -f iperf_server_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - result = re.search('-u', str(self.iperf_args)) - if result is None: - SSH.command('echo $USER; nohup adb -s ' + device_id + ' shell "/data/local/tmp/iperf -s -i 1" > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.ADBUserName, 5) - udpIperf = False - else: - SSH.command('echo $USER; nohup adb -s ' + device_id + ' shell "/data/local/tmp/iperf -u -s -i 1" > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.ADBUserName, 5) - else: - SSH.command('rm -f iperf_server_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - SSH.command('echo $USER; nohup ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "/data/local/tmp/iperf -u -s -i 1" \' 2>&1 > iperf_server_' + self.testCase_id + '_' + device_id + '.log &', self.ADBUserName, 60) - - time.sleep(0.5) - SSH.close() - - # Launch the IPERF client on the EPC side for DL (true for ltebox and old open-air-cn - # But for OAI-Rel14-CUPS, we launch from python executor - launchFromEpc = True - if re.match('OAI-Rel14-CUPS', EPC.Type, re.IGNORECASE): - launchFromEpc = False - if launchFromEpc: - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cd ' + EPC.SourceCodePath + '/scripts', '\$', 5) - iperf_time = self.Iperf_ComputeTime() - time.sleep(0.5) - - if udpIperf: - modified_options = self.Iperf_ComputeModifiedBW(idx, ue_num) - else: - modified_options = str(self.iperf_args) - time.sleep(0.5) - - if launchFromEpc: - SSH.command('rm -f iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - else: - if (os.path.isfile('iperf_' + self.testCase_id + '_' + device_id + '.log')): - os.remove('iperf_' + self.testCase_id + '_' + device_id + '.log') - if (useIperf3): - SSH.command('stdbuf -o0 iperf3 -c ' + UE_IPAddress + ' ' + modified_options + ' 2>&1 | stdbuf -o0 tee iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) - - clientStatus = 0 - self.Iperf_analyzeV3Output(lock, UE_IPAddress, device_id, statusQueue) - else: - if launchFromEpc: - iperf_status = SSH.command('stdbuf -o0 iperf -c ' + UE_IPAddress + ' ' + modified_options + ' 2>&1 | stdbuf -o0 tee iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', int(iperf_time)*5.0) - else: - if self.ueIperfVersion == self.dummyIperfVersion: - prefix = '' - else: - prefix = '' - if self.ueIperfVersion == '2.0.5': - prefix = '/opt/iperf-2.0.5/bin/' - cmd = prefix + 'iperf -c ' + UE_IPAddress + ' ' + modified_options + ' 2>&1 > iperf_' + self.testCase_id + '_' + device_id + '.log' - message = cmd + '\n' - logging.debug(cmd) - ret = subprocess.run(cmd, shell=True) - iperf_status = ret.returncode - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cat ' + EPC.SourceCodePath + '/scripts/iperf_' + self.testCase_id + '_' + device_id + '.log', '\$', 5) - if iperf_status < 0: - if launchFromEpc: - SSH.close() - message = 'iperf on UE (' + str(UE_IPAddress) + ') crashed due to TIMEOUT !' - logging.debug('\u001B[1;37;41m ' + message + ' \u001B[0m') - self.ping_iperf_wrong_exit(lock, UE_IPAddress, device_id, statusQueue, message) - return - clientStatus = self.Iperf_analyzeV2Output(lock, UE_IPAddress, device_id, statusQueue, modified_options) - SSH.close() - - # Kill the IPERF server that runs in background - if (device_id == 'OAI-UE'): - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('killall iperf', '\$', 5) - else: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell ps | grep --color=never iperf | grep -v grep', '\$', 5) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "ps" | grep --color=never iperf | grep -v grep\'', '\$', 60) - result = re.search('shell +(?P\d+)', SSH.getBefore()) - if result is not None: - pid_iperf = result.group('pid') - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell kill -KILL ' + pid_iperf, '\$', 5) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "kill -KILL ' + pid_iperf + '"\'', '\$', 60) - SSH.close() - # if the client report is absent, try to analyze the server log file - if (clientStatus == -1): - time.sleep(1) - if (os.path.isfile('iperf_server_' + self.testCase_id + '_' + device_id + '.log')): - os.remove('iperf_server_' + self.testCase_id + '_' + device_id + '.log') - if (device_id == 'OAI-UE'): - SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/iperf_server_' + self.testCase_id + '_' + device_id + '.log', '.') - else: - SSH.copyin(self.ADBIPAddress, self.ADBUserName, self.ADBPassword, EPC.SourceCodePath + '/scripts/iperf_server_' + self.testCase_id + '_' + device_id + '.log', '.') - # fromdos has to be called on the python executor not on ADB server - cmd = 'fromdos -o iperf_server_' + self.testCase_id + '_' + device_id + '.log' - subprocess.run(cmd, shell=True) - self.Iperf_analyzeV2Server(lock, UE_IPAddress, device_id, statusQueue, modified_options) - - # in case of OAI UE: - if (device_id == 'OAI-UE'): - if (os.path.isfile('iperf_server_' + self.testCase_id + '_' + device_id + '.log')): - if not launchFromEpc: - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') - else: - SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/iperf_server_' + self.testCase_id + '_' + device_id + '.log', '.') - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_' + device_id + '.log', EPC.SourceCodePath + '/scripts') - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def IperfNoS1(self): - 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 - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow(self.iperf_args, 'KO', pStatus) - self.AutoTerminateUEandeNB() - return - 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('rm -f /tmp/tmp_iperf_server_' + self.testCase_id + '.log', '\$', 5) - SSH.command('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('rm -f /tmp/tmp_iperf_' + self.testCase_id + '.log', '\$', 5) - iperf_status = SSH.command('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('\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) - 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('iperf_server_' + self.testCase_id + '.log')): - os.remove('iperf_server_' + self.testCase_id + '.log') - SSH.copyin(iServerIPAddr, iServerUser, iServerPasswd, '/tmp/tmp_iperf_server_' + self.testCase_id + '.log', 'iperf_server_' + self.testCase_id + '_OAI-UE.log') - self.Iperf_analyzeV2Server(lock, '10.0.1.2', 'OAI-UE', status_queue, modified_options) - - # copying on the EPC server for logCollection - if (clientStatus == -1): - copyin_res = SSH.copyin(iServerIPAddr, iServerUser, iServerPasswd, '/tmp/tmp_iperf_server_' + self.testCase_id + '.log', 'iperf_server_' + self.testCase_id + '_OAI-UE.log') - if (copyin_res == 0): - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_OAI-UE.log', EPC.SourceCodePath + '/scripts') - copyin_res = SSH.copyin(iClientIPAddr, iClientUser, iClientPasswd, '/tmp/tmp_iperf_' + self.testCase_id + '.log', 'iperf_' + self.testCase_id + '_OAI-UE.log') - if (copyin_res == 0): - SSH.copyout(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_' + self.testCase_id + '_OAI-UE.log', EPC.SourceCodePath + '/scripts') - iperf_noperf = False - if status_queue.empty(): - iperf_status = False - else: - iperf_status = True - html_queue = SimpleQueue() - 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() - html_cell = '
UE (' + device_id + ')\nIP Address  : ' + ip_addr + '\n' + message + '
' - html_queue.put(html_cell) - if (iperf_noperf and iperf_status): - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'PERF NOT MET', len(self.UEDevices), html_queue) - elif (iperf_status): - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'OK', len(self.UEDevices), html_queue) - else: - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', len(self.UEDevices), html_queue) - self.AutoTerminateUEandeNB() - - def Iperf(self): - result = re.search('noS1', str(RAN.Initialize_eNB_args)) - if result is not None: - self.IperfNoS1() - return - if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.SourceCodePath == '' or self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - check_eNB = True - if (len(self.UEDevices) == 1) and (self.UEDevices[0] == 'OAI-UE'): - check_OAI_UE = True - else: - check_OAI_UE = False - pStatus = self.CheckProcessExist(check_eNB, check_OAI_UE) - if (pStatus < 0): - HTML.CreateHtmlTestRow(self.iperf_args, 'KO', pStatus) - self.AutoTerminateUEandeNB() - return - ueIpStatus = self.GetAllUEIPAddresses() - if (ueIpStatus < 0): - logging.debug('going here') - HTML.CreateHtmlTestRow(self.iperf_args, 'KO', CONST.UE_IP_ADDRESS_ISSUE) - self.AutoTerminateUEandeNB() - return - - 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 = [] - i = 0 - ue_num = len(self.UEIPAddresses) - lock = Lock() - status_queue = SimpleQueue() - for UE_IPAddress in self.UEIPAddresses: - device_id = self.UEDevices[i] - p = Process(target = self.Iperf_common, args = (lock,UE_IPAddress,device_id,i,ue_num,status_queue,)) - 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() - else: - iperf_status = True - iperf_noperf = False - html_queue = SimpleQueue() - 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() - html_cell = '
UE (' + device_id + ')\nIP Address  : ' + ip_addr + '\n' + message + '
' - html_queue.put(html_cell) - if (iperf_noperf and iperf_status): - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'PERF NOT MET', len(self.UEDevices), html_queue) - elif (iperf_status): - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'OK', len(self.UEDevices), html_queue) - else: - HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', len(self.UEDevices), html_queue) - self.AutoTerminateUEandeNB() - - def CheckProcessExist(self, check_eNB, check_OAI_UE): - multi_jobs = [] - status_queue = SimpleQueue() - # in noS1 config, no need to check status from EPC - # in gNB also currently no need to check - result = re.search('noS1|band78', str(RAN.Initialize_eNB_args)) - if result is None: - p = Process(target = EPC.CheckHSSProcess, args = (status_queue,)) - p.daemon = True - p.start() - multi_jobs.append(p) - p = Process(target = EPC.CheckMMEProcess, args = (status_queue,)) - p.daemon = True - p.start() - multi_jobs.append(p) - p = Process(target = EPC.CheckSPGWProcess, args = (status_queue,)) - p.daemon = True - p.start() - multi_jobs.append(p) - else: - if (check_eNB == False) and (check_OAI_UE == False): - return 0 - if check_eNB: - p = Process(target = RAN.CheckeNBProcess, args = (status_queue,)) - p.daemon = True - p.start() - multi_jobs.append(p) - if check_OAI_UE: - p = Process(target = self.CheckOAIUEProcess, args = (status_queue,)) - p.daemon = True - p.start() - multi_jobs.append(p) - for job in multi_jobs: - job.join() - - if (status_queue.empty()): - return -15 - else: - result = 0 - while (not status_queue.empty()): - status = status_queue.get() - if (status < 0): - result = status - if result == CONST.ENB_PROCESS_FAILED: - fileCheck = re.search('enb_', str(RAN.eNBLogFiles[0])) - if fileCheck is not None: - SSH.copyin(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath + '/cmake_targets/' + RAN.eNBLogFiles[0], '.') - logStatus = RAN.AnalyzeLogFile_eNB(RAN.eNBLogFiles[0]) - if logStatus < 0: - result = logStatus - RAN.eNBLogFiles[0]='' - if RAN.flexranCtrlInstalled and RAN.flexranCtrlStarted: - self.TerminateFlexranCtrl() - return result - - def CheckOAIUEProcessExist(self, initialize_OAI_UE_flag): - multi_jobs = [] - status_queue = SimpleQueue() - if initialize_OAI_UE_flag == False: - p = Process(target = self.CheckOAIUEProcess, args = (status_queue,)) - p.daemon = True - p.start() - multi_jobs.append(p) - for job in multi_jobs: - job.join() - - if (status_queue.empty()): - return -15 - else: - result = 0 - while (not status_queue.empty()): - status = status_queue.get() - if (status < 0): - result = status - if result == CONST.OAI_UE_PROCESS_FAILED: - fileCheck = re.search('ue_', str(self.UELogFile)) - if fileCheck is not None: - SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/' + self.UELogFile, '.') - logStatus = self.AnalyzeLogFile_UE(self.UELogFile) - if logStatus < 0: - result = logStatus - return result - - def CheckOAIUEProcess(self, status_queue): - try: - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('stdbuf -o0 ps -aux | grep --color=never ' + self.air_interface + ' | grep -v grep', '\$', 5) - result = re.search(self.air_interface, SSH.getBefore()) - if result is None: - logging.debug('\u001B[1;37;41m OAI UE Process Not Found! \u001B[0m') - status_queue.put(CONST.OAI_UE_PROCESS_FAILED) - else: - status_queue.put(CONST.OAI_UE_PROCESS_OK) - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - - def AnalyzeLogFile_UE(self, UElogFile): - if (not os.path.isfile('./' + UElogFile)): - return -1 - ue_log_file = open('./' + UElogFile, 'r') - exitSignalReceived = False - foundAssertion = False - msgAssertion = '' - msgLine = 0 - foundSegFault = False - foundRealTimeIssue = False - uciStatMsgCount = 0 - pdcpDataReqFailedCount = 0 - badDciCount = 0 - f1aRetransmissionCount = 0 - fatalErrorCount = 0 - macBsrTimerExpiredCount = 0 - rrcConnectionRecfgComplete = 0 - no_cell_sync_found = False - mib_found = False - frequency_found = False - plmn_found = False - nrUEFlag = False - nrDecodeMib = 0 - nrFoundDCI = 0 - nrCRCOK = 0 - mbms_messages = 0 - HTML.htmlUEFailureMsg='' - global_status = CONST.ALL_PROCESSES_OK - for line in ue_log_file.readlines(): - result = re.search('nr_synchro_time', str(line)) - if result is not None: - nrUEFlag = True - if nrUEFlag: - result = re.search('decode mib', str(line)) - if result is not None: - nrDecodeMib += 1 - result = re.search('found 1 DCIs', str(line)) - if result is not None: - nrFoundDCI += 1 - result = re.search('CRC OK', str(line)) - if result is not None: - nrCRCOK += 1 - result = re.search('Exiting OAI softmodem', str(line)) - if result is not None: - exitSignalReceived = True - result = re.search('System error|[Ss]egmentation [Ff]ault|======= Backtrace: =========|======= Memory map: ========', str(line)) - if result is not None and not exitSignalReceived: - foundSegFault = True - result = re.search('[Cc]ore [dD]ump', str(line)) - if result is not None and not exitSignalReceived: - foundSegFault = True - result = re.search('./lte-uesoftmodem', str(line)) - if result is not None and not exitSignalReceived: - foundSegFault = True - result = re.search('[Aa]ssertion', str(line)) - if result is not None and not exitSignalReceived: - foundAssertion = True - result = re.search('LLL', str(line)) - if result is not None and not exitSignalReceived: - foundRealTimeIssue = True - if foundAssertion and (msgLine < 3): - msgLine += 1 - msgAssertion += str(line) - result = re.search('uci->stat', str(line)) - if result is not None and not exitSignalReceived: - uciStatMsgCount += 1 - result = re.search('PDCP data request failed', str(line)) - if result is not None and not exitSignalReceived: - pdcpDataReqFailedCount += 1 - result = re.search('bad DCI 1', str(line)) - if result is not None and not exitSignalReceived: - badDciCount += 1 - result = re.search('Format1A Retransmission but TBS are different', str(line)) - if result is not None and not exitSignalReceived: - f1aRetransmissionCount += 1 - result = re.search('FATAL ERROR', str(line)) - if result is not None and not exitSignalReceived: - fatalErrorCount += 1 - result = re.search('MAC BSR Triggered ReTxBSR Timer expiry', str(line)) - if result is not None and not exitSignalReceived: - macBsrTimerExpiredCount += 1 - result = re.search('Generating RRCConnectionReconfigurationComplete', str(line)) - if result is not None: - rrcConnectionRecfgComplete += 1 - # No cell synchronization found, abandoning - result = re.search('No cell synchronization found, abandoning', str(line)) - if result is not None: - no_cell_sync_found = True - if RAN.eNBmbmsEnables[0]: - result = re.search('TRIED TO PUSH MBMS DATA', str(line)) - if result is not None: - mbms_messages += 1 - result = re.search("MIB Information => ([a-zA-Z]{1,10}), ([a-zA-Z]{1,10}), NidCell (?P\d{1,3}), N_RB_DL (?P\d{1,3}), PHICH DURATION (?P\d), PHICH RESOURCE (?P.{1,4}), TX_ANT (?P\d)", str(line)) - if result is not None and (not mib_found): - try: - mibMsg = "MIB Information: " + result.group(1) + ', ' + result.group(2) - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' - logging.debug('\033[94m' + mibMsg + '\033[0m') - mibMsg = " nidcell = " + result.group('nidcell') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg - logging.debug('\033[94m' + mibMsg + '\033[0m') - mibMsg = " n_rb_dl = " + result.group('n_rb_dl') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' - logging.debug('\033[94m' + mibMsg + '\033[0m') - mibMsg = " phich_duration = " + result.group('phich_duration') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg - logging.debug('\033[94m' + mibMsg + '\033[0m') - mibMsg = " phich_resource = " + result.group('phich_resource') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' - logging.debug('\033[94m' + mibMsg + '\033[0m') - mibMsg = " tx_ant = " + result.group('tx_ant') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' - logging.debug('\033[94m' + mibMsg + '\033[0m') - mib_found = True - except Exception as e: - logging.error('\033[91m' + "MIB marker was not found" + '\033[0m') - result = re.search("Measured Carrier Frequency (?P\d{1,15}) Hz", str(line)) - if result is not None and (not frequency_found): - try: - mibMsg = "Measured Carrier Frequency = " + result.group('measured_carrier_frequency') + ' Hz' - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' - logging.debug('\033[94m' + mibMsg + '\033[0m') - frequency_found = True - except Exception as e: - logging.error('\033[91m' + "Measured Carrier Frequency not found" + '\033[0m') - result = re.search("PLMN MCC (?P\d{1,3}), MNC (?P\d{1,3}), TAC", str(line)) - if result is not None and (not plmn_found): - try: - mibMsg = 'PLMN MCC = ' + result.group('mcc') + ' MNC = ' + result.group('mnc') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' - logging.debug('\033[94m' + mibMsg + '\033[0m') - plmn_found = True - except Exception as e: - logging.error('\033[91m' + "PLMN not found" + '\033[0m') - result = re.search("Found (?P[\w,\s]{1,15}) \(name from internal table\)", str(line)) - if result is not None: - try: - mibMsg = "The operator is: " + result.group('operator') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + '\n' - logging.debug('\033[94m' + mibMsg + '\033[0m') - except Exception as e: - logging.error('\033[91m' + "Operator name not found" + '\033[0m') - result = re.search("SIB5 InterFreqCarrierFreq element (.{1,4})/(.{1,4})", str(line)) - if result is not None: - try: - mibMsg = "SIB5 InterFreqCarrierFreq element " + result.group(1) + '/' + result.group(2) - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + mibMsg + ' -> ' - logging.debug('\033[94m' + mibMsg + '\033[0m') - except Exception as e: - logging.error('\033[91m' + "SIB5 InterFreqCarrierFreq element not found" + '\033[0m') - result = re.search("DL Carrier Frequency/ARFCN : \-*(?P\d{1,15}/\d{1,4})", str(line)) - if result is not None: - try: - freq = result.group('carrier_frequency') - new_freq = re.sub('/[0-9]+','',freq) - float_freq = float(new_freq) / 1000000 - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + 'DL Freq: ' + ('%.1f' % float_freq) + ' MHz' - logging.debug('\033[94m' + " DL Carrier Frequency is: " + str(freq) + '\033[0m') - except Exception as e: - logging.error('\033[91m' + " DL Carrier Frequency not found" + '\033[0m') - result = re.search("AllowedMeasBandwidth : (?P\d{1,7})", str(line)) - if result is not None: - try: - prb = result.group('allowed_bandwidth') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + ' -- PRB: ' + prb + '\n' - logging.debug('\033[94m' + " AllowedMeasBandwidth: " + prb + '\033[0m') - except Exception as e: - logging.error('\033[91m' + " AllowedMeasBandwidth not found" + '\033[0m') - ue_log_file.close() - if rrcConnectionRecfgComplete > 0: - statMsg = 'UE connected to eNB (' + str(rrcConnectionRecfgComplete) + ' RRCConnectionReconfigurationComplete message(s) generated)' - logging.debug('\033[94m' + statMsg + '\033[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if nrUEFlag: - if nrDecodeMib > 0: - statMsg = 'UE showed ' + str(nrDecodeMib) + ' MIB decode message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if nrFoundDCI > 0: - statMsg = 'UE showed ' + str(nrFoundDCI) + ' DCI found message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if nrCRCOK > 0: - statMsg = 'UE showed ' + str(nrCRCOK) + ' PDSCH decoding message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if not frequency_found: - statMsg = 'NR-UE could NOT synch!' - logging.error('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if uciStatMsgCount > 0: - statMsg = 'UE showed ' + str(uciStatMsgCount) + ' "uci->stat" message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if pdcpDataReqFailedCount > 0: - statMsg = 'UE showed ' + str(pdcpDataReqFailedCount) + ' "PDCP data request failed" message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if badDciCount > 0: - statMsg = 'UE showed ' + str(badDciCount) + ' "bad DCI 1(A)" message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if f1aRetransmissionCount > 0: - statMsg = 'UE showed ' + str(f1aRetransmissionCount) + ' "Format1A Retransmission but TBS are different" message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if fatalErrorCount > 0: - statMsg = 'UE showed ' + str(fatalErrorCount) + ' "FATAL ERROR:" message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if macBsrTimerExpiredCount > 0: - statMsg = 'UE showed ' + str(fatalErrorCount) + ' "MAC BSR Triggered ReTxBSR Timer expiry" message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if RAN.eNBmbmsEnables[0]: - if mbms_messages > 0: - statMsg = 'UE showed ' + str(mbms_messages) + ' "TRIED TO PUSH MBMS DATA" message(s)' - logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m') - else: - statMsg = 'UE did NOT SHOW "TRIED TO PUSH MBMS DATA" message(s)' - logging.debug('\u001B[1;30;41m ' + statMsg + ' \u001B[0m') - global_status = CONST.OAI_UE_PROCESS_NO_MBMS_MSGS - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + statMsg + '\n' - if foundSegFault: - logging.debug('\u001B[1;37;41m UE ended with a Segmentation Fault! \u001B[0m') - if not nrUEFlag: - global_status = CONST.OAI_UE_PROCESS_SEG_FAULT - else: - if not frequency_found: - global_status = CONST.OAI_UE_PROCESS_SEG_FAULT - if foundAssertion: - logging.debug('\u001B[1;30;43m UE showed an assertion! \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + 'UE showed an assertion!\n' - if not nrUEFlag: - if not mib_found or not frequency_found: - global_status = CONST.OAI_UE_PROCESS_ASSERTION - else: - if not frequency_found: - global_status = CONST.OAI_UE_PROCESS_ASSERTION - if foundRealTimeIssue: - logging.debug('\u001B[1;37;41m UE faced real time issues! \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + 'UE faced real time issues!\n' - if nrUEFlag: - if not frequency_found: - global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC - else: - if no_cell_sync_found and not mib_found: - logging.debug('\u001B[1;37;41m UE could not synchronize ! \u001B[0m') - HTML.htmlUEFailureMsg=HTML.htmlUEFailureMsg + 'UE could not synchronize!\n' - global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC - return global_status - - - def TerminateFlexranCtrl(self): - if RAN.flexranCtrlInstalled == False or RAN.flexranCtrlStarted == False: - return - if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('echo ' + EPC.Password + ' | sudo -S daemon --name=flexran_rtc_daemon --stop', '\$', 5) - time.sleep(1) - SSH.command('echo ' + EPC.Password + ' | sudo -S killall --signal SIGKILL rt_controller', '\$', 5) - time.sleep(1) - SSH.close() - RAN.flexranCtrlStarted=False - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - - def TerminateUE_common(self, device_id, idx): - try: - SSH.open(self.ADBIPAddress, self.ADBUserName, self.ADBPassword) - # back in airplane mode on (ie radio off) - if self.ADBCentralized: - #RH quick add on to intgrate cots control defined by yaml - #if device Id exists in yaml dictionary, we execute the new procedure defined in cots_ue class - #otherwise we use the legacy procedure - if COTS_UE.Check_Exists(device_id): - #switch device to Airplane mode ON (ie Radio OFF) - COTS_UE.Set_Airplane(device_id, 'ON') - elif device_id == '84B7N16418004022': - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "su - root -c /data/local/tmp/off"', '\$', 60) - else: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell /data/local/tmp/off', '\$', 60) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' ' + self.UEDevicesOffCmd[idx], '\$', 60) - logging.debug('\u001B[1mUE (' + device_id + ') Detach Completed\u001B[0m') - - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "ps | grep --color=never iperf | grep -v grep"', '\$', 5) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "ps | grep --color=never iperf | grep -v grep"\'', '\$', 60) - result = re.search('shell +(?P\d+)', SSH.getBefore()) - if result is not None: - pid_iperf = result.group('pid') - if self.ADBCentralized: - SSH.command('stdbuf -o0 adb -s ' + device_id + ' shell "kill -KILL ' + pid_iperf + '"', '\$', 5) - else: - SSH.command('ssh ' + self.UEDevicesRemoteUser[idx] + '@' + self.UEDevicesRemoteServer[idx] + ' \'adb -s ' + device_id + ' shell "kill -KILL ' + pid_iperf + '"\'', '\$', 60) - SSH.close() - except: - os.kill(os.getppid(),signal.SIGUSR1) - - def TerminateUE(self): - terminate_ue_flag = False - self.GetAllUEDevices(terminate_ue_flag) - multi_jobs = [] - i = 0 - for device_id in self.UEDevices: - p = Process(target= self.TerminateUE_common, args = (device_id,i,)) - p.daemon = True - p.start() - multi_jobs.append(p) - i += 1 - for job in multi_jobs: - job.join() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - - def TerminateOAIUE(self): - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('cd ' + self.UESourceCodePath + '/cmake_targets', '\$', 5) - SSH.command('ps -aux | grep --color=never softmodem | grep -v grep', '\$', 5) - result = re.search('-uesoftmodem', SSH.getBefore()) - if result is not None: - SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal SIGINT -r .*-uesoftmodem || true', '\$', 5) - time.sleep(10) - SSH.command('ps -aux | grep --color=never softmodem | grep -v grep', '\$', 5) - result = re.search('-uesoftmodem', SSH.getBefore()) - if result is not None: - SSH.command('echo ' + self.UEPassword + ' | sudo -S killall --signal SIGKILL -r .*-uesoftmodem || true', '\$', 5) - time.sleep(5) - SSH.command('rm -f my-lte-uesoftmodem-run' + str(self.UE_instance) + '.sh', '\$', 5) - SSH.close() - result = re.search('ue_', str(self.UELogFile)) - if result is not None: - copyin_res = SSH.copyin(self.UEIPAddress, self.UEUserName, self.UEPassword, self.UESourceCodePath + '/cmake_targets/' + self.UELogFile, '.') - if (copyin_res == -1): - logging.debug('\u001B[1;37;41m Could not copy UE logfile to analyze it! \u001B[0m') - HTML.htmlUEFailureMsg='Could not copy UE logfile to analyze it!' - HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OAI_UE_PROCESS_NOLOGFILE_TO_ANALYZE, 'UE') - self.UELogFile = '' - return - logging.debug('\u001B[1m Analyzing UE logfile \u001B[0m') - logStatus = self.AnalyzeLogFile_UE(self.UELogFile) - result = re.search('--no-L2-connect', str(self.Initialize_OAI_UE_args)) - if result is not None: - ueAction = 'Sniffing' - else: - ueAction = 'Connection' - if (logStatus < 0): - logging.debug('\u001B[1m' + ueAction + ' Failed \u001B[0m') - HTML.htmlUEFailureMsg='' + ueAction + ' Failed\n' + HTML.htmlUEFailureMsg - HTML.CreateHtmlTestRow('N/A', 'KO', logStatus, 'UE') - if self.air_interface == 'lte-uesoftmodem': - # In case of sniffing on commercial eNBs we have random results - # Not an error then - if (logStatus != CONST.OAI_UE_PROCESS_COULD_NOT_SYNC) or (ueAction != 'Sniffing'): - self.Initialize_OAI_UE_args = '' - self.AutoTerminateUEandeNB() - else: - if (logStatus == CONST.OAI_UE_PROCESS_COULD_NOT_SYNC): - self.Initialize_OAI_UE_args = '' - self.AutoTerminateUEandeNB() - else: - logging.debug('\u001B[1m' + ueAction + ' Completed \u001B[0m') - HTML.htmlUEFailureMsg='' + ueAction + ' Completed\n' + HTML.htmlUEFailureMsg - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - self.UELogFile = '' - else: - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - - def AutoTerminateUEandeNB(self): - if (self.ADBIPAddress != 'none'): - self.testCase_id = 'AUTO-KILL-UE' - HTML.testCase_id=self.testCase_id - self.desc = 'Automatic Termination of UE' - HTML.desc='Automatic Termination of UE' - self.ShowTestID() - self.TerminateUE() - if (self.Initialize_OAI_UE_args != ''): - self.testCase_id = 'AUTO-KILL-OAI-UE' - HTML.testCase_id=self.testCase_id - self.desc = 'Automatic Termination of OAI-UE' - HTML.desc='Automatic Termination of OAI-UE' - self.ShowTestID() - self.TerminateOAIUE() - if (RAN.Initialize_eNB_args != ''): - self.testCase_id = 'AUTO-KILL-eNB' - HTML.testCase_id=self.testCase_id - self.desc = 'Automatic Termination of eNB' - HTML.desc='Automatic Termination of eNB' - self.ShowTestID() - RAN.eNB_instance=0 - RAN.TerminateeNB() - if RAN.flexranCtrlInstalled and RAN.flexranCtrlStarted: - self.testCase_id = 'AUTO-KILL-flexran-ctl' - HTML.testCase_id=self.testCase_id - self.desc = 'Automatic Termination of FlexRan CTL' - HTML.desc='Automatic Termination of FlexRan CTL' - self.ShowTestID() - self.TerminateFlexranCtrl() - RAN.prematureExit=True - - def IdleSleep(self): - time.sleep(self.idle_sleep_time) - HTML.CreateHtmlTestRow(str(self.idle_sleep_time) + ' sec', 'OK', CONST.ALL_PROCESSES_OK) - - def X2_Status(self, idx, fileName): - cmd = "curl --silent http://" + EPC.IPAddress + ":9999/stats | jq '.' > " + fileName - message = cmd + '\n' - logging.debug(cmd) - subprocess.run(cmd, shell=True) - if idx == 0: - cmd = "jq '.mac_stats | length' " + fileName - strNbEnbs = subprocess.check_output(cmd, shell=True, universal_newlines=True) - self.x2NbENBs = int(strNbEnbs.strip()) - cnt = 0 - while cnt < self.x2NbENBs: - cmd = "jq '.mac_stats[" + str(cnt) + "].bs_id' " + fileName - bs_id = subprocess.check_output(cmd, shell=True, universal_newlines=True) - self.x2ENBBsIds[idx].append(bs_id.strip()) - cmd = "jq '.mac_stats[" + str(cnt) + "].ue_mac_stats | length' " + fileName - stNbUEs = subprocess.check_output(cmd, shell=True, universal_newlines=True) - nbUEs = int(stNbUEs.strip()) - ueIdx = 0 - self.x2ENBConnectedUEs[idx].append([]) - while ueIdx < nbUEs: - cmd = "jq '.mac_stats[" + str(cnt) + "].ue_mac_stats[" + str(ueIdx) + "].rnti' " + fileName - rnti = subprocess.check_output(cmd, shell=True, universal_newlines=True) - self.x2ENBConnectedUEs[idx][cnt].append(rnti.strip()) - ueIdx += 1 - cnt += 1 - - msg = "FlexRan Controller is connected to " + str(self.x2NbENBs) + " eNB(s)" - logging.debug(msg) - message += msg + '\n' - cnt = 0 - while cnt < self.x2NbENBs: - msg = " -- eNB: " + str(self.x2ENBBsIds[idx][cnt]) + " is connected to " + str(len(self.x2ENBConnectedUEs[idx][cnt])) + " UE(s)" - logging.debug(msg) - message += msg + '\n' - ueIdx = 0 - while ueIdx < len(self.x2ENBConnectedUEs[idx][cnt]): - msg = " -- UE rnti: " + str(self.x2ENBConnectedUEs[idx][cnt][ueIdx]) - logging.debug(msg) - message += msg + '\n' - ueIdx += 1 - cnt += 1 - return message - - def Perform_X2_Handover(self): - html_queue = SimpleQueue() - fullMessage = '
'
-		msg = 'Doing X2 Handover w/ option ' + self.x2_ho_options
-		logging.debug(msg)
-		fullMessage += msg + '\n'
-		if self.x2_ho_options == 'network':
-			if RAN.flexranCtrlInstalled and RAN.flexranCtrlStarted:
-				self.x2ENBBsIds = []
-				self.x2ENBConnectedUEs = []
-				self.x2ENBBsIds.append([])
-				self.x2ENBBsIds.append([])
-				self.x2ENBConnectedUEs.append([])
-				self.x2ENBConnectedUEs.append([])
-				fullMessage += self.X2_Status(0, self.testCase_id + '_pre_ho.json') 
-
-				msg = "Activating the X2 Net control on each eNB"
-				logging.debug(msg)
-				fullMessage += msg + '\n'
-				eNB_cnt = self.x2NbENBs
-				cnt = 0
-				while cnt < eNB_cnt:
-					cmd = "curl -XPOST http://" + EPC.IPAddress + ":9999/rrc/x2_ho_net_control/enb/" + str(self.x2ENBBsIds[0][cnt]) + "/1"
-					logging.debug(cmd)
-					fullMessage += cmd + '\n'
-					subprocess.run(cmd, shell=True)
-					cnt += 1
-				# Waiting for the activation to be active
-				time.sleep(10)
-				msg = "Switching UE(s) from eNB to eNB"
-				logging.debug(msg)
-				fullMessage += msg + '\n'
-				cnt = 0
-				while cnt < eNB_cnt:
-					ueIdx = 0
-					while ueIdx < len(self.x2ENBConnectedUEs[0][cnt]):
-						cmd = "curl -XPOST http://" + EPC.IPAddress() + ":9999/rrc/ho/senb/" + str(self.x2ENBBsIds[0][cnt]) + "/ue/" + str(self.x2ENBConnectedUEs[0][cnt][ueIdx]) + "/tenb/" + str(self.x2ENBBsIds[0][eNB_cnt - cnt - 1])
-						logging.debug(cmd)
-						fullMessage += cmd + '\n'
-						subprocess.run(cmd, shell=True)
-						ueIdx += 1
-					cnt += 1
-				time.sleep(10)
-				# check
-				logging.debug("Checking the Status after X2 Handover")
-				fullMessage += self.X2_Status(1, self.testCase_id + '_post_ho.json') 
-				cnt = 0
-				x2Status = True
-				while cnt < eNB_cnt:
-					if len(self.x2ENBConnectedUEs[0][cnt]) == len(self.x2ENBConnectedUEs[1][cnt]):
-						x2Status = False
-					cnt += 1
-				if x2Status:
-					msg = "X2 Handover was successful"
-					logging.debug(msg)
-					fullMessage += msg + '
' - html_queue.put(fullMessage) - HTML.CreateHtmlTestRowQueue('N/A', 'OK', len(self.UEDevices), html_queue) - else: - msg = "X2 Handover FAILED" - logging.error(msg) - fullMessage += msg + '' - html_queue.put(fullMessage) - HTML.CreateHtmlTestRowQueue('N/A', 'OK', len(self.UEDevices), html_queue) - else: - HTML.CreateHtmlTestRow('Cannot perform requested X2 Handover', 'KO', CONST.ALL_PROCESSES_OK) - - def LogCollectBuild(self): - if (RAN.eNBIPAddress != '' and RAN.eNBUserName != '' and RAN.eNBPassword != ''): - IPAddress = RAN.eNBIPAddress - UserName = RAN.eNBUserName - Password = RAN.eNBPassword - SourceCodePath = RAN.eNBSourceCodePath - elif (self.UEIPAddress != '' and self.UEUserName != '' and self.UEPassword != ''): - IPAddress = self.UEIPAddress - UserName = self.UEUserName - Password = self.UEPassword - SourceCodePath = self.UESourceCodePath - else: - sys.exit('Insufficient Parameter') - SSH.open(IPAddress, UserName, Password) - SSH.command('cd ' + SourceCodePath, '\$', 5) - SSH.command('cd cmake_targets', '\$', 5) - SSH.command('rm -f build.log.zip', '\$', 5) - SSH.command('zip build.log.zip build_log_*/*', '\$', 60) - SSH.close() - def LogCollectPing(self): - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cd ' + EPC.SourceCodePath, '\$', 5) - SSH.command('cd scripts', '\$', 5) - SSH.command('rm -f ping.log.zip', '\$', 5) - SSH.command('zip ping.log.zip ping*.log', '\$', 60) - SSH.command('rm ping*.log', '\$', 5) - SSH.close() - - def LogCollectIperf(self): - SSH.open(EPC.IPAddress, EPC.UserName, EPC.Password) - SSH.command('cd ' + EPC.SourceCodePath, '\$', 5) - SSH.command('cd scripts', '\$', 5) - SSH.command('rm -f iperf.log.zip', '\$', 5) - SSH.command('zip iperf.log.zip iperf*.log', '\$', 60) - SSH.command('rm iperf*.log', '\$', 5) - SSH.close() - - def LogCollectOAIUE(self): - SSH.open(self.UEIPAddress, self.UEUserName, self.UEPassword) - SSH.command('cd ' + self.UESourceCodePath, '\$', 5) - SSH.command('cd cmake_targets', '\$', 5) - SSH.command('echo ' + self.UEPassword + ' | sudo -S rm -f ue.log.zip', '\$', 5) - SSH.command('echo ' + self.UEPassword + ' | sudo -S zip ue.log.zip ue*.log core* ue_*record.raw ue_*.pcap ue_*txt', '\$', 60) - SSH.command('echo ' + self.UEPassword + ' | sudo -S rm ue*.log core* ue_*record.raw ue_*.pcap ue_*txt', '\$', 5) - SSH.close() - - def RetrieveSystemVersion(self, machine): - if RAN.eNBIPAddress == 'none' or self.UEIPAddress == 'none': - HTML.OsVersion[0]='Ubuntu 16.04.5 LTS' - HTML.KernelVersion[0]='4.15.0-45-generic' - HTML.UhdVersion[0]='3.13.0.1-0' - HTML.UsrpBoard[0]='B210' - HTML.CpuNb[0]='4' - HTML.CpuModel[0]='Intel(R) Core(TM) i5-6200U' - HTML.CpuMHz[0]='2399.996 MHz' - return 0 - if machine == 'eNB': - if RAN.eNBIPAddress != '' and RAN.eNBUserName != '' and RAN.eNBPassword != '': - IPAddress = RAN.eNBIPAddress - UserName = RAN.eNBUserName - Password = RAN.eNBPassword - idx = 0 - else: - return -1 - if machine == 'UE': - if self.UEIPAddress != '' and self.UEUserName != '' and self.UEPassword != '': - IPAddress = self.UEIPAddress - UserName = self.UEUserName - Password = self.UEPassword - idx = 1 - else: - return -1 - - SSH.open(IPAddress, UserName, Password) - SSH.command('lsb_release -a', '\$', 5) - result = re.search('Description:\\\\t(?P[a-zA-Z0-9\-\_\.\ ]+)', SSH.getBefore()) - if result is not None: - OsVersion = result.group('os_type') - logging.debug('OS is: ' + OsVersion) - HTML.OsVersion[idx]=OsVersion - else: - SSH.command('hostnamectl', '\$', 5) - result = re.search('Operating System: (?P[a-zA-Z0-9\-\_\.\ ]+)', SSH.getBefore()) - if result is not None: - OsVersion = result.group('os_type') - if OsVersion == 'CentOS Linux 7 ': - SSH.command('cat /etc/redhat-release', '\$', 5) - result = re.search('CentOS Linux release (?P[0-9\.]+)', SSH.getBefore()) - if result is not None: - OsVersion = OsVersion.replace('7 ', result.group('os_version')) - logging.debug('OS is: ' + OsVersion) - HTML.OsVersion[idx]=OsVersion - SSH.command('uname -r', '\$', 5) - result = re.search('uname -r\\\\r\\\\n(?P[a-zA-Z0-9\-\_\.]+)', SSH.getBefore()) - if result is not None: - KernelVersion = result.group('kernel_version') - logging.debug('Kernel Version is: ' + KernelVersion) - HTML.KernelVersion[idx]=KernelVersion - SSH.command('dpkg --list | egrep --color=never libuhd003', '\$', 5) - result = re.search('libuhd003:amd64 *(?P[0-9\.]+)', SSH.getBefore()) - if result is not None: - UhdVersion = result.group('uhd_version') - logging.debug('UHD Version is: ' + UhdVersion) - HTML.UhdVersion[idx]=UhdVersion - else: - SSH.command('uhd_config_info --version', '\$', 5) - result = re.search('UHD (?P[a-zA-Z0-9\.\-]+)', SSH.getBefore()) - if result is not None: - UhdVersion = result.group('uhd_version') - logging.debug('UHD Version is: ' + UhdVersion) - HTML.UhdVersion[idx]=UhdVersion - SSH.command('echo ' + Password + ' | sudo -S uhd_find_devices', '\$', 60) - usrp_boards = re.findall('product: ([0-9A-Za-z]+)\\\\r\\\\n', SSH.getBefore()) - count = 0 - for board in usrp_boards: - if count == 0: - UsrpBoard = board - else: - UsrpBoard += ',' + board - count += 1 - if count > 0: - logging.debug('USRP Board(s) : ' + UsrpBoard) - HTML.UsrpBoard[idx]=UsrpBoard - SSH.command('lscpu', '\$', 5) - result = re.search('CPU\(s\): *(?P[0-9]+).*Model name: *(?P[a-zA-Z0-9\-\_\.\ \(\)]+).*CPU MHz: *(?P[0-9\.]+)', SSH.getBefore()) - if result is not None: - CpuNb = result.group('nb_cpus') - logging.debug('nb_cpus: ' + CpuNb) - HTML.CpuNb[idx]=CpuNb - CpuModel = result.group('model') - logging.debug('model: ' + CpuModel) - HTML.CpuModel[idx]=CpuModel - CpuMHz = result.group('cpu_mhz') + ' MHz' - logging.debug('cpu_mhz: ' + CpuMHz) - HTML.CpuMHz[idx]=CpuMHz - SSH.close() - - def ShowTestID(self): - logging.debug('\u001B[1m----------------------------------------\u001B[0m') - logging.debug('\u001B[1mTest ID:' + self.testCase_id + '\u001B[0m') - logging.debug('\u001B[1m' + self.desc + '\u001B[0m') - logging.debug('\u001B[1m----------------------------------------\u001B[0m') - - #----------------------------------------------------------- # General Functions @@ -3374,7 +328,7 @@ with open(yaml_file,'r') as f: mode = '' -CiTestObj = OaiCiTest() +CiTestObj = cls_oaicitest.OaiCiTest() SSH = sshconnection.SSHConnection() EPC = epc.EPCManagement() @@ -3400,19 +354,12 @@ py_param_file_present, py_params, mode = args_parse.ArgsParse(sys.argv,CiTestObj #----------------------------------------------------------- -# TEMPORARY params management +# TEMPORARY params management (UNUSED) #----------------------------------------------------------- #temporary solution for testing: if py_param_file_present == True: AssignParams(py_params) -#for debug -#print(RAN.__dict__) -#print(CiTestObj.__dict__) -#print(HTML.__dict__) -#print(ldpc.__dict__) -#for debug - #----------------------------------------------------------- # COTS UE instanciation @@ -3423,7 +370,7 @@ COTS_UE=cls_cots_ue.CotsUe(CiTestObj.ADBIPAddress, CiTestObj.ADBUserName,CiTestO #----------------------------------------------------------- -# XML class (action) analysis +# mode amd XML class (action) analysis #----------------------------------------------------------- cwd = os.getcwd() @@ -3440,13 +387,13 @@ elif re.match('^TerminateUE$', mode, re.IGNORECASE): HELP.GenericHelp(CONST.Version) sys.exit('Insufficient Parameter') signal.signal(signal.SIGUSR1, receive_signal) - CiTestObj.TerminateUE() + CiTestObj.TerminateUE(HTML,COTS_UE) elif re.match('^TerminateOAIUE$', mode, re.IGNORECASE): if CiTestObj.UEIPAddress == '' or CiTestObj.UEUserName == '' or CiTestObj.UEPassword == '': HELP.GenericHelp(CONST.Version) sys.exit('Insufficient Parameter') signal.signal(signal.SIGUSR1, receive_signal) - CiTestObj.TerminateOAIUE() + CiTestObj.TerminateOAIUE(HTML,RAN,COTS_UE) elif re.match('^TerminateHSS$', mode, re.IGNORECASE): if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.Type == '' or EPC.SourceCodePath == '': HELP.GenericHelp(CONST.Version) @@ -3466,7 +413,7 @@ elif re.match('^LogCollectBuild$', mode, re.IGNORECASE): if (RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '') and (CiTestObj.UEIPAddress == '' or CiTestObj.UEUserName == '' or CiTestObj.UEPassword == '' or CiTestObj.UESourceCodePath == ''): HELP.GenericHelp(CONST.Version) sys.exit('Insufficient Parameter') - CiTestObj.LogCollectBuild() + CiTestObj.LogCollectBuild(RAN) elif re.match('^LogCollecteNB$', mode, re.IGNORECASE): if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '': HELP.GenericHelp(CONST.Version) @@ -3491,12 +438,12 @@ elif re.match('^LogCollectPing$', mode, re.IGNORECASE): if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.SourceCodePath == '': HELP.GenericHelp(CONST.Version) sys.exit('Insufficient Parameter') - CiTestObj.LogCollectPing() + CiTestObj.LogCollectPing(EPC) elif re.match('^LogCollectIperf$', mode, re.IGNORECASE): if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.SourceCodePath == '': HELP.GenericHelp(CONST.Version) sys.exit('Insufficient Parameter') - CiTestObj.LogCollectIperf() + CiTestObj.LogCollectIperf(EPC) elif re.match('^LogCollectOAIUE$', mode, re.IGNORECASE): if CiTestObj.UEIPAddress == '' or CiTestObj.UEUserName == '' or CiTestObj.UEPassword == '' or CiTestObj.UESourceCodePath == '': HELP.GenericHelp(CONST.Version) @@ -3538,8 +485,8 @@ elif re.match('^FinalizeHtml$', mode, re.IGNORECASE): logging.debug('\u001B[1m Creating HTML footer \u001B[0m') logging.debug('\u001B[1m----------------------------------------\u001B[0m') - CiTestObj.RetrieveSystemVersion('eNB') - CiTestObj.RetrieveSystemVersion('UE') + CiTestObj.RetrieveSystemVersion('eNB',HTML,RAN) + CiTestObj.RetrieveSystemVersion('UE',HTML,RAN) HTML.CreateHtmlFooter(CiTestObj.finalStatus) elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re.IGNORECASE): if re.match('^TesteNB$', mode, re.IGNORECASE): @@ -3604,7 +551,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re logging.debug('ERROR: requested test is invalidly formatted: ' + test) sys.exit(1) if (EPC.IPAddress != '') and (EPC.IPAddress != 'none'): - CiTestObj.CheckFlexranCtrlInstallation() + CiTestObj.CheckFlexranCtrlInstallation(RAN,EPC) EPC.SetMmeIPAddress() #get the list of tests to be done @@ -3666,47 +613,47 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re elif action == 'Initialize_eNB': check_eNB = False check_OAI_UE = False - RAN.pStatus=CiTestObj.CheckProcessExist(check_eNB, check_OAI_UE) + RAN.pStatus=CiTestObj.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC) RAN.InitializeeNB() elif action == 'Terminate_eNB': RAN.TerminateeNB() elif action == 'Initialize_UE': - CiTestObj.InitializeUE() + CiTestObj.InitializeUE(HTML,COTS_UE) elif action == 'Terminate_UE': - CiTestObj.TerminateUE() + CiTestObj.TerminateUE(HTML,COTS_UE) elif action == 'Attach_UE': - CiTestObj.AttachUE() + CiTestObj.AttachUE(HTML,RAN,EPC,COTS_UE) elif action == 'Detach_UE': - CiTestObj.DetachUE() + CiTestObj.DetachUE(HTML,RAN,EPC,COTS_UE) elif action == 'DataDisable_UE': - CiTestObj.DataDisableUE() + CiTestObj.DataDisableUE(HTML) elif action == 'DataEnable_UE': - CiTestObj.DataEnableUE() + CiTestObj.DataEnableUE(HTML) elif action == 'CheckStatusUE': - CiTestObj.CheckStatusUE() + CiTestObj.CheckStatusUE(HTML,RAN,EPC,COTS_UE) elif action == 'Build_OAI_UE': - CiTestObj.BuildOAIUE() + CiTestObj.BuildOAIUE(HTML) elif action == 'Initialize_OAI_UE': - CiTestObj.InitializeOAIUE() + CiTestObj.InitializeOAIUE(HTML,RAN,EPC,COTS_UE) elif action == 'Terminate_OAI_UE': - CiTestObj.TerminateOAIUE() + CiTestObj.TerminateOAIUE(HTML,RAN,COTS_UE) elif action == 'Initialize_CatM_module': - CiTestObj.InitializeCatM() + CiTestObj.InitializeCatM(HTML) elif action == 'Terminate_CatM_module': - CiTestObj.TerminateCatM() + CiTestObj.TerminateCatM(HTML) elif action == 'Attach_CatM_module': - CiTestObj.AttachCatM() + CiTestObj.AttachCatM(HTML,RAN,COTS_UE) elif action == 'Detach_CatM_module': - CiTestObj.TerminateCatM() + CiTestObj.TerminateCatM(HTML) elif action == 'Ping_CatM_module': - CiTestObj.PingCatM() + CiTestObj.PingCatM(HTML,RAN,EPC,COTS_UE) elif action == 'Ping': - CiTestObj.Ping() + CiTestObj.Ping(HTML,RAN,EPC,COTS_UE) elif action == 'Iperf': - CiTestObj.Iperf() + CiTestObj.Iperf(HTML,RAN,EPC,COTS_UE) elif action == 'Reboot_UE': - CiTestObj.RebootUE() + CiTestObj.RebootUE(HTML,RAN,EPC) elif action == 'Initialize_HSS': EPC.InitializeHSS() elif action == 'Terminate_HSS': @@ -3720,13 +667,13 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re elif action == 'Terminate_SPGW': EPC.TerminateSPGW() elif action == 'Initialize_FlexranCtrl': - CiTestObj.InitializeFlexranCtrl() + CiTestObj.InitializeFlexranCtrl(HTML,RAN,EPC) elif action == 'Terminate_FlexranCtrl': - CiTestObj.TerminateFlexranCtrl() + CiTestObj.TerminateFlexranCtrl(HTML,RAN,EPC) elif action == 'IdleSleep': - CiTestObj.IdleSleep() + CiTestObj.IdleSleep(HTML) elif action == 'Perform_X2_Handover': - CiTestObj.Perform_X2_Handover() + CiTestObj.Perform_X2_Handover(HTML,RAN,EPC) elif action == 'Build_PhySim': HTML=ldpc.Build_PhySim(HTML,CONST) if ldpc.exitStatus==1:sys.exit() diff --git a/ci-scripts/runTestOnVM.sh b/ci-scripts/runTestOnVM.sh index 6ae4899456..734001040c 100755 --- a/ci-scripts/runTestOnVM.sh +++ b/ci-scripts/runTestOnVM.sh @@ -2253,6 +2253,60 @@ function run_test_on_vm { ######### start of PHY TEST loop while [ $try_cnt -lt 4 ] do + + #start RA test + SYNC_STATUS=0 + PING_STATUS=0 + IPERF_STATUS=0 + + echo "############################################################" + echo "${CN_CONFIG} : Starting the gNB" + echo "############################################################" + CURRENT_GNB_LOG_FILE=tdd_${PRB}prb_${CN_CONFIG}_gnb.log + #last argument = 1 is to enable --do-ra for RA test + start_rf_sim_gnb $GNB_VM_CMDS "$GNB_VM_IP_ADDR" $CURRENT_GNB_LOG_FILE $PRB $CONF_FILE $S1_NOS1_CFG 1 + + echo "############################################################" + echo "${CN_CONFIG} : Starting the NR-UE" + echo "############################################################" + CURRENT_NR_UE_LOG_FILE=tdd_${PRB}prb_${CN_CONFIG}_ue.log + #last argument = 1 is to enable --do-ra for RA test + start_rf_sim_nr_ue $NR_UE_VM_CMDS $NR_UE_VM_IP_ADDR $GNB_VM_IP_ADDR $CURRENT_NR_UE_LOG_FILE $PRB $FREQUENCY $S1_NOS1_CFG 1 + if [ $NR_UE_SYNC -eq 0 ] + then + echo "Problem w/ gNB and NR-UE not syncing" + terminate_enb_ue_basic_sim $NR_UE_VM_CMDS $NR_UE_VM_IP_ADDR 2 + terminate_enb_ue_basic_sim $GNB_VM_CMDS $GNB_VM_IP_ADDR 1 + scp -o StrictHostKeyChecking=no ubuntu@$GNB_VM_IP_ADDR:/home/ubuntu/tmp/cmake_targets/log/$CURRENT_GNB_LOG_FILE $ARCHIVES_LOC + scp -o StrictHostKeyChecking=no ubuntu@$NR_UE_VM_IP_ADDR:/home/ubuntu/tmp/cmake_targets/log/$CURRENT_NR_UE_LOG_FILE $ARCHIVES_LOC + SYNC_STATUS=-1 + try_cnt=$[$try_cnt+1] + continue + fi + + echo "############################################################" + echo "${CN_CONFIG} : Terminate gNB/NR-UE simulators" + echo "############################################################" + sleep 20 + terminate_enb_ue_basic_sim $NR_UE_VM_CMDS $NR_UE_VM_IP_ADDR 2 + terminate_enb_ue_basic_sim $GNB_VM_CMDS $GNB_VM_IP_ADDR 1 + scp -o StrictHostKeyChecking=no ubuntu@$GNB_VM_IP_ADDR:/home/ubuntu/tmp/cmake_targets/log/$CURRENT_GNB_LOG_FILE $ARCHIVES_LOC + scp -o StrictHostKeyChecking=no ubuntu@$NR_UE_VM_IP_ADDR:/home/ubuntu/tmp/cmake_targets/log/$CURRENT_NR_UE_LOG_FILE $ARCHIVES_LOC + + #check RA markers in gNB and NR UE log files + echo "############################################################" + echo "${CN_CONFIG} : Checking RA on gNB / NR-UE" + echo "############################################################" + + mv $ARCHIVES_LOC/$CURRENT_GNB_LOG_FILE $ARCHIVES_LOC/ra_check_$CURRENT_GNB_LOG_FILE + mv $ARCHIVES_LOC/$CURRENT_NR_UE_LOG_FILE $ARCHIVES_LOC/ra_check_$CURRENT_NR_UE_LOG_FILE + # Proper check to be done when RA test is working! + #check_ra_result $ARCHIVES_LOC/ra_check_$CURRENT_GNB_LOG_FILE $ARCHIVES_LOC/ra_check_$CURRENT_NR_UE_LOG_FILE + + + #end RA test + sleep 30 + SYNC_STATUS=0 PING_STATUS=0 IPERF_STATUS=0 diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index fcca53abe9..2ef5ab00df 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -1095,9 +1095,22 @@ add_library(FLEXRAN_AGENT ${OPENAIR2_DIR}/ENB_APP/flexran_agent_async.c ${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c ${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_slice_verification.c + ${OPENAIR2_DIR}/ENB_APP/flexran_agent_app.c ) add_dependencies(FLEXRAN_AGENT rrc_flag) set(FLEXRAN_AGENT_LIB FLEXRAN_AGENT) +add_library(flapp_sample SHARED + ${OPENAIR2_DIR}/ENB_APP/flexran_apps/sample.c +) +set_target_properties(flapp_sample PROPERTIES C_VISIBILITY_PRESET hidden) +add_library(flapp_imsi SHARED + ${OPENAIR2_DIR}/ENB_APP/flexran_apps/imsi.c +) +set_target_properties(flapp_imsi PROPERTIES C_VISIBILITY_PRESET hidden) +add_custom_target(flapp_all DEPENDS + flapp_sample + flapp_imsi +) #include_directories(${OPENAIR2_DIR}/ENB_APP) set(PROTOBUF_LIB "protobuf-c") diff --git a/executables/main-fs6.c b/executables/main-fs6.c index c4a0d85b34..c0836943ed 100644 --- a/executables/main-fs6.c +++ b/executables/main-fs6.c @@ -179,7 +179,7 @@ void prach_eNB_tosplit(uint8_t *bufferZone, int bufSize, PHY_VARS_eNB *eNB, L1_r ru=eNB->RU_list[i]; for (ru_aa=0,aa=0; ru_aanb_rx; ru_aa++,aa++) { - eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[i]->prach_rxsigF[ru_aa]; + eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[i]->prach_rxsigF[0][ru_aa]; int ce_level; for (ce_level=0; ce_level<4; ce_level++) diff --git a/executables/main-ocp.c b/executables/main-ocp.c index cd221b155a..a0fc42636b 100644 --- a/executables/main-ocp.c +++ b/executables/main-ocp.c @@ -228,7 +228,7 @@ void init_eNB_afterRU(void) { for (int i=0; iRU_list[ru_id]->nb_rx; aa++,i++) { LOG_I(PHY,"Attaching RU %d antenna %d to eNB antenna %d\n",eNB->RU_list[ru_id]->idx,i,aa); - eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[ru_id]->prach_rxsigF[i]; + eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[ru_id]->prach_rxsigF[0][i]; for (int ce_level=0; ce_level<4; ce_level++) eNB->prach_vars_br.rxsigF[ce_level][aa] = eNB->RU_list[ru_id]->prach_rxsigF_br[ce_level][i]; @@ -483,7 +483,7 @@ void prach_procedures_ocp(PHY_VARS_eNB *eNB, L1_rxtx_proc_t *proc, int br_flag) ru=eNB->RU_list[i]; for (ru_aa=0,aa=0; ru_aanb_rx; ru_aa++,aa++) { - eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[i]->prach_rxsigF[ru_aa]; + eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[i]->prach_rxsigF[0][ru_aa]; int ce_level; if (br_flag==1) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index a48b237845..a80410112b 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -255,11 +255,7 @@ static inline int rxtx(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int frame_t // TODO: check if this is correct for PARALLEL_RU_L1_TRX_SPLIT // Do PRACH RU processing - int prach_id=find_nr_prach(gNB,frame_rx,slot_rx,0,SEARCH_EXIST); - if (prach_id>=0) { - L1_nr_prach_procedures(gNB,frame_rx,slot_rx,&gNB->prach_vars.list[prach_id].pdu); - gNB->prach_vars.list[prach_id].frame=-1; - } + L1_nr_prach_procedures(gNB,frame_rx,slot_rx); phy_procedures_gNB_uespec_RX(gNB, frame_rx, slot_rx); } @@ -981,7 +977,7 @@ void init_eNB_afterRU(void) { for (i=0; iRU_list[ru_id]->nb_rx; aa++,i++) { LOG_I(PHY,"Attaching RU %d antenna %d to gNB antenna %d\n",gNB->RU_list[ru_id]->idx,i,aa); - gNB->prach_vars.rxsigF[aa] = gNB->RU_list[ru_id]->prach_rxsigF[i]; + gNB->prach_vars.rxsigF[aa] = gNB->RU_list[ru_id]->prach_rxsigF[0][i]; gNB->common_vars.rxdataF[aa] = gNB->RU_list[ru_id]->common.rxdataF[i]; } } diff --git a/executables/nr-ru.c b/executables/nr-ru.c index a0f5710128..ede6ff2458 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1594,13 +1594,36 @@ void *ru_thread( void *param ) { (void*)ru->common.rxdataF[aa], fp->symbols_per_slot*fp->ofdm_symbol_size*sizeof(int32_t)); // Do PRACH RU processing + int prach_id=find_nr_prach_ru(ru,proc->frame_rx,proc->tti_rx,SEARCH_EXIST); + uint8_t prachStartSymbol,N_dur; if (prach_id>=0) { - rx_nr_prach_ru(ru, - ru->prach_list[prach_id].fmt, - ru->prach_list[prach_id].numRA, - ru->prach_list[prach_id].prachStartSymbol, - proc->frame_rx,proc->tti_rx); + N_dur = get_nr_prach_duration(ru->prach_list[prach_id].fmt); + /* + get_nr_prach_info_from_index(ru->config.prach_config.prach_ConfigurationIndex.value, + proc->frame_rx,proc->tti_rx, + ru->config.carrier_config.dl_frequency.value, + fp->numerology_index, + fp->frame_type, + &format, + &start_symbol, + &N_t_slot, + &N_dur, + &RA_sfn_index, + &N_RA_slot, + &config_period); + */ + for (int prach_oc = 0; prach_ocprach_list[prach_id].num_prach_ocas; prach_oc++) { + prachStartSymbol = ru->prach_list[prach_id].prachStartSymbol+prach_oc*N_dur; + //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. + + rx_nr_prach_ru(ru, + ru->prach_list[prach_id].fmt, //could also use format + ru->prach_list[prach_id].numRA, + prachStartSymbol, + prach_oc, + proc->frame_rx,proc->tti_rx); + } free_nr_ru_prach_entry(ru,prach_id); } } diff --git a/nfapi/oai_integration/nfapi_vnf.c b/nfapi/oai_integration/nfapi_vnf.c index 7ab8be5746..6a33fba5c0 100644 --- a/nfapi/oai_integration/nfapi_vnf.c +++ b/nfapi/oai_integration/nfapi_vnf.c @@ -348,7 +348,15 @@ int wake_eNB_rxtx(PHY_VARS_eNB *eNB, uint16_t sfn, uint16_t sf) { old_sf = sf; old_sfn = sfn; - if (old_sf == 0 && old_sfn % 100==0) LOG_W( PHY,"[eNB] sfn/sf:%d%d old_sfn/sf:%d%d proc[rx:%d%d]\n", sfn, sf, old_sfn, old_sf, proc->frame_rx, proc->subframe_rx); + if (old_sf == 0 && old_sfn % 100==0) + LOG_D(PHY, + "[eNB] sfn/sf:%d%d old_sfn/sf:%d%d proc[rx:%d%d]\n", + sfn, + sf, + old_sfn, + old_sf, + proc->frame_rx, + proc->subframe_rx); } ++L1_proc->instance_cnt; @@ -435,6 +443,7 @@ int phy_rach_indication(struct nfapi_vnf_p7_config *config, nfapi_rach_indicatio } if(index == -1){ LOG_E(MAC,"phy_rach_indication : num of rach reach max \n"); + pthread_mutex_unlock(&eNB->UL_INFO_mutex); return 0; } UL_RCC_INFO.rach_ind[index] = *ind; @@ -499,6 +508,7 @@ int phy_harq_indication(struct nfapi_vnf_p7_config *config, nfapi_harq_indicatio } if(index == -1){ LOG_E(MAC,"phy_harq_indication : num of harq reach max \n"); + pthread_mutex_unlock(&eNB->UL_INFO_mutex); return 0; } UL_RCC_INFO.harq_ind[index] = *ind; @@ -538,6 +548,7 @@ int phy_crc_indication(struct nfapi_vnf_p7_config *config, nfapi_crc_indication_ } if(index == -1){ LOG_E(MAC,"phy_crc_indication : num of crc reach max \n"); + pthread_mutex_unlock(&eNB->UL_INFO_mutex); return 0; } UL_RCC_INFO.crc_ind[index] = *ind; @@ -603,6 +614,7 @@ int phy_rx_indication(struct nfapi_vnf_p7_config *config, nfapi_rx_indication_t } if(index == -1){ LOG_E(MAC,"phy_rx_indication : num of rx reach max \n"); + pthread_mutex_unlock(&eNB->UL_INFO_mutex); return 0; } UL_RCC_INFO.rx_ind[index] = *ind; @@ -686,6 +698,7 @@ int phy_sr_indication(struct nfapi_vnf_p7_config *config, nfapi_sr_indication_t } if(index == -1){ LOG_E(MAC,"phy_sr_indication : num of sr reach max \n"); + pthread_mutex_unlock(&eNB->UL_INFO_mutex); return 0; } UL_RCC_INFO.sr_ind[index] = *ind; @@ -737,6 +750,7 @@ int phy_cqi_indication(struct nfapi_vnf_p7_config *config, nfapi_cqi_indication_ } if(index == -1){ LOG_E(MAC,"phy_cqi_indication : num of cqi reach max \n"); + pthread_mutex_unlock(&eNB->UL_INFO_mutex); return 0; } UL_RCC_INFO.cqi_ind[index] = *ind; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index f84917f12e..63a1e3c6a8 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -321,6 +321,7 @@ typedef enum { #define NFAPI_NR_CONFIG_PRACH_SUB_C_SPACING_TAG 0x1012 #define NFAPI_NR_CONFIG_RESTRICTED_SET_CONFIG_TAG 0x1013 #define NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG 0x1014 +#define NFAPI_NR_CONFIG_PRACH_CONFIG_INDEX_TAG 0x1029 #define NFAPI_NR_CONFIG_PRACH_ROOT_SEQUENCE_INDEX_TAG 0x1015 #define NFAPI_NR_CONFIG_NUM_ROOT_SEQUENCES_TAG 0x1016 #define NFAPI_NR_CONFIG_K1_TAG 0x1017 @@ -385,7 +386,6 @@ typedef struct typedef struct { - uint8_t num_prach_fd_occasions; nfapi_uint16_tlv_t prach_root_sequence_index;//Starting logical root sequence index, 𝑖, equivalent to higher layer parameter prach-RootSequenceIndex [38.211, sec 6.3.3.1] Value: 0 -> 837 nfapi_uint8_tlv_t num_root_sequences;//Number of root sequences for a particular FD occasion that are required to generate the necessary number of preambles nfapi_uint16_tlv_t k1;//Frequency offset (from UL bandwidth part) for each FD. [38.211, sec 6.3.3.2] Value: from 0 to 272 @@ -401,7 +401,9 @@ typedef struct nfapi_uint8_tlv_t prach_sub_c_spacing;//Subcarrier spacing of PRACH. [38.211 sec 4.2] Value:0->4 nfapi_uint8_tlv_t restricted_set_config;//PRACH restricted set config Value: 0: unrestricted 1: restricted set type A 2: restricted set type B nfapi_uint8_tlv_t num_prach_fd_occasions;//Corresponds to the parameter 𝑀 in [38.211, sec 6.3.3.2] which equals the higher layer parameter msg1FDM Value: 1,2,4,8 + nfapi_uint8_tlv_t prach_ConfigurationIndex;//PRACH configuration index. Value:0->255 nfapi_nr_num_prach_fd_occasions_t* num_prach_fd_occasions_list; + nfapi_uint8_tlv_t ssb_per_rach;//SSB-per-RACH-occasion Value: 0: 1/8 1:1/4, 2:1/2 3:1 4:2 5:4, 6:8 7:16 nfapi_uint8_tlv_t prach_multiple_carriers_in_a_band;//0 = disabled 1 = enabled diff --git a/nfapi/open-nFAPI/vnf/src/vnf_p7.c b/nfapi/open-nFAPI/vnf/src/vnf_p7.c index 394bfc58ff..cff24500b6 100644 --- a/nfapi/open-nFAPI/vnf/src/vnf_p7.c +++ b/nfapi/open-nFAPI/vnf/src/vnf_p7.c @@ -955,7 +955,7 @@ void vnf_handle_ul_node_sync(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); - NFAPI_TRACE(NFAPI_TRACE_NOTE, "(%4d/%1d) %d.%d PNF to VNF phy_id:%2d (t1/2/3/4:%8u, %8u, %8u, %8u) txrx:%4u procT:%3u latency(us):%4d(avg:%4d) offset(us):%8d filtered(us):%8d wrap[t1:%u t2:%u]\n", + NFAPI_TRACE(NFAPI_TRACE_INFO, "(%4d/%1d) %d.%d PNF to VNF phy_id:%2d (t1/2/3/4:%8u, %8u, %8u, %8u) txrx:%4u procT:%3u latency(us):%4d(avg:%4d) offset(us):%8d filtered(us):%8d wrap[t1:%u t2:%u]\n", NFAPI_SFNSF2SFN(phy->sfn_sf), NFAPI_SFNSF2SF(phy->sfn_sf), ts.tv_sec, ts.tv_nsec, ind.header.phy_id, ind.t1, ind.t2, ind.t3, t4, tx_2_rx, pnf_proc_time, latency, phy->average_latency, phy->sf_offset, phy->sf_offset_filtered, @@ -1016,7 +1016,7 @@ void vnf_handle_ul_node_sync(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) { phy->adjustment = NFAPI_SFNSF2DEC(new_sfn_sf) - NFAPI_SFNSF2DEC(curr_sfn_sf); - NFAPI_TRACE(NFAPI_TRACE_NOTE, "PNF to VNF phy_id:%d adjustment%d phy->previous_sf_offset_filtered:%d phy->previous_sf_offset_filtered:%d phy->sf_offset_trend:%d\n", ind.header.phy_id, phy->adjustment, phy->previous_sf_offset_filtered, phy->previous_sf_offset_filtered, phy->sf_offset_trend); + NFAPI_TRACE(NFAPI_TRACE_INFO, "PNF to VNF phy_id:%d adjustment%d phy->previous_sf_offset_filtered:%d phy->previous_sf_offset_filtered:%d phy->sf_offset_trend:%d\n", ind.header.phy_id, phy->adjustment, phy->previous_sf_offset_filtered, phy->previous_sf_offset_filtered, phy->sf_offset_trend); phy->previous_t1 = 0; phy->previous_t2 = 0; diff --git a/openair1/PHY/INIT/lte_init_ru.c b/openair1/PHY/INIT/lte_init_ru.c index baea52083f..8ad928f8a0 100644 --- a/openair1/PHY/INIT/lte_init_ru.c +++ b/openair1/PHY/INIT/lte_init_ru.c @@ -121,13 +121,13 @@ int phy_init_RU(RU_t *ru) { /* number of elements of an array X is computed as sizeof(X) / sizeof(X[0]) */ //AssertFatal(ru->nb_rx <= sizeof(ru->prach_rxsigF) / sizeof(ru->prach_rxsigF[0]), //"nb_antennas_rx too large"); - ru->prach_rxsigF = (int16_t **)malloc(ru->nb_rx * sizeof(int16_t *)); + ru->prach_rxsigF[0] = (int16_t **)malloc(ru->nb_rx * sizeof(int16_t *)); for (j=0; j<4; j++) ru->prach_rxsigF_br[j] = (int16_t **)malloc(ru->nb_rx * sizeof(int16_t *)); for (i=0; inb_rx; i++) { - ru->prach_rxsigF[i] = (int16_t *)malloc16_clear( fp->ofdm_symbol_size*12*2*sizeof(int16_t) ); - LOG_D(PHY,"[INIT] prach_vars->rxsigF[%d] = %p\n",i,ru->prach_rxsigF[i]); + ru->prach_rxsigF[0][i] = (int16_t *)malloc16_clear( fp->ofdm_symbol_size*12*2*sizeof(int16_t) ); + LOG_D(PHY,"[INIT] prach_vars->rxsigF[%d] = %p\n",i,ru->prach_rxsigF[0][i]); for (j=0; j<4; j++) { ru->prach_rxsigF_br[j][i] = (int16_t *)malloc16_clear( fp->ofdm_symbol_size*12*2*sizeof(int16_t) ); @@ -230,14 +230,14 @@ void phy_free_RU(RU_t *ru) { } for (i = 0; i < ru->nb_rx; i++) { - free_and_zero(ru->prach_rxsigF[i]); + free_and_zero(ru->prach_rxsigF[0][i]); for (j = 0; j < 4; j++) free_and_zero(ru->prach_rxsigF_br[j][i]); } for (j = 0; j < 4; j++) free_and_zero(ru->prach_rxsigF_br[j]); - free_and_zero(ru->prach_rxsigF); + free_and_zero(ru->prach_rxsigF[0]); /* ru->prach_rxsigF_br is not allocated -> don't free */ for (i = 0; i < RC.nb_L1_inst; i++) { diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index a696059873..005ede615b 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -497,11 +497,12 @@ void nr_phy_config_request(NR_PHY_Config_t *phy_config) { fd_occasion = 0; nfapi_nr_prach_config_t *prach_config = &gNB_config->prach_config; short_sequence = prach_config->prach_sequence_length.value; +// for(fd_occasion = 0; fd_occasion <= prach_config->num_prach_fd_occasions.value ; fd_occasion) { // TODO Need to handle for msg1-fdm > 1 num_sequences = prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value; rootSequenceIndex = prach_config->num_prach_fd_occasions_list[fd_occasion].prach_root_sequence_index.value; compute_nr_prach_seq(short_sequence, num_sequences, rootSequenceIndex, RC.gNB[Mod_id]->X_u); - +// } RC.gNB[Mod_id]->configured = 1; init_symbol_rotation(fp,fp->dl_CarrierFreq); diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index 28b13112ce..648b820f07 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -107,12 +107,14 @@ int nr_phy_init_RU(RU_t *ru) { /* number of elements of an array X is computed as sizeof(X) / sizeof(X[0]) */ // AssertFatal(ru->nb_rx <= sizeof(ru->prach_rxsigF) / sizeof(ru->prach_rxsigF[0]), // "nb_antennas_rx too large"); - ru->prach_rxsigF = (int16_t**)malloc(ru->nb_rx * sizeof(int16_t*)); - - for (i=0; inb_rx; i++) { - // largest size for PRACH FFT is 4x98304 (16*24576) - ru->prach_rxsigF[i] = (int16_t*)malloc16_clear( 4*98304*2*sizeof(int16_t) ); - LOG_D(PHY,"[INIT] prach_vars->rxsigF[%d] = %p\n",i,ru->prach_rxsigF[i]); + for (j=0;jprach_rxsigF[j] = (int16_t**)malloc(ru->nb_rx * sizeof(int16_t*)); + + for (i=0; inb_rx; i++) { + // largest size for PRACH FFT is 4x98304 (16*24576) + ru->prach_rxsigF[j][i] = (int16_t*)malloc16_clear( 4*98304*2*sizeof(int16_t) ); + LOG_D(PHY,"[INIT] prach_vars->rxsigF[%d] = %p\n",i,ru->prach_rxsigF[j][i]); + } } AssertFatal(RC.nb_nr_L1_inst <= NUMBER_OF_eNB_MAX,"gNB instances %d > %d\n", @@ -186,10 +188,12 @@ void nr_phy_free_RU(RU_t *ru) for (i = 0; i < ru->nb_rx; i++) free_and_zero(ru->common.rxdataF[i]); free_and_zero(ru->common.rxdataF); - for (i = 0; i < ru->nb_rx; i++) { - free_and_zero(ru->prach_rxsigF[i]); + for (j=0;jnb_rx; i++) { + free_and_zero(ru->prach_rxsigF[j][i]); + } } - + for (i = 0; i < RC.nb_nr_L1_inst; i++) { for (p = 0; p < 15; p++) { for (j=0; jnb_tx; j++) free_and_zero(ru->beam_weights[i][p][j]); diff --git a/openair1/PHY/LTE_TRANSPORT/if4_tools.c b/openair1/PHY/LTE_TRANSPORT/if4_tools.c index a843fc1fd7..95ad420028 100644 --- a/openair1/PHY/LTE_TRANSPORT/if4_tools.c +++ b/openair1/PHY/LTE_TRANSPORT/if4_tools.c @@ -49,7 +49,7 @@ void send_IF4p5(RU_t *ru, LTE_DL_FRAME_PARMS *fp = ru->frame_parms; int32_t **txdataF = ru->common.txdataF_BF; int32_t **rxdataF = ru->common.rxdataF; - int16_t **prach_rxsigF = ru->prach_rxsigF; + int16_t **prach_rxsigF = ru->prach_rxsigF[0]; int16_t ***prach_rxsigF_br = ru->prach_rxsigF_br; void *tx_buffer = ru->ifbuffer.tx[subframe&1]; void *tx_buffer_prach = ru->ifbuffer.tx_prach; @@ -276,7 +276,7 @@ void recv_IF4p5(RU_t *ru, LTE_DL_FRAME_PARMS *fp = ru->frame_parms; int32_t **txdataF = ru->common.txdataF_BF; int32_t **rxdataF = ru->common.rxdataF; - int16_t **prach_rxsigF = ru->prach_rxsigF; + int16_t **prach_rxsigF = ru->prach_rxsigF[0]; int16_t ***prach_rxsigF_br = ru->prach_rxsigF_br; void *rx_buffer = ru->ifbuffer.rx; uint16_t element_id; diff --git a/openair1/PHY/LTE_TRANSPORT/prach.c b/openair1/PHY/LTE_TRANSPORT/prach.c index 24faf584d3..d3ed2ed439 100644 --- a/openair1/PHY/LTE_TRANSPORT/prach.c +++ b/openair1/PHY/LTE_TRANSPORT/prach.c @@ -167,7 +167,7 @@ void rx_prach0(PHY_VARS_eNB *eNB, br_flag,ce_level,frame_prach,subframe,fp->prach_emtc_config_common.prach_ConfigInfo.prach_FreqOffset[ce_level],prach_ConfigIndex); } } else { - rxsigF = ru->prach_rxsigF; + rxsigF = ru->prach_rxsigF[0]; if (LOG_DEBUGFLAG(PRACH)) { if (((frame_prach)&1023) < 20) LOG_I(PHY,"PRACH (RU) : running rx_prach for subframe %d, prach_FreqOffset %d, prach_ConfigIndex %d\n", diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.c b/openair1/PHY/NR_TRANSPORT/nr_dci.c index 2d79aa8fbd..cc71e9dd97 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.c @@ -64,37 +64,23 @@ void nr_pdcch_scrambling(uint32_t *in, } } - - -uint8_t nr_generate_dci_top(PHY_VARS_gNB *gNB, - nfapi_nr_dl_tti_pdcch_pdu *pdcch_pdu, - nfapi_nr_dl_tti_pdcch_pdu *ul_dci_pdu, - uint32_t **gold_pdcch_dmrs, - int32_t *txdataF, - int16_t amp, - NR_DL_FRAME_PARMS frame_parms) { +void nr_generate_dci(PHY_VARS_gNB *gNB, + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15, + uint32_t **gold_pdcch_dmrs, + int32_t *txdataF, + int16_t amp, + NR_DL_FRAME_PARMS frame_parms) { int16_t mod_dmrs[NR_MAX_CSET_DURATION][NR_MAX_PDCCH_DMRS_LENGTH>>1] __attribute__((aligned(16))); // 3 for the max coreset duration uint16_t cset_start_sc; uint8_t cset_start_symb, cset_nsymb; int k,l,k_prime,dci_idx, dmrs_idx; - /*First iteration: single DCI*/ - - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15=NULL; - // find coreset descriptor int rb_offset; int n_rb; - AssertFatal(pdcch_pdu!=NULL || ul_dci_pdu!=NULL,"At least one pointer has to be !NULL\n"); - AssertFatal(pdcch_pdu==NULL || ul_dci_pdu==NULL,"Can't handle both DL and UL DCI in same slot\n"); - - - if (pdcch_pdu) pdcch_pdu_rel15 = &pdcch_pdu->pdcch_pdu_rel15; - else if (ul_dci_pdu) pdcch_pdu_rel15 = &ul_dci_pdu->pdcch_pdu_rel15; - nr_fill_cce_list(gNB,0,pdcch_pdu_rel15); get_coreset_rballoc(pdcch_pdu_rel15->FreqDomainResource,&n_rb,&rb_offset); @@ -241,6 +227,25 @@ uint8_t nr_generate_dci_top(PHY_VARS_gNB *gNB, } // reg_idx } // for (int d=0;dnumDlDci;d++) - return 0; +} + +void nr_generate_dci_top(PHY_VARS_gNB *gNB, + nfapi_nr_dl_tti_pdcch_pdu *pdcch_pdu, + nfapi_nr_dl_tti_pdcch_pdu *ul_dci_pdu, + uint32_t **gold_pdcch_dmrs, + int32_t *txdataF, + int16_t amp, + NR_DL_FRAME_PARMS frame_parms) { + + AssertFatal(pdcch_pdu!=NULL || ul_dci_pdu!=NULL,"At least one pointer has to be !NULL\n"); + + if (pdcch_pdu && ul_dci_pdu) { + nr_generate_dci(gNB,&pdcch_pdu->pdcch_pdu_rel15,gold_pdcch_dmrs,txdataF,amp,frame_parms); + nr_generate_dci(gNB,&ul_dci_pdu->pdcch_pdu_rel15,gold_pdcch_dmrs,txdataF,amp,frame_parms); + } + else if (pdcch_pdu) + nr_generate_dci(gNB,&pdcch_pdu->pdcch_pdu_rel15,gold_pdcch_dmrs,txdataF,amp,frame_parms); + else + nr_generate_dci(gNB,&ul_dci_pdu->pdcch_pdu_rel15,gold_pdcch_dmrs,txdataF,amp,frame_parms); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.h b/openair1/PHY/NR_TRANSPORT/nr_dci.h index 288f525219..1b57acc6f4 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.h +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.h @@ -29,7 +29,7 @@ uint16_t nr_get_dci_size(nfapi_nr_dci_format_e format, nfapi_nr_rnti_type_e rnti_type, uint16_t N_RB); -uint8_t nr_generate_dci_top(PHY_VARS_gNB *gNB, +void nr_generate_dci_top(PHY_VARS_gNB *gNB, nfapi_nr_dl_tti_pdcch_pdu *pdcch_pdu, nfapi_nr_dl_tti_pdcch_pdu *ul_pdcch_pdu, uint32_t **gold_pdcch_dmrs, diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index e263c2c2cc..ba0705cde7 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -37,31 +37,42 @@ extern uint16_t prach_root_sequence_map_0_3[838]; extern uint16_t prach_root_sequence_map_abc[138]; extern uint16_t nr_du[838]; -extern const char *prachfmt[9]; +extern const char *prachfmt[]; void init_prach_list(PHY_VARS_gNB *gNB) { AssertFatal(gNB!=NULL,"gNB is null\n"); - for (int i=0; iprach_vars.list[i].frame=-1; + for (int i=0; iprach_vars.list[i].frame = -1; + gNB->prach_vars.list[i].slot = -1; + } } -int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame,int slot, int numRA, find_type_t type) { +void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id) { - uint16_t i; - int16_t first_free_index=-1; + gNB->prach_vars.list[prach_id].frame = -1; + gNB->prach_vars.list[prach_id].slot = -1; + +} + +int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame, int slot, find_type_t type) { AssertFatal(gNB!=NULL,"gNB is null\n"); - for (i=0; i %d.%d numRA %d\n", frame,slot,numRA,i, - gNB->prach_vars.list[i].frame,gNB->prach_vars.list[i].slot,gNB->prach_vars.list[i].pdu.num_ra); - if ((gNB->prach_vars.list[i].frame == frame) && - (gNB->prach_vars.list[i].slot == slot) && - (gNB->prach_vars.list[i].pdu.num_ra == numRA)) return i; - else if ((gNB->prach_vars.list[i].frame == -1) && (first_free_index==-1)) first_free_index=i; + for (uint16_t i=0; i %d.%d\n", frame,slot,i, + gNB->prach_vars.list[i].frame,gNB->prach_vars.list[i].slot); + if((type == SEARCH_EXIST_OR_FREE) && + (gNB->prach_vars.list[i].frame == -1) && + (gNB->prach_vars.list[i].slot == -1)) { + return i; + } + else if ((type == SEARCH_EXIST) && + (gNB->prach_vars.list[i].frame == frame) && + (gNB->prach_vars.list[i].slot == slot)) { + return i; + } } - if (type == SEARCH_EXIST) return -1; - - return first_free_index; + return -1; } void nr_fill_prach(PHY_VARS_gNB *gNB, @@ -69,9 +80,9 @@ void nr_fill_prach(PHY_VARS_gNB *gNB, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) { - int prach_id = find_nr_prach(gNB,SFN,Slot,prach_pdu->num_ra,SEARCH_EXIST_OR_FREE); - AssertFatal( (prach_id>=0) && (prach_idnum_ra,prach_id); + int prach_id = find_nr_prach(gNB,SFN,Slot,SEARCH_EXIST_OR_FREE); + AssertFatal( ((prach_id>=0) && (prach_idprach_vars.list[prach_id].frame=SFN; gNB->prach_vars.list[prach_id].slot=Slot; @@ -82,31 +93,35 @@ void nr_fill_prach(PHY_VARS_gNB *gNB, void init_prach_ru_list(RU_t *ru) { AssertFatal(ru!=NULL,"ruis null\n"); - for (int i=0; iprach_list[i].frame=-1; + for (int i=0; iprach_list[i].frame = -1; + ru->prach_list[i].slot = -1; + } pthread_mutex_init(&ru->prach_list_mutex,NULL); } int16_t find_nr_prach_ru(RU_t *ru,int frame,int slot, find_type_t type) { - uint16_t i; - int16_t first_free_index=-1; - AssertFatal(ru!=NULL,"ru is null\n"); pthread_mutex_lock(&ru->prach_list_mutex); - for (i=0; i %d.%d\n", frame,slot,i, ru->prach_list[i].frame,ru->prach_list[i].slot); - if ((ru->prach_list[i].frame == frame) && - (ru->prach_list[i].slot == slot)) { + if((type == SEARCH_EXIST_OR_FREE) && + (ru->prach_list[i].frame == -1) && + (ru->prach_list[i].slot == -1)) { + pthread_mutex_unlock(&ru->prach_list_mutex); + return i; + } + else if ((type == SEARCH_EXIST) && + (ru->prach_list[i].frame == frame) && + (ru->prach_list[i].slot == slot)) { pthread_mutex_unlock(&ru->prach_list_mutex); return i; } - else if ((ru->prach_list[i].frame == -1) && (first_free_index==-1)) first_free_index=i; } pthread_mutex_unlock(&ru->prach_list_mutex); - if (type == SEARCH_EXIST) return -1; - - return first_free_index; + return -1; } void nr_fill_prach_ru(RU_t *ru, @@ -115,7 +130,7 @@ void nr_fill_prach_ru(RU_t *ru, nfapi_nr_prach_pdu_t *prach_pdu) { int prach_id = find_nr_prach_ru(ru,SFN,Slot,SEARCH_EXIST_OR_FREE); - AssertFatal( (prach_id>=0) && (prach_id=0) && (prach_idprach_list_mutex); @@ -124,6 +139,7 @@ void nr_fill_prach_ru(RU_t *ru, ru->prach_list[prach_id].fmt = prach_pdu->prach_format; ru->prach_list[prach_id].numRA = prach_pdu->num_ra; ru->prach_list[prach_id].prachStartSymbol = prach_pdu->prach_start_symbol; + ru->prach_list[prach_id].num_prach_ocas = prach_pdu->num_prach_ocas; pthread_mutex_unlock(&ru->prach_list_mutex); } @@ -132,7 +148,8 @@ void free_nr_ru_prach_entry(RU_t *ru, int prach_id) { pthread_mutex_lock(&ru->prach_list_mutex); - ru->prach_list[prach_id].frame=-1; + ru->prach_list[prach_id].frame = -1; + ru->prach_list[prach_id].slot = -1; pthread_mutex_unlock(&ru->prach_list_mutex); } @@ -142,6 +159,7 @@ void rx_nr_prach_ru(RU_t *ru, int prachFormat, int numRA, int prachStartSymbol, + int prachOccasion, int frame, int slot) { @@ -156,15 +174,21 @@ void rx_nr_prach_ru(RU_t *ru, int msg1_frequencystart = ru->config.prach_config.num_prach_fd_occasions_list[numRA].k1.value; - rxsigF = ru->prach_rxsigF; + int sample_offset_slot = (prachStartSymbol==0?0:fp->ofdm_symbol_size*prachStartSymbol+fp->nb_prefix_samples0+fp->nb_prefix_samples*(prachStartSymbol-1)); + //to be checked for mu=0; + + LOG_D(PHY,"frame %d, slot %d: doing rx_nr_prach_ru for format %d, numRA %d, prachStartSymbol %d, prachOccasion %d\n",frame,slot,prachFormat,numRA,prachStartSymbol,prachOccasion); + + rxsigF = ru->prach_rxsigF[prachOccasion]; AssertFatal(ru->if_south == LOCAL_RF,"we shouldn't call this if if_south != LOCAL_RF\n"); for (int aa=0; aanb_rx; aa++){ if (prach_sequence_length == 0) slot2=(slot/fp->slots_per_subframe)*fp->slots_per_subframe; - prach[aa] = (int16_t*)&ru->common.rxdata[aa][(slot2*fp->get_samples_per_slot(slot,fp))-ru->N_TA_offset]; + prach[aa] = (int16_t*)&ru->common.rxdata[aa][(slot2*fp->get_samples_per_slot(slot,fp))+sample_offset_slot-ru->N_TA_offset]; } + int dftlen=0; int mu = fp->numerology_index; int Ncp = 0; @@ -198,22 +222,24 @@ void rx_nr_prach_ru(RU_t *ru, ru->idx,frame,slot,prachfmt[prachFormat],msg1_frequencystart,prachStartSymbol); switch (prachFormat) { - case 0: //A1 + case 4: //A1 Ncp = 288/(1<ofdm_symbol_size); k*=K; - k+=kbar; + k+=kbar; + int reps=1; for (int aa=0; aanb_rx; aa++) { @@ -308,21 +335,21 @@ void rx_nr_prach_ru(RU_t *ru, else if (mu==1) { dftlen=2048; dft(DFT_2048,prach2,rxsigF[aa],1); - if (prachFormat != 7) { // !=C0 + if (prachFormat != 9/*C0*/) { dft(DFT_2048,prach2+4096,rxsigF[aa]+4096,1); reps++; } - if (prachFormat == 1 || prachFormat == 2 || prachFormat == 4 || prachFormat == 5 || prachFormat == 6 || prachFormat == 8) { + if (prachFormat == 5/*A2*/ || prachFormat == 6/*A3*/|| prachFormat == 8/*B4*/ || prachFormat == 10/*C2*/) { dft(DFT_2048,prach2+4096*2,rxsigF[aa]+4096*2,1); dft(DFT_2048,prach2+4096*3,rxsigF[aa]+4096*3,1); reps+=2; } - if (prachFormat == 2 || prachFormat == 5 || prachFormat == 6) { + if (prachFormat == 6/*A3*/ || prachFormat == 8/*B4*/) { dft(DFT_2048,prach2+4096*4,rxsigF[aa]+4096*4,1); dft(DFT_2048,prach2+4096*5,rxsigF[aa]+4096*5,1); reps+=2; } - if (prachFormat == 6) { + if (prachFormat == 8/*B4*/) { for (int i=6;i<12;i++) dft(DFT_2048,prach2+(4096*i),rxsigF[aa]+(4096*i),1); reps+=6; } @@ -363,22 +390,22 @@ void rx_nr_prach_ru(RU_t *ru, else if (mu==1) { dftlen=1536; dft(DFT_1536,prach2,rxsigF[aa],1); - if (prachFormat != 7) { + if (prachFormat != 9/*C0*/) { dft(DFT_1536,prach2+3072,rxsigF[aa]+3072,1); reps++; } - if (prachFormat == 1 || prachFormat == 2 || prachFormat == 4 || prachFormat == 5 || prachFormat == 6 || prachFormat == 8) { + if (prachFormat == 5/*A2*/ || prachFormat == 6/*A3*/|| prachFormat == 8/*B4*/ || prachFormat == 10/*C2*/) { dft(DFT_1536,prach2+3072*2,rxsigF[aa]+3072*2,1); dft(DFT_1536,prach2+3072*3,rxsigF[aa]+3072*3,1); reps+=2; } - if (prachFormat == 2 || prachFormat == 5 || prachFormat == 6) { + if (prachFormat == 6/*A3*/ || prachFormat == 8/*B4*/) { dft(DFT_1536,prach2+3072*4,rxsigF[aa]+3072*4,1); dft(DFT_1536,prach2+3072*5,rxsigF[aa]+3072*5,1); reps+=2; } - if (prachFormat == 6) { + if (prachFormat == 8/*B4*/) { for (int i=6;i<12;i++) dft(DFT_1536,prach2+(3072*i),rxsigF[aa]+(3072*i),1); reps+=6; } @@ -423,22 +450,22 @@ void rx_nr_prach_ru(RU_t *ru, else if (mu==1) { dftlen=4096; dft(DFT_4096,prach2,rxsigF[aa],1); - if (prachFormat != 7) { //!=C0 + if (prachFormat != 9/*C0*/) { dft(DFT_4096,prach2+8192,rxsigF[aa]+8192,1); reps++; } - if (prachFormat == 1 || prachFormat == 2 || prachFormat == 4 || prachFormat == 5 || prachFormat == 6 || prachFormat == 8) { + if (prachFormat == 5/*A2*/ || prachFormat == 6/*A3*/|| prachFormat == 8/*B4*/ || prachFormat == 10/*C2*/) { dft(DFT_4096,prach2+8192*2,rxsigF[aa]+8192*2,1); dft(DFT_4096,prach2+8192*3,rxsigF[aa]+8192*3,1); reps+=2; } - if (prachFormat == 2 || prachFormat == 5 || prachFormat == 6) { + if (prachFormat == 6/*A3*/ || prachFormat == 8/*B4*/) { dft(DFT_4096,prach2+8192*4,rxsigF[aa]+8192*4,1); dft(DFT_4096,prach2+8192*5,rxsigF[aa]+8192*5,1); reps+=2; } - if (prachFormat == 6) { + if (prachFormat == 8/*B4*/) { for (int i=6;i<12;i++) dft(DFT_4096,prach2+(8192*i),rxsigF[aa]+(8192*i),1); reps+=6; } @@ -475,22 +502,22 @@ void rx_nr_prach_ru(RU_t *ru, else if (mu==1) { dftlen=3072; dft(DFT_3072,prach2,rxsigF[aa],1); - if (prachFormat != 7) { //!=C0 + if (prachFormat != 9/*C0*/) { dft(DFT_3072,prach2+6144,rxsigF[aa]+6144,1); reps++; } - if (prachFormat == 1 || prachFormat == 2 || prachFormat == 4 || prachFormat == 5 || prachFormat == 6 || prachFormat == 8) { + if (prachFormat == 5/*A2*/ || prachFormat == 6/*A3*/|| prachFormat == 8/*B4*/ || prachFormat == 10/*C2*/) { dft(DFT_3072,prach2+6144*2,rxsigF[aa]+6144*2,1); dft(DFT_3072,prach2+6144*3,rxsigF[aa]+6144*3,1); reps+=2; } - if (prachFormat == 2 || prachFormat == 5 || prachFormat == 6) { + if (prachFormat == 6/*A3*/ || prachFormat == 8/*B4*/) { dft(DFT_3072,prach2+6144*4,rxsigF[aa]+6144*4,1); dft(DFT_3072,prach2+6144*5,rxsigF[aa]+6144*5,1); reps+=2; } - if (prachFormat == 6) { + if (prachFormat == 8/*B4*/) { for (int i=6;i<12;i++) dft(DFT_3072,prach2+(6144*i),rxsigF[aa]+(6144*i),1); reps+=6; } @@ -515,13 +542,13 @@ void rx_nr_prach_ru(RU_t *ru, for (int i=1;iframe_parms; nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value; - - rootSequenceIndex = cfg->num_prach_fd_occasions_list[0].prach_root_sequence_index.value; - numrootSequenceIndex = cfg->num_prach_fd_occasions_list[0].num_root_sequences.value; + rootSequenceIndex = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].prach_root_sequence_index.value; + numrootSequenceIndex = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].num_root_sequences.value; NCS = prach_pdu->num_cs;//cfg->num_prach_fd_occasions_list[0].prach_zero_corr_conf.value; int prach_sequence_length = cfg->prach_sequence_length.value; - - int msg1_frequencystart = cfg->num_prach_fd_occasions_list[0].k1.value; + int msg1_frequencystart = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].k1.value; // int num_unused_root_sequences = cfg->num_prach_fd_occasions_list[0].num_unused_root_sequences.value; // cfg->num_prach_fd_occasions_list[0].unused_root_sequences_list @@ -585,7 +610,7 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, uint8_t prach_fmt = prach_pdu->prach_format; uint16_t N_ZC = (prach_sequence_length==0)?839:139; - LOG_D(PHY,"L1 PRACH RX: rooSequenceIndex %d, numRootSeqeuences %d, NCS %d, N_ZC %d \n", rootSequenceIndex,numrootSequenceIndex,NCS,N_ZC); + LOG_D(PHY,"L1 PRACH RX: rooSequenceIndex %d, numRootSeqeuences %d, NCS %d, N_ZC %d, format %d \n",rootSequenceIndex,numrootSequenceIndex,NCS,N_ZC,prach_fmt); prach_ifft = gNB->prach_vars.prach_ifft; prachF = gNB->prach_vars.prachF; diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 8cb7ad45e7..417f0d41a3 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -288,6 +288,7 @@ void nr_fill_prach(PHY_VARS_gNB *gNB, void rx_nr_prach(PHY_VARS_gNB *gNB, nfapi_nr_prach_pdu_t *prach_pdu, + int prachOccasion, int frame, int subframe, uint16_t *max_preamble, @@ -298,6 +299,7 @@ void rx_nr_prach_ru(RU_t *ru, int prach_fmt, int numRA, int prachStartSymbol, + int prachOccasion, int frame, int subframe); @@ -306,7 +308,7 @@ void nr_fill_prach_ru(RU_t *ru, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); -int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame,int slot, int numRA, find_type_t type); +int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame,int slot, find_type_t type); int16_t find_nr_prach_ru(RU_t *ru,int frame,int slot, find_type_t type); NR_gNB_PUCCH_t *new_gNB_pucch(void); @@ -325,7 +327,7 @@ int nr_find_pucch(uint16_t rnti, void init_prach_list(PHY_VARS_gNB *gNB); void init_prach_ru_list(RU_t *ru); void free_nr_ru_prach_entry(RU_t *ru, int prach_id); - +void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id); void nr_decode_pucch1(int32_t **rxdataF, pucch_GroupHopping_t pucch_GroupHopping, diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c index 29841c3186..621f5ef806 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c @@ -48,8 +48,7 @@ extern uint16_t prach_root_sequence_map_0_3[838]; extern uint16_t prach_root_sequence_map_abc[138]; extern uint16_t nr_du[838]; extern int16_t nr_ru[2*839]; -extern const char *prachfmt[9]; -extern const char *prachfmt03[4]; +extern const char *prachfmt[]; // Note: // - prach_fmt_id is an ID used to map to the corresponding PRACH format value in prachfmt @@ -181,14 +180,14 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, uint8_t slot){ } // now generate PRACH signal - #ifdef NR_PRACH_DEBUG +#ifdef NR_PRACH_DEBUG if (NCS>0) LOG_I(PHY, "PRACH [UE %d] generate PRACH in slot %d for RootSeqIndex %d, Preamble Index %d, PRACH Format %s, NCS %d (N_ZC %d): Preamble_offset %d, Preamble_shift %d msg1 frequency start %d\n", Mod_id, slot, rootSequenceIndex, preamble_index, - prach_sequence_length == 0 ? prachfmt03[prach_fmt_id] : prachfmt[prach_fmt_id], + prachfmt[prach_fmt_id], NCS, N_ZC, preamble_offset, @@ -362,31 +361,33 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, uint8_t slot){ } else { switch (prach_fmt_id) { - case 0: //A1 + case 4: //A1 Ncp = 288/(1<RU_list[i]; for (ru_aa=0,aa=0; ru_aanb_rx; ru_aa++,aa++) { - eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[i]->prach_rxsigF[ru_aa]; + eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[i]->prach_rxsigF[0][ru_aa]; int ce_level; if (br_flag==1) diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index a5d459a7c6..5a98b17cea 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -52,87 +52,114 @@ extern uint8_t nfapi_mode; extern int oai_nfapi_nr_rach_ind(nfapi_rach_indication_t *rach_ind); -void L1_nr_prach_procedures(PHY_VARS_gNB *gNB,int frame,int slot, - nfapi_nr_prach_pdu_t *prach_pdu) { +void L1_nr_prach_procedures(PHY_VARS_gNB *gNB,int frame,int slot) { uint16_t max_preamble[4]={0},max_preamble_energy[4]={0},max_preamble_delay[4]={0}; - uint16_t i; - gNB->UL_INFO.rach_ind.number_of_pdus=0; - + uint8_t pdu_index = 0; RU_t *ru; int aa=0; int ru_aa; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1); + gNB->UL_INFO.rach_ind.sfn = frame; + gNB->UL_INFO.rach_ind.slot = slot; + gNB->UL_INFO.rach_ind.pdu_list = gNB->prach_pdu_indication_list; + gNB->UL_INFO.rach_ind.number_of_pdus = 0; + ru=gNB->RU_list[0]; + int prach_id=find_nr_prach(gNB,frame,slot,SEARCH_EXIST); + + if (prach_id>=0) { + nfapi_nr_prach_pdu_t *prach_pdu = &gNB->prach_vars.list[prach_id].pdu; + uint8_t prachStartSymbol; + uint8_t N_dur = get_nr_prach_duration(prach_pdu->prach_format); + + /* + uint16_t format,RA_sfn_index; + uint8_t start_symbol,N_t_slot,N_dur,N_RA_slot,config_period; - for (i=0;inum_RU;i++) { - ru=gNB->RU_list[i]; - for (ru_aa=0,aa=0;ru_aanb_rx;ru_aa++,aa++) { - gNB->prach_vars.rxsigF[aa] = gNB->RU_list[i]->prach_rxsigF[ru_aa]; - } - } + get_nr_prach_info_from_index(gNB->gNB_config.prach_config.prach_ConfigurationIndex.value, + frame,slot, + gNB->gNB_config.carrier_config.dl_frequency.value, + gNB->frame_parms.numerology_index, + gNB->frame_parms.frame_type, + &format, + &start_symbol, + &N_t_slot, + &N_dur, + &RA_sfn_index, + &N_RA_slot, + &config_period); + */ + + for(int prach_oc = 0; prach_oc < prach_pdu->num_prach_ocas; prach_oc++) { + for (ru_aa=0,aa=0;ru_aanb_rx;ru_aa++,aa++) { + gNB->prach_vars.rxsigF[aa] = ru->prach_rxsigF[prach_oc][ru_aa]; + } - rx_nr_prach(gNB, - prach_pdu, + prachStartSymbol = prach_pdu->prach_start_symbol+prach_oc*N_dur; + //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. + + rx_nr_prach(gNB, + prach_pdu, + prach_oc, + frame, + slot, + &max_preamble[0], + &max_preamble_energy[0], + &max_preamble_delay[0] + ); + free_nr_prach_entry(gNB,prach_id); + LOG_D(PHY,"[RAPROC] Frame %d, slot %d, occasion %d (prachStartSymbol %d) : Most likely preamble %d, energy %d dB delay %d (prach_energy counter %d)\n", + frame,slot,prach_oc,prachStartSymbol, + max_preamble[0], + max_preamble_energy[0]/10, + max_preamble_delay[0], + gNB->prach_energy_counter); + + if ((gNB->prach_energy_counter == 100) && + (max_preamble_energy[0] > gNB->measurements.prach_I0+100)) { + + LOG_I(PHY,"[gNB %d][RAPROC] Frame %d, slot %d Initiating RA procedure with preamble %d, energy %d.%d dB, delay %d start symbol %u freq index %u\n", + gNB->Mod_id, frame, slot, - &max_preamble[0], - &max_preamble_energy[0], - &max_preamble_delay[0] - ); - - LOG_D(PHY,"[RAPROC] Frame %d, slot %d : Most likely preamble %d, energy %d dB delay %d (prach_energy counter %d)\n", - frame,slot, - max_preamble[0], - max_preamble_energy[0]/10, - max_preamble_delay[0], - gNB->prach_energy_counter); - - if ((gNB->prach_energy_counter == 100) && - (max_preamble_energy[0] > gNB->measurements.prach_I0+100)) { - - LOG_I(PHY,"[gNB %d][RAPROC] Frame %d, slot %d Initiating RA procedure with preamble %d, energy %d.%d dB, delay %d\n", - gNB->Mod_id, - frame, - slot, - max_preamble[0], - max_preamble_energy[0]/10, - max_preamble_energy[0]%10, - max_preamble_delay[0]); - - T(T_ENB_PHY_INITIATE_RA_PROCEDURE, T_INT(gNB->Mod_id), T_INT(frame), T_INT(slot), - T_INT(max_preamble[0]), T_INT(max_preamble_energy[0]), T_INT(max_preamble_delay[0])); - - - gNB->UL_INFO.rach_ind.number_of_pdus = 1; - gNB->UL_INFO.rach_ind.pdu_list = &gNB->prach_pdu_indication_list[0]; - gNB->UL_INFO.rach_ind.sfn = frame; - gNB->UL_INFO.rach_ind.slot = slot; - - gNB->prach_pdu_indication_list[0].phy_cell_id = gNB->gNB_config.cell_config.phy_cell_id.value; - gNB->prach_pdu_indication_list[0].symbol_index = prach_pdu->prach_start_symbol; // FIXME to be changed for multi-ssb (this is only the start symbol of first occasion) - gNB->prach_pdu_indication_list[0].slot_index = slot; - gNB->prach_pdu_indication_list[0].freq_index = prach_pdu->num_ra; - gNB->prach_pdu_indication_list[0].avg_rssi = (max_preamble_energy[0]<631) ? (128+(max_preamble_energy[0]/5)) : 254; - gNB->prach_pdu_indication_list[0].avg_snr = 0xff; // invalid for now - - - gNB->prach_pdu_indication_list[0].num_preamble = 1; - gNB->prach_pdu_indication_list[0].preamble_list = gNB->preamble_list; - gNB->prach_pdu_indication_list[0].preamble_list[0].preamble_index = max_preamble[0]; - gNB->prach_pdu_indication_list[0].preamble_list[0].timing_advance = max_preamble_delay[0]; - gNB->prach_pdu_indication_list[0].preamble_list[0].preamble_pwr = 0xffffffff; - } - gNB->measurements.prach_I0 = ((gNB->measurements.prach_I0*900)>>10) + ((max_preamble_energy[0]*124)>>10); - if (frame==0) LOG_I(PHY,"prach_I0 = %d.%d dB\n",gNB->measurements.prach_I0/10,gNB->measurements.prach_I0%10); - if (gNB->prach_energy_counter < 100) gNB->prach_energy_counter++; - - + max_preamble[0], + max_preamble_energy[0]/10, + max_preamble_energy[0]%10, + max_preamble_delay[0], + prachStartSymbol, + prach_pdu->num_ra); + + T(T_ENB_PHY_INITIATE_RA_PROCEDURE, T_INT(gNB->Mod_id), T_INT(frame), T_INT(slot), + T_INT(max_preamble[0]), T_INT(max_preamble_energy[0]), T_INT(max_preamble_delay[0])); + + + gNB->UL_INFO.rach_ind.number_of_pdus += 1; + + gNB->prach_pdu_indication_list[pdu_index].phy_cell_id = gNB->gNB_config.cell_config.phy_cell_id.value; + gNB->prach_pdu_indication_list[pdu_index].symbol_index = prachStartSymbol; + gNB->prach_pdu_indication_list[pdu_index].slot_index = slot; + gNB->prach_pdu_indication_list[pdu_index].freq_index = prach_pdu->num_ra; + gNB->prach_pdu_indication_list[pdu_index].avg_rssi = (max_preamble_energy[0]<631) ? (128+(max_preamble_energy[0]/5)) : 254; + gNB->prach_pdu_indication_list[pdu_index].avg_snr = 0xff; // invalid for now + + gNB->prach_pdu_indication_list[pdu_index].num_preamble = 1; + gNB->prach_pdu_indication_list[pdu_index].preamble_list = gNB->preamble_list; + gNB->prach_pdu_indication_list[pdu_index].preamble_list[0].preamble_index = max_preamble[0]; + gNB->prach_pdu_indication_list[pdu_index].preamble_list[0].timing_advance = max_preamble_delay[0]; + gNB->prach_pdu_indication_list[pdu_index].preamble_list[0].preamble_pwr = 0xffffffff; + pdu_index++; + } + gNB->measurements.prach_I0 = ((gNB->measurements.prach_I0*900)>>10) + ((max_preamble_energy[0]*124)>>10); + if (frame==0) LOG_I(PHY,"prach_I0 = %d.%d dB\n",gNB->measurements.prach_I0/10,gNB->measurements.prach_I0%10); + if (gNB->prach_energy_counter < 100) gNB->prach_energy_counter++; + } //if prach_id>0 + } //for NUMBER_OF_NR_PRACH_OCCASION_MAX VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); } diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 1024db3791..2d246101a6 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -536,7 +536,7 @@ void phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) (pucch->frame == frame_rx) && (pucch->slot == slot_rx) ) { - nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch[i].pucch_pdu; + nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; uint16_t num_ucis; switch (pucch_pdu->format_type) { diff --git a/openair1/SCHED_NR/sched_nr.h b/openair1/SCHED_NR/sched_nr.h index 59ac1496e7..24885f1bb4 100644 --- a/openair1/SCHED_NR/sched_nr.h +++ b/openair1/SCHED_NR/sched_nr.h @@ -39,7 +39,7 @@ void nr_set_ssb_first_subcarrier(nfapi_nr_config_request_scf_t *cfg, NR_DL_FRAME void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, int frame_tx, int slot_tx, int do_meas); void phy_procedures_gNB_common_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx); void phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx); -void L1_nr_prach_procedures(PHY_VARS_gNB *gNB,int frame,int slot,nfapi_nr_prach_pdu_t *prach_pdu); +void L1_nr_prach_procedures(PHY_VARS_gNB *gNB,int frame,int slot); void nr_common_signal_procedures (PHY_VARS_gNB *gNB,int frame, int slot); void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx); void nr_feptx_ofdm_2thread(RU_t *ru,int frame_tx,int tti_tx); diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 2c0641ba2d..79b5884a13 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -778,6 +778,8 @@ int main(int argc, char **argv) UE_info->UE_sched_ctrl[0].harq_processes[harq_pid].round = round; UE_info->UE_sched_ctrl[0].current_harq_pid = harq_pid; gNB->dlsch[0][0]->harq_processes[harq_pid]->round = round; + for (int i=0; iUE_info.num_pdcch_cand[0][i] = 0; if (css_flag == 0) nr_schedule_uss_dlsch_phytest(0,frame,slot,&pucch_sched,&dlsch_config); else nr_schedule_css_dlsch_phytest(0,frame,slot); diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index cb196afe9a..39aa9c3224 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -113,6 +113,10 @@ int main(int argc, char **argv){ uint16_t Nid_cell = 0, preamble_tx = 0, preamble_delay, format, format0, format1; uint32_t tx_lev = 10000, prach_errors = 0, samp_count; //,tx_lev_dB; uint64_t SSB_positions = 0x01, absoluteFrequencyPointA = 640000; + uint16_t RA_sfn_index; + uint8_t N_RA_slot; + uint8_t config_period; + int prachOccasion = 0; double DS_TDL = .03; // int8_t interf1=-19,interf2=-19; @@ -428,15 +432,18 @@ int main(int argc, char **argv){ gNB->proc.slot_rx = slot; int ret = get_nr_prach_info_from_index(config_index, - (int)frame, - (int)slot_gNB, - absoluteFrequencyPointA, - mu, - frame_parms->frame_type, - &format, - &start_symbol, - &N_t_slot, - &N_dur); + (int)frame, + (int)slot_gNB, + absoluteFrequencyPointA, + mu, + frame_parms->frame_type, + &format, + &start_symbol, + &N_t_slot, + &N_dur, + &RA_sfn_index, + &N_RA_slot, + &config_period); if (ret == 0) {printf("No prach in %d.%d, mu %d, config_index %d\n",frame,slot,mu,config_index); exit(-1);} format0 = format&0xff; // first column of format from table @@ -445,60 +452,54 @@ int main(int argc, char **argv){ if (format1 != 0xff) { switch(format0) { case 0xa1: - prach_format = 9; + prach_format = 11; break; case 0xa2: - prach_format = 10; + prach_format = 12; break; case 0xa3: - prach_format = 11; + prach_format = 13; break; default: AssertFatal(1==0, "Only formats A1/B1 A2/B2 A3/B3 are valid for dual format"); } } else { - switch(format0) { - case 0xa1: - prach_format = 0; - break; - case 0xa2: - prach_format = 1; - break; - case 0xa3: - prach_format = 2; - break; - case 0xb1: - prach_format = 3; - break; - case 0xb2: - prach_format = 4; - break; - case 0xb3: - prach_format = 5; - break; - case 0xb4: - prach_format = 6; - break; - case 0xc0: - prach_format = 7; - break; - case 0xc2: - prach_format = 8; - break; - case 0: - // long formats are handled @ PHY - break; - case 1: - // long formats are handled @ PHY - break; - case 2: - // long formats are handled @ PHY - break; - case 3: - // long formats are handled @ PHY - break; + switch(format0) { // single PRACH format + case 0: + prach_format = 0; + break; + case 1: + prach_format = 1; + break; + case 2: + prach_format = 2; + break; + case 3: + prach_format = 3; + break; + case 0xa1: + prach_format = 4; + break; + case 0xa2: + prach_format = 5; + break; + case 0xa3: + prach_format = 6; + break; + case 0xb1: + prach_format = 7; + break; + case 0xb4: + prach_format = 8; + break; + case 0xc0: + prach_format = 9; + break; + case 0xc2: + prach_format = 10; + break; default: - AssertFatal(1==0, "Invalid PRACH format"); + AssertFatal(1 == 0, "Invalid PRACH format"); } } @@ -721,11 +722,11 @@ int main(int argc, char **argv){ } - rx_nr_prach_ru(ru, prach_format, numRA, prachStartSymbol, frame, slot); + rx_nr_prach_ru(ru, prach_format, numRA, prachStartSymbol, prachOccasion, frame, slot); - gNB->prach_vars.rxsigF = ru->prach_rxsigF; + gNB->prach_vars.rxsigF = ru->prach_rxsigF[prachOccasion]; if (n_frames == 1) printf("ncs %d,num_seq %d\n",prach_pdu->num_cs, prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); - rx_nr_prach(gNB, prach_pdu, frame, subframe, &preamble_rx, &preamble_energy, &preamble_delay); + rx_nr_prach(gNB, prach_pdu, prachOccasion, frame, subframe, &preamble_rx, &preamble_energy, &preamble_delay); // printf(" preamble_energy %d preamble_rx %d preamble_tx %d \n", preamble_energy, preamble_rx, preamble_tx); diff --git a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.c b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.c index f1daacbb65..7ca466e8ff 100644 --- a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.c +++ b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.c @@ -40,6 +40,8 @@ #include "common/utils/LOG/log.h" +#include + /*Array containing the Agent-MAC interfaces*/ AGENT_MAC_xface *agent_mac_xface[NUM_MAX_ENB]; @@ -53,10 +55,20 @@ struct lfds700_ringbuffer_state ringbuffer_state[NUM_MAX_ENB]; struct lfds700_ringbuffer_element *slice_config_array[NUM_MAX_ENB]; struct lfds700_ringbuffer_state slice_config_ringbuffer_state[NUM_MAX_ENB]; +/* Ringbuffer related to slice configuration if we need to wait for additional + * controller information */ +struct lfds700_ringbuffer_element *store_slice_config_array[NUM_MAX_ENB]; +struct lfds700_ringbuffer_state store_slice_config_ringbuffer_state[NUM_MAX_ENB]; +/* stores the (increasing) xid for messages to prevent double use */ +int xid = 50; + /* Ringbuffer-related to slice configuration */ struct lfds700_ringbuffer_element *ue_assoc_array[NUM_MAX_ENB]; struct lfds700_ringbuffer_state ue_assoc_ringbuffer_state[NUM_MAX_ENB]; +/* a list of shared objects that are loaded into the user plane */ +SLIST_HEAD(flexran_so_handle, flexran_agent_so_handle_s) flexran_handles[NUM_MAX_ENB]; + int flexran_agent_mac_stats_reply_ue(mid_t mod_id, Protocol__FlexUeStatsReport **ue_report, int n_ue, @@ -1363,6 +1375,19 @@ void flexran_agent_init_mac_agent(mid_t mod_id) { &scrng, NULL); + struct lfds700_misc_prng_state sscrng; + i = posix_memalign((void **) &store_slice_config_array[mod_id], + LFDS700_PAL_ATOMIC_ISOLATION_IN_BYTES, + sizeof(struct lfds700_ringbuffer_element) * num_elements); + AssertFatal(i == 0, + "posix_memalign(): could not allocate aligned memory\n"); + lfds700_ringbuffer_init_valid_on_current_logical_core( + &store_slice_config_ringbuffer_state[mod_id], + store_slice_config_array[mod_id], + num_elements, + &sscrng, + NULL); + struct lfds700_misc_prng_state uarng; i = posix_memalign((void **) &ue_assoc_array[mod_id], LFDS700_PAL_ATOMIC_ISOLATION_IN_BYTES, @@ -1382,6 +1407,8 @@ void flexran_agent_init_mac_agent(mid_t mod_id) { RC.mac[mod_id]->UE_info.eNB_UE_stats[UE_PCCID(mod_id,i)][i].harq_pid = 0; } } + + SLIST_INIT(&flexran_handles[mod_id]); } /*********************************************** @@ -1659,6 +1686,8 @@ int flexran_agent_unregister_mac_xface(mid_t mod_id) free(dl_mac_config_array[mod_id]); lfds700_ringbuffer_cleanup(&slice_config_ringbuffer_state[mod_id], NULL ); free(slice_config_array[mod_id]); + lfds700_ringbuffer_cleanup(&store_slice_config_ringbuffer_state[mod_id], NULL ); + free(store_slice_config_array[mod_id]); lfds700_ringbuffer_cleanup(&ue_assoc_ringbuffer_state[mod_id], NULL ); free(ue_assoc_array[mod_id]); lfds700_misc_library_cleanup(); @@ -1698,7 +1727,81 @@ void helper_destroy_mac_slice_config(Protocol__FlexSliceConfig *slice_config) { flexran_agent_destroy_mac_slice_config(&helper); } -void prepare_update_slice_config(mid_t mod_id, Protocol__FlexSliceConfig **slice_config) { +int check_scheduler(mid_t mod_id, char *s) { + if (!s) + return 1; + if (dlsym(NULL, s)) + return 1; + flexran_agent_so_handle_t *so = NULL; + SLIST_FOREACH(so, &flexran_handles[mod_id], entries) { + if (strcmp(so->name, s) == 0) + return 1; + } + return 0; +} + +void request_scheduler(mid_t mod_id, char *s, int xid) { + LOG_W(FLEXRAN_AGENT, + "unknown scheduler %s, requesting controller to send it\n", + s); + Protocol__FlexControlDelegationRequest *req = malloc(sizeof(Protocol__FlexControlDelegationRequest)); + DevAssert(req); + protocol__flex_control_delegation_request__init(req); + Protocol__FlexHeader *header = NULL; + int rc = flexran_create_header( + xid, PROTOCOL__FLEX_TYPE__FLPT_DELEGATE_REQUEST, &header); + AssertFatal(rc == 0, "%s(): cannot create header\n", __func__); + req->header = header; + req->delegation_type = PROTOCOL__FLEX_CONTROL_DELEGATION_TYPE__FLCDT_MAC_DL_UE_SCHEDULER; + req->name = strdup(s); + + Protocol__FlexranMessage *msg = malloc(sizeof(Protocol__FlexranMessage)); + DevAssert(msg); + protocol__flexran_message__init(msg); + msg->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_CONTROL_DEL_REQ_MSG; + msg->msg_dir = PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE; + msg->control_del_req_msg = req; + + int size = 0; + void *data = flexran_agent_pack_message(msg, &size); + if (flexran_agent_msg_send(mod_id, FLEXRAN_AGENT_DEFAULT, data, size, 0) < 0) + LOG_E(FLEXRAN_AGENT, "%s(): error while sending message\n", __func__); +} + +Protocol__FlexranMessage *request_scheduler_timeout( + mid_t mod_id, + const Protocol__FlexranMessage *msg) { + const Protocol__FlexEnbConfigReply *ecr = msg->enb_config_reply_msg; + + struct lfds700_misc_prng_state ls; + LFDS700_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE; + lfds700_misc_prng_init(&ls); + + Protocol__FlexranMessage *smsg = NULL; + struct lfds700_ringbuffer_state *state = &store_slice_config_ringbuffer_state[mod_id]; + if (lfds700_ringbuffer_read(state, NULL, (void **) &smsg, &ls)) { + AssertFatal(msg == smsg, + "expected and returned enb_config_reply are not the same\n"); + AssertFatal(ecr->header->xid == smsg->enb_config_reply_msg->header->xid, + "expected and returned enb_config_reply are not the same\n"); + } else { + LOG_E(FLEXRAN_AGENT, "%s(): could not read config from ringbuffer\n", __func__); + } + /* call the helper so that scheduler names are correctly freed */ + helper_destroy_mac_slice_config(ecr->cell_config[0]->slice_config); + ecr->cell_config[0]->slice_config = NULL; + /* we should not call flexran_agent_destroy_enb_config_reply(smsg); since + * upon timer removal, this is automatically done */ + LOG_W(FLEXRAN_AGENT, + "remove stored slice config (xid %d) after timeout\n", + ecr->header->xid); + flexran_agent_destroy_timer(mod_id, ecr->header->xid); + return NULL; +} + +void prepare_update_slice_config(mid_t mod_id, + Protocol__FlexSliceConfig **slice_config, + int request_objects) { if (!*slice_config) return; /* just use the memory and set to NULL in original */ Protocol__FlexSliceConfig *sc = *slice_config; @@ -1709,6 +1812,63 @@ void prepare_update_slice_config(mid_t mod_id, Protocol__FlexSliceConfig **slice lfds700_misc_prng_init(&ls); enum lfds700_misc_flag overwrite_occurred_flag; + int put_on_hold = 0; + if (request_objects + && sc->dl->scheduler + && !check_scheduler(mod_id, sc->dl->scheduler)) { + request_scheduler(mod_id, sc->dl->scheduler, xid++); + put_on_hold = 1; + } + for (int i = 0; request_objects && i < sc->dl->n_slices; ++i) { + Protocol__FlexSlice *sl = sc->dl->slices[i]; + if (sl->scheduler && !check_scheduler(mod_id, sl->scheduler)) { + request_scheduler(mod_id, sl->scheduler, xid++); + put_on_hold = 1; + } + } + if (put_on_hold) { + Protocol__FlexEnbConfigReply *reply = malloc(sizeof(Protocol__FlexEnbConfigReply)); + DevAssert(reply); + protocol__flex_enb_config_reply__init(reply); + /* use the last used xid so we can correlate answer and waiting config */ + int rc = flexran_create_header( + xid - 1, PROTOCOL__FLEX_TYPE__FLPT_GET_ENB_CONFIG_REPLY, &reply->header); + AssertFatal(rc == 0, "%s(): cannot create header\n", __func__); + reply->n_cell_config = 1; + reply->cell_config = malloc(sizeof(Protocol__FlexCellConfig *)); + DevAssert(reply->cell_config); + reply->cell_config[0] = malloc(sizeof(Protocol__FlexCellConfig)); + DevAssert(reply->cell_config[0]); + protocol__flex_cell_config__init(reply->cell_config[0]); + reply->cell_config[0]->slice_config = sc; + + Protocol__FlexranMessage *msg = malloc(sizeof(Protocol__FlexranMessage)); + DevAssert(msg); + protocol__flexran_message__init(msg); + msg->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_ENB_CONFIG_REPLY_MSG; + msg->msg_dir = PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE; + msg->enb_config_reply_msg = reply; + + Protocol__FlexEnbConfigReply *o; + lfds700_ringbuffer_write(&store_slice_config_ringbuffer_state[mod_id], + NULL, + (void *)msg, + &overwrite_occurred_flag, + NULL, + (void **) &o, + &ls); + AssertFatal(overwrite_occurred_flag == LFDS700_MISC_FLAG_LOWERED, + "unhandled: stored slice config for controller has been overwritten\n"); + + flexran_agent_create_timer(mod_id, + 3000, + FLEXRAN_AGENT_TIMER_TYPE_PERIODIC, + xid - 1, + request_scheduler_timeout, + msg); /* need reply for xid */ + return; + } + Protocol__FlexSliceConfig *overwritten_sc; lfds700_ringbuffer_write(&slice_config_ringbuffer_state[mod_id], NULL, @@ -1756,6 +1916,69 @@ void flexran_agent_slice_update(mid_t mod_id) { apply_update_dl_slice_config(mod_id, sc->dl); apply_update_ul_slice_config(mod_id, sc->ul); helper_destroy_mac_slice_config(sc); + + flexran_agent_so_handle_t *so = NULL; + flexran_agent_so_handle_t *prev = NULL; // the previous to current element, if we delete in order to go back + //SLIST_FOREACH(so, &flexran_handles[mod_id], entries) + for(so = flexran_handles[mod_id].slh_first; so;) { + char *name = so->name; + int in_use = strcmp(flexran_get_dl_scheduler_name(mod_id), name) == 0 + || strcmp(flexran_get_ul_scheduler_name(mod_id), name) == 0; + Protocol__FlexSliceAlgorithm dl_algo = flexran_get_dl_slice_algo(mod_id); + if (!in_use && dl_algo != PROTOCOL__FLEX_SLICE_ALGORITHM__None) { + int n = flexran_get_num_dl_slices(mod_id); + for (int i = 0; i < n; ++i) { + Protocol__FlexSlice s; + /* NONE so it won't do any allocations */ + flexran_get_dl_slice(mod_id, i, &s, PROTOCOL__FLEX_SLICE_ALGORITHM__None); + if (strcmp(s.scheduler, name) == 0) { + in_use = 1; + break; + } + } + } + Protocol__FlexSliceAlgorithm ul_algo = flexran_get_ul_slice_algo(mod_id); + if (!in_use && ul_algo != PROTOCOL__FLEX_SLICE_ALGORITHM__None) { + int n = flexran_get_num_ul_slices(mod_id); + for (int i = 0; i < n; ++i) { + Protocol__FlexSlice s; + /* NONE so it won't do any allocations */ + flexran_get_ul_slice(mod_id, i, &s, PROTOCOL__FLEX_SLICE_ALGORITHM__None); + if (strcmp(s.scheduler, name) == 0) { + in_use = 1; + break; + } + } + } + if (!in_use) { + char s[512]; + int rc = flexran_agent_map_name_to_delegated_object(mod_id, so->name, s, 512); + LOG_W(FLEXRAN_AGENT, + "removing %s (library handle %p) since it is not used in the user " + "plane anymore\n", + s, + so->dl_handle); + dlclose(so->dl_handle); + if (rc < 0) { + LOG_E(FLEXRAN_AGENT, "cannot map name %s\n", so->name); + } else { + int rc = remove(s); + if (rc < 0) + LOG_E(FLEXRAN_AGENT, "cannot remove file %s: %s\n", s, strerror(errno)); + } + free (so->name); + if (!prev) { //it's the head, start over + SLIST_REMOVE_HEAD(&flexran_handles[mod_id], entries); + so = flexran_handles[mod_id].slh_first; + } else { + SLIST_REMOVE(&flexran_handles[mod_id], so, flexran_agent_so_handle_s, entries); + so = prev->entries.sle_next; + } + } else { + prev = so; + so = so->entries.sle_next; + } + } } Protocol__FlexUeConfig *ue_config = NULL; @@ -1765,3 +1988,92 @@ void flexran_agent_slice_update(mid_t mod_id) { free(ue_config); } } + +void flexran_agent_mac_inform_delegation(mid_t mod_id, + Protocol__FlexControlDelegation *cdm) { + LOG_W(FLEXRAN_AGENT, + "received FlexControlDelegation message for object '%s' xid %d\n", + cdm->name, + cdm->header->xid); + if (cdm->header->xid < xid - 1) { + LOG_I(FLEXRAN_AGENT, + "waiting for %d more messages (up to xid %d)\n", + xid - 1 - cdm->header->xid, + xid - 1); + return; + } + /* should receive up to xid - 1, otherwise it means there was no request */ + AssertFatal(xid > cdm->header->xid, + "received control delegation with xid %d that we never requested " + "(last request %d)\n", + cdm->header->xid, + xid - 1); + + /* Load the library so the user plane can search it */ + char s[512]; + int rc = flexran_agent_map_name_to_delegated_object(mod_id, cdm->name, s, 512); + if (rc < 0) { + LOG_E(FLEXRAN_AGENT, "cannot map name %s\n", cdm->name); + return; + } + /* TODO where to unload/save handle?? */ + void *h = dlopen(s, RTLD_NOW); + if (!h) { + LOG_E(FLEXRAN_AGENT, "dlopen(): %s\n", dlerror()); + return; + } + LOG_I(FLEXRAN_AGENT, "library handle %p\n", h); + + struct lfds700_misc_prng_state ls; + LFDS700_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE; + lfds700_misc_prng_init(&ls); + + Protocol__FlexranMessage *msg = NULL; + struct lfds700_ringbuffer_state *state = &store_slice_config_ringbuffer_state[mod_id]; + if (lfds700_ringbuffer_read(state, NULL, (void **) &msg, &ls) == 0) { + LOG_E(FLEXRAN_AGENT, "%s(): could not read config from ringbuffer\n", __func__); + return; + } + AssertFatal(cdm->header->xid == msg->enb_config_reply_msg->header->xid, + "expected and retrieved xid of stored slice configuration does " + "not match (expected %d, retrieved %d)\n", + cdm->header->xid, + msg->enb_config_reply_msg->header->xid); + + /* Since we recover, stop the timeout */ + rc = flexran_agent_destroy_timer(mod_id, cdm->header->xid); + + prepare_update_slice_config( + mod_id, + &msg->enb_config_reply_msg->cell_config[0]->slice_config, + 0 /* don't do a request */); + /* we should not call flexran_agent_destroy_enb_config_reply(smsg); since + * upon timer removal, this is automatically done. + * prepare_update_slice_config() takes ownership of the slice config, it is + * freed inside */ + + flexran_agent_so_handle_t *so = malloc(sizeof(flexran_agent_so_handle_t)); + DevAssert(so); + so->name = strdup(cdm->name); + DevAssert(so->name); + so->dl_handle = h; + SLIST_INSERT_HEAD(&flexran_handles[mod_id], so, entries); +} + +void flexran_agent_mac_fill_loaded_mac_objects( + mid_t mod_id, + Protocol__FlexEnbConfigReply *reply) { + int n = 0; + flexran_agent_so_handle_t *so = NULL; + SLIST_FOREACH(so, &flexran_handles[mod_id], entries) + ++n; + reply->n_loadedmacobjects = n; + reply->loadedmacobjects = calloc(n, sizeof(char *)); + if (!reply->loadedmacobjects) { + reply->n_loadedmacobjects = 0; + return; + } + n = 0; + SLIST_FOREACH(so, &flexran_handles[mod_id], entries) + reply->loadedmacobjects[n++] = so->name; +} diff --git a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h index 5a8d4f31aa..18e567c061 100644 --- a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h +++ b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h @@ -39,6 +39,12 @@ // for flexran_agent_get_mac_xface() #include "flexran_agent_extern.h" +/* Type for a list of shared objects used in the user plane (schedulers) */ +typedef struct flexran_agent_so_handle_s { + char *name; // name of the object + void *dl_handle; // handle of associated objects + SLIST_ENTRY (flexran_agent_so_handle_s) entries; +} flexran_agent_so_handle_t; /* Initialization function for the agent structures etc */ void flexran_agent_init_mac_agent(mid_t mod_id); @@ -117,7 +123,9 @@ void flexran_agent_slice_update(mid_t mod_id); /* marks slice_config so that it can be applied later. Takes ownership of the * FlexSliceConfig message */ -void prepare_update_slice_config(mid_t mod_id, Protocol__FlexSliceConfig **slice); +void prepare_update_slice_config(mid_t mod_id, + Protocol__FlexSliceConfig **slice, + int request_objects); /* inserts a new ue_config into the structure keeping ue to slice association * updates and marks so it can be applied. Takes ownership of the FlexUeConfig message */ @@ -127,4 +135,13 @@ void prepare_ue_slice_assoc_update(mid_t mod_id, Protocol__FlexUeConfig **ue_con * flexran_agent_fill_mac_cell_config() */ void flexran_agent_destroy_mac_slice_config(Protocol__FlexCellConfig *conf); +/* information about a new (potentially relevant) control delegation message */ +void flexran_agent_mac_inform_delegation(mid_t mod_id, + Protocol__FlexControlDelegation *cdm); + +/* fill the enb_config_reply with the shared objects in use by the MAC sublayer */ +void flexran_agent_mac_fill_loaded_mac_objects( + mid_t mod_id, + Protocol__FlexEnbConfigReply *reply); + #endif diff --git a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c index 93de0b11ec..42246c3ebf 100644 --- a/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c +++ b/openair2/ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac_internal.c @@ -33,6 +33,9 @@ #include "flexran_agent_mac_internal.h" #include "flexran_agent_mac_slice_verification.h" +extern SLIST_HEAD(flexran_so_handle, + flexran_agent_so_handle_s) flexran_handles[NUM_MAX_ENB]; + Protocol__FlexranMessage * flexran_agent_generate_diff_mac_stats_report(Protocol__FlexranMessage *new_message, Protocol__FlexranMessage *old_message) { @@ -1025,6 +1028,19 @@ int load_dl_scheduler_function(mid_t mod_id, const char *function_name) { } +void *search_so(mid_t mod_id, char *name) { + if (!name) // no name -> nothing to return, so use NULL for executable + return NULL; + if (dlsym(NULL, name)) // found it in executable + return NULL; + flexran_agent_so_handle_t *so = NULL; + SLIST_FOREACH(so, &flexran_handles[mod_id], entries) { + if (strcmp(so->name, name) == 0) + return so->dl_handle; + } + return NULL; +} + void update_or_remove_dl(mid_t mod_id, Protocol__FlexSlice *s) { if (s->params_case == PROTOCOL__FLEX_SLICE__PARAMS__NOT_SET && !s->label && !s->scheduler) { @@ -1033,8 +1049,12 @@ void update_or_remove_dl(mid_t mod_id, Protocol__FlexSlice *s) { if (!rc) LOG_W(FLEXRAN_AGENT, "error while removing slice ID %d\n", s->id); } else { - LOG_I(FLEXRAN_AGENT, "updating DL slice ID %d\n", s->id); - const int rc = flexran_create_dl_slice(mod_id, s); + void *lib = search_so(mod_id, s->scheduler); + LOG_I(FLEXRAN_AGENT, + "updating DL slice ID %d (library handle %p)\n", + s->id, + lib); + const int rc = flexran_create_dl_slice(mod_id, s, lib); if (rc < 0) LOG_W(FLEXRAN_AGENT, "error while update slice ID %d: flexran_create_dl_slice() -> %d\n", @@ -1050,8 +1070,12 @@ void update_or_remove_ul(mid_t mod_id, Protocol__FlexSlice *s) { if (!rc) LOG_W(FLEXRAN_AGENT, "error while removing slice ID %d\n", s->id); } else { - LOG_I(FLEXRAN_AGENT, "updating UL slice ID %d\n", s->id); - const int rc = flexran_create_ul_slice(mod_id, s); + void *lib = search_so(mod_id, s->scheduler); + LOG_I(FLEXRAN_AGENT, + "updating UL slice ID %d (library handle %p)\n", + s->id, + lib); + const int rc = flexran_create_ul_slice(mod_id, s, lib); if (rc < 0) LOG_W(FLEXRAN_AGENT, "error while updating slice ID %d: flexran_create_ul_slice() -> %d)\n", @@ -1099,8 +1123,12 @@ void apply_update_dl_slice_config(mid_t mod_id, Protocol__FlexSliceDlUlConfig *d LOG_E(FLEXRAN_AGENT, "cannot update scheduling algorithm: slice algorithm loaded\n"); return; } - LOG_I(FLEXRAN_AGENT, "loading DL new scheduling algorithm '%s'\n", dl->scheduler); - const int rc = flexran_set_dl_scheduler(mod_id, dl->scheduler); + void *lib = search_so(mod_id, dl->scheduler); + LOG_I(FLEXRAN_AGENT, + "loading DL new scheduling algorithm '%s' (library handle %p)\n", + dl->scheduler, + lib); + const int rc = flexran_set_dl_scheduler(mod_id, dl->scheduler, lib); if (rc < 0) { LOG_E(FLEXRAN_AGENT, "error while updating scheduling algorithm: " @@ -1147,8 +1175,12 @@ void apply_update_ul_slice_config(mid_t mod_id, Protocol__FlexSliceDlUlConfig *u LOG_E(FLEXRAN_AGENT, "cannot update scheduling algorithm: slice algorithm loaded\n"); return; } - LOG_I(FLEXRAN_AGENT, "loading new UL scheduling algorithm '%s'\n", ul->scheduler); - const int rc = flexran_set_ul_scheduler(mod_id, ul->scheduler); + void *lib = search_so(mod_id, ul->scheduler); + LOG_I(FLEXRAN_AGENT, + "loading new UL scheduling algorithm '%s' (library handle %p)\n", + ul->scheduler, + lib); + const int rc = flexran_set_ul_scheduler(mod_id, ul->scheduler, lib); if (rc < 0) { LOG_E(FLEXRAN_AGENT, "error while updating scheduling algorithm: " diff --git a/openair2/ENB_APP/MESSAGES/V2/control_delegation.proto b/openair2/ENB_APP/MESSAGES/V2/control_delegation.proto index ef0eea3bd2..a646a978a9 100644 --- a/openair2/ENB_APP/MESSAGES/V2/control_delegation.proto +++ b/openair2/ENB_APP/MESSAGES/V2/control_delegation.proto @@ -3,4 +3,25 @@ package protocol; enum flex_control_delegation_type { FLCDT_MAC_DL_UE_SCHEDULER = 1; // DL UE scheduler delegation + FLCDT_AGENT_CONTROL_APP = 2; // Generic control application +} + +message flex_agent_reconfiguration_system { + optional string system = 1; + repeated flex_agent_reconfiguration_subsystem subsystems = 2; +} + +message flex_agent_reconfiguration_subsystem { + optional string name = 1; // Name of the object (app name or user plane object) + optional string behavior = 2; // For an app: a callback in a shared library. For any other subsystem: an object name (shared library) with defined syntax. + map params = 3; // Param configurations depending on callback/shared library above. +} + +message flex_agent_reconfiguration_param { + oneof param { + int32 integer = 1; + float floating = 2; + bool boolean = 3; + string str = 4; + } } diff --git a/openair2/ENB_APP/MESSAGES/V2/flexran.proto b/openair2/ENB_APP/MESSAGES/V2/flexran.proto index 3b47ff263b..cec7f84b6e 100644 --- a/openair2/ENB_APP/MESSAGES/V2/flexran.proto +++ b/openair2/ENB_APP/MESSAGES/V2/flexran.proto @@ -7,6 +7,7 @@ import "time_common.proto"; import "config_messages.proto"; import "controller_commands.proto"; import "config_common.proto"; +import "control_delegation.proto"; message flexran_message { optional flexran_direction msg_dir = 100; @@ -32,6 +33,7 @@ message flexran_message { flex_ul_mac_config ul_mac_config_msg = 19; flex_disconnect disconnect_msg = 20; flex_ho_command ho_command_msg = 21; + flex_control_delegation_request control_del_req_msg = 22; } } @@ -157,6 +159,8 @@ message flex_enb_config_reply { repeated flex_cell_config cell_config = 3; optional uint32 device_spec = 4; optional flex_s1ap_config s1ap = 5; + repeated string loadedApps = 6; + repeated string loadedMacObjects = 7; } message flex_ue_config_request { @@ -225,11 +229,17 @@ message flex_ue_state_change { message flex_control_delegation { optional flex_header header = 1; - optional uint32 delegation_type = 2; // Bitmap of FLCDT_* flags + optional flex_control_delegation_type delegation_type = 2; // The delegated object type, decides about further processing optional bytes payload = 3; // Byte array of shared lib containing the delegated functions optional string name = 4; // The delegated functions names ordered based on bitmap flags } +message flex_control_delegation_request { + optional flex_header header = 1; + optional flex_control_delegation_type delegation_type = 2; + optional string name = 4; +} + // // Agent reconfiguration message // @@ -237,6 +247,7 @@ message flex_control_delegation { message flex_agent_reconfiguration { optional flex_header header = 1; optional string policy = 2; // The policy changes using YAML syntax in string format + repeated flex_agent_reconfiguration_system systems = 3; // The policy changes. } // Extensions of the echo request and reply diff --git a/openair2/ENB_APP/MESSAGES/V2/header.proto b/openair2/ENB_APP/MESSAGES/V2/header.proto index 41b2b37f7a..5e224f64f7 100644 --- a/openair2/ENB_APP/MESSAGES/V2/header.proto +++ b/openair2/ENB_APP/MESSAGES/V2/header.proto @@ -44,5 +44,6 @@ enum flex_type { FLPT_RECONFIGURE_AGENT = 16; FLPT_RRC_TRIGGERING = 17; FLPT_UL_MAC_CONFIG = 18; + FLPT_DELEGATE_REQUEST = 22; } diff --git a/openair2/ENB_APP/flexran_agent.c b/openair2/ENB_APP/flexran_agent.c index 519d2bef4b..7d4546815f 100644 --- a/openair2/ENB_APP/flexran_agent.c +++ b/openair2/ENB_APP/flexran_agent.c @@ -115,7 +115,6 @@ error: return NULL; } - int channel_container_init = 0; int flexran_agent_start(mid_t mod_id) { @@ -233,6 +232,9 @@ int flexran_agent_start(mid_t mod_id) agent_task_created = 1; } + /* Init the app sublayer */ + flexran_agent_app_init(mod_id); + pthread_mutex_init(&flexran->mutex_node_ctrl, NULL); pthread_cond_init(&flexran->cond_node_ctrl, NULL); diff --git a/openair2/ENB_APP/flexran_agent.h b/openair2/ENB_APP/flexran_agent.h index 42295fc111..43e7d7210a 100644 --- a/openair2/ENB_APP/flexran_agent.h +++ b/openair2/ENB_APP/flexran_agent.h @@ -41,6 +41,7 @@ #include "flexran_agent_rrc.h" #include "flexran_agent_pdcp.h" #include "flexran_agent_s1ap.h" +#include "flexran_agent_app.h" #include "common/utils/LOG/log.h" #include "assertions.h" diff --git a/openair2/ENB_APP/flexran_agent_app.c b/openair2/ENB_APP/flexran_agent_app.c new file mode 100644 index 0000000000..5bf06ca631 --- /dev/null +++ b/openair2/ENB_APP/flexran_agent_app.c @@ -0,0 +1,207 @@ +/* + * 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 + */ + +/*! \file flexran_agent_app.c + * \brief CM for handling of applications + * \author Robert Schmidt + * \date 2020 + * \version 0.1 + */ + +#include "flexran_agent_app.h" +#include "queue.h" +#include + +/* app definition */ +typedef struct flexran_agent_app_internal_s { + char *name; + void *dl_handle; + flexran_agent_app_t *app; + + SLIST_ENTRY (flexran_agent_app_internal_s) entries; +} flexran_agent_app_internal_t; + +/* a list of all apps */ +SLIST_HEAD(flexran_apps_s, flexran_agent_app_internal_s) flexran_apps[NUM_MAX_ENB]; + +void flexran_agent_app_init(mid_t mod_id) { + //flexran_apps[mod_id] = SLIST_HEAD_INITIALIZER(flexran_apps[mod_id]); + SLIST_INIT(&flexran_apps[mod_id]); +} + +int flexran_agent_start_app_direct(mid_t mod_id, flexran_agent_app_t *app, char *name) { + app->start(mod_id, NULL, 0); + + flexran_agent_app_internal_t *so = malloc(sizeof(flexran_agent_app_internal_t)); + DevAssert(so); + so->name = strdup(name); + DevAssert(so->name); + so->dl_handle = NULL; + so->app = app; + SLIST_INSERT_HEAD(&flexran_apps[mod_id], so, entries); + return 0; +} + +int flexran_agent_start_app(mid_t mod_id, Protocol__FlexAgentReconfigurationSubsystem *sub) { + char s[512]; + int rc = flexran_agent_map_name_to_delegated_object(mod_id, sub->name, s, 512); + if (rc < 0) { + LOG_E(FLEXRAN_AGENT, "cannot map name %s\n", sub->name); + return -1; + } + void *h = dlopen(s, RTLD_NOW); + if (!h) { + LOG_E(FLEXRAN_AGENT, "cannot open shared object %s\n", s); + return -1; + } + flexran_agent_app_t *app = dlsym(h, "app"); + if (!app) { + LOG_E(FLEXRAN_AGENT, "cannot locate app inside of shared object %s\n", s); + return -1; + } + app->start(mod_id, sub->params, sub->n_params); + + flexran_agent_app_internal_t *so = malloc(sizeof(flexran_agent_app_internal_t)); + DevAssert(so); + so->name = strdup(sub->name); + DevAssert(so->name); + so->dl_handle = h; + so->app = app; + SLIST_INSERT_HEAD(&flexran_apps[mod_id], so, entries); + + return 0; +} + +int flexran_agent_stop_app(mid_t mod_id, char *name) { + flexran_agent_app_internal_t *so = NULL; + SLIST_FOREACH(so, &flexran_apps[mod_id], entries) { + if (strcmp(so->name, name) == 0) { + LOG_I(FLEXRAN_AGENT, "application %s seems to be running\n", name); + break; + } + } + if (!so) { + LOG_E(FLEXRAN_AGENT, "no such application %s found\n", name); + return -1; + } + flexran_agent_app_t *app = so->app; + app->stop(mod_id); + + SLIST_REMOVE(&flexran_apps[mod_id], so, flexran_agent_app_internal_s, entries); + free(so->name); + if (so->dl_handle) + dlclose(so->dl_handle); + free(so); + so = NULL; + return 0; +} + +int flexran_agent_reconfig_app( + mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem *sub) +{ + flexran_agent_app_internal_t *so = NULL; + SLIST_FOREACH(so, &flexran_apps[mod_id], entries) { + if (strcmp(so->name, sub->name) == 0) { + LOG_I(FLEXRAN_AGENT, "application %s is running\n", sub->name); + break; + } + } + if (!so) { + LOG_E(FLEXRAN_AGENT, "no such application %s found\n", sub->name); + return -1; + } + flexran_agent_app_t *app = so->app; + LOG_I(FLEXRAN_AGENT, "reconfiguring app %s\n", so->name); + app->reconfig(mod_id, sub->params, sub->n_params); + return 0; +} + +int flexran_agent_custom_command_app( + mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem *sub) +{ + flexran_agent_app_internal_t *so = NULL; + SLIST_FOREACH(so, &flexran_apps[mod_id], entries) { + if (strcmp(so->name, sub->name) == 0) { + LOG_I(FLEXRAN_AGENT, "application %s is running\n", sub->name); + break; + } + } + if (!so) { + LOG_E(FLEXRAN_AGENT, "no such application %s found\n", sub->name); + return -1; + } + void (*cmd)(mid_t, Protocol__FlexAgentReconfigurationSubsystem *) = dlsym(so->dl_handle, sub->behavior); + if (!cmd) { + LOG_E(FLEXRAN_AGENT, + "cannot locate command %s inside of app %s\n", + sub->behavior, + so->name); + return -1; + } + + LOG_I(FLEXRAN_AGENT, + "calling command %s inside of app %s\n", + sub->behavior, + so->name); + (*cmd)(mod_id, sub); + return 0; +} + +void flexran_agent_handle_apps( + mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem **subs, + int n_subs) { + for (int i = 0; i < n_subs; ++i) { + Protocol__FlexAgentReconfigurationSubsystem *s = subs[i]; + if (strcmp(s->behavior, "start") == 0) { + int rc = flexran_agent_start_app(mod_id, s); + if (rc == 0) + LOG_I(FLEXRAN_AGENT, "application \"%s\" started\n", s->name); + } else if (strcmp(s->behavior, "stop") == 0) { + int rc = flexran_agent_stop_app(mod_id, s->name); + if (rc == 0) + LOG_I(FLEXRAN_AGENT, "application \"%s\" stopped\n", s->name); + } else if (strcmp(s->behavior, "reconfig") == 0) { + flexran_agent_reconfig_app(mod_id, s); + } else { + flexran_agent_custom_command_app(mod_id, s); + } + } +} + +void flexran_agent_fill_loaded_apps(mid_t mod_id, + Protocol__FlexEnbConfigReply *reply) { + int n = 0; + flexran_agent_app_internal_t *so = NULL; + SLIST_FOREACH(so, &flexran_apps[mod_id], entries) + ++n; + reply->n_loadedapps = n; + reply->loadedapps = calloc(n, sizeof(char *)); + if (!reply->loadedapps) { + reply->n_loadedapps = 0; + return; + } + n = 0; + SLIST_FOREACH(so, &flexran_apps[mod_id], entries) + reply->loadedapps[n++] = so->name; +} diff --git a/openair2/ENB_APP/flexran_agent_app.h b/openair2/ENB_APP/flexran_agent_app.h new file mode 100644 index 0000000000..4723b344bf --- /dev/null +++ b/openair2/ENB_APP/flexran_agent_app.h @@ -0,0 +1,74 @@ +/* + * 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 + */ + +/*! \file flexran_agent_app.h + * \brief Common app definitions + * \author Robert Schmidt + * \date 2020 + * \version 0.1 + */ + +#ifndef BURST_ANALYSIS_LL_H_ +#define BURST_ANALYSIS_LL_H_ + +#include "flexran_agent_common.h" +#include "flexran_agent_async.h" +#include "flexran_agent_extern.h" +#include "flexran_agent_timer.h" +#include "flexran_agent_defs.h" +#include "flexran_agent_net_comm.h" +#include "flexran_agent_ran_api.h" +#include "flexran_agent_phy.h" +#include "flexran_agent_mac.h" +#include "flexran_agent_rrc.h" +#include "flexran_agent_pdcp.h" +#include "flexran_agent_s1ap.h" +#include "common/utils/LOG/log.h" +#include "assertions.h" + +/* App type: to be implemented by shared libraries */ +typedef struct { + int (*start)(mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, + int n_p); + int (*reconfig)(mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, + int n_p); + int (* stop)(mid_t mod_id); +} flexran_agent_app_t; + +/* FlexRAN agent app handling init */ +void flexran_agent_app_init(mid_t mod_id); + +/* Start an app from within the agent */ +int flexran_agent_start_app_direct(mid_t mod_id, flexran_agent_app_t *app, char *name); + +/* FlexRAN agent handler to setup/teardown apps */ +void flexran_agent_handle_apps( + mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem **subs, + int n_subs); + +/* Fills the enb_config_reply with the currently loaded (started) apps */ +void flexran_agent_fill_loaded_apps(mid_t mod_id, + Protocol__FlexEnbConfigReply *reply); + +#endif diff --git a/openair2/ENB_APP/flexran_agent_common.c b/openair2/ENB_APP/flexran_agent_common.c index 7569ca1ac1..abb5b57e0c 100644 --- a/openair2/ENB_APP/flexran_agent_common.c +++ b/openair2/ENB_APP/flexran_agent_common.c @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include "flexran_agent_common.h" #include "flexran_agent_common_internal.h" @@ -39,6 +42,7 @@ #include "flexran_agent_mac.h" #include "flexran_agent_rrc.h" #include "flexran_agent_s1ap.h" +#include "flexran_agent_app.h" //#include "PHY/extern.h" #include "common/utils/LOG/log.h" #include "flexran_agent_mac_internal.h" @@ -326,6 +330,12 @@ int flexran_agent_destroy_enb_config_reply(Protocol__FlexranMessage *msg) { if (reply->s1ap) flexran_agent_free_s1ap_cell_config(&reply->s1ap); + if (reply->loadedapps) + free(reply->loadedapps); + + if (reply->loadedmacobjects) + free(reply->loadedmacobjects); + free(reply->cell_config); free(reply); free(msg); @@ -424,26 +434,65 @@ long timer_end(struct timespec start_time) { int flexran_agent_control_delegation(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) { Protocol__FlexranMessage *input = (Protocol__FlexranMessage *)params; Protocol__FlexControlDelegation *control_delegation_msg = input->control_delegation_msg; - // struct timespec vartime = timer_start(); - //Write the payload lib into a file in the cache and load the lib - char lib_name[120]; - char target[512]; - snprintf(lib_name, sizeof(lib_name), "/%s.so", control_delegation_msg->name); - strcpy(target, RC.flexran[mod_id]->cache_name); - strcat(target, lib_name); - FILE *f; - f = fopen(target, "wb"); + *msg = NULL; - if (f) { - fwrite(control_delegation_msg->payload.data, control_delegation_msg->payload.len, 1, f); - fclose(f); + char target[512]; + int len = snprintf(target, sizeof(target), "%s/libflex.%s.so", + RC.flexran[mod_id]->cache_name, + control_delegation_msg->name); + if (len >= sizeof(target)) { + LOG_E(FLEXRAN_AGENT, "target has been truncated, cannot write file name\n"); + return 0; + } + + if (control_delegation_msg->has_payload) { + /* use low-level API: check whether exists while creating so we can abort if + * it exists to not overwrite anything */ + int fd = open(target, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); + if (fd >= 0) { + ssize_t l = write(fd, + control_delegation_msg->payload.data, + control_delegation_msg->payload.len); + close(fd); + if (l < control_delegation_msg->payload.len) { + LOG_E(FLEXRAN_AGENT, + "could not write complete control delegation to %s: only %ld out of " + "%ld bytes\n", + target, + l, + control_delegation_msg->payload.len); + return 0; + } else if (l < 0) { + LOG_E(FLEXRAN_AGENT, "can not write control delegation data to %s: %s\n", + target, strerror(errno)); + return 0; + } + LOG_I(FLEXRAN_AGENT, "wrote shared object %s\n", target); + } else { + if (errno == EEXIST) { + LOG_W(FLEXRAN_AGENT, "file %s already exists, remove it first\n", target); + } else { + LOG_E(FLEXRAN_AGENT, "can not write control delegation data to %s: %s\n", + target, strerror(errno)); + } + return 0; + } } else { - LOG_W(FLEXRAN_AGENT, "[%d] can not write control delegation data to %s\n", - mod_id, target); + LOG_W(FLEXRAN_AGENT, "remove file %s\n", target); + int rc = remove(target); + if (rc < 0) + LOG_E(FLEXRAN_AGENT, "cannot remove file %s: %s\n", target, strerror(errno)); + } + + if (control_delegation_msg->has_delegation_type + && control_delegation_msg->delegation_type == PROTOCOL__FLEX_CONTROL_DELEGATION_TYPE__FLCDT_MAC_DL_UE_SCHEDULER + && control_delegation_msg->header + && control_delegation_msg->header->has_xid) { + /* Inform the MAC subsystem that a control delegation for it has arrived */ + /* TODO this should be triggered by an agent reconfiguration? */ + flexran_agent_mac_inform_delegation(mod_id, control_delegation_msg); } - // long time_elapsed_nanos = timer_end(vartime); - *msg = NULL; return 0; } @@ -452,10 +501,44 @@ int flexran_agent_destroy_control_delegation(Protocol__FlexranMessage *msg) { return 0; } +int flexran_agent_map_name_to_delegated_object(mid_t mod_id, const char *name, + char *path, int maxlen) { + int len = snprintf(path, maxlen, "%s/libflex.%s.so", + RC.flexran[mod_id]->cache_name, name); + if (len >= maxlen) { + LOG_E(FLEXRAN_AGENT, "path has been truncated, cannot read object\n"); + return -1; + } + + struct stat buf; + int status = stat(path, &buf); + if (status < 0) { + LOG_E(FLEXRAN_AGENT, "Could not stat object %s: %s\n", path, strerror(errno)); + return -1; + } + return 0; +} + int flexran_agent_reconfiguration(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) { Protocol__FlexranMessage *input = (Protocol__FlexranMessage *)params; Protocol__FlexAgentReconfiguration *agent_reconfiguration_msg = input->agent_reconfiguration_msg; - apply_reconfiguration_policy(mod_id, agent_reconfiguration_msg->policy, strlen(agent_reconfiguration_msg->policy)); + if (agent_reconfiguration_msg->policy) { + /* for compatibility: call old YAML configuration code, although we don't + * use it anymore */ + apply_reconfiguration_policy(mod_id, + agent_reconfiguration_msg->policy, + strlen(agent_reconfiguration_msg->policy)); + } + for (int i = 0; i < agent_reconfiguration_msg->n_systems; ++i) { + const Protocol__FlexAgentReconfigurationSystem *sys = agent_reconfiguration_msg->systems[i]; + if (strcmp(sys->system, "app") == 0) { + flexran_agent_handle_apps(mod_id, sys->subsystems, sys->n_subsystems); + } else { + LOG_E(FLEXRAN_AGENT, + "unknown system name %s in flex_agent_reconfiguration message\n", + sys->system); + } + } *msg = NULL; return 0; } @@ -465,6 +548,15 @@ int flexran_agent_destroy_agent_reconfiguration(Protocol__FlexranMessage *msg) { return 0; } +int flexran_agent_destroy_control_delegation_request(Protocol__FlexranMessage *msg) { + if(msg->msg_case != PROTOCOL__FLEXRAN_MESSAGE__MSG_CONTROL_DEL_REQ_MSG) + return -1; + + free(msg->control_del_req_msg->header); + free(msg->control_del_req_msg->name); + free(msg); + return 0; +} int flexran_agent_lc_config_reply(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) { xid_t xid; @@ -750,6 +842,10 @@ int flexran_agent_enb_config_reply(mid_t mod_id, const void *params, Protocol__F if (flexran_agent_get_s1ap_xface(mod_id)) flexran_agent_fill_s1ap_cell_config(mod_id, &enb_config_reply_msg->s1ap); + flexran_agent_fill_loaded_apps(mod_id, enb_config_reply_msg); + + flexran_agent_mac_fill_loaded_mac_objects(mod_id, enb_config_reply_msg); + *msg = malloc(sizeof(Protocol__FlexranMessage)); if(*msg == NULL) { @@ -880,7 +976,9 @@ int flexran_agent_handle_enb_config_reply(mid_t mod_id, const void *params, Prot if (enb_config->n_cell_config > 0) { if (flexran_agent_get_mac_xface(mod_id) && enb_config->cell_config[0]->slice_config) { - prepare_update_slice_config(mod_id, &enb_config->cell_config[0]->slice_config); + prepare_update_slice_config(mod_id, + &enb_config->cell_config[0]->slice_config, + 1 /* request objects if necessary */); } if (enb_config->cell_config[0]->has_eutra_band && enb_config->cell_config[0]->has_dl_freq diff --git a/openair2/ENB_APP/flexran_agent_common.h b/openair2/ENB_APP/flexran_agent_common.h index fe7b49107c..ae690c7124 100644 --- a/openair2/ENB_APP/flexran_agent_common.h +++ b/openair2/ENB_APP/flexran_agent_common.h @@ -131,11 +131,20 @@ int flexran_agent_destroy_lc_config_request(Protocol__FlexranMessage *msg); /* Control delegation message constructor and destructor */ int flexran_agent_control_delegation(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg); int flexran_agent_destroy_control_delegation(Protocol__FlexranMessage *msg); +/* Map a name from a control_delegation message to a file name. The function + * stat()s the file to check existence and returns the complete path name in + * path. The length in path needs to be maxlen, the maximum length the path may + * have. */ +int flexran_agent_map_name_to_delegated_object(mid_t mod_id, const char *name, + char *path, int maxlen); /* Policy reconfiguration message constructor and destructor */ int flexran_agent_reconfiguration(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg); int flexran_agent_destroy_agent_reconfiguration(Protocol__FlexranMessage *msg); +/* Policy reconfiguration request message destructor */ +int flexran_agent_destroy_control_delegation_request(Protocol__FlexranMessage *msg); + /* rrc triggering measurement message constructor and destructor */ int flexran_agent_rrc_reconfiguration(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg); int flexran_agent_destroy_rrc_reconfiguration(Protocol__FlexranMessage *msg); diff --git a/openair2/ENB_APP/flexran_agent_handler.c b/openair2/ENB_APP/flexran_agent_handler.c index d991078cf7..addcd6a142 100644 --- a/openair2/ENB_APP/flexran_agent_handler.c +++ b/openair2/ENB_APP/flexran_agent_handler.c @@ -32,6 +32,7 @@ #include "flexran_agent_rrc.h" #include "flexran_agent_pdcp.h" #include "flexran_agent_s1ap.h" +#include "flexran_agent_app.h" #include "flexran_agent_timer.h" #include "flexran_agent_ran_api.h" #include "common/utils/LOG/log.h" @@ -80,6 +81,11 @@ flexran_agent_message_destruction_callback message_destruction_callback[] = { flexran_agent_destroy_ue_state_change, flexran_agent_destroy_control_delegation, flexran_agent_destroy_agent_reconfiguration, + NULL, /* flex_rrc_triggering */ + NULL, /* flex_ul_mac_config */ + NULL, /* flex_disconnect */ + NULL, /* flex_ho_command */ + flexran_agent_destroy_control_delegation_request, }; /* static const char *flexran_agent_direction2String[] = { */ diff --git a/openair2/ENB_APP/flexran_agent_ran_api.c b/openair2/ENB_APP/flexran_agent_ran_api.c index 79c992a4d7..d126012737 100644 --- a/openair2/ENB_APP/flexran_agent_ran_api.c +++ b/openair2/ENB_APP/flexran_agent_ran_api.c @@ -3092,12 +3092,13 @@ int flexran_get_ue_dl_slice_id(mid_t mod_id, mid_t ue_id) { return slices->s[idx]->id; } -void flexran_set_ue_dl_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id) { - if (!mac_is_present(mod_id)) return; +int flexran_set_ue_dl_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id) { + if (!mac_is_present(mod_id)) return 0; int idx = flexran_find_dl_slice(mod_id, slice_id); - if (idx < 0) return; + if (idx < 0) return 0; pp_impl_param_t *dl = &RC.mac[mod_id]->pre_processor_dl; dl->move_UE(dl->slices, ue_id, idx); + return 1; } int flexran_get_ue_ul_slice_id(mid_t mod_id, mid_t ue_id) { @@ -3108,15 +3109,16 @@ int flexran_get_ue_ul_slice_id(mid_t mod_id, mid_t ue_id) { return slices->s[idx]->id; } -void flexran_set_ue_ul_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id) { - if (!mac_is_present(mod_id)) return; +int flexran_set_ue_ul_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id) { + if (!mac_is_present(mod_id)) return 0; int idx = flexran_find_ul_slice(mod_id, slice_id); - if (idx < 0) return; + if (idx < 0) return 0; pp_impl_param_t *ul = &RC.mac[mod_id]->pre_processor_ul; ul->move_UE(ul->slices, ue_id, idx); + return 1; } -int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s) { +int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s, void *object) { if (!mac_is_present(mod_id)) return 0; void *params = NULL; switch (s->params_case) { @@ -3133,7 +3135,7 @@ int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s) { char *l = s->label ? strdup(s->label) : NULL; void *algo = &dl->dl_algo; // default scheduler if (s->scheduler) { - algo = dlsym(NULL, s->scheduler); + algo = dlsym(object, s->scheduler); if (!algo) { free(params); LOG_E(FLEXRAN_AGENT, "cannot locate scheduler '%s'\n", s->scheduler); @@ -3193,7 +3195,7 @@ int flexran_get_num_dl_slices(mid_t mod_id) { return RC.mac[mod_id]->pre_processor_dl.slices->num; } -int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s) { +int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s, void *object) { if (!mac_is_present(mod_id)) return -1; void *params = NULL; switch (s->params_case) { @@ -3210,7 +3212,7 @@ int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s) { char *l = s->label ? strdup(s->label) : NULL; void *algo = &ul->ul_algo; // default scheduler if (s->scheduler) { - algo = dlsym(NULL, s->scheduler); + algo = dlsym(object, s->scheduler); if (!algo) { free(params); LOG_E(FLEXRAN_AGENT, "cannot locate scheduler '%s'\n", s->scheduler); @@ -3276,9 +3278,9 @@ char *flexran_get_dl_scheduler_name(mid_t mod_id) { return RC.mac[mod_id]->pre_processor_dl.dl_algo.name; } -int flexran_set_dl_scheduler(mid_t mod_id, char *sched) { +int flexran_set_dl_scheduler(mid_t mod_id, char *sched, void *object) { if (!mac_is_present(mod_id)) return -1; - void *d = dlsym(NULL, sched); + void *d = dlsym(object, sched); if (!d) return -2; pp_impl_param_t *dl_pp = &RC.mac[mod_id]->pre_processor_dl; dl_pp->dl_algo.unset(&dl_pp->dl_algo.data); @@ -3292,9 +3294,9 @@ char *flexran_get_ul_scheduler_name(mid_t mod_id) { return RC.mac[mod_id]->pre_processor_ul.ul_algo.name; } -int flexran_set_ul_scheduler(mid_t mod_id, char *sched) { +int flexran_set_ul_scheduler(mid_t mod_id, char *sched, void *object) { if (!mac_is_present(mod_id)) return -1; - void *d = dlsym(NULL, sched); + void *d = dlsym(object, sched); if (!d) return -2; pp_impl_param_t *ul_pp = &RC.mac[mod_id]->pre_processor_ul; ul_pp->ul_algo.unset(&ul_pp->ul_algo.data); diff --git a/openair2/ENB_APP/flexran_agent_ran_api.h b/openair2/ENB_APP/flexran_agent_ran_api.h index 2d8a86f7d0..79115b0a16 100644 --- a/openair2/ENB_APP/flexran_agent_ran_api.h +++ b/openair2/ENB_APP/flexran_agent_ran_api.h @@ -669,15 +669,16 @@ int flexran_set_ul_slice_algo(mid_t mod_id, Protocol__FlexSliceAlgorithm algo); /* Get the DL slice ID for a UE */ int flexran_get_ue_dl_slice_id(mid_t mod_id, mid_t ue_id); /* Set the DL slice for a UE */ -void flexran_set_ue_dl_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id); +int flexran_set_ue_dl_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id); /* Get the UL slice ID for a UE */ int flexran_get_ue_ul_slice_id(mid_t mod_id, mid_t ue_id); /* Set the UL slice for a UE */ -void flexran_set_ue_ul_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id); +int flexran_set_ue_ul_slice_id(mid_t mod_id, mid_t ue_id, slice_id_t slice_id); -/* Create slice in DL, returns the new slice index */ -int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s); +/* Create slice in DL, returns the new slice index, object can be shared + * library for the scheduler or NULL */ +int flexran_create_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s, void *object); /* Remove slice in DL, returns new number of slices or -1 on error */ int flexran_remove_dl_slice(mid_t mod_id, const Protocol__FlexSlice *s); @@ -691,8 +692,9 @@ void flexran_get_dl_slice(mid_t mod_id, /* Get the number of slices in DL */ int flexran_get_num_dl_slices(mid_t mod_id); -/* Create slice in UL, returns the new slice index */ -int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s); +/* Create slice in UL, returns the new slice index, object can be shared library + * for the scheduler or NULL */ +int flexran_create_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s, void *object); /* Remove slice in UL */ int flexran_remove_ul_slice(mid_t mod_id, const Protocol__FlexSlice *s); @@ -708,14 +710,14 @@ int flexran_get_num_ul_slices(mid_t mod_id); /* Get the name of/Set the DL scheduling algorithm. If slicing is active, this * corresponds to the default algorithm for slices, otherwise the currently - * used one. */ + * used one. object is a shared object in which to find the scheduler */ char *flexran_get_dl_scheduler_name(mid_t mod_id); -int flexran_set_dl_scheduler(mid_t mod_id, char *sched); +int flexran_set_dl_scheduler(mid_t mod_id, char *sched, void *object); /* Get the name of/Set the UL scheduler algorithm. Same applies as for the DL - * case */ + * case. object is a shared object in which to find the scheduler */ char *flexran_get_ul_scheduler_name(mid_t mod_id); -int flexran_set_ul_scheduler(mid_t mod_id, char *sched); +int flexran_set_ul_scheduler(mid_t mod_id, char *sched, void *object); /************************** S1AP **************************/ /* Get the number of MMEs to be connected */ diff --git a/openair2/ENB_APP/flexran_agent_timer.c b/openair2/ENB_APP/flexran_agent_timer.c index ffbac39983..3a613ca8e5 100644 --- a/openair2/ENB_APP/flexran_agent_timer.c +++ b/openair2/ENB_APP/flexran_agent_timer.c @@ -58,6 +58,11 @@ struct timesync { int timer_num; flexran_agent_timer_element_t *timer[MAX_NUM_TIMERS]; + + int add_num; + flexran_agent_timer_element_t *add_list[MAX_NUM_TIMERS]; + int remove_num; + int remove_list[MAX_NUM_TIMERS]; pthread_mutex_t mutex_timer; int exit; @@ -76,8 +81,12 @@ err_code_t flexran_agent_timer_init(mid_t mod_id) { sync->current = 0; sync->next = 0; sync->timer_num = 0; - for (int i = 0; i < MAX_NUM_TIMERS; ++i) + sync->add_num = 0; + sync->remove_num = 0; + for (int i = 0; i < MAX_NUM_TIMERS; ++i) { sync->timer[i] = NULL; + sync->add_list[i] = NULL; + } pthread_mutex_init(&sync->mutex_timer, NULL); sync->exit = 0; @@ -170,12 +179,19 @@ void *flexran_agent_timer_thread(void *args) { break; pthread_mutex_lock(&sync->mutex_timer); - sync->current++; - - if (sync->current < sync->next) { - pthread_mutex_unlock(&sync->mutex_timer); - continue; + for (int i = 0; i < sync->add_num; ++i) { + sync->timer[sync->timer_num] = sync->add_list[i]; + sync->timer_num++; } + sync->add_num = 0; + for (int i = 0; i < sync->remove_num; ++i) + flexran_agent_timer_remove_internal(sync, sync->remove_list[i]); + sync->remove_num = 0; + pthread_mutex_unlock(&sync->mutex_timer); + + sync->current++; + if (sync->current < sync->next) + continue; for (int i = 0; i < sync->timer_num; ++i) { flexran_agent_timer_element_t *t = sync->timer[i]; @@ -186,7 +202,6 @@ void *flexran_agent_timer_thread(void *args) { if (sync->next == sync->current || t->next < sync->next) sync->next = t->next; } - pthread_mutex_unlock(&sync->mutex_timer); } LOG_W(FLEXRAN_AGENT, "terminated timer thread\n"); return NULL; @@ -230,45 +245,35 @@ err_code_t flexran_agent_create_timer(mid_t mod_id, struct timesync *sync = ×ync[mod_id]; pthread_mutex_lock(&sync->mutex_timer); - if (sync->timer_num >= MAX_NUM_TIMERS) { + if (sync->timer_num + sync->add_num >= MAX_NUM_TIMERS) { pthread_mutex_unlock(&sync->mutex_timer); LOG_E(FLEXRAN_AGENT, "maximum number of timers (%d) reached while adding timer %d\n", - sync->timer_num, xid); + sync->timer_num + sync->add_num, xid); free(t); return TIMER_SETUP_FAILED; } /* TODO check that xid does not exist? */ - t->next = sync->current + 1; + t->next = sync->current + sf; if (sync->next <= sync->current || t->next < sync->next) sync->next = t->next; - sync->timer[sync->timer_num] = t; - sync->timer_num++; - pthread_mutex_unlock(&sync->mutex_timer); - LOG_D(FLEXRAN_AGENT, "added new timer xid %d for agent %d\n", xid, mod_id); - return 0; -} - -err_code_t flexran_agent_destroy_timers(mid_t mod_id) { - struct timesync *sync = ×ync[mod_id]; - pthread_mutex_lock(&sync->mutex_timer); - for (int i = sync->timer_num - 1; i < 0; --i) { - flexran_agent_timer_remove_internal(sync, i); - } + sync->add_list[sync->add_num] = t; + sync->add_num++; pthread_mutex_unlock(&sync->mutex_timer); + LOG_I(FLEXRAN_AGENT, "added new timer xid %d for agent %d\n", xid, mod_id); return 0; } err_code_t flexran_agent_destroy_timer(mid_t mod_id, xid_t xid) { struct timesync *sync = ×ync[mod_id]; - pthread_mutex_lock(&sync->mutex_timer); for (int i = 0; i < sync->timer_num; ++i) { if (sync->timer[i]->xid == xid) { - flexran_agent_timer_remove_internal(sync, i); + pthread_mutex_lock(&sync->mutex_timer); + sync->remove_list[sync->remove_num] = i; + sync->remove_num++; pthread_mutex_unlock(&sync->mutex_timer); return 0; } } - pthread_mutex_unlock(&sync->mutex_timer); LOG_E(FLEXRAN_AGENT, "could not find timer %d\n", xid); return TIMER_ELEMENT_NOT_FOUND; } @@ -277,7 +282,8 @@ err_code_t flexran_agent_destroy_timer(mid_t mod_id, xid_t xid) { void flexran_agent_timer_remove_internal(struct timesync *sync, int index) { LOG_I(FLEXRAN_AGENT, "remove timer xid %d (index %d) for agent %d\n", sync->timer[index]->xid, index, sync->timer[index]->mod_id); - flexran_agent_destroy_flexran_message(sync->timer[index]->msg); + if (sync->timer[index]->msg) + flexran_agent_destroy_flexran_message(sync->timer[index]->msg); free(sync->timer[index]); for (int i = index + 1; i < sync->timer_num; ++i) sync->timer[i - 1] = sync->timer[i]; diff --git a/openair2/ENB_APP/flexran_agent_timer.h b/openair2/ENB_APP/flexran_agent_timer.h index b1cf8df51f..c61f2543a3 100644 --- a/openair2/ENB_APP/flexran_agent_timer.h +++ b/openair2/ENB_APP/flexran_agent_timer.h @@ -61,9 +61,6 @@ err_code_t flexran_agent_create_timer(mid_t mod_id, flexran_agent_timer_callback_t cb, Protocol__FlexranMessage *msg); -/* Destroy all existing timers */ -err_code_t flexran_agent_destroy_timers(mid_t mod_id); - /* Destroy the timer for task with id xid */ err_code_t flexran_agent_destroy_timer(mid_t mod_id, xid_t xid); diff --git a/openair2/ENB_APP/flexran_apps/imsi.c b/openair2/ENB_APP/flexran_apps/imsi.c new file mode 100644 index 0000000000..6f75033f38 --- /dev/null +++ b/openair2/ENB_APP/flexran_apps/imsi.c @@ -0,0 +1,245 @@ +/* + * 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 + */ + +/*! \file imsi.c + * \brief flexran IMSI UE-slice association app + * \author Robert Schmidt, Firas Abdeljelil + * \date 2020 + * \version 0.1 + */ + +#include +#include +#include +#include "flexran_agent_common.h" +#include "flexran_agent_async.h" +#include "flexran_agent_extern.h" +#include "flexran_agent_timer.h" +#include "flexran_agent_defs.h" +#include "flexran_agent_net_comm.h" +#include "flexran_agent_ran_api.h" +#include "flexran_agent_phy.h" +#include "flexran_agent_mac.h" +#include "flexran_agent_rrc.h" +#include "flexran_agent_pdcp.h" +#include "flexran_agent_s1ap.h" +#include "flexran_agent_app.h" +#include "common/utils/LOG/log.h" +#include "assertions.h" +#include "queue.h" +typedef struct Map_regex_slice slist_data_t; +struct Map_regex_slice { + char regex_[30] ; + int slice_dl; + int slice_ul; + regex_t regex; + SLIST_ENTRY (Map_regex_slice) entries; +}; +bool reconfig = false; +int ue[MAX_MOBILES_PER_ENB]={ [0 ... MAX_MOBILES_PER_ENB - 1] = -1 }; + +SLIST_HEAD(slisthead, Map_regex_slice) head; + +int dump_list(void) { + int len = 1; + slist_data_t *datap = NULL; + SLIST_FOREACH(datap, &head, entries) { + LOG_W(FLEXRAN_AGENT, "%d: regex %s DL %d UL %d\n", len++, datap->regex_, + datap->slice_dl, datap->slice_ul); + } + return len; +} + +void add(char *regex, int dl, int ul) { + slist_data_t *datap = malloc(sizeof(slist_data_t)); + if (!datap) { + LOG_E(FLEXRAN_AGENT, "cannot allocate memory for slist_data_t\n"); + return; + } + strcpy(datap->regex_, regex); + datap->slice_dl = dl; + datap->slice_ul = ul; + regcomp(&datap->regex, datap->regex_, 0); + SLIST_INSERT_HEAD(&head, datap, entries); + if (ul==-1) + LOG_I(FLEXRAN_AGENT, "added new element to list: regex %s slice DL %d\n" ,regex, dl); + else if (dl==-1) + LOG_I(FLEXRAN_AGENT, "added new element to list: regex %s slice UL %d\n" ,regex, ul); + else + LOG_I(FLEXRAN_AGENT, "added new element to list: regex %s slice DL %d UL %d\n", + regex, dl, ul); +} +void remove_(char *regex) { + slist_data_t *datap = NULL; + SLIST_FOREACH(datap, &head, entries) { + if (strcmp(datap->regex_, regex) == 0) { + SLIST_REMOVE(&head, datap, Map_regex_slice, entries); + if (datap->slice_dl==-1) + LOG_I(FLEXRAN_AGENT, "removed element from list: regex %s slice UL %d\n",datap->regex_, datap->slice_ul); + else if (datap->slice_ul==-1) + LOG_I(FLEXRAN_AGENT, "removed element from list: regex %s slice DL %d\n",datap->regex_, datap->slice_dl); + else + LOG_I(FLEXRAN_AGENT, "removed element from list: regex %s slice DL %d UL %d\n", + datap->regex_, datap->slice_dl, datap->slice_ul); + regfree(&datap->regex); + free(datap); + return; + } + } + LOG_E(FLEXRAN_AGENT, "no regex %s found in list\n", regex); +} + +Protocol__FlexranMessage *matching_tick( + mid_t mod_id, + const Protocol__FlexranMessage *msg) { + const int num = flexran_get_mac_num_ues(mod_id); + for (int i = 0; i < num; i++) { + int ue_id = flexran_get_mac_ue_id(mod_id, i); + if (ue[ue_id] >= 0 && !reconfig) + continue; + rnti_t rnti = flexran_get_mac_ue_crnti(mod_id, ue_id); + uint64_t imsi = flexran_get_ue_imsi(mod_id, rnti); + char snum[20]; + sprintf(snum, "%" PRIu64, imsi); + slist_data_t *datap = NULL; + SLIST_FOREACH(datap, &head, entries) { + if (regexec(&datap->regex, snum, 0, NULL, 0) == 0) { + + int dl = datap->slice_dl; + if (dl >= 0 && dl != flexran_get_ue_dl_slice_id(mod_id, ue_id)) { + int ra = flexran_set_ue_dl_slice_id(mod_id, ue_id, dl); + if (ra) + LOG_I(FLEXRAN_AGENT, + "RNTI %04x/IMSI %s in the slice_dl %d\n", + rnti, + snum, + dl); + else + LOG_W(FLEXRAN_AGENT, "No such DL slice %d\n", dl); + } + + int ul = datap->slice_ul; + if (ul >= 0 && ul != flexran_get_ue_ul_slice_id(mod_id, ue_id)) { + int rb = flexran_set_ue_ul_slice_id(mod_id, ue_id, ul); + if (rb) + LOG_I(FLEXRAN_AGENT, + "RNTI %04x/IMSI %s in the slice_ul %d\n", + rnti, + snum, + ul); + else + LOG_W(FLEXRAN_AGENT, "No such DL slice %d\n", dl); + } + } + } + ue[ue_id] = ue_id; + } + reconfig = false; + return NULL; +} +void add_param(Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, int n_p) { + char s[30]; + reconfig = true; + bool supp=false; + bool sl_E=false; + int dl=-1; + int ul=-1; + slist_data_t *datap = NULL; + for (int i = 0; i < n_p; ++i) { + switch (p[i]->value->param_case) { + case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM__NOT_SET: + LOG_I(FLEXRAN_AGENT, " param %s is not set\n", p[i]->key); + break; + case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_INTEGER: + if( strcmp(p[i]->key, "slice_dl")==0) + dl= p[i]->value->integer; + else + ul= p[i]->value->integer; + sl_E=true; + break; + case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_STR: + strcpy(s,p[i]->value->str); + break; + default: + LOG_W(FLEXRAN_AGENT, " type of param %s is unknown\n", p[i]->key); + break; + }; + } + if (!SLIST_EMPTY(&head)){ + SLIST_FOREACH(datap, &head, entries) { + if (strcmp(datap->regex_, s) == 0) { + if (datap->slice_dl!=dl || datap->slice_ul!=ul) + supp=true; + } + } + } + if (sl_E==false) + remove_(s); + else { + if (supp==true) + remove_(s); + add(s,dl,ul); + } + /* re-verify all UEs again */ + for (int i = 0; i < MAX_MOBILES_PER_ENB; ++i) + ue[i] = -1; +} +/* imsi_start: entry point for imsi app. Installs a callback using the timer + * API to have function imsi_tick() called every 5s */ +#define TIMER 32 +int imsi_start(mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, + int n_p) +{ + LOG_W(FLEXRAN_AGENT, "%s(): enable IMSI app timer\n", __func__); + flexran_agent_create_timer(mod_id, + 500, + FLEXRAN_AGENT_TIMER_TYPE_PERIODIC, + TIMER, + matching_tick, + NULL); + SLIST_INIT(&head); + return 0; +} + +int imsi_reconfig(mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, + int n_p) +{ + add_param(p, n_p); + return 0; +} + +/* stop function: cleans up before app can be unloaded. Here: stop the timer */ +int imsi_stop(mid_t mod_id) { + LOG_W(FLEXRAN_AGENT, "%s(): disable IMSI app timer\n", __func__); + flexran_agent_destroy_timer(mod_id, TIMER); + + return 0; +} + +/* app definition. visibility should be set to default so this symbol is + * reachable from outside */ +flexran_agent_app_t __attribute__ ((visibility ("default"))) app = { + .start = imsi_start, + .reconfig = imsi_reconfig, + .stop = imsi_stop +}; diff --git a/openair2/ENB_APP/flexran_apps/sample.c b/openair2/ENB_APP/flexran_apps/sample.c new file mode 100644 index 0000000000..65be2a43df --- /dev/null +++ b/openair2/ENB_APP/flexran_apps/sample.c @@ -0,0 +1,121 @@ +/* + * 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 + */ + +/*! \file sample.c + * \brief flexran sample app + * \author Robert Schmidt + * \date 2020 + * \version 0.1 + */ + +#include "flexran_agent_common.h" +#include "flexran_agent_async.h" +#include "flexran_agent_extern.h" +#include "flexran_agent_timer.h" +#include "flexran_agent_defs.h" +#include "flexran_agent_net_comm.h" +#include "flexran_agent_ran_api.h" +#include "flexran_agent_phy.h" +#include "flexran_agent_mac.h" +#include "flexran_agent_rrc.h" +#include "flexran_agent_pdcp.h" +#include "flexran_agent_s1ap.h" +#include "flexran_agent_app.h" +#include "common/utils/LOG/log.h" +#include "assertions.h" + +/* callback to be called for event: shows exemplary variable use and use of RAN + * API */ +int tick; +Protocol__FlexranMessage *sample_tick( + mid_t mod_id, + const Protocol__FlexranMessage *msg) { + tick++; + const int num = flexran_get_mac_num_ues(mod_id); + LOG_I(FLEXRAN_AGENT, "%s(): tick %d number UEs %d\n", __func__, tick, num); + return NULL; +} + +void print_param(Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, int n_p) { + LOG_I(FLEXRAN_AGENT, "%s(): received %d parameters\n", __func__, n_p); + for (int i = 0; i < n_p; ++i) { + switch (p[i]->value->param_case) { + case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM__NOT_SET: + LOG_I(FLEXRAN_AGENT, " param %s is not set\n", p[i]->key); + break; + case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_INTEGER: + LOG_I(FLEXRAN_AGENT, " param %s is %d\n", p[i]->key, p[i]->value->integer); + break; + case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_FLOATING: + LOG_I(FLEXRAN_AGENT, " param %s is %f\n", p[i]->key, p[i]->value->floating); + break; + case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_BOOLEAN: + LOG_I(FLEXRAN_AGENT, " param %s is %s\n", p[i]->key, p[i]->value->boolean ? "true" : "false"); + break; + case PROTOCOL__FLEX_AGENT_RECONFIGURATION_PARAM__PARAM_STR: + LOG_I(FLEXRAN_AGENT, " param %s is %s\n", p[i]->key, p[i]->value->str); + break; + default: + LOG_W(FLEXRAN_AGENT, " type of param %s is unknown\n", p[i]->key); + break; + }; + } +} + +/* sa_start: entry point for sample app. Installs a callback using the timer + * API to have function sample_tick() called every 5s */ +#define TIMER 22 +int sa_start(mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, + int n_p) { + LOG_W(FLEXRAN_AGENT, "%s(): enable sample app timer\n", __func__); + flexran_agent_create_timer(mod_id, + 5000, + FLEXRAN_AGENT_TIMER_TYPE_PERIODIC, + TIMER, + sample_tick, + NULL); + print_param(p, n_p); + return 0; +} + +int sa_reconfig(mid_t mod_id, + Protocol__FlexAgentReconfigurationSubsystem__ParamsEntry **p, + int n_p) +{ + print_param(p, n_p); + return 0; +} + +/* stop function: cleans up before app can be unloaded. Here: stop the timer */ +int sa_stop(mid_t mod_id) { + LOG_W(FLEXRAN_AGENT, "%s(): disable sample app timer\n", __func__); + flexran_agent_destroy_timer(mod_id, TIMER); + return 0; +} + +/* app definition. visibility should be set to default so this symbol is + * reachable from outside */ +flexran_agent_app_t __attribute__ ((visibility ("default"))) app = { + .start = sa_start, + .reconfig = sa_reconfig, + .stop = sa_stop +}; diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 8c8e78bbd0..977807d150 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -153,7 +153,7 @@ void prepare_scc(NR_ServingCellConfigCommon_t *scc) { scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon = CALLOC(1,sizeof(NR_SetupRelease_RACH_ConfigCommon_t)); scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->present = NR_SetupRelease_RACH_ConfigCommon_PR_setup; scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup = CALLOC(1,sizeof(struct NR_RACH_ConfigCommon)); - // scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles = CALLOC(1,sizeof(long)); + // scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles = CALLOC(1,sizeof(long)); scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ssb_perRACH_OccasionAndCB_PreamblesPerSSB = CALLOC(1,sizeof(struct NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB)); // scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->groupBconfigured = CALLOC(1,sizeof(struct NR_RACH_ConfigCommon__groupBconfigured)); scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rsrp_ThresholdSSB = CALLOC(1,sizeof(NR_RSRP_Range_t)); diff --git a/openair2/LAYER2/MAC/main_ue.c b/openair2/LAYER2/MAC/main_ue.c index e8b0593717..2868d94930 100644 --- a/openair2/LAYER2/MAC/main_ue.c +++ b/openair2/LAYER2/MAC/main_ue.c @@ -44,7 +44,6 @@ #include "common/ran_context.h" -extern FILL_UL_INFO_MUTEX_t fill_ul_mutex; extern void openair_rrc_top_init_ue( int eMBMS_active, char *uecap_xer, uint8_t cba_group_active, uint8_t HO_active); void dl_phy_sync_success(module_id_t module_idP, frame_t frameP, unsigned char eNB_index, uint8_t first_sync) { //init as MR @@ -90,16 +89,6 @@ mac_top_init_ue(int eMBMS_active, char *uecap_xer, UE_mac_inst = NULL; } - // mutex below are used for multiple UE's L2 FAPI simulation. - if (NFAPI_MODE == NFAPI_UE_STUB_PNF) { - pthread_mutex_init(&fill_ul_mutex.rx_mutex,NULL); - pthread_mutex_init(&fill_ul_mutex.crc_mutex,NULL); - pthread_mutex_init(&fill_ul_mutex.sr_mutex,NULL); - pthread_mutex_init(&fill_ul_mutex.harq_mutex,NULL); - pthread_mutex_init(&fill_ul_mutex.cqi_mutex,NULL); - pthread_mutex_init(&fill_ul_mutex.rach_mutex,NULL); - } - LOG_I(MAC, "[MAIN] calling RRC\n"); openair_rrc_top_init_ue(eMBMS_active, uecap_xer, cba_group_active, HO_active); diff --git a/openair2/LAYER2/MAC/pre_processor.c b/openair2/LAYER2/MAC/pre_processor.c index 6477d60021..e720c1dfec 100644 --- a/openair2/LAYER2/MAC/pre_processor.c +++ b/openair2/LAYER2/MAC/pre_processor.c @@ -69,6 +69,70 @@ int get_rbg_size_last(module_id_t Mod_id, int CC_id) { return RBGsize; } +bool try_allocate_harq_retransmission(module_id_t Mod_id, + int CC_id, + int frame, + int subframe, + int UE_id, + int start_rbg, + int *n_rbg_sched, + uint8_t *rbgalloc_mask) { + const int N_RBG = to_rbg(RC.mac[Mod_id]->common_channels[CC_id].mib->message.dl_Bandwidth); + const int RBGsize = get_min_rb_unit(Mod_id, CC_id); + const int RBGlastsize = get_rbg_size_last(Mod_id, CC_id); + UE_info_t *UE_info = &RC.mac[Mod_id]->UE_info; + // check whether there are HARQ retransmissions + const COMMON_channels_t *cc = &RC.mac[Mod_id]->common_channels[CC_id]; + const uint8_t harq_pid = frame_subframe2_dl_harq_pid(cc->tdd_Config, frame, subframe); + UE_sched_ctrl_t *ue_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + // retransmission: allocate + const int nb_rb = UE_info->UE_template[CC_id][UE_id].nb_rb[harq_pid]; + if (nb_rb == 0) { + return false; + } + int nb_rbg = (nb_rb + (nb_rb % RBGsize)) / RBGsize; + // needs more RBGs than we can allocate + if (nb_rbg > *n_rbg_sched) { + LOG_D(MAC, + "retransmission of UE %d needs more RBGs (%d) than we have (%d)\n", + UE_id, nb_rbg, *n_rbg_sched); + return false; + } + // ensure that the number of RBs can be contained by the RBGs (!), i.e. + // if we allocate the last RBG this one should have the full RBGsize + if ((nb_rb % RBGsize) == 0 && nb_rbg == *n_rbg_sched + && rbgalloc_mask[N_RBG - 1] && RBGlastsize != RBGsize) { + LOG_D(MAC, + "retransmission of UE %d needs %d RBs, but the last RBG %d is too small (%d, normal %d)\n", + UE_id, nb_rb, N_RBG - 1, RBGlastsize, RBGsize); + return false; + } + const uint8_t cqi = ue_ctrl->dl_cqi[CC_id]; + const int idx = CCE_try_allocate_dlsch(Mod_id, CC_id, subframe, UE_id, cqi); + if (idx < 0) { // cannot allocate CCE + LOG_D(MAC, "cannot allocate UE %d: no CCE can be allocated\n", UE_id); + return false; + } + ue_ctrl->pre_dci_dl_pdu_idx = idx; + // retransmissions: directly allocate + *n_rbg_sched -= nb_rbg; + ue_ctrl->pre_nb_available_rbs[CC_id] += nb_rb; + for (; nb_rbg > 0; start_rbg++) { + if (!rbgalloc_mask[start_rbg]) + continue; + ue_ctrl->rballoc_sub_UE[CC_id][start_rbg] = 1; + rbgalloc_mask[start_rbg] = 0; + nb_rbg--; + } + LOG_D(MAC, + "%4d.%d n_rbg_sched %d after retransmission reservation for UE %d " + "retx nb_rb %d pre_nb_available_rbs %d\n", + frame, subframe, *n_rbg_sched, UE_id, + UE_info->UE_template[CC_id][UE_id].nb_rb[harq_pid], + ue_ctrl->pre_nb_available_rbs[CC_id]); + return true; +} + void *rr_dl_setup(void) { void *data = malloc(sizeof(int)); *(int *) data = 0; @@ -115,64 +179,25 @@ int rr_dl_run(module_id_t Mod_id, const uint8_t harq_pid = frame_subframe2_dl_harq_pid(cc->tdd_Config, frame, subframe); UE_sched_ctrl_t *ue_ctrl = &UE_info->UE_sched_ctrl[UE_id]; const uint8_t round = ue_ctrl->round[CC_id][harq_pid]; - if (round != 8) { // retransmission: allocate - const int nb_rb = UE_info->UE_template[CC_id][UE_id].nb_rb[harq_pid]; - if (nb_rb == 0) - goto skip_ue; - int nb_rbg = (nb_rb + (nb_rb % RBGsize)) / RBGsize; - // needs more RBGs than we can allocate - if (nb_rbg > n_rbg_sched) { - LOG_D(MAC, - "retransmission of UE %d needs more RBGs (%d) than we have (%d)\n", - UE_id, nb_rbg, n_rbg_sched); - goto skip_ue; + if (round != 8) { + bool r = try_allocate_harq_retransmission(Mod_id, CC_id, frame, subframe, + UE_id, rbg, &n_rbg_sched, + rbgalloc_mask); + if (r) { + /* if there are no more RBG to give, return */ + if (n_rbg_sched <= 0) + return 0; + max_num_ue--; + if (max_num_ue == 0) + return n_rbg_sched; + for (; !rbgalloc_mask[rbg]; rbg++) /* fast-forward */ ; } - // ensure that the number of RBs can be contained by the RBGs (!), i.e. - // if we allocate the last RBG this one should have the full RBGsize - if ((nb_rb % RBGsize) == 0 && nb_rbg == n_rbg_sched - && rbgalloc_mask[N_RBG - 1] && RBGlastsize != RBGsize) { - LOG_D(MAC, - "retransmission of UE %d needs %d RBs, but the last RBG %d is too small (%d, normal %d)\n", - UE_id, nb_rb, N_RBG - 1, RBGlastsize, RBGsize); - goto skip_ue; - } - const uint8_t cqi = ue_ctrl->dl_cqi[CC_id]; - const int idx = CCE_try_allocate_dlsch(Mod_id, CC_id, subframe, UE_id, cqi); - if (idx < 0) - goto skip_ue; // cannot allocate CCE - ue_ctrl->pre_dci_dl_pdu_idx = idx; - // retransmissions: directly allocate - n_rbg_sched -= nb_rbg; - ue_ctrl->pre_nb_available_rbs[CC_id] += nb_rb; - for (; nb_rbg > 0; rbg++) { - if (!rbgalloc_mask[rbg]) - continue; - ue_ctrl->rballoc_sub_UE[CC_id][rbg] = 1; - rbgalloc_mask[rbg] = 0; - nb_rbg--; - } - LOG_D(MAC, - "%4d.%d n_rbg_sched %d after retransmission reservation for UE %d " - "round %d retx nb_rb %d pre_nb_available_rbs %d\n", - frame, subframe, n_rbg_sched, UE_id, round, - UE_info->UE_template[CC_id][UE_id].nb_rb[harq_pid], - ue_ctrl->pre_nb_available_rbs[CC_id]); - /* if there are no more RBG to give, return */ - if (n_rbg_sched <= 0) - return 0; - max_num_ue--; - /* if there are no UEs that can be allocated anymore, return */ - if (max_num_ue == 0) - return n_rbg_sched; - for (; !rbgalloc_mask[rbg]; rbg++) /* fast-forward */ ; } else { if (UE_info->UE_template[CC_id][UE_id].dl_buffer_total > 0) { *cur_UE = UE_id; cur_UE = &UE_sched.next[UE_id]; } } - -skip_ue: UE_id = next_ue_list_looped(UE_list, UE_id); } while (UE_id != *start_ue); *cur_UE = -1; // mark end @@ -187,11 +212,12 @@ skip_ue: cur_UE = &UE_sched.head; while (*cur_UE >= 0 && max_num_ue > 0) { const int UE_id = *cur_UE; - cur_UE = &UE_sched.next[UE_id]; // go to next const uint8_t cqi = UE_info->UE_sched_ctrl[UE_id].dl_cqi[CC_id]; const int idx = CCE_try_allocate_dlsch(Mod_id, CC_id, subframe, UE_id, cqi); if (idx < 0) { LOG_D(MAC, "cannot allocate CCE for UE %d, skipping\n", UE_id); + // SKIP this UE in the list by marking the next as the current + *cur_UE = UE_sched.next[UE_id]; continue; } UE_info->UE_sched_ctrl[UE_id].pre_dci_dl_pdu_idx = idx; @@ -200,6 +226,7 @@ skip_ue: const uint32_t B = UE_info->UE_template[CC_id][UE_id].dl_buffer_total; rb_required[UE_id] = find_nb_rb_DL(mcs, B, n_rbg_sched * RBGsize, RBGsize); max_num_ue--; + cur_UE = &UE_sched.next[UE_id]; // go to next } *cur_UE = -1; // not all UEs might be allocated, mark end @@ -284,56 +311,19 @@ int pf_wbcqi_dl_run(module_id_t Mod_id, const uint8_t harq_pid = frame_subframe2_dl_harq_pid(cc->tdd_Config, frame, subframe); UE_sched_ctrl_t *ue_ctrl = &UE_info->UE_sched_ctrl[UE_id]; const uint8_t round = ue_ctrl->round[CC_id][harq_pid]; - if (round != 8) { // retransmission: allocate - const int nb_rb = UE_info->UE_template[CC_id][UE_id].nb_rb[harq_pid]; - if (nb_rb == 0) - continue; - int nb_rbg = (nb_rb + (nb_rb % RBGsize)) / RBGsize; - // needs more RBGs than we can allocate - if (nb_rbg > n_rbg_sched) { - LOG_D(MAC, - "retransmission of UE %d needs more RBGs (%d) than we have (%d)\n", - UE_id, nb_rbg, n_rbg_sched); - continue; + if (round != 8) { + bool r = try_allocate_harq_retransmission(Mod_id, CC_id, frame, subframe, + UE_id, rbg, &n_rbg_sched, + rbgalloc_mask); + if (r) { + /* if there are no more RBG to give, return */ + if (n_rbg_sched <= 0) + return 0; + max_num_ue--; + if (max_num_ue == 0) + return n_rbg_sched; + for (; !rbgalloc_mask[rbg]; rbg++) /* fast-forward */ ; } - // ensure that the number of RBs can be contained by the RBGs (!), i.e. - // if we allocate the last RBG this one should have the full RBGsize - if ((nb_rb % RBGsize) == 0 && nb_rbg == n_rbg_sched - && rbgalloc_mask[N_RBG - 1] && RBGlastsize != RBGsize) { - LOG_D(MAC, - "retransmission of UE %d needs %d RBs, but the last RBG %d is too small (%d, normal %d)\n", - UE_id, nb_rb, N_RBG - 1, RBGlastsize, RBGsize); - continue; - } - const uint8_t cqi = ue_ctrl->dl_cqi[CC_id]; - const int idx = CCE_try_allocate_dlsch(Mod_id, CC_id, subframe, UE_id, cqi); - if (idx < 0) - continue; // cannot allocate CCE - ue_ctrl->pre_dci_dl_pdu_idx = idx; - // retransmissions: directly allocate - n_rbg_sched -= nb_rbg; - ue_ctrl->pre_nb_available_rbs[CC_id] += nb_rb; - for (; nb_rbg > 0; rbg++) { - if (!rbgalloc_mask[rbg]) - continue; - ue_ctrl->rballoc_sub_UE[CC_id][rbg] = 1; - rbgalloc_mask[rbg] = 0; - nb_rbg--; - } - LOG_D(MAC, - "%4d.%d n_rbg_sched %d after retransmission reservation for UE %d " - "round %d retx nb_rb %d pre_nb_available_rbs %d\n", - frame, subframe, n_rbg_sched, UE_id, round, - UE_info->UE_template[CC_id][UE_id].nb_rb[harq_pid], - ue_ctrl->pre_nb_available_rbs[CC_id]); - /* if there are no more RBG to give, return */ - if (n_rbg_sched <= 0) - return 0; - max_num_ue--; - /* if there are no UEs that can be allocated anymore, return */ - if (max_num_ue == 0) - return n_rbg_sched; - for (; !rbgalloc_mask[rbg]; rbg++) /* fast-forward */ ; } else { if (UE_info->UE_template[CC_id][UE_id].dl_buffer_total == 0) continue; @@ -436,56 +426,19 @@ int mt_wbcqi_dl_run(module_id_t Mod_id, const uint8_t harq_pid = frame_subframe2_dl_harq_pid(cc->tdd_Config, frame, subframe); UE_sched_ctrl_t *ue_ctrl = &UE_info->UE_sched_ctrl[UE_id]; const uint8_t round = ue_ctrl->round[CC_id][harq_pid]; - if (round != 8) { // retransmission: allocate - const int nb_rb = UE_info->UE_template[CC_id][UE_id].nb_rb[harq_pid]; - if (nb_rb == 0) - continue; - int nb_rbg = (nb_rb + (nb_rb % RBGsize)) / RBGsize; - // needs more RBGs than we can allocate - if (nb_rbg > n_rbg_sched) { - LOG_D(MAC, - "retransmission of UE %d needs more RBGs (%d) than we have (%d)\n", - UE_id, nb_rbg, n_rbg_sched); - continue; + if (round != 8) { + bool r = try_allocate_harq_retransmission(Mod_id, CC_id, frame, subframe, + UE_id, rbg, &n_rbg_sched, + rbgalloc_mask); + if (r) { + /* if there are no more RBG to give, return */ + if (n_rbg_sched <= 0) + return 0; + max_num_ue--; + if (max_num_ue == 0) + return n_rbg_sched; + for (; !rbgalloc_mask[rbg]; rbg++) /* fast-forward */ ; } - // ensure that the number of RBs can be contained by the RBGs (!), i.e. - // if we allocate the last RBG this one should have the full RBGsize - if ((nb_rb % RBGsize) == 0 && nb_rbg == n_rbg_sched - && rbgalloc_mask[N_RBG - 1] && RBGlastsize != RBGsize) { - LOG_D(MAC, - "retransmission of UE %d needs %d RBs, but the last RBG %d is too small (%d, normal %d)\n", - UE_id, nb_rb, N_RBG - 1, RBGlastsize, RBGsize); - continue; - } - const uint8_t cqi = ue_ctrl->dl_cqi[CC_id]; - const int idx = CCE_try_allocate_dlsch(Mod_id, CC_id, subframe, UE_id, cqi); - if (idx < 0) - continue; // cannot allocate CCE - ue_ctrl->pre_dci_dl_pdu_idx = idx; - // retransmissions: directly allocate - n_rbg_sched -= nb_rbg; - ue_ctrl->pre_nb_available_rbs[CC_id] += nb_rb; - for (; nb_rbg > 0; rbg++) { - if (!rbgalloc_mask[rbg]) - continue; - ue_ctrl->rballoc_sub_UE[CC_id][rbg] = 1; - rbgalloc_mask[rbg] = 0; - nb_rbg--; - } - LOG_D(MAC, - "%4d.%d n_rbg_sched %d after retransmission reservation for UE %d " - "round %d retx nb_rb %d pre_nb_available_rbs %d\n", - frame, subframe, n_rbg_sched, UE_id, round, - UE_info->UE_template[CC_id][UE_id].nb_rb[harq_pid], - ue_ctrl->pre_nb_available_rbs[CC_id]); - /* if there are no more RBG to give, return */ - if (n_rbg_sched <= 0) - return 0; - max_num_ue--; - /* if there are no UEs that can be allocated anymore, return */ - if (max_num_ue == 0) - return n_rbg_sched; - for (; !rbgalloc_mask[rbg]; rbg++) /* fast-forward */ ; } else { if (UE_info->UE_template[CC_id][UE_id].dl_buffer_total == 0) continue; @@ -942,7 +895,10 @@ int rr_ul_run(module_id_t Mod_id, &tx_power); UE_template->pre_assigned_mcs_ul = mcs; - rb_idx_required[UE_id] = rb_table_index; + /* rb_idx_given >= 22: apparently the PHY cannot support more than 48 + * RBs in the uplink. Hence, we limit every UE to 48 RBs, which is at + * index 22 */ + rb_idx_required[UE_id] = min(22, rb_table_index); //UE_template->pre_allocated_nb_rb_ul = rb_table[rb_table_index]; /* only print log when PHR changed */ static int phr = 0; diff --git a/openair2/LAYER2/MAC/slicing/slicing.c b/openair2/LAYER2/MAC/slicing/slicing.c index 9c88f6cffb..48dd99afeb 100644 --- a/openair2/LAYER2/MAC/slicing/slicing.c +++ b/openair2/LAYER2/MAC/slicing/slicing.c @@ -336,6 +336,8 @@ void static_dl(module_id_t mod_id, rbgalloc_slice_mask[rbg] = rbgalloc_mask[rbg]; n_rbg_sched += rbgalloc_mask[rbg]; } + if (n_rbg_sched == 0) /* no free RBGs, e.g., taken by RA */ + continue; s->s[i]->dl_algo.run(mod_id, CC_id, @@ -445,6 +447,8 @@ void static_ul(module_id_t mod_id, } if (!last_rb_blocked) rbs[n_contig - 1].length = p->posHigh - rbs[n_contig - 1].start + 1; + if (n_contig == 1 && rbs[0].length == 0) /* no RBs, e.g., taken by RA */ + continue; s->s[i]->ul_algo.run(mod_id, CC_id, diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 8fe83dd40d..f968f1b926 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -48,8 +48,7 @@ uint16_t NCS_restricted_TypeB_delta_f_RA_5[14] = {36,57,60,63,65,68,71,77,81,8 // Table 6.3.3.1-7 (38.211) NCS for preamble formats with delta_f_RA = 15 * 2mu KHz where mu = {0,1,2,3} uint16_t NCS_unrestricted_delta_f_RA_15[16] = {0,2,4,6,8,10,12,13,15,17,19,23,27,34,46,69}; -const char *prachfmt[]={"A1","A2","A3","B1","B2","B3","B4","C0","C2"}; -const char *prachfmt03[]={"0","1","2","3"}; +const char *prachfmt[]={"0","1","2","3", "A1","A2","A3","B1","B4","C0","C2","A1/B1","A2/B2","A3/B3"}; uint16_t get_NCS(uint8_t index, uint16_t format0, uint8_t restricted_set_config) { @@ -86,7 +85,71 @@ uint16_t get_NCS(uint8_t index, uint16_t format0, uint8_t restricted_set_config) } } +//38.211 Table 6.3.3.2-1 +int16_t table_6_3_3_2_1[16][5] = { +//Length_RA, delta_f_RA_PRACH, delta_f_PUSCH, N_RA_RB, kbar +{ 839, 1.25, 15, 6, 7}, +{ 839, 1.25, 30, 3, 1}, +{ 839, 1.25, 60, 2, 133}, +{ 839, 5, 15, 24, 12}, +{ 839, 5, 30, 12, 10}, +{ 839, 5, 60, 6, 7}, +{ 139, 15, 15, 12, 2}, +{ 139, 15, 30, 6, 2}, +{ 139, 15, 60, 3, 2}, +{ 139, 30, 15, 24, 2}, +{ 139, 30, 30, 12, 2}, +{ 139, 30, 60, 6, 2}, +{ 139, 60, 60, 12, 2}, +{ 139, 60, 120, 6, 2}, +{ 139, 120, 60, 24, 2}, +{ 139, 120, 120, 12, 2} +}; +/* Function to get number of RBs required for prach occasion based on + * 38.211 Table 6.3.3.2-1 */ +int16_t get_N_RA_RB (int delta_f_RA_PRACH,int delta_f_PUSCH) { + + int8_t index = 0; + switch(delta_f_RA_PRACH) { + case 0 : index = 6; + if (delta_f_PUSCH == 0) + index += 0; + else if(delta_f_PUSCH == 1) + index += 1; + else + index += 2; + break; + + case 1 : index = 9; + if (delta_f_PUSCH == 0) + index += 0; + else if(delta_f_PUSCH == 1) + index += 1; + else + index += 2; + break; + + case 2 : index = 11; + if (delta_f_PUSCH == 2) + index += 0; + else + index += 1; + break; + + case 3: index = 13; + if (delta_f_PUSCH == 2) + index += 0; + else + index += 1; + break; + + default : index = 10;/*30khz prach scs and 30khz pusch scs*/ + + } + + return table_6_3_3_2_1[index][3]; +} // Table 6.3.3.2-2: Random access configurations for FR1 and paired spectrum/supplementary uplink // the column 5, (SFN_nbr is a bitmap where we set bit to '1' in the position of the subframe where the RACH can be sent. // E.g. in row 4, and column 5 we have set value 512 ('1000000000') which means RACH can be sent at subframe 9. @@ -955,6 +1018,162 @@ void find_monitoring_periodicity_offset_common(NR_SearchSpace_t *ss, } } +int get_nr_prach_occasion_info_from_index(uint8_t index, + uint32_t pointa, + uint8_t mu, + uint8_t unpaired, + uint16_t *format, + uint8_t *start_symbol, + uint8_t *N_t_slot, + uint8_t *N_dur, + uint8_t *N_RA_slot, + uint16_t *N_RA_sfn, + uint8_t *max_association_period) { + + int x; + int64_t s_map; + uint8_t format2 = 0xff; + if (pointa > 2016666) { //FR2 + x = table_6_3_3_2_4_prachConfig_Index[index][2]; + s_map = table_6_3_3_2_4_prachConfig_Index[index][5]; + for(int i = 0; i < 64 ;i++) { + if ( (s_map >> i) & 0x01) { + (*N_RA_sfn)++; + } + } + *N_RA_slot = table_6_3_3_2_4_prachConfig_Index[index][7]; // Number of RACH slots within a subframe + *max_association_period = 160/(x * 10); + if (start_symbol != NULL && N_t_slot != NULL && N_dur != NULL && format != NULL){ + *start_symbol = table_6_3_3_2_4_prachConfig_Index[index][6];//multiple prach occasions in diff slot + *N_t_slot = table_6_3_3_2_4_prachConfig_Index[index][8]; + *N_dur = table_6_3_3_2_4_prachConfig_Index[index][9]; + if (table_6_3_3_2_4_prachConfig_Index[index][1] != -1) + format2 = (uint8_t) table_6_3_3_2_4_prachConfig_Index[index][1]; + + *format = ((uint8_t) table_6_3_3_2_4_prachConfig_Index[index][0]) | (format2<<8); + LOG_D(MAC,"Getting Total PRACH info from index %d absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u N_RA_sfn = %u\n", + index, + pointa, + mu, + unpaired, + *start_symbol, + *N_t_slot, + *N_dur, + *N_RA_sfn); + } + return 1; + } + else { + + if (unpaired) { + x = table_6_3_3_2_3_prachConfig_Index[index][2]; + s_map = table_6_3_3_2_3_prachConfig_Index[index][4]; + for(int i = 0; i < 64 ;i++) { + if ( (s_map >> i) & 0x01) { + (*N_RA_sfn)++; + } + } + *N_RA_slot = table_6_3_3_2_3_prachConfig_Index[index][6]; // Number of RACH slots within a subframe + *max_association_period = 160/(x * 10); + if (start_symbol != NULL && N_t_slot != NULL && N_dur != NULL && format != NULL){ + *start_symbol = table_6_3_3_2_3_prachConfig_Index[index][5]; + *N_t_slot = table_6_3_3_2_3_prachConfig_Index[index][7]; + *N_dur = table_6_3_3_2_3_prachConfig_Index[index][8]; + if (table_6_3_3_2_3_prachConfig_Index[index][1] != -1) + format2 = (uint8_t) table_6_3_3_2_3_prachConfig_Index[index][1]; + *format = ((uint8_t) table_6_3_3_2_3_prachConfig_Index[index][0]) | (format2<<8); + LOG_I(MAC,"Getting Total PRACH info from index %d (col %lu ) absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u N_RA_sfn = %u\n", + index, table_6_3_3_2_3_prachConfig_Index[index][6], + pointa, + mu, + unpaired, + *start_symbol, + *N_t_slot, + *N_dur, + *N_RA_sfn); + } + return 1; + } + else { // FDD + x = table_6_3_3_2_2_prachConfig_Index[index][2]; + s_map = table_6_3_3_2_2_prachConfig_Index[index][4]; + *N_RA_slot = table_6_3_3_2_2_prachConfig_Index[index][6]; + if (start_symbol != NULL && N_t_slot != NULL && N_dur != NULL && format != NULL){ + *start_symbol = table_6_3_3_2_2_prachConfig_Index[index][5]; + *N_t_slot = table_6_3_3_2_2_prachConfig_Index[index][7]; + *N_dur = table_6_3_3_2_2_prachConfig_Index[index][8]; + if (table_6_3_3_2_2_prachConfig_Index[index][1] != -1) + format2 = (uint8_t) table_6_3_3_2_2_prachConfig_Index[index][1]; + *format = ((uint8_t) table_6_3_3_2_2_prachConfig_Index[index][0]) | (format2<<8); + LOG_D(MAC,"Getting Total PRACH info from index %d absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u \n", + index, + pointa, + mu, + unpaired, + *start_symbol, + *N_t_slot, + *N_dur); + } + return 1; + } + } +} + + +uint8_t get_nr_prach_duration(uint8_t prach_format){ + + switch(prach_format){ + + case 0: // format 0 + return 0; + + case 1: // format 1 + return 0; + + case 2: // format 2 + return 0; + + case 3: // format 3 + return 0; + + case 4: // format A1 + return 2; + + case 5: // format A2 + return 4; + + case 6: // format A3 + return 6; + + case 7: // format B1 + return 2; + + case 8: // format B4 + return 12; + + case 9: // format C0 + return 2; + + case 10: // format C2 + return 6; + + case 11: // format A1/B1 + return 2; + + case 12: // format A2/B2 + return 4; + + case 13: // format A3/B3 + return 6; + + default : + AssertFatal(1==0,"Invalid Prach format\n"); + break; + + } + +} + int get_nr_prach_info_from_index(uint8_t index, int frame, int slot, @@ -964,7 +1183,10 @@ int get_nr_prach_info_from_index(uint8_t index, uint16_t *format, uint8_t *start_symbol, uint8_t *N_t_slot, - uint8_t *N_dur) { + uint8_t *N_dur, + uint16_t *RA_sfn_index, + uint8_t *N_RA_slot, + uint8_t *config_period) { int x,y; int64_t s_map; @@ -980,27 +1202,39 @@ int get_nr_prach_info_from_index(uint8_t index, if ( (frame%x)==y || (frame%x)==y2 ) { slot_60khz = slot >> (mu-2); // in table slots are numbered wrt 60kHz s_map = table_6_3_3_2_4_prachConfig_Index[index][5]; + if ((s_map >> slot_60khz) & 0x01 ) { + for(int i = 0; i <= slot_60khz ;i++) { + if ( (s_map >> i) & 0x01) { + (*RA_sfn_index)++; + } + } + } if ( ((s_map>>slot_60khz)&0x01) ) { + *N_RA_slot = table_6_3_3_2_4_prachConfig_Index[index][7]; // Number of RACH slots within a subframe if (mu == 3) { - if ( (table_6_3_3_2_4_prachConfig_Index[index][7] == 1) && (slot%2 == 0) ) + if ( (*N_RA_slot == 1) && (slot%2 == 0) ) return 0; // no prach in even slots @ 120kHz for 1 prach per 60khz slot } if (start_symbol != NULL && N_t_slot != NULL && N_dur != NULL && format != NULL){ + *config_period = x; *start_symbol = table_6_3_3_2_4_prachConfig_Index[index][6]; *N_t_slot = table_6_3_3_2_4_prachConfig_Index[index][8]; *N_dur = table_6_3_3_2_4_prachConfig_Index[index][9]; if (table_6_3_3_2_4_prachConfig_Index[index][1] != -1) format2 = (uint8_t) table_6_3_3_2_4_prachConfig_Index[index][1]; *format = ((uint8_t) table_6_3_3_2_4_prachConfig_Index[index][0]) | (format2<<8); - LOG_D(MAC,"Frame %d slot %d: Getting PRACH info from index %d absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u \n", frame, - slot, - index, - pointa, - mu, - unpaired, - *start_symbol, - *N_t_slot, - *N_dur); + LOG_D(MAC,"Frame %d slot %d: Getting PRACH info from index %d absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u N_RA_slot %u RA_sfn_index %u\n", + frame, + slot, + index, + pointa, + mu, + unpaired, + *start_symbol, + *N_t_slot, + *N_dur, + *N_RA_slot, + *RA_sfn_index); } return 1; } @@ -1018,27 +1252,40 @@ int get_nr_prach_info_from_index(uint8_t index, if ( (frame%x)==y ) { subframe = slot >> mu; s_map = table_6_3_3_2_3_prachConfig_Index[index][4]; - if ( (s_map>>subframe)&0x01 ) { - if (mu == 1) { - if ( (table_6_3_3_2_3_prachConfig_Index[index][6] <= 1) && (slot%2 == 0) ) - return 0; // no prach in even slots @ 30kHz for 1 prach per subframe + if ((s_map >> subframe) & 0x01 ) { + for(int i = 0; i <= subframe ;i++) { + if ( (s_map >> i) & 0x01) { + (*RA_sfn_index)++; + } } + } + if ( (s_map>>subframe)&0x01 ) { + *N_RA_slot = table_6_3_3_2_3_prachConfig_Index[index][6]; // Number of RACH slots within a subframe + if (mu == 1) { + if ( (*N_RA_slot <= 1) && (slot%2 == 0) ) + return 0; // no prach in even slots @ 30kHz for 1 prach per subframe + } if (start_symbol != NULL && N_t_slot != NULL && N_dur != NULL && format != NULL){ + *config_period = x; *start_symbol = table_6_3_3_2_3_prachConfig_Index[index][5]; *N_t_slot = table_6_3_3_2_3_prachConfig_Index[index][7]; *N_dur = table_6_3_3_2_3_prachConfig_Index[index][8]; if (table_6_3_3_2_3_prachConfig_Index[index][1] != -1) format2 = (uint8_t) table_6_3_3_2_3_prachConfig_Index[index][1]; *format = ((uint8_t) table_6_3_3_2_3_prachConfig_Index[index][0]) | (format2<<8); - LOG_D(MAC,"Frame %d slot %d: Getting PRACH info from index %d (col 6 %ld) absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u \n", frame, - slot, - index, table_6_3_3_2_3_prachConfig_Index[index][6], - pointa, - mu, - unpaired, - *start_symbol, - *N_t_slot, - *N_dur); + LOG_D(MAC,"Frame %d slot %d: Getting PRACH info from index %d (col 6 %lu) absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u N_RA_slot %u RA_sfn_index %u \n", + frame, + slot, + index, + table_6_3_3_2_3_prachConfig_Index[index][6], + pointa, + mu, + unpaired, + *start_symbol, + *N_t_slot, + *N_dur, + *N_RA_slot, + *RA_sfn_index); } return 1; } @@ -1066,15 +1313,16 @@ int get_nr_prach_info_from_index(uint8_t index, if (table_6_3_3_2_2_prachConfig_Index[index][1] != -1) format2 = (uint8_t) table_6_3_3_2_2_prachConfig_Index[index][1]; *format = ((uint8_t) table_6_3_3_2_2_prachConfig_Index[index][0]) | (format2<<8); - LOG_D(MAC,"Frame %d slot %d: Getting PRACH info from index %d absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u \n", frame, - slot, - index, - pointa, - mu, - unpaired, - *start_symbol, - *N_t_slot, - *N_dur); + LOG_D(MAC,"Frame %d slot %d: Getting PRACH info from index %d absoluteFrequencyPointA %u mu %u frame_type %u start_symbol %u N_t_slot %u N_dur %u \n", + frame, + slot, + index, + pointa, + mu, + unpaired, + *start_symbol, + *N_t_slot, + *N_dur); } return 1; } diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index f19a46cb34..0a5a8c110c 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -99,7 +99,24 @@ int get_nr_prach_info_from_index(uint8_t index, uint16_t *format, uint8_t *start_symbol, uint8_t *N_t_slot, - uint8_t *N_dur); + uint8_t *N_dur, + uint16_t *RA_sfn_index, + uint8_t *N_RA_slot, + uint8_t *config_period); + +int get_nr_prach_occasion_info_from_index(uint8_t index, + uint32_t pointa, + uint8_t mu, + uint8_t unpaired, + uint16_t *format, + uint8_t *start_symbol, + uint8_t *N_t_slot, + uint8_t *N_dur, + uint8_t *N_RA_slot, + uint16_t *N_RA_sfn, + uint8_t *max_association_period); + +uint8_t get_nr_prach_duration(uint8_t prach_format); uint8_t get_pusch_mcs_table(long *mcs_Table, int is_tp, @@ -125,4 +142,5 @@ int32_t get_l_prime(uint8_t duration_in_symbols, uint8_t mapping_type, pusch_dmr uint8_t get_L_ptrs(uint8_t mcs1, uint8_t mcs2, uint8_t mcs3, uint8_t I_mcs, uint8_t mcs_table); uint8_t get_K_ptrs(uint16_t nrb0, uint16_t nrb1, uint16_t N_RB); +int16_t get_N_RA_RB (int delta_f_RA_PRACH,int delta_f_PUSCH); #endif diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 12333ba9f1..4c950a92e6 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -54,13 +54,15 @@ void fill_dci_search_candidates(NR_SearchSpace_t *ss,fapi_nr_dl_config_dci_dl_pd LOG_D(MAC,"Filling search candidates for DCI\n"); - rel15->number_of_candidates=3; + rel15->number_of_candidates=4; rel15->CCE[0]=0; rel15->L[0]=4; rel15->CCE[1]=4; rel15->L[1]=4; rel15->CCE[2]=8; rel15->L[2]=4; + rel15->CCE[3]=12; + rel15->L[3]=4; } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index e237a910c6..529ab13d9d 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -1073,6 +1073,9 @@ void nr_ue_msg3_scheduler(NR_UE_MAC_INST_t *mac, void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t slotP) { uint8_t config_index, mu, N_dur, N_t_slot, start_symbol; + uint16_t RA_sfn_index; + uint8_t N_RA_slot; + uint8_t config_period; uint16_t format, format0, format1, ncs; int msg1_FDM, is_nr_prach_slot, fdm; @@ -1109,7 +1112,10 @@ void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t s &format, &start_symbol, &N_t_slot, - &N_dur); + &N_dur, + &RA_sfn_index, + &N_RA_slot, + &config_period); if (is_nr_prach_slot && mac->ra_state == RA_UE_IDLE) { @@ -1156,57 +1162,51 @@ void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t s if (format1 != 0xff) { switch(format0) { // dual PRACH format case 0xa1: - prach_config_pdu->prach_format = 9; + prach_config_pdu->prach_format = 11; break; case 0xa2: - prach_config_pdu->prach_format = 10; + prach_config_pdu->prach_format = 12; break; case 0xa3: - prach_config_pdu->prach_format = 11; + prach_config_pdu->prach_format = 13; break; default: AssertFatal(1 == 0, "Only formats A1/B1 A2/B2 A3/B3 are valid for dual format"); } } else { switch(format0) { // single PRACH format - case 0xa1: + case 0: prach_config_pdu->prach_format = 0; break; - case 0xa2: + case 1: prach_config_pdu->prach_format = 1; break; - case 0xa3: + case 2: prach_config_pdu->prach_format = 2; break; - case 0xb1: + case 3: prach_config_pdu->prach_format = 3; break; - case 0xb2: + case 0xa1: prach_config_pdu->prach_format = 4; break; - case 0xb3: + case 0xa2: prach_config_pdu->prach_format = 5; break; - case 0xb4: + case 0xa3: prach_config_pdu->prach_format = 6; break; - case 0xc0: + case 0xb1: prach_config_pdu->prach_format = 7; break; - case 0xc2: + case 0xb4: prach_config_pdu->prach_format = 8; break; - case 0: - // long formats are handled @ PHY + case 0xc0: + prach_config_pdu->prach_format = 9; break; - case 1: - // long formats are handled @ PHY - break; - case 2: - // long formats are handled @ PHY - break; - case 3: - // long formats are handled @ PHY + case 0xc2: + prach_config_pdu->prach_format = 10; break; default: AssertFatal(1 == 0, "Invalid PRACH format"); diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index ad8409de69..6bd7940480 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -150,6 +150,10 @@ void config_common(int Mod_idP, int pdsch_AntennaPorts, NR_ServingCellConfigComm cfg->ssb_config.ss_pbch_power.tl.tag = NFAPI_NR_CONFIG_SS_PBCH_POWER_TAG; cfg->num_tlv++; + cfg->ssb_config.bch_payload.value = 1; + cfg->ssb_config.bch_payload.tl.tag = NFAPI_NR_CONFIG_BCH_PAYLOAD_TAG; + cfg->num_tlv++; + cfg->ssb_config.scs_common.value = *scc->ssbSubcarrierSpacing; cfg->ssb_config.scs_common.tl.tag = NFAPI_NR_CONFIG_SCS_COMMON_TAG; cfg->num_tlv++; @@ -173,6 +177,9 @@ void config_common(int Mod_idP, int pdsch_AntennaPorts, NR_ServingCellConfigComm cfg->prach_config.restricted_set_config.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->restrictedSetConfig; cfg->prach_config.restricted_set_config.tl.tag = NFAPI_NR_CONFIG_RESTRICTED_SET_CONFIG_TAG; cfg->num_tlv++; + cfg->prach_config.prach_ConfigurationIndex.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.prach_ConfigurationIndex; + cfg->prach_config.prach_ConfigurationIndex.tl.tag = NFAPI_NR_CONFIG_PRACH_CONFIG_INDEX_TAG; + cfg->num_tlv++; switch (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FDM) { case 0 : @@ -193,26 +200,30 @@ void config_common(int Mod_idP, int pdsch_AntennaPorts, NR_ServingCellConfigComm cfg->prach_config.num_prach_fd_occasions.tl.tag = NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG; cfg->num_tlv++; + cfg->prach_config.prach_ConfigurationIndex.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.prach_ConfigurationIndex; + cfg->prach_config.prach_ConfigurationIndex.tl.tag = NFAPI_NR_CONFIG_PRACH_CONFIG_INDEX_TAG; + cfg->num_tlv++; + cfg->prach_config.num_prach_fd_occasions_list = (nfapi_nr_num_prach_fd_occasions_t *) malloc(cfg->prach_config.num_prach_fd_occasions.value*sizeof(nfapi_nr_num_prach_fd_occasions_t)); for (i=0; iprach_config.num_prach_fd_occasions.value; i++) { - cfg->prach_config.num_prach_fd_occasions_list[i].num_prach_fd_occasions = i; +// cfg->prach_config.num_prach_fd_occasions_list[i].num_prach_fd_occasions = i; if (cfg->prach_config.prach_sequence_length.value) cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.choice.l139; else cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.choice.l839; cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index.tl.tag = NFAPI_NR_CONFIG_PRACH_ROOT_SEQUENCE_INDEX_TAG; cfg->num_tlv++; - cfg->prach_config.num_prach_fd_occasions_list[i].k1.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FrequencyStart; + cfg->prach_config.num_prach_fd_occasions_list[i].k1.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FrequencyStart + (get_N_RA_RB( cfg->prach_config.prach_sub_c_spacing.value, scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing ) * i); +//k1= msg1_FrequencyStart + 12 (no. of FDM)(RB for PRACH occasion); cfg->prach_config.num_prach_fd_occasions_list[i].k1.tl.tag = NFAPI_NR_CONFIG_K1_TAG; cfg->num_tlv++; cfg->prach_config.num_prach_fd_occasions_list[i].prach_zero_corr_conf.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.zeroCorrelationZoneConfig; cfg->prach_config.num_prach_fd_occasions_list[i].prach_zero_corr_conf.tl.tag = NFAPI_NR_CONFIG_PRACH_ZERO_CORR_CONF_TAG; cfg->num_tlv++; - cfg->prach_config.num_prach_fd_occasions_list[i].num_root_sequences.value = compute_nr_root_seq(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup, - nb_preambles, frame_type); + cfg->prach_config.num_prach_fd_occasions_list[i].num_root_sequences.value = compute_nr_root_seq(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup,nb_preambles, frame_type); cfg->prach_config.num_prach_fd_occasions_list[i].num_root_sequences.tl.tag = NFAPI_NR_CONFIG_NUM_ROOT_SEQUENCES_TAG; cfg->num_tlv++; - //cfg->prach_config.num_prach_fd_occasions_list[i].num_unused_root_sequences.value = ??? + cfg->prach_config.num_prach_fd_occasions_list[i].num_unused_root_sequences.value = 1; } cfg->prach_config.ssb_per_rach.value = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present-1; @@ -344,7 +355,10 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, phycfg.cfg->ssb_table.ssb_subcarrier_offset.tl.tag = NFAPI_NR_CONFIG_SSB_SUBCARRIER_OFFSET_TAG; phycfg.cfg->num_tlv++; - if (RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req) RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req(&phycfg); + if (RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req) RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req(&phycfg); + + find_SSB_and_RO_available(Mod_idP); + } if (secondaryCellGroup) { @@ -367,12 +381,18 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, NR_RA_t *ra = &RC.nrmac[Mod_idP]->common_channels[CC_id].ra[0]; ra->state = RA_IDLE; ra->secondaryCellGroup = secondaryCellGroup; - ra->crnti = rnti; - uint8_t num_preamble = secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list.count; - ra->preambles.num_preambles = num_preamble; - ra->preambles.preamble_list = (uint8_t *) malloc(num_preamble*sizeof(uint8_t)); - for (int i = 0; i < num_preamble; i++) - ra->preambles.preamble_list[i] = secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list.array[i]->ra_PreambleIndex; + if (secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated!=NULL) { + if (secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra != NULL) { + ra->cfra = true; + ra->rnti = rnti; + struct NR_CFRA cfra = *secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra; + uint8_t num_preamble = cfra.resources.choice.ssb->ssb_ResourceList.list.count; + ra->preambles.num_preambles = num_preamble; + ra->preambles.preamble_list = (uint8_t *) malloc(num_preamble*sizeof(uint8_t)); + for (int i = 0; i < num_preamble; i++) + ra->preambles.preamble_list[i] = cfra.resources.choice.ssb->ssb_ResourceList.list.array[i]->ra_PreambleIndex; + } + } LOG_I(PHY,"Added new RA process for UE RNTI %04x with initial secondaryCellGroup\n", rnti); } else { // secondaryCellGroup has been updated const int UE_id = find_nr_UE_id(Mod_idP,rnti); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 58de000ac5..63ca351911 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -435,11 +435,13 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, nr_rrc_trigger(&ctxt, 0 /*CC_id*/, frame, slot >> *scc->ssbSubcarrierSpacing); } - const uint64_t dlsch_in_slot_bitmap = (1 << 1); + const uint64_t dlsch_in_slot_bitmap = (1 << 1) | (1 << 2); const uint64_t ulsch_in_slot_bitmap = (1 << 8); memset(RC.nrmac[module_idP]->cce_list[bwp_id][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 memset(RC.nrmac[module_idP]->cce_list[bwp_id][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 + for (int i=0; iUE_info.num_pdcch_cand[UE_id][i] = 0; for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { //mbsfn_status[CC_id] = 0; @@ -469,6 +471,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, // This schedule RA procedure if not in phy_test mode // Otherwise already consider 5G already connected + RC.nrmac[module_idP]->current_slot=slot; if (get_softmodem_params()->phy_test == 0) { nr_schedule_RA(module_idP, frame, slot); nr_schedule_reception_msg3(module_idP, 0, frame, slot); @@ -497,7 +500,6 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, if (UE_info->active[UE_id] && (is_xlsch_in_slot(dlsch_in_slot_bitmap, slot % num_slots_per_tdd)) - && (!get_softmodem_params()->phy_test || slot == 1) && slot < 10) { ue_sched_ctl->current_harq_pid = slot % num_slots_per_tdd; //int pucch_sched; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 413727b9cb..af40c9ccd8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -46,121 +46,295 @@ extern const uint8_t nr_slots_per_frame[5]; uint8_t DELTA[4]= {2,3,4,6}; +#define MAX_NUMBER_OF_SSB 64 +float ssb_per_rach_occasion[8] = {0.125,0.25,0.5,1,2,4,8}; + +int16_t ssb_index_from_prach(module_id_t module_idP, + frame_t frameP, + sub_frame_t slotP, + uint16_t preamble_index, + uint8_t freq_index, + uint8_t symbol) { + + gNB_MAC_INST *gNB = RC.nrmac[module_idP]; + NR_COMMON_channels_t *cc = &gNB->common_channels[0]; + NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; + nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0]; + + uint8_t config_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.prach_ConfigurationIndex; + uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value; + + uint8_t total_RApreambles = 64; + if( scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles != NULL) + total_RApreambles = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles; + + float num_ssb_per_RO = ssb_per_rach_occasion[cfg->prach_config.ssb_per_rach.value]; + uint16_t start_symbol_index = 0; + uint8_t mu,N_dur,N_t_slot,start_symbol = 0, temp_start_symbol = 0, N_RA_slot; + uint16_t format,RA_sfn_index = -1; + uint8_t config_period = 1; + uint16_t prach_occasion_id = -1; + uint8_t num_active_ssb = cc->num_active_ssb; + + if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing) + mu = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing; + else + mu = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + + get_nr_prach_info_from_index(config_index, + (int)frameP, + (int)slotP, + scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA, + mu, + cc->frame_type, + &format, + &start_symbol, + &N_t_slot, + &N_dur, + &RA_sfn_index, + &N_RA_slot, + &config_period); + uint8_t index = 0,slot_index = 0; + for (slot_index = 0;slot_index < N_RA_slot; slot_index++) { + if (N_RA_slot <= 1) { //1 PRACH slot in a subframe + if((mu == 1) || (mu == 3)) + slot_index = 1; //For scs = 30khz and 120khz + } + for (int i=0; i< N_t_slot; i++) { + temp_start_symbol = (start_symbol + i * N_dur + 14 * slot_index) % 14; + if(symbol == temp_start_symbol) { + start_symbol_index = i; + break; + } + } + } + if (N_RA_slot <= 1) { //1 PRACH slot in a subframe + if((mu == 1) || (mu == 3)) + slot_index = 0; //For scs = 30khz and 120khz + } + +// prach_occasion_id = subframe_index * N_t_slot * N_RA_slot * fdm + N_RA_slot_index * N_t_slot * fdm + freq_index + fdm * start_symbol_index; + prach_occasion_id = (((frameP % (cc->max_association_period * config_period))/config_period)*cc->total_prach_occasions_per_config_period) + (RA_sfn_index + slot_index) * N_t_slot * fdm + start_symbol_index * fdm + freq_index; +//one RO is shared by one or more SSB + if(num_ssb_per_RO <= 1 ) + index = (int) (prach_occasion_id / (int)(1/num_ssb_per_RO)) % num_active_ssb; +//one SSB have more than one continuous RO + else if ( num_ssb_per_RO > 1) { + index = (prach_occasion_id * (int)num_ssb_per_RO)% num_active_ssb ; + for(int j = 0;j < num_ssb_per_RO;j++) { + if(preamble_index < (((j+1) * total_RApreambles) / num_ssb_per_RO)) + index = index + j; + } + } + + LOG_D(MAC, "Frame %d, Slot %d: Prach Occasion id = %d ssb per RO = %f number of active SSB %u index = %d fdm %u symbol index %u freq_index %u total_RApreambles %u\n", frameP, slotP, prach_occasion_id, num_ssb_per_RO, num_active_ssb, index, fdm, start_symbol_index, freq_index, total_RApreambles); + return index; +} +//Compute Total active SSBs and RO available +void find_SSB_and_RO_available(module_id_t module_idP) { + + gNB_MAC_INST *gNB = RC.nrmac[module_idP]; + NR_COMMON_channels_t *cc = &gNB->common_channels[0]; + NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; + nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0]; + + uint8_t config_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.prach_ConfigurationIndex; + uint8_t mu,N_dur,N_t_slot,start_symbol,N_RA_slot = 0; + uint16_t format,N_RA_sfn = 0,unused_RA_occasion,repetition = 0; + uint8_t num_active_ssb = 0; + uint8_t max_association_period = 1; + + if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing) + mu = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing; + else + mu = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + + // prach is scheduled according to configuration index and tables 6.3.3.2.2 to 6.3.3.2.4 + get_nr_prach_occasion_info_from_index(config_index, + scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA, + mu, + cc->frame_type, + &format, + &start_symbol, + &N_t_slot, + &N_dur, + &N_RA_slot, + &N_RA_sfn, + &max_association_period); + + float num_ssb_per_RO = ssb_per_rach_occasion[cfg->prach_config.ssb_per_rach.value]; + uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value; + uint64_t L_ssb = (((uint64_t) cfg->ssb_table.ssb_mask_list[0].ssb_mask.value)<<32) | cfg->ssb_table.ssb_mask_list[1].ssb_mask.value ; + uint32_t total_RA_occasions = N_RA_sfn * N_t_slot * N_RA_slot * fdm; + + for(int i = 0;i < 64;i++) { + if ((L_ssb >> (63-i)) & 0x01) { // only if the bit of L_ssb at current ssb index is 1 + cc->ssb_index[num_active_ssb] = i; + num_active_ssb++; + } + } + + for(int i = 1; (1 << (i-1)) <= max_association_period;i++) { + if(total_RA_occasions >= (int) (num_active_ssb/num_ssb_per_RO)) { + repetition = (uint16_t)((total_RA_occasions * num_ssb_per_RO )/num_active_ssb); + break; + } + else { + total_RA_occasions = total_RA_occasions * i; + cc->max_association_period = i; + } + } + if(cc->max_association_period == 0) + cc->max_association_period = 1; + + unused_RA_occasion = total_RA_occasions - (int)((num_active_ssb * repetition)/num_ssb_per_RO); + cc->total_prach_occasions = total_RA_occasions - unused_RA_occasion; + cc->num_active_ssb = num_active_ssb; + + LOG_I(MAC, "Total available RO %d, num of active SSB %d: unused RO = %d max_association_period %u N_RA_sfn %u \n", cc->total_prach_occasions, cc->num_active_ssb, unused_RA_occasion, max_association_period, N_RA_sfn); + +} + void schedule_nr_prach(module_id_t module_idP, frame_t frameP, sub_frame_t slotP) { gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; nfapi_nr_ul_tti_request_t *UL_tti_req = &RC.nrmac[module_idP]->UL_tti_req[0]; + nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0]; if (is_nr_UL_slot(scc,slotP)) { + uint8_t config_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.prach_ConfigurationIndex; + uint8_t mu,N_dur,N_t_slot,start_symbol = 0,N_RA_slot; + uint16_t RA_sfn_index = -1; + uint8_t config_period = 1; + uint16_t format; + int slot_index = 0; + uint16_t prach_occasion_id = -1; - uint8_t config_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.prach_ConfigurationIndex; - uint8_t mu,N_dur,N_t_slot,start_symbol; - uint16_t format; + if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing) + mu = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing; + else + mu = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; - if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing) - mu = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing; - else - mu = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value; + // prach is scheduled according to configuration index and tables 6.3.3.2.2 to 6.3.3.2.4 + if ( get_nr_prach_info_from_index(config_index, + (int)frameP, + (int)slotP, + scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA, + mu, + cc->frame_type, + &format, + &start_symbol, + &N_t_slot, + &N_dur, + &RA_sfn_index, + &N_RA_slot, + &config_period) ) { + uint16_t format0 = format&0xff; // first column of format from table + uint16_t format1 = (format>>8)&0xff; // second column of format from table - // prach is scheduled according to configuration index and tables 6.3.3.2.2 to 6.3.3.2.4 - if ( get_nr_prach_info_from_index(config_index, - (int)frameP, - (int)slotP, - scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA, - mu, - cc->frame_type, - &format, - &start_symbol, - &N_t_slot, - &N_dur) ) { + if (N_RA_slot > 1) { //more than 1 PRACH slot in a subframe + if (slotP%2 == 1){ + slot_index = 1; + } + else { + slot_index = 0; + } + }else if (N_RA_slot <= 1) { //1 PRACH slot in a subframe + slot_index = 0; + } - int fdm = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FDM; - uint16_t format0 = format&0xff; // first column of format from table - uint16_t format1 = (format>>8)&0xff; // second column of format from table - UL_tti_req->SFN = frameP; - UL_tti_req->Slot = slotP; - for (int n=0; n<(1<pdus_list[UL_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE; - UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_prach_pdu_t); - nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[UL_tti_req->n_pdus].prach_pdu; - memset(prach_pdu,0,sizeof(nfapi_nr_prach_pdu_t)); - UL_tti_req->n_pdus+=1; + UL_tti_req->SFN = frameP; + UL_tti_req->Slot = slotP; + for (int fdm_index=0; fdm_index < fdm; fdm_index++) { // one structure per frequency domain occasion + for (int td_index=0; td_indexphys_cell_id = *scc->physCellId; - prach_pdu->num_prach_ocas = N_t_slot; - prach_pdu->prach_start_symbol = start_symbol; - prach_pdu->num_ra = n; - prach_pdu->num_cs = get_NCS(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.zeroCorrelationZoneConfig, - format0, - scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->restrictedSetConfig); - // SCF PRACH PDU format field does not consider A1/B1 etc. possibilities - // We added 9 = A1/B1 10 = A2/B2 11 A3/B3 - if (format1!=0xff) { - switch(format0) { - case 0xa1: - prach_pdu->prach_format = 9; - break; - case 0xa2: - prach_pdu->prach_format = 10; - break; - case 0xa3: - prach_pdu->prach_format = 11; - break; - default: - AssertFatal(1==0,"Only formats A1/B1 A2/B2 A3/B3 are valid for dual format"); - } - } - else{ - switch(format0) { - case 0xa1: - prach_pdu->prach_format = 0; - break; - case 0xa2: - prach_pdu->prach_format = 1; - break; - case 0xa3: - prach_pdu->prach_format = 2; - break; - case 0xb1: - prach_pdu->prach_format = 3; - break; - case 0xb2: - prach_pdu->prach_format = 4; - break; - case 0xb3: - prach_pdu->prach_format = 5; - break; - case 0xb4: - prach_pdu->prach_format = 6; - break; - case 0xc0: - prach_pdu->prach_format = 7; - break; - case 0xc2: - prach_pdu->prach_format = 8; - break; - case 0: - // long formats are handled @ PHY - break; - case 1: - // long formats are handled @ PHY - break; - case 2: - // long formats are handled @ PHY - break; - case 3: - // long formats are handled @ PHY - break; - default: - AssertFatal(1==0,"Invalid PRACH format"); - } + prach_occasion_id = (((frameP % (cc->max_association_period * config_period))/config_period) * cc->total_prach_occasions_per_config_period) + (RA_sfn_index + slot_index) * N_t_slot * fdm + td_index * fdm + fdm_index; + if((prach_occasion_id < cc->total_prach_occasions) && (td_index == 0)){ + + UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE; + UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_prach_pdu_t); + nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[UL_tti_req->n_pdus].prach_pdu; + memset(prach_pdu,0,sizeof(nfapi_nr_prach_pdu_t)); + UL_tti_req->n_pdus+=1; + + // filling the prach fapi structure + prach_pdu->phys_cell_id = *scc->physCellId; + prach_pdu->num_prach_ocas = N_t_slot; + prach_pdu->prach_start_symbol = start_symbol; + prach_pdu->num_ra = fdm_index; + prach_pdu->num_cs = get_NCS(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.zeroCorrelationZoneConfig, + format0, + scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->restrictedSetConfig); + + LOG_D(MAC, "Frame %d, Slot %d: Prach Occasion id = %u fdm index = %u start symbol = %u slot index = %u subframe index = %u \n", + frameP, slotP, + prach_occasion_id, prach_pdu->num_ra, + prach_pdu->prach_start_symbol, + slot_index, RA_sfn_index); + // SCF PRACH PDU format field does not consider A1/B1 etc. possibilities + // We added 9 = A1/B1 10 = A2/B2 11 A3/B3 + if (format1!=0xff) { + switch(format0) { + case 0xa1: + prach_pdu->prach_format = 11; + break; + case 0xa2: + prach_pdu->prach_format = 12; + break; + case 0xa3: + prach_pdu->prach_format = 13; + break; + default: + AssertFatal(1==0,"Only formats A1/B1 A2/B2 A3/B3 are valid for dual format"); } } + else{ + switch(format0) { + case 0: + prach_pdu->prach_format = 0; + break; + case 1: + prach_pdu->prach_format = 1; + break; + case 2: + prach_pdu->prach_format = 2; + break; + case 3: + prach_pdu->prach_format = 3; + break; + case 0xa1: + prach_pdu->prach_format = 4; + break; + case 0xa2: + prach_pdu->prach_format = 5; + break; + case 0xa3: + prach_pdu->prach_format = 6; + break; + case 0xb1: + prach_pdu->prach_format = 7; + break; + case 0xb4: + prach_pdu->prach_format = 8; + break; + case 0xc0: + prach_pdu->prach_format = 9; + break; + case 0xc2: + prach_pdu->prach_format = 10; + break; + default: + AssertFatal(1==0,"Invalid PRACH format"); + } + } + } } + } + } } } @@ -168,7 +342,7 @@ void nr_schedule_msg2(uint16_t rach_frame, uint16_t rach_slot, uint16_t *msg2_frame, uint16_t *msg2_slot, NR_ServingCellConfigCommon_t *scc, uint16_t monitoring_slot_period, - uint16_t monitoring_offset){ + uint16_t monitoring_offset,uint8_t index,uint8_t num_active_ssb){ // preferentially we schedule the msg2 in the mixed slot or in the last dl slot // if they are allowed by search space configuration @@ -189,7 +363,7 @@ void nr_schedule_msg2(uint16_t rach_frame, uint16_t rach_slot, uint8_t start_next_period = (rach_slot-(rach_slot%tdd_period_slot)+tdd_period_slot)%nr_slots_per_frame[mu]; *msg2_slot = start_next_period + last_dl_slot_period; // initializing scheduling of slot to next mixed (or last dl) slot *msg2_frame = (*msg2_slot>(rach_slot))? rach_frame : (rach_frame +1); - + switch(response_window){ case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl1: slot_window = 1; @@ -255,7 +429,6 @@ void nr_initiate_ra_proc(module_id_t module_idP, NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id]; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; NR_RA_t *ra = &cc->ra[0]; - // if the preamble received correspond to one of the listed // the UE sent a RACH either for starting RA procedure or RA procedure failed and UE retries int pr_found=0; @@ -270,7 +443,6 @@ void nr_initiate_ra_proc(module_id_t module_idP, module_idP, preamble_index); return; // if the PRACH preamble does not correspond to any of the ones sent through RRC abort RA proc } - // This should be handled differently when we use the initialBWP for RA ra->bwp_id=1; NR_BWP_Downlink_t *bwp=ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; @@ -280,6 +452,13 @@ void nr_initiate_ra_proc(module_id_t module_idP, LOG_I(MAC, "[gNB %d][RAPROC] CC_id %d Frame %d, Slot %d Initiating RA procedure for preamble index %d\n", module_idP, CC_id, frameP, slotP, preamble_index); if (ra->state == RA_IDLE) { + + uint8_t beam_index = ssb_index_from_prach(module_idP, + frameP, + slotP, + preamble_index, + freq_index, + symbol); int loop = 0; LOG_D(MAC, "Frame %d, Slot %d: Activating RA process \n", frameP, slotP); ra->state = Msg2; @@ -301,33 +480,35 @@ void nr_initiate_ra_proc(module_id_t module_idP, &monitoring_slot_period, &monitoring_offset); - nr_schedule_msg2(frameP, slotP, &msg2_frame, &msg2_slot, scc, monitoring_slot_period, monitoring_offset); + nr_schedule_msg2(frameP, slotP, &msg2_frame, &msg2_slot, scc, monitoring_slot_period, monitoring_offset,beam_index,cc->num_active_ssb); ra->Msg2_frame = msg2_frame; ra->Msg2_slot = msg2_slot; - LOG_D(MAC, "%s() Msg2[%04d%d] SFN/SF:%04d%d\n", __FUNCTION__, ra->Msg2_frame, ra->Msg2_slot, frameP, slotP); - - do { - ra->rnti = (taus() % 65518) + 1; - loop++; + LOG_I(MAC, "%s() Msg2[%04d%d] SFN/SF:%04d%d\n", __FUNCTION__, ra->Msg2_frame, ra->Msg2_slot, frameP, slotP); + if (!ra->cfra) { + do { + ra->rnti = (taus() % 65518) + 1; + loop++; + } + while (loop != 100 && !((find_nr_UE_id(module_idP, ra->rnti) == -1) && (find_nr_RA_id(module_idP, CC_id, ra->rnti) == -1) && ra->rnti >= 1 && ra->rnti <= 65519)); + if (loop == 100) { + LOG_E(MAC,"%s:%d:%s: [RAPROC] initialisation random access aborted\n", __FILE__, __LINE__, __FUNCTION__); + abort(); + } } - while (loop != 100 && !(find_nr_UE_id(module_idP, ra->rnti) == -1 && ra->rnti >= 1 && ra->rnti <= 65519)); - if (loop == 100) { - LOG_E(MAC,"%s:%d:%s: [RAPROC] initialisation random access aborted\n", __FILE__, __LINE__, __FUNCTION__); - abort(); - } - ra->RA_rnti = ra_rnti; ra->preamble_index = preamble_index; + ra->beam_id = beam_index; - LOG_I(MAC,"[gNB %d][RAPROC] CC_id %d Frame %d Activating Msg2 generation in frame %d, slot %d using RA rnti %x\n", + LOG_I(MAC,"[gNB %d][RAPROC] CC_id %d Frame %d Activating Msg2 generation in frame %d, slot %d using RA rnti %x SSB index %u\n", module_idP, CC_id, frameP, ra->Msg2_frame, ra->Msg2_slot, - ra->RA_rnti); + ra->RA_rnti, + cc->ssb_index[beam_index]); return; } @@ -342,26 +523,31 @@ void nr_schedule_RA(module_id_t module_idP, frame_t frameP, sub_frame_t slotP){ int CC_id = 0; gNB_MAC_INST *mac = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = &mac->common_channels[CC_id]; - NR_RA_t *ra = &cc->ra[0]; start_meas(&mac->schedule_ra); - //for (i = 0; i < NR_NB_RA_PROC_MAX; i++) { +// for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { +// for (int i = 0; i < NR_NB_RA_PROC_MAX; i++) { + +// NR_RA_t *ra = &cc->ra[i]; + NR_RA_t *ra = &cc->ra[0]; + LOG_D(MAC,"RA[state:%d]\n",ra->state); switch (ra->state){ case Msg2: nr_generate_Msg2(module_idP, CC_id, frameP, slotP); break; case Msg4: - //generate_Msg4(module_idP, CC_id, frameP, slotP, ra); + //generate_Msg4(module_idP, CC_id, frameP, slotP); break; case WAIT_Msg4_ACK: - //check_Msg4_retransmission(module_idP, CC_id, frameP, slotP, ra); + //check_Msg4_retransmission(module_idP, CC_id, frameP, slotP); break; default: break; } - //} +// } +// } stop_meas(&mac->schedule_ra); } @@ -398,6 +584,7 @@ void nr_get_Msg3alloc(NR_ServingCellConfigCommon_t *scc, else ra->Msg3_frame = current_frame + (temp_slot/nr_slots_per_frame[mu]); + LOG_I(MAC, "[RAPROC] Msg3 slot %d: current slot %u Msg3 frame %u k2 %u Msg3_tda_id %u start symbol index %u\n", ra->Msg3_slot, current_slot, ra->Msg3_frame, k2,ra->Msg3_tda_id, StartSymbolIndex); ra->msg3_nb_rb = 18; ra->msg3_first_rb = 0; } @@ -433,7 +620,7 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t return; } - LOG_D(MAC, "[gNB %d][RAPROC] Frame %d, Subframe %d : CC_id %d RA is active, Msg3 in (%d,%d)\n", module_idP, frameP, slotP, CC_id, ra->Msg3_frame, ra->Msg3_slot); + LOG_I(MAC, "[gNB %d][RAPROC] Frame %d, Subframe %d : CC_id %d RA is active, Msg3 in (%d,%d)\n", module_idP, frameP, slotP, CC_id, ra->Msg3_frame, ra->Msg3_slot); nfapi_nr_pusch_pdu_t *pusch_pdu = &ra->pusch_pdu; memset(pusch_pdu, 0, sizeof(nfapi_nr_pusch_pdu_t)); @@ -530,13 +717,14 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, - sub_frame_t slotP){ + sub_frame_t slotP) +{ int dci_formats[2], rnti_types[2], mcsIndex; int startSymbolAndLength = 0, StartSymbolIndex = -1, NrOfSymbols = 14, StartSymbolIndex_tmp, NrOfSymbols_tmp, x_Overhead, time_domain_assignment = 0; gNB_MAC_INST *nr_mac = RC.nrmac[module_idP]; - NR_COMMON_channels_t *cc = &nr_mac->common_channels[0]; - NR_RA_t *ra = &cc->ra[0]; + NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id]; + NR_RA_t *ra = &cc->ra[CC_id]; NR_SearchSpace_t *ss = ra->ra_ss; uint16_t RA_rnti = ra->RA_rnti; @@ -593,7 +781,7 @@ void nr_generate_Msg2(module_id_t module_idP, NR_BWP_Downlink_t *bwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; NR_BWP_Uplink_t *ubwp=ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id-1]; - LOG_D(MAC, "[RAPROC] Scheduling common search space DCI type 1 dlBWP BW %d\n", dci10_bw); + LOG_I(MAC, "[RAPROC] Scheduling common search space DCI type 1 dlBWP BW %d\n", dci10_bw); // Qm>2 not allowed for RAR if (get_softmodem_params()->do_ra) @@ -674,13 +862,13 @@ void nr_generate_Msg2(module_id_t module_idP, uint8_t nr_of_candidates, aggregation_level; find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss); NR_ControlResourceSet_t *coreset = get_coreset(bwp, ss, 0 /* common */); - int CCEIndex = allocate_nr_CCEs( - nr_mac, - bwp, - coreset, - aggregation_level, - 0, /* n_RNTI 0: common search space */ - 0); // m + int CCEIndex = allocate_nr_CCEs(nr_mac, + bwp, + coreset, + aggregation_level, + 0, // Y + 0, // m + nr_of_candidates); if (CCEIndex < 0) { LOG_E(MAC, "%s(): cannot find free CCE for RA RNTI %04x!\n", __func__, ra->rnti); @@ -701,7 +889,7 @@ void nr_generate_Msg2(module_id_t module_idP, dci_formats[0] = NR_DL_DCI_FORMAT_1_0; rnti_types[0] = NR_RNTI_RA; - LOG_D(MAC, "[RAPROC] DCI params: rnti %d, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", + LOG_I(MAC, "[RAPROC] DCI params: rnti %d, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", pdcch_pdu_rel15->dci_pdu.RNTI[0], rnti_types[0], dci_formats[0], @@ -718,7 +906,7 @@ void nr_generate_Msg2(module_id_t module_idP, LOG_I(MAC, "Frame %d, Subframe %d: Setting Msg3 reception for Frame %d Subframe %d\n", frameP, slotP, ra->Msg3_frame, ra->Msg3_slot); nr_add_msg3(module_idP, CC_id, frameP, slotP); ra->state = WAIT_Msg3; - LOG_D(MAC,"[gNB %d][RAPROC] Frame %d, Subframe %d: RA state %d\n", module_idP, frameP, slotP, ra->state); + LOG_I(MAC,"[gNB %d][RAPROC] Frame %d, Subframe %d: RA state %d\n", module_idP, frameP, slotP, ra->state); x_Overhead = 0; nr_get_tbs_dl(&dl_tti_pdsch_pdu->pdsch_pdu, x_Overhead, pdsch_pdu_rel15->numDmrsCdmGrpsNoData, dci_pdu_rel15[0].tb_scaling); @@ -741,6 +929,7 @@ void nr_generate_Msg2(module_id_t module_idP, } void nr_clear_ra_proc(module_id_t module_idP, int CC_id, frame_t frameP){ + NR_RA_t *ra = &RC.nrmac[module_idP]->common_channels[CC_id].ra[0]; LOG_D(MAC,"[gNB %d][RAPROC] CC_id %d Frame %d Clear Random access information rnti %x\n", module_idP, CC_id, frameP, ra->rnti); ra->state = IDLE; @@ -786,7 +975,7 @@ void nr_fill_rar(uint8_t Mod_idP, uint8_t * dlsch_buffer, nfapi_nr_pusch_pdu_t *pusch_pdu){ - LOG_D(MAC, "[gNB] Generate RAR MAC PDU frame %d slot %d ", ra->Msg2_frame, ra-> Msg2_slot); + LOG_I(MAC, "[gNB] Generate RAR MAC PDU frame %d slot %d preamble index %u", ra->Msg2_frame, ra-> Msg2_slot, ra->preamble_index); NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) dlsch_buffer; NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 1); unsigned char csi_req = 0, tpc_command; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index 989d6fb952..daa1fd9c82 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -54,6 +54,7 @@ #include "common/ran_context.h" extern RAN_CONTEXT_t RC; +extern uint8_t SSB_Table[38]; void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t slotP){ @@ -66,15 +67,24 @@ void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t slotP){ int mib_sdu_length; int CC_id; - AssertFatal(slotP == 0, "Subframe must be 0\n"); AssertFatal((frameP & 7) == 0, "Frame must be a multiple of 8\n"); for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { + nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0]; dl_tti_request = &gNB->DL_req[CC_id]; dl_req = &dl_tti_request->dl_tti_request_body; cc = &gNB->common_channels[CC_id]; +#if 0 + //SSB is transmitted based on SSB periodicity + if((frameP % cfg->ssb_table.ssb_period.value) == 0) { + uint64_t L_ssb = (((uint64_t) cfg->ssb_table.ssb_mask_list[0].ssb_mask.value)<<32) | cfg->ssb_table.ssb_mask_list[1].ssb_mask.value ; + uint32_t ssb_index = -1; + for (int i=0; i<2; i++) { // max two SSB per slot + ssb_index = i + SSB_Table[slotP]; // computing the ssb_index + if ((ssb_index<64) && ((L_ssb >> (63-ssb_index)) & 0x01)) { // generating the ssb only if the bit of L_ssb at current ssb index is 1 +#endif mib_sdu_length = mac_rrc_nr_data_req(module_idP, CC_id, frameP, MIBCH, 1, &cc->MIB_pdu.payload[0]); // not used in this case @@ -96,7 +106,7 @@ void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t slotP){ AssertFatal(cc->ServingCellConfigCommon->physCellId!=NULL,"cc->ServingCellConfigCommon->physCellId is null\n"); dl_config_pdu->ssb_pdu.ssb_pdu_rel15.PhysCellId = *cc->ServingCellConfigCommon->physCellId; dl_config_pdu->ssb_pdu.ssb_pdu_rel15.BetaPss = 0; - dl_config_pdu->ssb_pdu.ssb_pdu_rel15.SsbBlockIndex = 0; + dl_config_pdu->ssb_pdu.ssb_pdu_rel15.SsbBlockIndex = 0;//ssb_index ;//SSB index for each SSB AssertFatal(cc->ServingCellConfigCommon->downlinkConfigCommon!=NULL,"scc->downlinkConfigCommonL is null\n"); AssertFatal(cc->ServingCellConfigCommon->downlinkConfigCommon->frequencyInfoDL!=NULL,"scc->downlinkConfigCommon->frequencyInfoDL is null\n"); AssertFatal(cc->ServingCellConfigCommon->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB!=NULL,"scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB is null\n"); @@ -132,8 +142,8 @@ void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t slotP){ default: AssertFatal(1==0,"SCS %ld not allowed for SSB \n", *cc->ServingCellConfigCommon->ssbSubcarrierSpacing); } - dl_config_pdu->ssb_pdu.ssb_pdu_rel15.SsbSubcarrierOffset = 0; //kSSB - dl_config_pdu->ssb_pdu.ssb_pdu_rel15.ssbOffsetPointA = ssb_offset0/(ratio*12) - 10; // absoluteFrequencySSB is the center of SSB + dl_config_pdu->ssb_pdu.ssb_pdu_rel15.SsbSubcarrierOffset = cfg->ssb_table.ssb_subcarrier_offset.value; //kSSB + dl_config_pdu->ssb_pdu.ssb_pdu_rel15.ssbOffsetPointA = ssb_offset0/(ratio*12) - 10;/*cfg->ssb_table.ssb_offset_point_a.value;*/ // absoluteFrequencySSB is the center of SSB dl_config_pdu->ssb_pdu.ssb_pdu_rel15.bchPayloadFlag = 1; dl_config_pdu->ssb_pdu.ssb_pdu_rel15.bchPayload = (*(uint32_t*)cc->MIB_pdu.payload) & ((1<<24)-1); dl_req->nPDUs++; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index ed142a42c8..4c6558d4f6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -488,16 +488,21 @@ void nr_simple_dlsch_preprocessor(module_id_t module_id, sched_ctrl->search_space); sched_ctrl->coreset = get_coreset( sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /* dedicated */); + int cid = sched_ctrl->coreset->controlResourceSetId; + const uint16_t Y = UE_info->Y[UE_id][cid][slot]; + const int m = UE_info->num_pdcch_cand[UE_id][cid]; sched_ctrl->cce_index = allocate_nr_CCEs(RC.nrmac[module_id], - sched_ctrl->active_bwp, - sched_ctrl->coreset, - sched_ctrl->aggregation_level, - UE_info->rnti[UE_id], - 0); // m + sched_ctrl->active_bwp, + sched_ctrl->coreset, + sched_ctrl->aggregation_level, + Y, + m, + nr_of_candidates); if (sched_ctrl->cce_index < 0) { LOG_E(MAC, "%s(): could not find CCE for UE %d\n", __func__, UE_id); return; } + UE_info->num_pdcch_cand[UE_id][cid]++; /* Find PUCCH occasion */ nr_update_pucch_scheduling( diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 5f9d5d8a10..4ee4e29213 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -417,18 +417,23 @@ int configure_fapi_dl_pdu_phytest(int Mod_idP, uint8_t nr_of_candidates, aggregation_level; find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss); NR_ControlResourceSet_t *coreset = get_coreset(bwp, ss, 1 /* dedicated */); - int CCEIndex = allocate_nr_CCEs( - nr_mac, - bwp, - coreset, - aggregation_level, - UE_info->rnti[UE_id], - 0); // m + const int cid = coreset->controlResourceSetId; + const uint16_t Y = UE_info->Y[UE_id][cid][nr_mac->current_slot]; + const int m = UE_info->num_pdcch_cand[UE_id][cid]; + int CCEIndex = allocate_nr_CCEs(nr_mac, + bwp, + coreset, + aggregation_level, + Y, + m, + nr_of_candidates); if (CCEIndex < 0) { LOG_E(MAC, "%s(): CCE list not empty, couldn't schedule PDSCH\n", __func__); free(dci_pdu_rel15); return 0; } + UE_info->num_pdcch_cand[UE_id][cid]++; + nr_configure_pdcch(nr_mac, pdcch_pdu_rel15, UE_info->rnti[UE_id], @@ -864,7 +869,8 @@ void schedule_fapi_ul_pdu(int Mod_idP, * conditions might exclude each other and never be true */ const int slot_idx = (slotP + K2) % num_slots_per_tdd; if (is_xlsch_in_slot(ulsch_in_slot_bitmap, slot_idx) - && (!get_softmodem_params()->phy_test || slot_idx == 8)) { + && (!get_softmodem_params()->phy_test || slot_idx == 8)) { + nfapi_nr_ul_dci_request_t *UL_dci_req = &RC.nrmac[Mod_idP]->UL_dci_req[0]; UL_dci_req->SFN = frameP; UL_dci_req->Slot = slotP; @@ -1126,19 +1132,23 @@ void schedule_fapi_ul_pdu(int Mod_idP, uint8_t nr_of_candidates, aggregation_level; find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss); NR_ControlResourceSet_t *coreset = get_coreset(bwp, ss, 1 /* dedicated */); - int CCEIndex = allocate_nr_CCEs( - nr_mac, - bwp, - coreset, - aggregation_level, - UE_info->rnti[UE_id], - 0); // m + const int cid = coreset->controlResourceSetId; + const uint16_t Y = UE_info->Y[UE_id][cid][nr_mac->current_slot]; + const int m = UE_info->num_pdcch_cand[UE_id][cid]; + int CCEIndex = allocate_nr_CCEs(nr_mac, + bwp, + coreset, + aggregation_level, + Y, + m, + nr_of_candidates); if (CCEIndex < 0) { LOG_E(MAC, "%s(): CCE list not empty, couldn't schedule PUSCH\n", __func__); pusch_sched->active = false; return; } else { + UE_info->num_pdcch_cand[UE_id][cid]++; nr_configure_pdcch(nr_mac, pdcch_pdu_rel15, UE_info->rnti[UE_id], diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index a686215fd5..89bb360ff8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -172,8 +172,9 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, NR_BWP_Downlink_t *bwp, NR_ControlResourceSet_t *coreset, int aggregation, - uint16_t n_RNTI, - int m) { + uint16_t Y, + int m, + int nr_of_candidates) { // uncomment these when we allocate for common search space // NR_COMMON_channels_t *cc = nr_mac->common_channels; // NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; @@ -189,15 +190,19 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, n_rb*=6; uint16_t N_reg = n_rb * coreset->duration; - uint16_t n_CI=0; - const uint32_t A[3]={39827,39829,39839}; - /* if n_RNTI is zero, this results in zero, too! */ - uint16_t Y = (A[0]*n_RNTI)%65537; // Candidate 0, antenna port 0 + uint16_t n_CI = 0; - uint16_t N_cce = N_reg / NR_NB_REG_PER_CCE; + const uint16_t N_cce = N_reg / NR_NB_REG_PER_CCE; + const uint16_t M_s_max = nr_of_candidates; - uint16_t M_s_max = (aggregation==4)?4:(aggregation==8)?2:1; - int first_cce = aggregation * (( Y + (m*N_cce)/(aggregation*M_s_max) + n_CI ) % CEILIDIV(N_cce,aggregation)); + AssertFatal(m < nr_of_candidates, + "PDCCH candidate index %d in CORESET %d exceeds the maximum " + "number of PDCCH candidates (%d)\n", + m, + coreset_id, + nr_of_candidates); + + int first_cce = aggregation * (( Y + CEILIDIV((m*N_cce),(aggregation*M_s_max)) + n_CI ) % CEILIDIV(N_cce,aggregation)); for (int i=0;idmrs_sequence_initialization.val&0x1)<<(dci_size-pos); } + LOG_D(MAC, "DCI index %d has %d bits and the payload is %lx\n", d, dci_size, *dci_pdu); } } @@ -1619,6 +1625,40 @@ int find_nr_UE_id(module_id_t mod_idP, rnti_t rntiP) return -1; } +void set_Y(int Y[3][160], rnti_t rnti) { + const int A[3] = {39827, 39829, 39839}; + const int D = 65537; + + Y[0][0] = (A[0] * rnti) % D; + Y[1][0] = (A[1] * rnti) % D; + Y[2][0] = (A[2] * rnti) % D; + + for (int s = 1; s < 160; s++) { + Y[0][s] = (A[0] * Y[0][s - 1]) % D; + Y[1][s] = (A[1] * Y[1][s - 1]) % D; + Y[2][s] = (A[2] * Y[2][s - 1]) % D; + } +} + +int find_nr_RA_id(module_id_t mod_idP, int CC_idP, rnti_t rntiP) { +//------------------------------------------------------------------------------ + int RA_id; + RA_t *ra = (RA_t *) &RC.nrmac[mod_idP]->common_channels[CC_idP].ra[0]; + + for (RA_id = 0; RA_id < NB_RA_PROC_MAX; RA_id++) { + LOG_D(MAC, "Checking RA_id %d for %x : state %d\n", + RA_id, + rntiP, + ra[RA_id].state); + + if (ra[RA_id].state != IDLE && ra[RA_id].rnti == rntiP) + return RA_id; + } + + return -1; +} + +//------------------------------------------------------------------------------ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){ NR_UE_info_t *UE_info = &RC.nrmac[mod_idP]->UE_info; @@ -1642,6 +1682,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){ UE_info->active[UE_id] = true; UE_info->rnti[UE_id] = rntiP; add_nr_ue_list(&UE_info->list, UE_id); + set_Y(UE_info->Y[UE_id], rntiP); memset((void *) &UE_info->UE_sched_ctrl[UE_id], 0, sizeof(NR_UE_sched_ctrl_t)); @@ -1684,6 +1725,7 @@ void mac_remove_nr_ue(module_id_t mod_id, rnti_t rnti) NR_UE_info_t *UE_info = &RC.nrmac[mod_id]->UE_info; for (i = 0; i < MAX_MOBILES_PER_GNB; i++) { + if (UE_info->active[i] != TRUE) continue; if (UE_info->rnti[i] != rnti) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index a3dc3872c4..0479c4da30 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -322,6 +322,42 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, UE_info = &gNB_mac->UE_info; int target_snrx10 = gNB_mac->pusch_target_snrx10; + NR_RA_t *ra = &gNB_mac->common_channels[CC_idP].ra[0]; + + // random access pusch with TC-RNTI + if (ra->state == WAIT_Msg3) { + if (sduP != NULL) { // if the CRC passed + + if (ra->rnti != current_rnti) { + LOG_E(MAC, + "expected TC-RNTI %04x to match current RNTI %04x\n", + ra->rnti, + current_rnti); + return; + } + free(ra->preambles.preamble_list); + ra->state = RA_IDLE; + LOG_I(MAC, "reset RA state information for RA-RNTI %04x\n", ra->rnti); + const int UE_id = add_new_nr_ue(gnb_mod_idP, ra->rnti); + UE_info->secondaryCellGroup[UE_id] = ra->secondaryCellGroup; + UE_info->UE_beam_index[UE_id] = ra->beam_id; + struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList; + AssertFatal(bwpList->list.count == 1, + "downlinkBWP_ToAddModList has %d BWP!\n", + bwpList->list.count); + const int bwp_id = 1; + UE_info->UE_sched_ctrl[UE_id].active_bwp = bwpList->list.array[bwp_id - 1]; + LOG_I(MAC, + "[gNB %d][RAPROC] PUSCH with TC_RNTI %x received correctly, " + "adding UE MAC Context UE_id %d/RNTI %04x\n", + gnb_mod_idP, + current_rnti, + UE_id, + ra->rnti); + } + return; + } + if (UE_id != -1) { UE_scheduling_control = &(UE_info->UE_sched_ctrl[UE_id]); @@ -366,44 +402,5 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, } } - else if (sduP != NULL) { // if the CRC passed - // random access pusch with TC-RNTI - NR_RA_t *ra = &gNB_mac->common_channels[CC_idP].ra[0]; - if (ra->state != WAIT_Msg3) { - LOG_E(MAC, - "expected RA state WAIT_Msg3/%d (but is %d) for RA-RNTI %04x\n", - WAIT_Msg3, - ra->state, - ra->rnti); - return; - } - if (ra->rnti != current_rnti) { - LOG_E(MAC, - "expected RA-RNTI %04x (C-RNTI %04x) to match current RNTI %04x\n", - ra->rnti, - ra->crnti, - current_rnti); - return; - } - free(ra->preambles.preamble_list); - ra->state = RA_IDLE; - LOG_I(MAC, "reset RA state information for RA-RNTI %04x\n", ra->rnti); - const int UE_id = add_new_nr_ue(gnb_mod_idP, ra->crnti); - UE_info->secondaryCellGroup[UE_id] = ra->secondaryCellGroup; - struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = - ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList; - AssertFatal(bwpList->list.count == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", - bwpList->list.count); - const int bwp_id = 1; - UE_info->UE_sched_ctrl[UE_id].active_bwp = bwpList->list.array[bwp_id - 1]; - LOG_W(MAC, - "[gNB %d][RAPROC] PUSCH with TC_RNTI %x received correctly, " - "adding UE MAC Context UE_id %d/RNTI %04x\n", - gnb_mod_idP, - current_rnti, - UE_id, - ra->crnti); - } } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 726615b6e3..f7e62c9755 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -306,6 +306,8 @@ void add_nr_ue_list(NR_UE_list_t *listP, int UE_id); int find_nr_UE_id(module_id_t mod_idP, rnti_t rntiP); +int find_nr_RA_id(module_id_t mod_idP, int CC_idP, rnti_t rntiP); + int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP); void mac_remove_nr_ue(module_id_t mod_id, rnti_t rnti); @@ -314,8 +316,9 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, NR_BWP_Downlink_t *bwp, NR_ControlResourceSet_t *coreset, int aggregation, - uint16_t n_RNTI, - int m); + uint16_t Y, + int m, + int nr_of_candidates); int get_dlscs(nfapi_nr_config_request_t *cfg); @@ -386,5 +389,14 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, void handle_nr_ul_harq(uint16_t slot, NR_UE_sched_ctrl_t *sched_ctrl, NR_mac_stats_t *stats, nfapi_nr_crc_t crc_pdu); +int16_t ssb_index_from_prach(module_id_t module_idP, + frame_t frameP, + sub_frame_t slotP, + uint16_t preamble_index, + uint8_t freq_index, + uint8_t symbol); + +void find_SSB_and_RO_available(module_id_t module_idP); + void handle_nr_uci(NR_UL_IND_t *UL_info, NR_UE_sched_ctrl_t *sched_ctrl, NR_mac_stats_t *stats, int target_snrx10); #endif /*__LAYER2_NR_MAC_PROTO_H__*/ diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 0ed64a9621..f6fdea064c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -74,6 +74,7 @@ #define MAX_NUM_CCE 90 /*!\brief Maximum number of random access process */ #define NR_NB_RA_PROC_MAX 4 +#define MAX_NUM_OF_SSB 64 typedef enum { RA_IDLE = 0, @@ -146,12 +147,16 @@ typedef struct { int msg4_mcs; /// RA search space NR_SearchSpace_t *ra_ss; + // Beam index + uint8_t beam_id; /// secondaryCellGroup for UE in NSA that is to come NR_CellGroupConfig_t *secondaryCellGroup; /// Preambles for contention-free access NR_preamble_ue_t preambles; /// NSA: the UEs C-RNTI to use rnti_t crnti; + /// CFRA flag + bool cfra; } NR_RA_t; /*! \brief gNB common channels */ @@ -188,6 +193,16 @@ typedef struct { uint8_t vrb_map_UL[275]; /// number of subframe allocation pattern available for MBSFN sync area uint8_t num_sf_allocation_pattern; + ///Number of active SSBs + uint8_t num_active_ssb; + //Total available prach occasions per configuration period + uint32_t total_prach_occasions_per_config_period; + //Total available prach occasions + uint32_t total_prach_occasions; + //Max Association period + uint8_t max_association_period; + //SSB index + uint8_t ssb_index[MAX_NUM_OF_SSB]; } NR_COMMON_channels_t; @@ -380,6 +395,12 @@ typedef struct { bool active[MAX_MOBILES_PER_GNB]; rnti_t rnti[MAX_MOBILES_PER_GNB]; NR_CellGroupConfig_t *secondaryCellGroup[MAX_MOBILES_PER_GNB]; + /// CCE indexing + int Y[MAX_MOBILES_PER_GNB][3][160]; + int m[MAX_MOBILES_PER_GNB]; + int num_pdcch_cand[MAX_MOBILES_PER_GNB][MAX_NUM_CORESET]; + // UE selected beam index + uint8_t UE_beam_index[MAX_MOBILES_PER_GNB]; } NR_UE_info_t; /*! \brief top level eNB MAC structure */ @@ -442,6 +463,8 @@ typedef struct gNB_MAC_INST_s { time_stats_t schedule_pch; /// CCE lists int cce_list[MAX_NUM_BWP][MAX_NUM_CORESET][MAX_NUM_CCE]; + /// current slot + int current_slot; } gNB_MAC_INST; #endif /*__LAYER2_NR_MAC_GNB_H__ */ diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index 22c48d11eb..22099c340a 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -56,24 +56,25 @@ extern uint16_t sf_ahead; extern uint16_t sl_ahead; void handle_nr_rach(NR_UL_IND_t *UL_info) { + if (UL_info->rach_ind.number_of_pdus>0) { - AssertFatal(UL_info->rach_ind.number_of_pdus==1,"More than 1 RACH pdu not supported\n"); - UL_info->rach_ind.number_of_pdus=0; - LOG_D(MAC,"UL_info[Frame %d, Slot %d] Calling initiate_ra_proc RACH:SFN/SLOT:%d/%d\n",UL_info->frame,UL_info->slot, UL_info->rach_ind.sfn,UL_info->rach_ind.slot); - - if (UL_info->rach_ind.pdu_list[0].num_preamble>0) - AssertFatal(UL_info->rach_ind.pdu_list[0].num_preamble==1, - "More than 1 preamble not supported\n"); + LOG_I(MAC,"UL_info[Frame %d, Slot %d] Calling initiate_ra_proc RACH:SFN/SLOT:%d/%d\n",UL_info->frame,UL_info->slot, UL_info->rach_ind.sfn,UL_info->rach_ind.slot); + int npdus = UL_info->rach_ind.number_of_pdus; + for(int i = 0; i < npdus; i++) { + UL_info->rach_ind.number_of_pdus--; + if (UL_info->rach_ind.pdu_list[i].num_preamble>0) + AssertFatal(UL_info->rach_ind.pdu_list[i].num_preamble==1, + "More than 1 preamble not supported\n"); - nr_initiate_ra_proc(UL_info->module_id, - UL_info->CC_id, - UL_info->rach_ind.sfn, - UL_info->rach_ind.slot, - UL_info->rach_ind.pdu_list[0].preamble_list[0].preamble_index, - UL_info->rach_ind.pdu_list[0].freq_index, - UL_info->rach_ind.pdu_list[0].symbol_index, - UL_info->rach_ind.pdu_list[0].preamble_list[0].timing_advance); - + nr_initiate_ra_proc(UL_info->module_id, + UL_info->CC_id, + UL_info->rach_ind.sfn, + UL_info->rach_ind.slot, + UL_info->rach_ind.pdu_list[i].preamble_list[0].preamble_index, + UL_info->rach_ind.pdu_list[i].freq_index, + UL_info->rach_ind.pdu_list[i].symbol_index, + UL_info->rach_ind.pdu_list[i].preamble_list[0].timing_advance); + } } } diff --git a/openair2/PHY_INTERFACE/IF_Module.h b/openair2/PHY_INTERFACE/IF_Module.h index 42a4459732..1470cbc006 100644 --- a/openair2/PHY_INTERFACE/IF_Module.h +++ b/openair2/PHY_INTERFACE/IF_Module.h @@ -203,17 +203,6 @@ typedef struct IF_Module_s{ pthread_mutex_t if_mutex; } IF_Module_t; -// These mutex is used for multiple UEs L2 FAPI simulator. -// Each UEs set these value in UL and UL_INFO is shared in all UE's thread. -typedef struct { - pthread_mutex_t rx_mutex; - pthread_mutex_t crc_mutex; - pthread_mutex_t sr_mutex; - pthread_mutex_t harq_mutex; - pthread_mutex_t cqi_mutex; - pthread_mutex_t rach_mutex; -} FILL_UL_INFO_MUTEX_t; - /*Initial */ IF_Module_t *IF_Module_init(int Mod_id); void IF_Module_kill(int Mod_id); diff --git a/openair2/PHY_INTERFACE/phy_stub_UE.c b/openair2/PHY_INTERFACE/phy_stub_UE.c index de42d67624..b4a7f8c6e7 100644 --- a/openair2/PHY_INTERFACE/phy_stub_UE.c +++ b/openair2/PHY_INTERFACE/phy_stub_UE.c @@ -64,8 +64,6 @@ void fill_rx_indication_UE_MAC(module_id_t Mod_id, nfapi_rx_indication_pdu_t *pdu; int timing_advance_update; - pthread_mutex_lock(&fill_ul_mutex.rx_mutex); - UL_INFO->rx_ind.sfn_sf = frame << 4 | subframe; UL_INFO->rx_ind.rx_indication_body.tl.tag = NFAPI_RX_INDICATION_BODY_TAG; UL_INFO->rx_ind.vendor_extension = ul_config_req->vendor_extension; @@ -101,7 +99,6 @@ void fill_rx_indication_UE_MAC(module_id_t Mod_id, UL_INFO->rx_ind.rx_indication_body.number_of_pdus++; UL_INFO->rx_ind.sfn_sf = frame << 4 | subframe; - pthread_mutex_unlock(&fill_ul_mutex.rx_mutex); } void fill_sr_indication_UE_MAC(int Mod_id, @@ -109,8 +106,6 @@ void fill_sr_indication_UE_MAC(int Mod_id, int subframe, UL_IND_t *UL_INFO, uint16_t rnti) { - pthread_mutex_lock(&fill_ul_mutex.sr_mutex); - nfapi_sr_indication_t *sr_ind = &UL_INFO->sr_ind; nfapi_sr_indication_body_t *sr_ind_body = &sr_ind->sr_indication_body; nfapi_sr_indication_pdu_t *pdu = &sr_ind_body->sr_pdu_list[sr_ind_body->number_of_srs]; @@ -139,7 +134,6 @@ void fill_sr_indication_UE_MAC(int Mod_id, // UL_INFO->rx_ind.rx_indication_body.number_of_pdus++; sr_ind_body->number_of_srs++; - pthread_mutex_unlock(&fill_ul_mutex.sr_mutex); } void fill_crc_indication_UE_MAC(int Mod_id, @@ -149,8 +143,6 @@ void fill_crc_indication_UE_MAC(int Mod_id, uint8_t crc_flag, int index, uint16_t rnti) { - pthread_mutex_lock(&fill_ul_mutex.crc_mutex); - nfapi_crc_indication_pdu_t *pdu = &UL_INFO->crc_ind.crc_indication_body .crc_pdu_list[UL_INFO->crc_ind.crc_indication_body.number_of_crcs]; @@ -174,8 +166,6 @@ void fill_crc_indication_UE_MAC(int Mod_id, __FUNCTION__, pdu->rx_ue_information.rnti, UL_INFO->crc_ind.crc_indication_body.number_of_crcs); - - pthread_mutex_unlock(&fill_ul_mutex.crc_mutex); } void fill_rach_indication_UE_MAC(int Mod_id, @@ -184,10 +174,6 @@ void fill_rach_indication_UE_MAC(int Mod_id, UL_IND_t *UL_INFO, uint8_t ra_PreambleIndex, uint16_t ra_RNTI) { - LOG_D(MAC, "fill_rach_indication_UE_MAC 1 \n"); - - pthread_mutex_lock(&fill_ul_mutex.rach_mutex); - UL_INFO->rach_ind.rach_indication_body.number_of_preambles = 1; UL_INFO->rach_ind.header.message_id = NFAPI_RACH_INDICATION; @@ -232,8 +218,6 @@ void fill_rach_indication_UE_MAC(int Mod_id, // should call it when we merge with that branch. oai_nfapi_rach_ind(&UL_INFO->rach_ind); free(UL_INFO->rach_ind.rach_indication_body.preamble_list); - - pthread_mutex_unlock(&fill_ul_mutex.rach_mutex); } void fill_ulsch_cqi_indication_UE_MAC(int Mod_id, @@ -241,7 +225,6 @@ void fill_ulsch_cqi_indication_UE_MAC(int Mod_id, uint8_t subframe, UL_IND_t *UL_INFO, uint16_t rnti) { - pthread_mutex_lock(&fill_ul_mutex.cqi_mutex); nfapi_cqi_indication_pdu_t *pdu = &UL_INFO->cqi_ind.cqi_indication_body .cqi_pdu_list[UL_INFO->cqi_ind.cqi_indication_body.number_of_cqis]; @@ -275,7 +258,6 @@ void fill_ulsch_cqi_indication_UE_MAC(int Mod_id, raw_pdu->pdu[0] = cqi << 4; UL_INFO->cqi_ind.cqi_indication_body.number_of_cqis++; - pthread_mutex_unlock(&fill_ul_mutex.cqi_mutex); } void fill_ulsch_harq_indication_UE_MAC( @@ -285,8 +267,6 @@ void fill_ulsch_harq_indication_UE_MAC( UL_IND_t *UL_INFO, nfapi_ul_config_ulsch_harq_information *harq_information, uint16_t rnti) { - pthread_mutex_lock(&fill_ul_mutex.harq_mutex); - nfapi_harq_indication_pdu_t *pdu = &UL_INFO->harq_ind.harq_indication_body.harq_pdu_list [UL_INFO->harq_ind.harq_indication_body.number_of_harqs]; @@ -318,7 +298,6 @@ void fill_ulsch_harq_indication_UE_MAC( } UL_INFO->harq_ind.harq_indication_body.number_of_harqs++; - pthread_mutex_unlock(&fill_ul_mutex.harq_mutex); } void fill_uci_harq_indication_UE_MAC(int Mod_id, @@ -327,8 +306,6 @@ void fill_uci_harq_indication_UE_MAC(int Mod_id, UL_IND_t *UL_INFO, nfapi_ul_config_harq_information *harq_information, uint16_t rnti) { - pthread_mutex_lock(&fill_ul_mutex.harq_mutex); - nfapi_harq_indication_t *ind = &UL_INFO->harq_ind; nfapi_harq_indication_body_t *body = &ind->harq_indication_body; nfapi_harq_indication_pdu_t *pdu = @@ -410,7 +387,6 @@ void fill_uci_harq_indication_UE_MAC(int Mod_id, LOG_D(PHY, "Incremented eNB->UL_INFO.harq_ind.number_of_harqs:%d\n", UL_INFO->harq_ind.harq_indication_body.number_of_harqs); - pthread_mutex_unlock(&fill_ul_mutex.harq_mutex); } void handle_nfapi_ul_pdu_UE_MAC(module_id_t Mod_id, diff --git a/openair2/PHY_INTERFACE/phy_stub_UE.h b/openair2/PHY_INTERFACE/phy_stub_UE.h index 09356fc9f8..547fc2dfc4 100644 --- a/openair2/PHY_INTERFACE/phy_stub_UE.h +++ b/openair2/PHY_INTERFACE/phy_stub_UE.h @@ -17,8 +17,6 @@ //#include "openair1/PHY/defs.h" //#include "openair1/PHY/LTE_TRANSPORT/defs.h" -// this mutex is used to set multiple UE's UL value in L2 FAPI simulator. -FILL_UL_INFO_MUTEX_t fill_ul_mutex; //below 2 difinitions move to phy_stub_UE.c to add initialization when difinition. extern UL_IND_t *UL_INFO; extern nfapi_tx_request_pdu_t* tx_request_pdu_list; diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index b8962051d8..5863b8102b 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -174,10 +174,41 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.present = NR_CFRA__resources_PR_ssb; secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb = calloc(1,sizeof(struct NR_CFRA__resources__ssb)); secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ra_ssb_OccasionMaskIndex = 0; - struct NR_CFRA_SSB_Resource *ssbElem = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); - ssbElem->ssb = 0; - ssbElem->ra_PreambleIndex= 63; - ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem); + struct NR_CFRA_SSB_Resource *ssbElem[8]; + ssbElem[0] = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); + ssbElem[0]->ssb = 0; + ssbElem[0]->ra_PreambleIndex = 63; + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem[0]); +#if 0 + ssbElem[1] = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); + ssbElem[1]->ssb = 1; + ssbElem[1]->ra_PreambleIndex = 62; + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem[1]); + ssbElem[2] = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); + ssbElem[2]->ssb = 2; + ssbElem[2]->ra_PreambleIndex = 63; + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem[2]); + ssbElem[3] = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); + ssbElem[3]->ssb = 3; + ssbElem[3]->ra_PreambleIndex = 63; + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem[3]); + ssbElem[4] = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); + ssbElem[4]->ssb = 4; + ssbElem[4]->ra_PreambleIndex = 63; + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem[4]); + ssbElem[5] = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); + ssbElem[5]->ssb = 5; + ssbElem[5]->ra_PreambleIndex = 63; + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem[5]); + ssbElem[6] = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); + ssbElem[6]->ssb = 6; + ssbElem[6]->ra_PreambleIndex = 63; + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem[6]); + ssbElem[7] = calloc(1,sizeof(struct NR_CFRA_SSB_Resource)); + ssbElem[7]->ssb = 7; + ssbElem[7]->ra_PreambleIndex = 63; + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ssb_ResourceList.list,ssbElem[7]); +#endif secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->ext1 = NULL; secondaryCellGroup->spCellConfig->rlf_TimersAndConstants = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->rlf_TimersAndConstants)); secondaryCellGroup->spCellConfig->rlf_TimersAndConstants->present = NR_SetupRelease_RLF_TimersAndConstants_PR_setup; diff --git a/targets/RT/USER/lte-enb.c b/targets/RT/USER/lte-enb.c index 147bc955ae..7d6cb9c429 100644 --- a/targets/RT/USER/lte-enb.c +++ b/targets/RT/USER/lte-enb.c @@ -1177,7 +1177,7 @@ void init_eNB_afterRU(void) { for (i=0; iRU_list[ru_id]->nb_rx; aa++,i++) { LOG_I(PHY,"Attaching RU %d antenna %d to eNB antenna %d\n",eNB->RU_list[ru_id]->idx,i,aa); - eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[ru_id]->prach_rxsigF[i]; + eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[ru_id]->prach_rxsigF[0][i]; for (int ce_level=0; ce_level<4; ce_level++) eNB->prach_vars_br.rxsigF[ce_level][aa] = eNB->RU_list[ru_id]->prach_rxsigF_br[ce_level][i];