Compare commits

..

1 Commits

Author SHA1 Message Date
Reem Bahsoun
cf95607417 run 2 DL throughput tests in a row 2025-07-25 11:56:00 +02:00
540 changed files with 10560 additions and 17007 deletions

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@
cmake_targets/log/
cmake_targets/*/build/
cmake_targets/ran_build/
cmake_targets/nas_sim_tools/build/
log/
lte_build_oai/

View File

@@ -36,8 +36,6 @@ if(NOT DEFINED ENV{CPM_SOURCE_CACHE})
endif()
include("cmake_targets/CPM.cmake")
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}")
##############################
### CCache: reduce compilation time
##############################
@@ -174,7 +172,7 @@ else ()
endif()
# add autotools definitions that were maybe used!
add_definitions(-DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_FCNTL_H=1 -DHAVE_ARPA_INET_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_SYS_SOCKET_H=1 -DHAVE_STRERROR=1 -DHAVE_SOCKET=1 -DHAVE_MEMSET=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_STDLIB_H=1 -DHAVE_MALLOC=1 -DHAVE_LIBSCTP)
add_definitions("-DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_FCNTL_H=1 -DHAVE_ARPA_INET_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_SYS_SOCKET_H=1 -DHAVE_STRERROR=1 -DHAVE_SOCKET=1 -DHAVE_MEMSET=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_STDLIB_H=1 -DHAVE_MALLOC=1 -DHAVE_LIBSCTP")
# we need -rdynamic to incorporate all symbols in shared objects, see man page
set(commonOpts "-pipe -fPIC -Wall -fno-strict-aliasing -rdynamic")
@@ -194,26 +192,26 @@ set(CMAKE_CXX_FLAGS
"${C_FLAGS_PROCESSOR} ${commonOpts} -std=c++11 ${CMAKE_CXX_FLAGS}")
add_boolean_option(SANITIZE_ADDRESS OFF "enable the address sanitizer (ASan)" ON)
add_boolean_option(SANITIZE_ADDRESS False "enable the address sanitizer (ASan)" ON)
if (SANITIZE_ADDRESS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-common")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-common")
endif ()
add_boolean_option(SANITIZE_THREAD OFF "enable the address sanitizer (TSan)" ON)
add_boolean_option(SANITIZE_THREAD False "enable the address sanitizer (TSan)" ON)
if (SANITIZE_THREAD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fno-omit-frame-pointer -fno-common")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fno-omit-frame-pointer -fno-common")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread")
endif()
add_boolean_option(SANITIZE_UNDEFINED OFF "enable the undefined behavior sanitizer (UBSan)" ON)
add_boolean_option(SANITIZE_UNDEFINED False "enable the undefined behavior sanitizer (UBSan)" ON)
if (SANITIZE_UNDEFINED)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
endif ()
add_boolean_option(SANITIZE_MEMORY OFF "enable the memory sanitizer (MSan, requires clang, incompatible with ASan/UBSan)" ON)
add_boolean_option(SANITIZE_MEMORY False "enable the memory sanitizer (MSan, requires clang, incompatible with ASan/UBSan)" ON)
if(SANITIZE_MEMORY)
if (SANITIZE_UNDEFINED OR SANITIZE_ADDRESS)
message(FATAL_ERROR "memory sanitizer cannot coexist with address sanitizer or undefined behavior sanitizer, please disable either MSan or ASan and UBSan")
@@ -278,16 +276,15 @@ endif()
# OAI uses this feature to re-use OAI LOG_I(ASN1, ...)
# see common/utils/config.h
add_boolean_option(TRACE_ASN1C_ENC_DEC OFF "Enable ASN1 encoder/decoder debug traces via OAI logging system" ON)
add_boolean_option(T_TRACER ON "Activate the T tracer, a debugging/monitoring framework" ON)
add_boolean_option(ENABLE_LTTNG OFF "Activate the LTTNG tracer, a debugging/monitoring framework" ON)
add_boolean_option(UE_DEBUG_TRACE OFF "Activate UE debug trace" ON)
add_boolean_option(UE_TIMING_TRACE OFF "Activate UE timing trace" ON)
add_boolean_option(T_TRACER True "Activate the T tracer, a debugging/monitoring framework" ON)
add_boolean_option(ENABLE_LTTNG False "Activate the LTTNG tracer, a debugging/monitoring framework" ON)
add_boolean_option(UE_AUTOTEST_TRACE False "Activate UE autotest specific logs" ON)
add_boolean_option(UE_DEBUG_TRACE False "Activate UE debug trace" ON)
add_boolean_option(UE_TIMING_TRACE False "Activate UE timing trace" ON)
add_boolean_option(TRACY_ENABLE OFF "Enable tracy instrumentation" ON)
if (TRACY_ENABLE)
# the tracy version here should match the tracy server version
# below is latest release as of this commit
CPMAddPackage("gh:wolfpld/tracy#0.12.2")
CPMAddPackage("gh:wolfpld/tracy#v0.11.1")
endif()
set (OCP_ITTI ${OPENAIR_DIR}/common/utils/ocp_itti)
@@ -339,6 +336,7 @@ target_link_libraries(nr_rrc PUBLIC asn1_nr_rrc asn1_lte_rrc)
set(S1AP_DIR ${OPENAIR3_DIR}/S1AP)
include_directories ("${S1AP_DIR}")
add_library(s1ap
${S1AP_DIR}/s1ap_common.c
${S1AP_DIR}/s1ap_eNB.c
${S1AP_DIR}/s1ap_eNB_context_management_procedures.c
${S1AP_DIR}/s1ap_eNB_decoder.c
@@ -510,34 +508,34 @@ include_directories ("${OPENAIR_DIR}/radio/COMMON")
# ???!!! TO BE DOCUMENTED OPTIONS !!!???
##############################################################
add_boolean_option(UE_EXPANSION OFF "enable UE_EXPANSION with max 256 UE" ON)
add_boolean_option(PHY_TX_THREAD OFF "enable UE_EXPANSION with max 256 UE" ON)
add_boolean_option(PRE_SCD_THREAD OFF "enable UE_EXPANSION with max 256 UE" ON)
add_boolean_option(UE_EXPANSION False "enable UE_EXPANSION with max 256 UE" ON)
add_boolean_option(PHY_TX_THREAD False "enable UE_EXPANSION with max 256 UE" ON)
add_boolean_option(PRE_SCD_THREAD False "enable UE_EXPANSION with max 256 UE" ON)
##########################
# SCHEDULING/REAL-TIME/PERF options
##########################
add_boolean_option(ENABLE_USE_CPU_EXECUTION_TIME OFF "Add data in vcd traces: disable it if perf issues" ON)
add_boolean_option(ENABLE_VCD OFF "always true now, time measurements of proc calls and var displays" ON)
add_boolean_option(ENABLE_VCD_FIFO OFF "time measurements of proc calls and var displays sent to FIFO (one more thread)" ON)
add_boolean_option(ENABLE_USE_CPU_EXECUTION_TIME False "Add data in vcd traces: disable it if perf issues" ON)
add_boolean_option(ENABLE_VCD False "always true now, time measurements of proc calls and var displays" ON)
add_boolean_option(ENABLE_VCD_FIFO False "time measurements of proc calls and var displays sent to FIFO (one more thread)" ON)
##########################
# PHY options
##########################
add_integer_option(MAX_NUM_CCs 1 "Carrier component data arrays size (oai doesn't support carrier aggregation for now)" ON)
add_boolean_option(SMBV OFF "Rohde&Schwarz SMBV100A vector signal generator" ON)
add_boolean_option(DEBUG_PHY OFF "Enable PHY layer debugging options" ON)
add_boolean_option(DEBUG_PHY_PROC OFF "Enable debugging of PHY layer procedures" ON)
add_integer_option(MAX_NUM_CCs 1 "Carrier component data arrays size (oai doesn't support carrier aggreagtion for now)" ON)
add_boolean_option(SMBV False "Rohde&Schwarz SMBV100A vector signal generator" ON)
add_boolean_option(DEBUG_PHY False "Enable PHY layer debugging options" ON)
add_boolean_option(DEBUG_PHY_PROC False "Enable debugging of PHY layer procedures" ON)
##########################
# NAS LAYER OPTIONS
##########################
add_boolean_option(NAS_BUILT_IN_UE ON "UE NAS layer present in this executable" ON)
add_boolean_option(NAS_BUILT_IN_UE True "UE NAS layer present in this executable" ON)
##########################
# RRC LAYER OPTIONS
##########################
add_boolean_option(RRC_DEFAULT_RAB_IS_AM ON "set the LTE RLC mode to AM for the default bearer, otherwise it is UM." ON)
add_boolean_option(RRC_DEFAULT_RAB_IS_AM True "set the LTE RLC mode to AM for the default bearer, otherwise it is UM." ON)
# add the binary tree to the search path for include files
# We will find common/oai_version.h after generation in target directory
@@ -604,7 +602,7 @@ include_directories("${OPENAIR_DIR}")
###############
# include T directory even if the T is off because T macros are in the code
# no matter what. Note: some files are generated, so we have both source and
# no matter what. Note: sone files are generated, so we have both source and
# binary directories
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common/utils/T
${CMAKE_CURRENT_BINARY_DIR}/common/utils/T)
@@ -850,7 +848,6 @@ set(PHY_SRC_COMMON
${OPENAIR1_DIR}/PHY/TOOLS/sqrt.c
${OPENAIR1_DIR}/PHY/TOOLS/get_sin_cos.c
${OPENAIR1_DIR}/PHY/TOOLS/oai_arith_operations.c
${OPENAIR1_DIR}/PHY/log_tools.c
)
set(PHY_SRC
@@ -1396,10 +1393,9 @@ include_directories("${OPENAIR1_DIR}/SCHED_NR_UE")
add_library (GTPV1U
${RRC_DIR}/rrc_eNB_GTPV1U.c
${OPENAIR3_DIR}/ocp-gtpu/gtp_itf.cpp
${OPENAIR3_DIR}/ocp-gtpu/gtpu_extensions.c
)
target_link_libraries(GTPV1U PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
target_link_libraries(GTPV1U PRIVATE SIMU ds)
target_link_libraries(GTPV1U PRIVATE SIMU)
include_directories(${OPENAIR3_DIR}/ocp-gtp)
set (MME_APP_SRC
@@ -1410,14 +1406,12 @@ add_library(MME_APP ${MME_APP_SRC})
target_link_libraries(MME_APP PRIVATE m2ap m3ap)
target_link_libraries(MME_APP PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
find_package(sctp REQUIRED)
set(SCTP_SRC
${OPENAIR3_DIR}/SCTP/sctp_common.c
${OPENAIR3_DIR}/SCTP/sctp_eNB_task.c
${OPENAIR3_DIR}/SCTP/sctp_eNB_itti_messaging.c
)
add_library(SCTP_CLIENT ${SCTP_SRC})
target_link_libraries(SCTP_CLIENT PRIVATE sctp::sctp)
target_link_libraries(SCTP_CLIENT PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
set(NAS_SRC ${OPENAIR3_DIR}/NAS/)
@@ -1716,6 +1710,7 @@ include_directories("${NFAPI_DIR}/pnf_sim/inc")
add_library(oai_iqplayer MODULE
${OPENAIR_DIR}/radio/iqplayer/iqplayer_lib.c
)
set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}")
#################################
# add executables for operation
@@ -1762,7 +1757,7 @@ target_link_libraries(lte-softmodem PRIVATE
${NAS_UE_LIB} ITTI SIMU shlib_loader
-Wl,--end-group z dl)
target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt)
target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt sctp)
target_link_libraries(lte-softmodem PRIVATE ${T_LIB})
target_link_libraries(lte-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
target_link_libraries(lte-softmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES} ${lapack_LIBRARIES})
@@ -1818,7 +1813,7 @@ target_link_libraries(lte-uesoftmodem PRIVATE
${NAS_UE_LIB} ITTI shlib_loader
-Wl,--end-group z dl)
target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt)
target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt sctp)
target_link_libraries(lte-uesoftmodem PRIVATE ${T_LIB})
target_link_libraries(lte-uesoftmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
target_link_libraries(lte-uesoftmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES} ${lapack_LIBRARIES})
@@ -1855,7 +1850,7 @@ target_link_libraries(nr-softmodem PRIVATE
time_management
-Wl,--end-group z dl)
target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt)
target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt sctp)
target_link_libraries(nr-softmodem PRIVATE ${T_LIB})
target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
@@ -1892,7 +1887,7 @@ target_link_libraries(nr-cuup PRIVATE
CONFIG_LIB ITTI SCTP_CLIENT
GTPV1U e1ap f1ap
time_management
z dl pthread shlib_loader ${T_LIB})
z sctp dl pthread shlib_loader ${T_LIB})
target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
if(E2_AGENT)
target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup)
@@ -2186,4 +2181,3 @@ add_subdirectory(openair1)
add_subdirectory(openair2)
add_subdirectory(openair3)
add_subdirectory(radio)
add_subdirectory(tests)

View File

@@ -835,13 +835,11 @@ def finalizeSlaveJob(jobName) {
selector: lastCompleted())
if (fileExists(fileName)) {
sh "sed -i -e 's#TEMPLATE_BUILD_TIME#${JOB_TIMESTAMP}#' ${fileName}"
} else {
sh "echo \"could not copy results from ${jobName}, please check pipeline ${BUILD_URL}\" > ${fileName}"
archiveArtifacts artifacts: fileName
// BUILD_URL is like http://server:port/jenkins/job/foo/15/
// no need to add a prefixed '/'
artifactUrl += 'artifact/' + fileName
}
archiveArtifacts artifacts: fileName
// BUILD_URL is like http://server:port/jenkins/job/foo/15/
// no need to add a prefixed '/'
artifactUrl += 'artifact/' + fileName
}
artifactUrl = "\n * [${jobName}](${artifactUrl})"
return artifactUrl

View File

@@ -98,10 +98,18 @@ pipeline {
echo "no LockResources given"
allParametersPresent = false
}
if (params.eNB_IPAddress == null) {
echo "no eNB_IPAddress given"
allParametersPresent = false
}
if (params.eNB_SourceCodePath == null) {
echo "no eNB_SourceCodePath given"
allParametersPresent = false
}
if (params.eNB_Credentials == null) {
echo "no eNB_Credentials given"
allParametersPresent = false
}
// the following 4 parameters should be pushed by the master trigger
// if not present, take the job GIT variables (used for developing)
if (params.eNB_Repository == null) {
@@ -180,6 +188,7 @@ pipeline {
}
}
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'],
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password']
]) {
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
@@ -187,7 +196,7 @@ pipeline {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName}"
sh "python3 main.py --mode=TesteNB --eNBIPAddress=${params.eNB_IPAddress} --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
@@ -195,7 +204,7 @@ pipeline {
}
}
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}"
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
}
}

View File

@@ -91,9 +91,24 @@ pipeline {
echo "no LockResources given"
allParametersPresent = false
}
if (params.eNB_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB_Credentials == null) {
allParametersPresent = false
}
if (params.eNB1_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB1_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB1_Credentials == null) {
allParametersPresent = false
}
// the following 4 parameters should be pushed by the master trigger
// if not present, take the job GIT variables (used for developing)
if (params.eNB_Repository == null) {
@@ -172,6 +187,8 @@ pipeline {
}
}
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'],
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB1_Credentials}", usernameVariable: 'eNB1_Username', passwordVariable: 'eNB1_Password'],
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password']
]) {
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
@@ -180,7 +197,7 @@ pipeline {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName}"
sh "python3 main.py --mode=TesteNB --eNBIPAddress=${params.eNB_IPAddress} --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName} --eNB1IPAddress=${params.eNB1_IPAddress} --eNB1UserName=${eNB1_Username} --eNB1Password=${eNB1_Password} --eNB1SourceCodePath=${params.eNB1_SourceCodePath}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
@@ -188,7 +205,7 @@ pipeline {
}
}
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}"
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
}
}

View File

@@ -92,9 +92,15 @@ pipeline {
echo "no LockResources given"
allParametersPresent = false
}
if (params.eNB_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB_Credentials == null) {
allParametersPresent = false
}
// the following 4 parameters should be pushed by the master trigger
// if not present, take the job GIT variables (used for developing)
if (params.eNB_Repository == null) {
@@ -173,6 +179,7 @@ pipeline {
}
}
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'],
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password']
]) {
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
@@ -181,7 +188,7 @@ pipeline {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName}"
sh "python3 main.py --mode=TesteNB --eNBIPAddress=${params.eNB_IPAddress} --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
@@ -189,7 +196,7 @@ pipeline {
}
}
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}"
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
}
}

View File

@@ -91,9 +91,15 @@ pipeline {
echo "no LockResources given"
allParametersPresent = false
}
if (params.eNB_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB_Credentials == null) {
allParametersPresent = false
}
// the following 4 parameters should be pushed by the master trigger
// if not present, take the job GIT variables (used for developing)
if (params.eNB_Repository == null) {
@@ -164,21 +170,25 @@ pipeline {
}
}
}
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
String[] myXmlTestSuite = testXMLFile.split("\\r?\\n")
for (xmlFile in myXmlTestSuite) {
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'],
]) {
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
String[] myXmlTestSuite = testXMLFile.split("\\r?\\n")
for (xmlFile in myXmlTestSuite) {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}"
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
buildStageStatus = false
}
}
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}"
}
}
}
@@ -188,7 +198,11 @@ pipeline {
stage('Terminate eNB') {
steps {
echo '\u2705 \u001B[32mTerminate eNB\u001B[0m'
sh "python3 ci-scripts/main.py --mode=TerminateeNB"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
sh "python3 ci-scripts/main.py --mode=TerminateeNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
}
post {
success {
@@ -202,11 +216,35 @@ pipeline {
}
stage('Log Collection') {
parallel {
stage('Log Collection (eNB - Build)') {
steps {
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}"
echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m'
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true"
}
script {
if(fileExists("build.log.${env.BUILD_ID}.zip")) {
archiveArtifacts "build.log.${env.BUILD_ID}.zip"
}
}
}
}
stage('Log Collection (eNB - Run)') {
steps {
echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}"
echo '\u2705 \u001B[32mLog Transfer (eNB - Run)\u001B[0m'
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/enb.log.zip ./enb.log.${env.BUILD_ID}.zip || true"
}
script {
if(fileExists("enb.log.${env.BUILD_ID}.zip")) {
archiveArtifacts "enb.log.${env.BUILD_ID}.zip"
@@ -242,7 +280,11 @@ pipeline {
failure {
script {
if (!termStatusArray[termENB]) {
sh "python3 ci-scripts/main.py --mode=TerminateeNB"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
sh "python3 ci-scripts/main.py --mode=TerminateeNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
}
}
}

View File

@@ -81,9 +81,36 @@ pipeline {
echo "no LockResources given"
allParametersPresent = false
}
// 1st eNB parameters
if (params.eNB_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB_Credentials == null) {
allParametersPresent = false
}
// 2nd eNB parameters
if (params.eNB1_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB1_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB1_Credentials == null) {
allParametersPresent = false
}
// 3rd eNB parameters
if (params.eNB2_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB2_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB2_Credentials == null) {
allParametersPresent = false
}
// the following 4 parameters should be pushed by the master trigger
// if not present, take the job GIT variables (used for developing)
if (params.eNB_Repository == null) {
@@ -154,21 +181,27 @@ pipeline {
}
}
}
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
String[] myXmlTestSuite = testXMLFile.split("\\r?\\n")
for (xmlFile in myXmlTestSuite) {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
buildStageStatus = false
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'],
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB1_Credentials}", usernameVariable: 'eNB1_Username', passwordVariable: 'eNB1_Password'],
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB2_Credentials}", usernameVariable: 'eNB2_Username', passwordVariable: 'eNB2_Password'],
]) {
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
String[] myXmlTestSuite = testXMLFile.split("\\r?\\n")
for (xmlFile in myXmlTestSuite) {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --eNB1IPAddress=${params.eNB1_IPAddress} --eNB1UserName=${eNB1_Username} --eNB1Password=${eNB1_Password} --eNB1SourceCodePath=${params.eNB1_SourceCodePath} --eNB2IPAddress=${params.eNB2_IPAddress} --eNB2UserName=${eNB2_Username} --eNB2Password=${eNB2_Password} --eNB2SourceCodePath=${params.eNB2_SourceCodePath} --XMLTestFile=${xmlFile}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
buildStageStatus = false
}
}
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}"
}
}
}
@@ -178,18 +211,46 @@ pipeline {
stage('Terminate eNB') {
steps {
echo '\u2705 \u001B[32mTerminate eNB\u001B[0m'
sh "python3 ci-scripts/main.py --mode=TerminateeNB"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
sh "python3 ci-scripts/main.py --mode=TerminateeNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
}
}
}
}
stage('Log Collection') {
parallel {
stage('Log Collection (eNB - Build)') {
steps {
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}"
echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m'
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true"
}
script {
if(fileExists("build.log.${env.BUILD_ID}.zip")) {
archiveArtifacts "build.log.${env.BUILD_ID}.zip"
}
}
}
}
stage('Log Collection (eNB - Run)') {
steps {
echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}"
echo '\u2705 \u001B[32mLog Transfer (eNB - Run)\u001B[0m'
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/enb.log.zip ./enb.log.${env.BUILD_ID}.zip || true"
}
script {
if(fileExists("enb.log.${env.BUILD_ID}.zip")) {
archiveArtifacts "enb.log.${env.BUILD_ID}.zip"

View File

@@ -83,9 +83,36 @@ pipeline {
echo "no LockResources given"
allParametersPresent = false
}
// 1st eNB parameters
if (params.eNB_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB_Credentials == null) {
allParametersPresent = false
}
// 2nd eNB parameters
if (params.eNB1_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB1_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB1_Credentials == null) {
allParametersPresent = false
}
// 3rd eNB parameters
if (params.eNB2_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB2_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB2_Credentials == null) {
allParametersPresent = false
}
// the following 4 parameters should be pushed by the master trigger
// if not present, take the job GIT variables (used for developing)
if (params.eNB_Repository == null) {
@@ -156,31 +183,62 @@ pipeline {
}
}
}
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
String[] myXmlTestSuite = testXMLFile.split("\\r?\\n")
for (xmlFile in myXmlTestSuite) {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
buildStageStatus = false
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'],
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB1_Credentials}", usernameVariable: 'eNB1_Username', passwordVariable: 'eNB1_Password'],
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB2_Credentials}", usernameVariable: 'eNB2_Username', passwordVariable: 'eNB2_Password'],
]) {
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}"
String[] myXmlTestSuite = testXMLFile.split("\\r?\\n")
for (xmlFile in myXmlTestSuite) {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --eNB1IPAddress=${params.eNB1_IPAddress} --eNB1UserName=${eNB1_Username} --eNB1Password=${eNB1_Password} --eNB1SourceCodePath=${params.eNB1_SourceCodePath} --eNB2IPAddress=${params.eNB2_IPAddress} --eNB2UserName=${eNB2_Username} --eNB2Password=${eNB2_Password} --eNB2SourceCodePath=${params.eNB2_SourceCodePath} --XMLTestFile=${xmlFile}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
buildStageStatus = false
}
}
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}"
}
}
}
}
stage('Log Collection') {
parallel {
stage('Log Collection (eNB - Build)') {
steps {
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}"
echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m'
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true"
}
script {
if(fileExists("build.log.${env.BUILD_ID}.zip")) {
archiveArtifacts "build.log.${env.BUILD_ID}.zip"
}
}
}
}
stage('Log Collection (eNB - Run)') {
steps {
echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}"
echo '\u2705 \u001B[32mLog Transfer (eNB - Run)\u001B[0m'
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/enb.log.zip ./enb.log.${env.BUILD_ID}.zip || true"
}
script {
if(fileExists("enb.log.${env.BUILD_ID}.zip")) {
archiveArtifacts "enb.log.${env.BUILD_ID}.zip"

View File

@@ -96,9 +96,15 @@ pipeline {
echo "no LockResources given"
allParametersPresent = false
}
if (params.eNB_IPAddress == null) {
allParametersPresent = false
}
if (params.eNB_SourceCodePath == null) {
allParametersPresent = false
}
if (params.eNB_Credentials == null) {
allParametersPresent = false
}
// the following 4 parameters should be pushed by the master trigger
// if not present, take the job GIT variables (used for developing)
if (params.eNB_Repository == null) {
@@ -175,21 +181,25 @@ pipeline {
}
}
}
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${flexricOption} ${mainPythonAllXmlFiles}"
String[] myXmlTestSuite = testXMLFile.split("\\r?\\n")
for (xmlFile in myXmlTestSuite) {
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'],
]) {
sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${flexricOption} ${mainPythonAllXmlFiles}"
String[] myXmlTestSuite = testXMLFile.split("\\r?\\n")
for (xmlFile in myXmlTestSuite) {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${flexricOption} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}"
sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${flexricOption} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
buildStageStatus = false
}
}
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus} --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}"
}
}
}
@@ -199,7 +209,11 @@ pipeline {
stage('Terminate eNB') {
steps {
echo '\u2705 \u001B[32mTerminate eNB\u001B[0m'
sh "python3 ci-scripts/main.py --mode=TerminateeNB"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
sh "python3 ci-scripts/main.py --mode=TerminateeNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
}
post {
success {
@@ -213,10 +227,35 @@ pipeline {
}
stage('Log Collection') {
parallel {
stage('Log Collection (eNB - Build)') {
steps {
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}"
echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m'
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true"
}
script {
if(fileExists("build.log.${env.BUILD_ID}.zip")) {
archiveArtifacts "build.log.${env.BUILD_ID}.zip"
}
}
}
}
stage('Log Collection (eNB - Run)') {
steps {
echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m'
sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}"
echo '\u2705 \u001B[32mLog Transfer (eNB - Run)\u001B[0m'
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/enb.log.zip ./enb.log.${env.BUILD_ID}.zip || true"
}
script {
if(fileExists("enb.log.${env.BUILD_ID}.zip")) {
archiveArtifacts "enb.log.${env.BUILD_ID}.zip"
@@ -252,7 +291,11 @@ pipeline {
failure {
script {
if (!termStatusArray[termENB]) {
sh "python3 ci-scripts/main.py --mode=TerminateeNB"
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
]) {
sh "python3 ci-scripts/main.py --mode=TerminateeNB --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password}"
}
}
}
}

View File

@@ -1,60 +0,0 @@
This document outlines how to use the OAI CI framework and gives a quickstart.
[[_TOC_]]
# Quickstart
There is a script `main.py` that
- reads scenario files from XML
- executes the steps (e.g., deployment of CN/gNB/UE, attach, ping, ...)
To simplify, a script `run_locally.sh` can be used to run a single scenario
locally. First, download images for gNB&UE (we assume you already downloaded
the CN) and tag them "latest", which is expected by `run_locally.sh`:
docker pull oaisoftwarealliance/oai-gnb:develop
docker tag oaisoftwarealliance/oai-gnb:develop oai-gnb
docker pull oaisoftwarealliance/oai-nr-ue:develop
docker tag oaisoftwarealliance/oai-nr-ue:develop oai-nr-ue
Now, run the scenario:
cd ~/openairinterface5g/ci-scripts/
./run_locally.sh xml_files/container_5g_rfsim_simple.xml
Output should look like
```
[2025-08-07 18:07:49,631] INFO: ----------------------------------------
[2025-08-07 18:07:49,631] INFO: Creating HTML header
[2025-08-07 18:07:49,631] INFO: ----------------------------------------
[2025-08-07 18:07:49,766] INFO: ----------------------------------------
[2025-08-07 18:07:49,767] INFO: Starting Scenario: xml_files/container_5g_rfsim_simple.xml
[2025-08-07 18:07:49,767] INFO: ----------------------------------------
[2025-08-07 18:07:49,767] INFO: placing all artifacts for this run in /home/richie/oai/cmake_targets/log/container_5g_rfsim_simple.xml.d/
[...]
[2025-08-07 18:07:49,771] INFO: ----------------------------------------
[2025-08-07 18:07:49,771] INFO: Test ID: 1 (#1)
[2025-08-07 18:07:49,771] INFO: Deploy OAI 5G CoreNetwork
[2025-08-07 18:07:49,771] INFO: ----------------------------------------
```
it will run an end-to-end test, connecting one UE to the gNB, and ping, before
undeploying. As shown, logs will be under
`/home/richie/oai/cmake_targets/log/container_5g_rfsim_simple.xml.d/`. The
python code also produces an HTML report in `ci-scripts/test_results.html`.
# Running with local changes
In order to run the tests as above, with local changes, please [build images
locally](../docker/README.md).
It is not yet possible to override an existing image with changes made locally
to a source file and run it directly through the CI framework. However, it is
possible to override an existing image with local changes, and run the scenario
manually following the console logs steps provided above. This is described in
the [5G RFsimulator
README](yaml_files/5g_rfsimulator/README.md#6-running-with-local-changes).

View File

@@ -59,6 +59,17 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER):
force_local = True
#--apply=<filename> as parameters file, to replace inline parameters
elif re.match(r'^\-\-Apply=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-Apply=(.+)$', myArgv, re.IGNORECASE)
py_params_file = matchReg.group(1)
with open(py_params_file,'r') as file:
# The FullLoader parameter handles the conversion from YAML
# scalar values to Python dictionary format
py_params = yaml.load(file,Loader=yaml.FullLoader)
py_param_file_present = True #to be removed once validated
#AssignParams(py_params) #to be uncommented once validated
#consider inline parameters
elif re.match(r'^\-\-mode=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-mode=(.+)$', myArgv, re.IGNORECASE)
@@ -121,11 +132,50 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER):
SCA.ranTargetBranch=matchReg.group(1)
CLUSTER.ranTargetBranch=matchReg.group(1)
elif re.match(r'^\-\-eNBIPAddress=(.+)$|^\-\-eNB[1-2]IPAddress=(.+)$', myArgv, re.IGNORECASE):
print("parameters --eNB*IPAddress ignored")
if re.match(r'^\-\-eNBIPAddress=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNBIPAddress=(.+)$', myArgv, re.IGNORECASE)
RAN.eNBIPAddress=matchReg.group(1)
CONTAINERS.eNBIPAddress=matchReg.group(1)
SCA.eNBIPAddress=matchReg.group(1)
CLUSTER.eNBIPAddress=matchReg.group(1)
elif re.match(r'^\-\-eNB1IPAddress=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNB1IPAddress=(.+)$', myArgv, re.IGNORECASE)
RAN.eNB1IPAddress=matchReg.group(1)
CONTAINERS.eNB1IPAddress=matchReg.group(1)
elif re.match(r'^\-\-eNB2IPAddress=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNB2IPAddress=(.+)$', myArgv, re.IGNORECASE)
RAN.eNB2IPAddress=matchReg.group(1)
CONTAINERS.eNB2IPAddress=matchReg.group(1)
elif re.match(r'^\-\-eNBUserName=(.+)$|^\-\-eNB[1-2]UserName=(.+)$', myArgv, re.IGNORECASE):
print("parameters --eNB*UserName ignored")
if re.match(r'^\-\-eNBUserName=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNBUserName=(.+)$', myArgv, re.IGNORECASE)
RAN.eNBUserName=matchReg.group(1)
CONTAINERS.eNBUserName=matchReg.group(1)
SCA.eNBUserName=matchReg.group(1)
CLUSTER.eNBUserName=matchReg.group(1)
elif re.match(r'^\-\-eNB1UserName=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNB1UserName=(.+)$', myArgv, re.IGNORECASE)
RAN.eNB1UserName=matchReg.group(1)
CONTAINERS.eNB1UserName=matchReg.group(1)
elif re.match(r'^\-\-eNB2UserName=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNB2UserName=(.+)$', myArgv, re.IGNORECASE)
RAN.eNB2UserName=matchReg.group(1)
CONTAINERS.eNB2UserName=matchReg.group(1)
elif re.match(r'^\-\-eNBPassword=(.+)$|^\-\-eNB[1-2]Password=(.+)$', myArgv, re.IGNORECASE):
print("parameter --eNB*Password ignored")
if re.match(r'^\-\-eNBPassword=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNBPassword=(.+)$', myArgv, re.IGNORECASE)
RAN.eNBPassword=matchReg.group(1)
CONTAINERS.eNBPassword=matchReg.group(1)
SCA.eNBPassword=matchReg.group(1)
CLUSTER.eNBPassword=matchReg.group(1)
elif re.match(r'^\-\-eNB1Password=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNB1Password=(.+)$', myArgv, re.IGNORECASE)
RAN.eNB1Password=matchReg.group(1)
CONTAINERS.eNB1Password=matchReg.group(1)
elif re.match(r'^\-\-eNB2Password=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNB2Password=(.+)$', myArgv, re.IGNORECASE)
RAN.eNB2Password=matchReg.group(1)
CONTAINERS.eNB2Password=matchReg.group(1)
elif re.match(r'^\-\-eNBSourceCodePath=(.+)$|^\-\-eNB[1-2]SourceCodePath=(.+)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-eNBSourceCodePath=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNBSourceCodePath=(.+)$', myArgv, re.IGNORECASE)
@@ -134,14 +184,26 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER):
SCA.eNBSourceCodePath=matchReg.group(1)
CLUSTER.eNBSourceCodePath=matchReg.group(1)
elif re.match(r'^\-\-eNB1SourceCodePath=(.+)$', myArgv, re.IGNORECASE):
print("parameter --eNB1SourceCodePath ignored")
matchReg = re.match(r'^\-\-eNB1SourceCodePath=(.+)$', myArgv, re.IGNORECASE)
RAN.eNB1SourceCodePath=matchReg.group(1)
CONTAINERS.eNB1SourceCodePath=matchReg.group(1)
elif re.match(r'^\-\-eNB2SourceCodePath=(.+)$', myArgv, re.IGNORECASE):
print("parameter --eNB2SourceCodePath ignored")
matchReg = re.match(r'^\-\-eNB2SourceCodePath=(.+)$', myArgv, re.IGNORECASE)
RAN.eNB2SourceCodePath=matchReg.group(1)
CONTAINERS.eNB2SourceCodePath=matchReg.group(1)
elif re.match(r'^\-\-XMLTestFile=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-XMLTestFile=(.+)$', myArgv, re.IGNORECASE)
CiTestObj.testXMLfiles.append(matchReg.group(1))
HTML.testXMLfiles.append(matchReg.group(1))
HTML.nbTestXMLfiles=HTML.nbTestXMLfiles+1
elif re.match(r'^\-\-UEIPAddress=(.+)$', myArgv, re.IGNORECASE): # cleanup
print("parameter --UEIPAddress ignored")
elif re.match(r'^\-\-UEUserName=(.+)$', myArgv, re.IGNORECASE):
print("parameter --UEUserName ignored")
elif re.match(r'^\-\-UEPassword=(.+)$', myArgv, re.IGNORECASE):
print("parameter --UEPassword ignored")
elif re.match(r'^\-\-UESourceCodePath=(.+)$', myArgv, re.IGNORECASE):
print("parameter --UESourceCodePath ignored")
elif re.match(r'^\-\-finalStatus=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-finalStatus=(.+)$', myArgv, re.IGNORECASE)
finalStatus = matchReg.group(1)

View File

@@ -5,7 +5,7 @@
{
#define N_ANTENNA_DL 1
#define N_ANTENNA_UL 1
#define DL_ARFCN 627360
#define DL_ARFCN 631296
#define TDD 1
log_options: "all.level=debug,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
@@ -46,7 +46,7 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
apn:"oai",
attach_pdn_type:"ipv4",
@@ -61,9 +61,8 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
redcap: "redcap",
apn:"oai",
attach_pdn_type:"ipv4",
default_nssai: [ { sst: 1, }, ],
@@ -77,7 +76,7 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
apn:"oai",
attach_pdn_type:"ipv4",
@@ -92,7 +91,7 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
apn:"oai",
attach_pdn_type:"ipv4",
@@ -107,9 +106,8 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
redcap: "redcap",
apn:"oai",
attach_pdn_type:"ipv4",
default_nssai: [ { sst: 1, }, ],
@@ -123,9 +121,8 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
redcap: "redcap",
apn:"oai",
attach_pdn_type:"ipv4",
default_nssai: [ { sst: 1, }, ],
@@ -139,7 +136,7 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
apn:"oai",
attach_pdn_type:"ipv4",
@@ -154,7 +151,7 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
apn:"oai",
attach_pdn_type:"ipv4",
@@ -169,7 +166,7 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
apn:"oai",
attach_pdn_type:"ipv4",
@@ -184,9 +181,8 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
redcap: "redcap",
apn:"oai",
attach_pdn_type:"ipv4",
default_nssai: [ { sst: 1, }, ],
@@ -200,9 +196,8 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
redcap: "redcap",
apn:"oai",
attach_pdn_type:"ipv4",
default_nssai: [ { sst: 1, }, ],
@@ -216,9 +211,8 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
redcap: "redcap",
apn:"oai",
attach_pdn_type:"ipv4",
default_nssai: [ { sst: 1, }, ],
@@ -232,7 +226,7 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
apn:"oai",
attach_pdn_type:"ipv4",
@@ -247,7 +241,7 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
apn:"oai",
attach_pdn_type:"ipv4",
@@ -262,9 +256,8 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
redcap: "redcap",
apn:"oai",
attach_pdn_type:"ipv4",
default_nssai: [ { sst: 1, }, ],
@@ -278,9 +271,8 @@ allow dynamic UE creation from remote API */
"sim_algo":"milenage",
"op": "1006020f0a478bf6b699f15c062e42b3",
as_release: 17,
as_release: 15,
ue_category: "nr",
redcap: "redcap",
apn:"oai",
attach_pdn_type:"ipv4",
default_nssai: [ { sst: 1, }, ],

View File

@@ -5,12 +5,10 @@ idefix:
AttachScript: sudo python3 ci_ctl_qtel.py /dev/ttyUSB2 wup
DetachScript: sudo python3 ci_ctl_qtel.py /dev/ttyUSB2 detach
NetworkScript: ip a show dev wwan0
#Tracing:
# Start: 'sudo rm -f ci_qlog/*; nohup sudo -b QLog/QLog -s ci_qlog -f NR5G.cfg &'
# Stop: 'sudo killall QLog'
# Collect: 'sudo mv ci_qlog/* %%log_dir%%/'
IF: wwan0
MTU: 1500
Trace: True
LogStore: /media/usb-drive/ci_qlogs
up2:
Host: up2
@@ -48,6 +46,7 @@ adb_ue_1:
NetworkScript: adb -s R3CM40LZPHT shell "ip address show | grep rmnet_data0"
CmdPrefix: adb -s R3CM40LZPHT shell
MTU: 1500
LogStore: /media/usb-drive/ci_adb_1-logs
adb_ue_2:
Host: nano
InitScript: /home/oaicicd/ci_ctl_adb.sh initialize 5200c00db4413517
@@ -60,6 +59,7 @@ adb_ue_2:
NetworkScript: adb -s 5200c00db4413517 shell "ip address show | grep rmnet"
CmdPrefix: adb -s 5200c00db4413517 shell
MTU: 1500
LogStore: /media/usb-drive/ci_adb_2-logs
oc-cn5g:
Host: avra
@@ -147,16 +147,12 @@ ltebox-nepes:
amarisoft_ue:
Host: amariue
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue_20MHz_redcap/aw2s-multi-00102-20.cfg &
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue/aw2s-multi-00102-20.cfg &
TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2
NetworkScript: ip netns exec ue1 ip a show dev pdn0
#Tracing:
# Start: '' # nothing to be done
# Stop: '' # nothing to be done
# Collect: 'cp /tmp/ue.log %%log_dir%%'
amarisoft_ue_2x2:
Host: amariue
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue_20MHz_2x2/aw2s-multi-00102-2x2-v2.cfg &
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue_2x2/aw2s-multi-00102-2x2-v2.cfg &
TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2
NetworkScript: ip netns exec ue1 ip a show dev pdn0
amarisoft_ue_fhi72:
@@ -305,15 +301,6 @@ rfsim5g_ue:
AttachScript: docker start rfsim5g-oai-nr-ue
DetachScript: docker stop rfsim5g-oai-nr-ue
MTU: 1500
# for tests with second PDU session ID 2, needs to match requested PDU session name
rfsim5g_ue_pdu_2:
Host: "%%current_host%%"
NetworkScript: docker exec rfsim5g-oai-nr-ue ip a show dev oaitun_ue1p2
CmdPrefix: docker exec rfsim5g-oai-nr-ue
IF: oaitun_ue1p2
AttachScript: docker start rfsim5g-oai-nr-ue
DetachScript: docker stop rfsim5g-oai-nr-ue
MTU: 1500
rfsim5g_ue2:
Host: "%%current_host%%"

View File

@@ -84,7 +84,7 @@ class Analysis():
return success,msg
def analyze_oc_physim(result_junit, details_json, logPath):
def analyze_oc_physim(result_junit, details_json):
try:
tree = ET.parse(result_junit)
root = tree.getroot()
@@ -124,7 +124,7 @@ class Analysis():
output = test.findtext("system-out")
output_check = "exceeds the threshold" not in output
# collect logs
log_dir = f'{logPath}/{test_exec}'
log_dir = f'../cmake_targets/log/{test_exec}'
os.makedirs(log_dir, exist_ok=True)
with open(f'{log_dir}/{test_name}.log', 'w') as f:
f.write(output)

View File

@@ -1,44 +0,0 @@
#/*
# * 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
# */
import os
from typing import NamedTuple
import logging
class TestCaseCtx(NamedTuple):
count: int
test_id: int
logPath: str
def Default(logPath):
return TestCaseCtx(123, 112233, logPath)
def baseFilename(self):
# typically, the test ID is of form 001234 (6 digits)
return f"{self.logPath}/{self.count}-{self.test_id:06d}"
def archiveArtifact(cmd, ctx, remote_path):
base = os.path.basename(remote_path)
local = f"{ctx.baseFilename()}-{base}"
logging.info(f"Archive artifact '{local}'")
success = cmd.copyin(remote_path, local)
if success:
cmd.run(f'rm {remote_path}', silent=True)
return local if success else None

View File

@@ -39,7 +39,6 @@ import constants as CONST
import helpreadme as HELP
import cls_containerize
import cls_cmd
from cls_ci_helper import archiveArtifact
IMAGE_REGISTRY_SERVICE_NAME = "image-registry.openshift-image-registry.svc"
NAMESPACE = "oaicicd-ran"
@@ -69,7 +68,9 @@ def OC_logout(cmd):
class Cluster:
def __init__(self):
self.eNBIPAddress = ""
self.eNBSourceCodePath = ""
self.forcedWorkspaceCleanup = False
self.OCUserName = ""
self.OCPassword = ""
self.OCProjectName = ""
@@ -163,6 +164,24 @@ class Cluster:
return -1
return int(result.group("size"))
def _deploy_pod(self, filename, timeout = 120):
ret = self.cmd.run(f'oc create -f {filename}')
result = re.search(r'pod/(?P<pod>[a-zA-Z0-9_\-]+) created', ret.stdout)
if result is None:
logging.error(f'could not deploy pod: {ret.stdout}')
return None
pod = result.group("pod")
logging.debug(f'checking if pod {pod} is in Running state')
ret = self.cmd.run(f'oc wait --for=condition=ready pod {pod} --timeout={timeout}s', silent=True)
if ret.returncode == 0:
return pod
logging.error(f'pod {pod} did not reach Running state')
self._undeploy_pod(filename)
return None
def _undeploy_pod(self, filename):
self.cmd.run(f'oc delete -f {filename}')
def PullClusterImage(self, HTML, node, images, tag_prefix):
logging.debug(f'Pull OC image {images} to server {node}')
self.testCase_id = HTML.testCase_id
@@ -188,17 +207,13 @@ class Cluster:
HTML.CreateHtmlTestRowQueue(param, 'KO', [msg])
return success
def _retrieveOCLog(self, ctx, job, lSourcePath, image):
fn = f'{lSourcePath}/cmake_targets/log/{image}.log'
self.cmd.run(f'oc logs {job} &> {fn}')
return (image, archiveArtifact(self.cmd, ctx, fn))
def BuildClusterImage(self, ctx, node, HTML):
def BuildClusterImage(self, HTML):
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
HELP.GenericHelp(CONST.Version)
raise ValueError(f'Insufficient Parameter: ranRepository {self.ranRepository} ranBranch {ranBranch} ranCommitID {self.ranCommitID}')
lIpAddr = self.eNBIPAddress
lSourcePath = self.eNBSourceCodePath
if node == '' or lSourcePath == '':
if lIpAddr == '' or lSourcePath == '':
raise ValueError('Insufficient Parameter: eNBSourceCodePath missing')
ocUserName = self.OCUserName
ocPassword = self.OCPassword
@@ -209,8 +224,8 @@ class Cluster:
if self.OCRegistry.startswith("http") or self.OCRegistry.endswith("/"):
raise ValueError(f'ocRegistry {self.OCRegistry} should not start with http:// or https:// and not end on a slash /')
logging.debug(f'Building on cluster triggered from server: {node}')
self.cmd = cls_cmd.RemoteCmd(node)
logging.debug(f'Building on cluster triggered from server: {lIpAddr}')
self.cmd = cls_cmd.RemoteCmd(lIpAddr)
self.testCase_id = HTML.testCase_id
@@ -265,45 +280,124 @@ class Cluster:
self._recreate_entitlements()
status = True # flag to abandon compiling if any image fails
log_files = []
build_metrics = f"{lSourcePath}/cmake_targets/log/build-metrics.log"
attemptedImages = []
if forceBaseImageBuild:
self._recreate_is_tag('ran-base', baseTag, 'openshift/ran-base-is.yaml')
self._recreate_bc('ran-base', baseTag, 'openshift/ran-base-bc.yaml')
ranbase_job = self._start_build('ran-base')
attemptedImages += ['ran-base']
status = ranbase_job is not None and self._wait_build_end([ranbase_job], 1000)
if not status: logging.error('failure during build of ran-base')
log_files.append(self._retrieveOCLog(ctx, ranbase_job, lSourcePath, 'ran-base'))
self.cmd.run(f'oc logs {ranbase_job} &> cmake_targets/log/ran-base.log') # cannot use cmd.run because of redirect
# recover logs by mounting image
self._retag_image_statement('ran-base', 'ran-base', baseTag, 'openshift/ran-base-log-retrieval.yaml')
pod = self._deploy_pod('openshift/ran-base-log-retrieval.yaml')
if pod is not None:
self.cmd.run(f'mkdir -p cmake_targets/log/ran-base')
self.cmd.run(f'oc rsync {pod}:/oai-ran/cmake_targets/log/ cmake_targets/log/ran-base')
self._undeploy_pod('openshift/ran-base-log-retrieval.yaml')
else:
status = False
if status:
self._recreate_is_tag('oai-physim', imageTag, 'openshift/oai-physim-is.yaml')
self._recreate_bc('oai-physim', imageTag, 'openshift/oai-physim-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.phySim.rhel9')
physim_job = self._start_build('oai-physim')
attemptedImages += ['oai-physim']
self._recreate_is_tag('ran-build', imageTag, 'openshift/ran-build-is.yaml')
self._recreate_bc('ran-build', imageTag, 'openshift/ran-build-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.build.rhel9')
ranbuild_job = self._start_build('ran-build')
attemptedImages += ['ran-build']
self._recreate_is_tag('oai-clang', imageTag, 'openshift/oai-clang-is.yaml')
self._recreate_bc('oai-clang', imageTag, 'openshift/oai-clang-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.clang.rhel9')
clang_job = self._start_build('oai-clang')
attemptedImages += ['oai-clang']
wait = ranbuild_job is not None and physim_job is not None and clang_job is not None and self._wait_build_end([ranbuild_job, physim_job, clang_job], 1200)
if not wait: logging.error('error during build of ranbuild_job or physim_job or clang_job')
status = status and wait
self.cmd.run(f'oc logs {ranbuild_job} &> cmake_targets/log/ran-build.log')
self.cmd.run(f'oc logs {physim_job} &> cmake_targets/log/oai-physim.log')
self.cmd.run(f'oc logs {clang_job} &> cmake_targets/log/oai-clang.log')
self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log')
if status:
self._recreate_is_tag('oai-enb', imageTag, 'openshift/oai-enb-is.yaml')
self._recreate_bc('oai-enb', imageTag, 'openshift/oai-enb-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.eNB.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.eNB.rhel9')
enb_job = self._start_build('oai-enb')
attemptedImages += ['oai-enb']
self._recreate_is_tag('oai-gnb', imageTag, 'openshift/oai-gnb-is.yaml')
self._recreate_bc('oai-gnb', imageTag, 'openshift/oai-gnb-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.gNB.rhel9')
gnb_job = self._start_build('oai-gnb')
attemptedImages += ['oai-gnb']
self._recreate_is_tag('oai-gnb-aw2s', imageTag, 'openshift/oai-gnb-aw2s-is.yaml')
self._recreate_bc('oai-gnb-aw2s', imageTag, 'openshift/oai-gnb-aw2s-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.aw2s.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.gNB.aw2s.rhel9')
gnb_aw2s_job = self._start_build('oai-gnb-aw2s')
attemptedImages += ['oai-gnb-aw2s']
wait = enb_job is not None and gnb_job is not None and gnb_aw2s_job is not None and self._wait_build_end([enb_job, gnb_job, gnb_aw2s_job], 800)
if not wait: logging.error('error during build of eNB/gNB')
status = status and wait
# recover logs
self.cmd.run(f'oc logs {enb_job} &> cmake_targets/log/oai-enb.log')
self.cmd.run(f'oc logs {gnb_job} &> cmake_targets/log/oai-gnb.log')
self.cmd.run(f'oc logs {gnb_aw2s_job} &> cmake_targets/log/oai-gnb-aw2s.log')
self._recreate_is_tag('oai-nr-cuup', imageTag, 'openshift/oai-nr-cuup-is.yaml')
self._recreate_bc('oai-nr-cuup', imageTag, 'openshift/oai-nr-cuup-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.nr-cuup.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.nr-cuup.rhel9')
nr_cuup_job = self._start_build('oai-nr-cuup')
attemptedImages += ['oai-nr-cuup']
self._recreate_is_tag('oai-lte-ue', imageTag, 'openshift/oai-lte-ue-is.yaml')
self._recreate_bc('oai-lte-ue', imageTag, 'openshift/oai-lte-ue-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.lteUE.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.lteUE.rhel9')
lteue_job = self._start_build('oai-lte-ue')
attemptedImages += ['oai-lte-ue']
self._recreate_is_tag('oai-nr-ue', imageTag, 'openshift/oai-nr-ue-is.yaml')
self._recreate_bc('oai-nr-ue', imageTag, 'openshift/oai-nr-ue-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.nrUE.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.nrUE.rhel9')
nrue_job = self._start_build('oai-nr-ue')
attemptedImages += ['oai-nr-ue']
wait = nr_cuup_job is not None and lteue_job is not None and nrue_job is not None and self._wait_build_end([nr_cuup_job, lteue_job, nrue_job], 800)
if not wait: logging.error('error during build of nr-cuup/lteUE/nrUE')
status = status and wait
# recover logs
self.cmd.run(f'oc logs {nr_cuup_job} &> cmake_targets/log/oai-nr-cuup.log')
self.cmd.run(f'oc logs {lteue_job} &> cmake_targets/log/oai-lte-ue.log')
self.cmd.run(f'oc logs {nrue_job} &> cmake_targets/log/oai-nr-ue.log')
self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log')
if status:
self._recreate_is_tag('ran-build-fhi72', imageTag, 'openshift/ran-build-fhi72-is.yaml')
self._recreate_bc('ran-build-fhi72', imageTag, 'openshift/ran-build-fhi72-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.build.fhi72.rhel9')
ranbuildfhi72_job = self._start_build('ran-build-fhi72')
attemptedImages += ['ran-build-fhi72']
self._recreate_is_tag('oai-physim', imageTag, 'openshift/oai-physim-is.yaml')
self._recreate_bc('oai-physim', imageTag, 'openshift/oai-physim-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.phySim.rhel9')
physim_job = self._start_build('oai-physim')
self._recreate_is_tag('ran-build', imageTag, 'openshift/ran-build-is.yaml')
self._recreate_bc('ran-build', imageTag, 'openshift/ran-build-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.build.rhel9')
ranbuild_job = self._start_build('ran-build')
self._recreate_is_tag('oai-clang', imageTag, 'openshift/oai-clang-is.yaml')
self._recreate_bc('oai-clang', imageTag, 'openshift/oai-clang-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.clang.rhel9')
clang_job = self._start_build('oai-clang')
wait = ranbuildfhi72_job is not None and ranbuild_job is not None and physim_job is not None and clang_job is not None and self._wait_build_end([ranbuildfhi72_job, ranbuild_job, physim_job, clang_job], 1200)
if not wait: logging.error('error during build of ranbuildfhi72_job or ranbuild_job or physim_job or clang_job')
wait = ranbuildfhi72_job is not None and self._wait_build_end([ranbuildfhi72_job], 1200)
if not wait: logging.error('error during build of ranbuildfhi72_job')
status = status and wait
log_files.append(self._retrieveOCLog(ctx, ranbuildfhi72_job, lSourcePath, 'ran-build-fhi72'))
log_files.append(self._retrieveOCLog(ctx, ranbuild_job, lSourcePath, 'ran-build'))
log_files.append(self._retrieveOCLog(ctx, physim_job, lSourcePath, 'oai-physim'))
log_files.append(self._retrieveOCLog(ctx, clang_job, lSourcePath, 'oai-clang'))
self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}')
self.cmd.run(f'oc logs {ranbuildfhi72_job} &> cmake_targets/log/ran-build-fhi72.log')
self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log')
if status:
self._recreate_is_tag('oai-gnb-fhi72', imageTag, 'openshift/oai-gnb-fhi72-is.yaml')
@@ -311,65 +405,19 @@ class Cluster:
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.fhi72.rhel9')
self._retag_image_statement('ran-build-fhi72', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build-fhi72', imageTag, 'docker/Dockerfile.gNB.fhi72.rhel9')
gnb_fhi72_job = self._start_build('oai-gnb-fhi72')
attemptedImages += ['oai-gnb-fhi72']
self._recreate_is_tag('oai-enb', imageTag, 'openshift/oai-enb-is.yaml')
self._recreate_bc('oai-enb', imageTag, 'openshift/oai-enb-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.eNB.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.eNB.rhel9')
enb_job = self._start_build('oai-enb')
self._recreate_is_tag('oai-gnb', imageTag, 'openshift/oai-gnb-is.yaml')
self._recreate_bc('oai-gnb', imageTag, 'openshift/oai-gnb-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.gNB.rhel9')
gnb_job = self._start_build('oai-gnb')
self._recreate_is_tag('oai-gnb-aw2s', imageTag, 'openshift/oai-gnb-aw2s-is.yaml')
self._recreate_bc('oai-gnb-aw2s', imageTag, 'openshift/oai-gnb-aw2s-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.aw2s.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.gNB.aw2s.rhel9')
gnb_aw2s_job = self._start_build('oai-gnb-aw2s')
wait = gnb_fhi72_job is not None and enb_job is not None and gnb_job is not None and gnb_aw2s_job is not None and self._wait_build_end([gnb_fhi72_job, enb_job, gnb_job, gnb_aw2s_job], 800)
if not wait: logging.error('error during build of eNB/gNB')
wait = gnb_fhi72_job is not None and self._wait_build_end([gnb_fhi72_job], 600)
if not wait: logging.error('error during build of gNB-fhi72')
status = status and wait
# recover logs
log_files.append(self._retrieveOCLog(ctx, gnb_fhi72_job, lSourcePath, 'oai-gnb-fhi72'))
log_files.append(self._retrieveOCLog(ctx, enb_job, lSourcePath, 'oai-enb'))
log_files.append(self._retrieveOCLog(ctx, gnb_job, lSourcePath, 'oai-gnb'))
log_files.append(self._retrieveOCLog(ctx, gnb_aw2s_job, lSourcePath, 'oai-gnb-aw2s'))
self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}')
self._recreate_is_tag('oai-nr-cuup', imageTag, 'openshift/oai-nr-cuup-is.yaml')
self._recreate_bc('oai-nr-cuup', imageTag, 'openshift/oai-nr-cuup-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.nr-cuup.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.nr-cuup.rhel9')
nr_cuup_job = self._start_build('oai-nr-cuup')
self._recreate_is_tag('oai-lte-ue', imageTag, 'openshift/oai-lte-ue-is.yaml')
self._recreate_bc('oai-lte-ue', imageTag, 'openshift/oai-lte-ue-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.lteUE.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.lteUE.rhel9')
lteue_job = self._start_build('oai-lte-ue')
self._recreate_is_tag('oai-nr-ue', imageTag, 'openshift/oai-nr-ue-is.yaml')
self._recreate_bc('oai-nr-ue', imageTag, 'openshift/oai-nr-ue-bc.yaml')
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.nrUE.rhel9')
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.nrUE.rhel9')
nrue_job = self._start_build('oai-nr-ue')
wait = nr_cuup_job is not None and lteue_job is not None and nrue_job is not None and self._wait_build_end([nr_cuup_job, lteue_job, nrue_job], 800)
if not wait: logging.error('error during build of nr-cuup/lteUE/nrUE')
status = status and wait
# recover logs
log_files.append(self._retrieveOCLog(ctx, nr_cuup_job, lSourcePath, 'oai-nr-cuup'))
log_files.append(self._retrieveOCLog(ctx, lteue_job, lSourcePath, 'oai-lte-ue'))
log_files.append(self._retrieveOCLog(ctx, nrue_job, lSourcePath, 'oai-nr-ue'))
self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}')
self.cmd.run(f'oc logs {gnb_fhi72_job} &> cmake_targets/log/oai-gnb-fhi72.log')
self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log')
# split and analyze logs
imageSize = {}
for image, _ in log_files:
for image in attemptedImages:
self.cmd.run(f'mkdir -p cmake_targets/log/{image}')
tag = imageTag if image != 'ran-base' else baseTag
size = self._get_image_size(image, tag)
if size <= 0:
@@ -380,14 +428,12 @@ class Cluster:
imageSize[image] = f'{sizeMb:.1f} Mbytes (uncompressed: ~{sizeMb*2.5:.1f} Mbytes)'
logging.info(f'\u001B[1m{image} size is {imageSize[image]}\u001B[0m')
archiveArtifact(self.cmd, ctx, build_metrics)
logfile = f'{lSourcePath}/cmake_targets/log/image_registry.log'
grep_exp = r"\|".join([i for i,f in log_files])
self.cmd.run(f'oc get images | grep -e \'{grep_exp}\' &> {logfile}');
archiveArtifact(self.cmd, ctx, logfile)
logfile = f'{lSourcePath}/cmake_targets/log/build_pod_summary.log'
self.cmd.run(f'for pod in $(oc get pods | tail -n +2 | awk \'{{print $1}}\'); do oc get pod $pod -o json &>> {logfile}; done')
archiveArtifact(self.cmd, ctx, logfile)
grep_exp = r"\|".join(attemptedImages)
self.cmd.run(f'oc get images | grep -e \'{grep_exp}\' &> cmake_targets/log/image_registry.log');
self.cmd.run(f'for pod in $(oc get pods | tail -n +2 | awk \'{{print $1}}\'); do oc get pod $pod -o json &>> cmake_targets/log/build_pod_summary.log; done')
build_log_name = f'build_log_{self.testCase_id}'
cls_containerize.CopyLogsToExecutor(self.cmd, lSourcePath, build_log_name)
self.cmd.run('for pod in $(oc get pods | tail -n +2 | awk \'{print $1}\'); do oc delete pod ${pod}; done')
@@ -396,52 +442,52 @@ class Cluster:
self.cmd.close()
# Analyze the logs
collectInfo = {}
for image, lf in log_files:
ret = cls_containerize.AnalyzeBuildLogs(image, lf)
imgStatus = ret['status']
msg = f"size {imageSize[image]}, analysis of {os.path.basename(lf)}: {ret['errors']} errors, {ret['warnings']} warnings"
HTML.CreateHtmlTestRowQueue(image, 'OK' if imgStatus else 'KO', [msg])
status = status and imgStatus
collectInfo = cls_containerize.AnalyzeBuildLogs(build_log_name, attemptedImages, status)
for img in collectInfo:
for f in collectInfo[img]:
status = status and collectInfo[img][f]['status']
if not status:
logging.debug(collectInfo)
if status:
logging.info('\u001B[1m Building OAI Image(s) Pass\u001B[0m')
HTML.CreateHtmlTestRow('all', 'OK', CONST.ALL_PROCESSES_OK)
else:
logging.error('\u001B[1m Building OAI Images Failed\u001B[0m')
HTML.CreateHtmlTestRow('all', 'KO', CONST.ALL_PROCESSES_OK)
# TODO fix groovy script, remove the following.
# the groovy scripts expects all logs in
# <jenkins-workspace>/<pipeline>/ci-scripts, so copy it there
with cls_cmd.LocalCmd() as c:
c.run(f'mkdir -p {os.getcwd()}/test_log_{ctx.test_id}/')
c.run(f'cp -r {ctx.logPath} {os.getcwd()}/test_log_{ctx.test_id}/')
HTML.CreateHtmlNextTabHeaderTestRow(collectInfo, imageSize)
return status
def deploy_oc_physim(self, ctx, HTML, oc_release, node):
def deploy_oc_physim(self, HTML, oc_release, svr_id):
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
HELP.GenericHelp(CONST.Version)
raise ValueError(f'Insufficient Parameter: ranRepository {self.ranRepository} ranBranch {self.ranBranch} ranCommitID {self.ranCommitID}')
image_tag = cls_containerize.CreateTag(self.ranCommitID, self.ranBranch, self.ranAllowMerge)
logging.debug(f'Running physims from server: {node}')
logging.debug(f'Running physims from server: {svr_id}')
script = "scripts/oc-deploy-physims.sh"
options = f"oaicicd-core-for-ci-ran {oc_release} {image_tag} {self.eNBSourceCodePath}"
ret = cls_cmd.runScript(node, script, 600, options)
ret = cls_cmd.runScript(svr_id, script, 600, options)
logging.debug(f'"{script}" finished with code {ret.returncode}, output:\n{ret.stdout}')
with cls_cmd.getConnection(node) as ssh:
details_json = archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/{oc_release}-tests.json')
result_junit = archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/{oc_release}-run.xml')
archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/physim_log.txt')
archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/physim_pods_summary.txt')
archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/LastTestsFailed.log')
test_status, test_summary, test_result = cls_analysis.Analysis.analyze_oc_physim(result_junit, details_json, ctx.logPath)
log_dir = f'{os.getcwd()}/../cmake_targets/log'
os.makedirs(log_dir, exist_ok=True)
result_junit = f'{oc_release}-run.xml'
details_json = f'{oc_release}-tests.json'
with cls_cmd.getConnection(svr_id) as ssh:
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/{details_json}', tgt=f'{log_dir}/{details_json}')
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/{result_junit}', tgt=f'{log_dir}/{result_junit}')
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/physim_log.txt', tgt=f'{log_dir}/physim_log.txt')
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/physim_pods_summary.txt', tgt=f'{log_dir}/physim_pods_summary.txt')
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/LastTestsFailed.log', tgt=f'{log_dir}/LastTestsFailed.log')
test_status, test_summary, test_result = cls_analysis.Analysis.analyze_oc_physim(f'{log_dir}/{result_junit}', f'{log_dir}/{details_json}')
if test_summary:
if test_status:
HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
HTML.CreateHtmlTestRowPhySimTestResult(test_summary, test_result)
logging.info('\u001B[1m Physical Simulator Pass\u001B[0m')
else:
HTML.CreateHtmlTestRowQueue('At least one physical simulator test failed!', 'KO', ["See below for details"])
HTML.CreateHtmlTestRow('Some test(s) failed!', 'KO', CONST.OC_PHYSIM_DEPLOY_FAIL)
HTML.CreateHtmlTestRowPhySimTestResult(test_summary, test_result)
logging.error('\u001B[1m Physical Simulator Fail\u001B[0m')
else:

View File

@@ -46,7 +46,6 @@ import cls_cmd
import helpreadme as HELP
import constants as CONST
import cls_oaicitest
from cls_ci_helper import archiveArtifact
#-----------------------------------------------------------
# Helper functions used here and in other classes
@@ -82,23 +81,69 @@ def CreateTag(ranCommitID, ranBranch, ranAllowMerge):
tagToUse = f'develop-{shortCommit}'
return tagToUse
def AnalyzeBuildLogs(image, lf):
errorandwarnings = {}
committed = False
tagged = False
with open(lf, mode='r') as inputfile:
for line in inputfile:
lineHasTag = re.search(f'Successfully tagged {image}:', str(line)) is not None
lineHasTag2 = re.search(f'naming to docker.io/library/{image}:', str(line)) is not None
tagged = tagged or lineHasTag or lineHasTag2
# the OpenShift Cluster builder prepends image registry URL
lineHasCommit = re.search(r'COMMIT [a-zA-Z0-9\.:/\-]*' + image, str(line)) is not None
committed = committed or lineHasCommit
errorandwarnings['errors'] = 0 if committed or tagged else 1
errorandwarnings['warnings'] = 0
errorandwarnings['status'] = committed or tagged
logging.info(f"Analyzing {image}, file {lf}: {errorandwarnings}")
return errorandwarnings
def CopyLogsToExecutor(cmd, sourcePath, log_name):
cmd.cd(f'{sourcePath}/cmake_targets')
cmd.run(f'rm -f {log_name}.zip')
cmd.run(f'mkdir -p {log_name}')
cmd.run(f'mv log/* {log_name}')
cmd.run(f'zip -r -qq {log_name}.zip {log_name}')
# copy zip to executor for analysis
if (os.path.isfile(f'./{log_name}.zip')):
os.remove(f'./{log_name}.zip')
if (os.path.isdir(f'./{log_name}')):
shutil.rmtree(f'./{log_name}')
cmd.copyin(src=f'{sourcePath}/cmake_targets/{log_name}.zip', tgt=f'{os.getcwd()}/{log_name}.zip')
cmd.run(f'rm -f {log_name}.zip')
ZipFile(f'{log_name}.zip').extractall('.')
def AnalyzeBuildLogs(buildRoot, images, globalStatus):
collectInfo = {}
for image in images:
files = {}
file_list = [f for f in os.listdir(f'{buildRoot}/{image}') if os.path.isfile(os.path.join(f'{buildRoot}/{image}', f)) and f.endswith('.txt')]
# Analyze the "sub-logs" of every target image
for fil in file_list:
errorandwarnings = {}
warningsNo = 0
errorsNo = 0
with open(f'{buildRoot}/{image}/{fil}', mode='r') as inputfile:
for line in inputfile:
result = re.search(' ERROR ', str(line))
if result is not None:
errorsNo += 1
result = re.search(' error:', str(line))
if result is not None:
errorsNo += 1
result = re.search(' WARNING ', str(line))
if result is not None:
warningsNo += 1
result = re.search(' warning:', str(line))
if result is not None:
warningsNo += 1
errorandwarnings['errors'] = errorsNo
errorandwarnings['warnings'] = warningsNo
errorandwarnings['status'] = globalStatus
files[fil] = errorandwarnings
# Analyze the target image
if os.path.isfile(f'{buildRoot}/{image}.log'):
errorandwarnings = {}
committed = False
tagged = False
with open(f'{buildRoot}/{image}.log', mode='r') as inputfile:
for line in inputfile:
lineHasTag = re.search(f'Successfully tagged {image}:', str(line)) is not None
lineHasTag2 = re.search(f'naming to docker.io/library/{image}:', str(line)) is not None
tagged = tagged or lineHasTag or lineHasTag2
# the OpenShift Cluster builder prepends image registry URL
lineHasCommit = re.search(f'COMMIT [a-zA-Z0-9\.:/\-]*{image}', str(line)) is not None
committed = committed or lineHasCommit
errorandwarnings['errors'] = 0 if committed or tagged else 1
errorandwarnings['warnings'] = 0
errorandwarnings['status'] = committed or tagged
files['Target Image Creation'] = errorandwarnings
collectInfo[image] = files
return collectInfo
def GetImageName(ssh, svcName, file):
ret = ssh.run(f"docker compose -f {file} config --format json {svcName} | jq -r '.services.\"{svcName}\".image'", silent=True)
@@ -157,10 +202,12 @@ def GetServices(ssh, requested, file):
else:
return requested
def CopyinServiceLog(ssh, lSourcePath, svcName, wd_yaml, ctx):
remote_filename = f"{lSourcePath}/cmake_targets/log/{svcName}.logs"
def CopyinServiceLog(ssh, lSourcePath, yaml, svcName, wd_yaml, filename):
remote_filename = f"{lSourcePath}/cmake_targets/log/{filename}"
ssh.run(f'docker compose -f {wd_yaml} logs {svcName} --no-log-prefix &> {remote_filename}')
return archiveArtifact(ssh, ctx, remote_filename)
local_dir = f"{os.getcwd()}/../cmake_targets/log/{yaml}"
local_filename = f"{local_dir}/{filename}"
return ssh.copyin(remote_filename, local_filename)
def GetRunningServices(ssh, file):
ret = ssh.run(f'docker compose -f {file} config --services')
@@ -182,13 +229,14 @@ def GetRunningServices(ssh, file):
logging.info(f'stopping services: {running_services}')
return running_services
def CheckLogs(self, filename, HTML, RAN):
def CheckLogs(self, yaml, service_name, HTML, RAN):
logPath = f'{os.getcwd()}/../cmake_targets/log/{yaml}'
filename = f'{logPath}/{service_name}-{HTML.testCase_id}.log'
success = True
name = os.path.basename(filename)
if (any(sub in name for sub in ['oai_ue','oai-nr-ue','lte_ue'])):
if (any(sub in service_name for sub in ['oai_ue','oai-nr-ue','lte_ue'])):
logging.debug(f'\u001B[1m Analyzing UE logfile {filename} \u001B[0m')
logStatus = cls_oaicitest.OaiCiTest().AnalyzeLogFile_UE(filename, HTML, RAN)
opt = f"UE log analysis ({name})"
opt = f"UE log analysis for service {service_name}"
# usage of htmlUEFailureMsg/htmleNBFailureMsg is because Analyze log files
# abuse HTML to store their reports, and we here want to put custom options,
# which is not possible with CreateHtmlTestRow
@@ -199,13 +247,13 @@ def CheckLogs(self, filename, HTML, RAN):
else:
HTML.CreateHtmlTestRowQueue(opt, 'OK', [HTML.htmlUEFailureMsg])
HTML.htmlUEFailureMsg = ""
elif 'nv-cubb' in name:
elif service_name == 'nv-cubb':
msg = 'Undeploy PNF/Nvidia CUBB'
HTML.CreateHtmlTestRow(msg, 'OK', CONST.ALL_PROCESSES_OK)
elif (any(sub in name for sub in ['enb','rru','rcc','cu','du','gnb'])):
elif (any(sub in service_name for sub in ['enb','rru','rcc','cu','du','gnb'])):
logging.debug(f'\u001B[1m Analyzing XnB logfile {filename}\u001B[0m')
logStatus = RAN.AnalyzeLogFile_eNB(filename, HTML, self.ran_checkers)
opt = f"xNB log analysis ({name})"
opt = f"xNB log analysis for service {service_name}"
if (logStatus < 0):
HTML.CreateHtmlTestRowQueue(opt, 'KO', [HTML.htmleNBFailureMsg])
success = False
@@ -213,8 +261,9 @@ def CheckLogs(self, filename, HTML, RAN):
HTML.CreateHtmlTestRowQueue(opt, 'OK', [HTML.htmleNBFailureMsg])
HTML.htmleNBFailureMsg = ""
else:
logging.info(f"Skipping analysis of log '{filename}': no submatch for xNB/UE")
logging.debug(f"log check: file {filename} passed analysis {success}")
logging.info(f'Skipping to analyze log for service name {service_name}')
HTML.CreateHtmlTestRowQueue(f"service {service_name}", 'OK', ["no analysis function"])
logging.debug(f"log check: service {service_name} passed analysis {success}")
return success
#-----------------------------------------------------------
@@ -229,13 +278,29 @@ class Containerize():
self.ranAllowMerge = False
self.ranCommitID = ''
self.ranTargetBranch = ''
self.eNBIPAddress = ''
self.eNBUserName = ''
self.eNBPassword = ''
self.eNBSourceCodePath = ''
self.eNB1IPAddress = ''
self.eNB1UserName = ''
self.eNB1Password = ''
self.eNB1SourceCodePath = ''
self.eNB2IPAddress = ''
self.eNB2UserName = ''
self.eNB2Password = ''
self.eNB2SourceCodePath = ''
self.forcedWorkspaceCleanup = False
self.imageKind = ''
self.proxyCommit = None
self.yamlPath = ''
self.services = ''
self.eNB_instance = 0
self.eNB_serverId = ['', '', '']
self.yamlPath = ['', '', '']
self.services = ['', '', '']
self.deploymentTag = ''
self.testCase_id = ''
self.cli = ''
self.cliBuildOptions = ''
self.dockerfileprefix = ''
@@ -251,11 +316,25 @@ class Containerize():
# Container management functions
#-----------------------------------------------------------
def BuildImage(self, ctx, node, HTML):
lSourcePath = self.eNBSourceCodePath
logging.debug('Building on server: ' + node)
cmd = cls_cmd.getConnection(node)
log_files = []
def GetCredentials(self, server_id):
if server_id == '0':
ip, path = self.eNBIPAddress, self.eNBSourceCodePath
elif server_id == '1':
ip, path = self.eNB1IPAddress, self.eNB1SourceCodePath
elif server_id == '2':
ip, path = self.eNB2IPAddress, self.eNB2SourceCodePath
else:
raise ValueError(f"unknown server ID '{server_id}'")
if ip == '' or path == '':
HELP.GenericHelp(CONST.Version)
raise ValueError(f'Insufficient Parameter: IP/node {ip}, path {path}')
return (ip, path)
def BuildImage(self, HTML):
svr = self.eNB_serverId[self.eNB_instance]
lIpAddr, lSourcePath = self.GetCredentials(svr)
logging.debug('Building on server: ' + lIpAddr)
cmd = cls_cmd.getConnection(lIpAddr)
# Checking the hostname to get adapted on cli and dockerfileprefixes
cmd.run('hostnamectl')
@@ -263,7 +342,7 @@ class Containerize():
self.host = result.group(0)
if self.host == 'Ubuntu':
self.cli = 'docker'
self.dockerfileprefix = '.ubuntu'
self.dockerfileprefix = '.ubuntu22'
self.cliBuildOptions = ''
elif self.host == 'Red Hat':
self.cli = 'sudo podman'
@@ -302,7 +381,7 @@ class Containerize():
imageNames.append(('oai-gnb', 'gNB.fhi72', 'oai-gnb-fhi72', ''))
result = re.search('build_cross_arm64', self.imageKind)
if result is not None:
self.dockerfileprefix = '.ubuntu.cross-arm64'
self.dockerfileprefix = '.ubuntu22.cross-arm64'
result = re.search('native_arm', self.imageKind)
if result is not None:
imageNames.append(('oai-gnb', 'gNB', 'oai-gnb', ''))
@@ -311,6 +390,7 @@ class Containerize():
imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', ''))
imageNames.append(('oai-gnb-aerial', 'gNB.aerial', 'oai-gnb-aerial', ''))
self.testCase_id = HTML.testCase_id
cmd.cd(lSourcePath)
# if asterix, copy the entitlement and subscription manager configurations
if self.host == 'Red Hat':
@@ -340,17 +420,13 @@ class Containerize():
# Let's remove any previous run artifacts if still there
cmd.run(f"{self.cli} image prune --force")
for image,pattern,name,option in imageNames:
cmd.run(f"{self.cli} image rm {name}:{imageTag}", reportNonZero=False)
cmd.run(f"{self.cli} image rm {name}:{imageTag}")
# Build the base image only on Push Events (not on Merge Requests)
# On when the base image docker file is being modified.
if forceBaseImageBuild:
cmd.run(f"{self.cli} image rm {baseImage}:{baseTag}")
logfile = f'{lSourcePath}/cmake_targets/log/ran-base.docker.log'
cmd.run(f"{self.cli} build {self.cliBuildOptions} --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{self.dockerfileprefix} . &> {logfile}", timeout=1600)
t = ("ran-base", archiveArtifact(cmd, ctx, logfile))
log_files.append(t)
cmd.run(f"{self.cli} build {self.cliBuildOptions} --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{self.dockerfileprefix} . &> cmake_targets/log/ran-base.log", timeout=1600)
# First verify if the base image was properly created.
ret = cmd.run(f"{self.cli} image inspect --format=\'Size = {{{{.Size}}}} bytes\' {baseImage}:{baseTag}")
allImagesSize = {}
@@ -365,7 +441,7 @@ class Containerize():
HTML.CreateHtmlTabFooter(False)
return False
else:
result = re.search(r'Size *= *(?P<size>[0-9\-]+) *bytes', cmd.getBefore())
result = re.search('Size *= *(?P<size>[0-9\-]+) *bytes', cmd.getBefore())
if result is not None:
size = float(result.group("size")) / 1000000
imageSizeStr = f'{size:.1f}'
@@ -374,9 +450,17 @@ class Containerize():
else:
logging.debug('ran-base size is unknown')
# Recover build logs, for the moment only possible when build is successful
cmd.run(f"{self.cli} create --name test {baseImage}:{baseTag}")
cmd.run("mkdir -p cmake_targets/log/ran-base")
cmd.run(f"{self.cli} cp test:/oai-ran/cmake_targets/log/. cmake_targets/log/ran-base")
cmd.run(f"{self.cli} rm -f test")
# Build the target image(s)
status = True
attemptedImages = ['ran-base']
for image,pattern,name,option in imageNames:
attemptedImages += [name]
# the archived Dockerfiles have "ran-base:latest" as base image
# we need to update them with proper tag
cmd.run(f'git checkout -- docker/Dockerfile.{pattern}{self.dockerfileprefix}')
@@ -390,12 +474,15 @@ class Containerize():
cmd.run(f'sed -i -e "s#ran-build:latest#ran-build:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}')
if image == 'oai-gnb-aerial':
cmd.run('cp -f /opt/nvidia-ipc/nvipc.src.2025.05.20.tar.gz .')
logfile = f'{lSourcePath}/cmake_targets/log/{name}.docker.log'
ret = cmd.run(f'{self.cli} build {self.cliBuildOptions} --target {image} --tag {name}:{imageTag} --file docker/Dockerfile.{pattern}{self.dockerfileprefix} {option} . > {logfile} 2>&1', timeout=1200)
t = (name, archiveArtifact(cmd, ctx, logfile))
log_files.append(t)
ret = cmd.run(f'{self.cli} build {self.cliBuildOptions} --target {image} --tag {name}:{imageTag} --file docker/Dockerfile.{pattern}{self.dockerfileprefix} {option} . > cmake_targets/log/{name}.log 2>&1', timeout=1200)
if image == 'oai-gnb-aerial':
cmd.run('rm -f nvipc.src.2025.05.20.tar.gz')
if image == 'ran-build' and ret.returncode == 0:
cmd.run(f"docker run --name test-log -d {name}:{imageTag} /bin/true")
cmd.run(f"docker cp test-log:/oai-ran/cmake_targets/log/ cmake_targets/log/{name}/")
cmd.run(f"docker rm -f test-log")
else:
cmd.run(f"mkdir -p cmake_targets/log/{name}")
# check the status of the build
ret = cmd.run(f"{self.cli} image inspect --format=\'Size = {{{{.Size}}}} bytes\' {name}:{imageTag}")
if ret.returncode != 0:
@@ -406,7 +493,7 @@ class Containerize():
allImagesSize[name] = 'N/A -- Build Failed'
break
else:
result = re.search(r'Size *= *(?P<size>[0-9\-]+) *bytes', cmd.getBefore())
result = re.search('Size *= *(?P<size>[0-9\-]+) *bytes', cmd.getBefore())
if result is not None:
size = float(result.group("size")) / 1000000 # convert to MB
imageSizeStr = f'{size:.1f}'
@@ -429,27 +516,33 @@ class Containerize():
logging.debug(cmd.run("df -h").stdout)
logging.debug(cmd.run("docker system df").stdout)
# create a zip with all logs
build_log_name = f'build_log_{self.testCase_id}'
CopyLogsToExecutor(cmd, lSourcePath, build_log_name)
cmd.close()
# Analyze the logs
for name, lf in log_files:
ret = AnalyzeBuildLogs(name, lf)
imgStatus = ret['status']
msg = f"size {allImagesSize[name]}, analysis of {os.path.basename(lf)}: {ret['errors']} errors, {ret['warnings']} warnings"
HTML.CreateHtmlTestRowQueue(name, 'OK' if imgStatus else 'KO', [msg])
status = status and imgStatus
collectInfo = AnalyzeBuildLogs(build_log_name, attemptedImages, status)
if status:
logging.info('\u001B[1m Building OAI Image(s) Pass\u001B[0m')
HTML.CreateHtmlTestRow(self.imageKind, 'OK', CONST.ALL_PROCESSES_OK)
HTML.CreateHtmlNextTabHeaderTestRow(collectInfo, allImagesSize)
return True
else:
logging.error('\u001B[1m Building OAI Images Failed\u001B[0m')
return status
HTML.CreateHtmlTestRow(self.imageKind, 'KO', CONST.ALL_PROCESSES_OK)
HTML.CreateHtmlNextTabHeaderTestRow(collectInfo, allImagesSize)
HTML.CreateHtmlTabFooter(False)
return False
def BuildProxy(self, ctx, node, HTML):
lSourcePath = self.eNBSourceCodePath
logging.debug('Building on server: ' + node)
ssh = cls_cmd.getConnection(node)
def BuildProxy(self, HTML):
svr = self.eNB_serverId[self.eNB_instance]
lIpAddr, lSourcePath = self.GetCredentials(svr)
logging.debug('Building on server: ' + lIpAddr)
ssh = cls_cmd.getConnection(lIpAddr)
self.testCase_id = HTML.testCase_id
oldRanCommidID = self.ranCommitID
oldRanRepository = self.ranRepository
oldRanAllowMerge = self.ranAllowMerge
@@ -471,14 +564,19 @@ class Containerize():
buildProxy = ret.returncode != 0 # if no image, build new proxy
if buildProxy:
ssh.run(f'rm -Rf {lSourcePath}')
success = CreateWorkspace(node, lSourcePath, self.ranRepository, self.ranCommitID, self.ranTargetBranch, self.ranAllowMerge)
success = CreateWorkspace(lIpAddr, lSourcePath, self.ranRepository, self.ranCommitID, self.ranTargetBranch, self.ranAllowMerge)
if not success:
raise Exception("could not clone proxy repository")
fullpath = f'{lSourcePath}/proxy_build.log'
filename = f'build_log_{self.testCase_id}'
fullpath = f'{lSourcePath}/{filename}'
ssh.run(f'docker build --target oai-lte-multi-ue-proxy --tag proxy:{tag} --file {lSourcePath}/docker/Dockerfile.ubuntu18.04 {lSourcePath} > {fullpath} 2>&1')
archiveArtifact(ssh, ctx, fullpath)
ssh.run(f'zip -r -qq {fullpath}.zip {fullpath}')
local_file = f"{os.getcwd()}/../cmake_targets/log/{filename}.zip"
ssh.copyin(f'{fullpath}.zip', local_file)
# don't delete such that we might recover the zips
#ssh.run(f'rm -f {fullpath}.zip')
ssh.run('docker image prune --force')
ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}')
@@ -507,8 +605,18 @@ class Containerize():
self.ranAllowMerge = oldRanAllowMerge
self.ranTargetBranch = oldRanTargetBranch
# we do not analyze the logs (we assume the proxy builds fine at this stage),
# but need to have the following information to correctly display the HTML
files = {}
errorandwarnings = {}
errorandwarnings['errors'] = 0
errorandwarnings['warnings'] = 0
errorandwarnings['status'] = True
files['Target Image Creation'] = errorandwarnings
collectInfo = {}
collectInfo['proxy'] = files
ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}')
result = re.search(r'Size *= *(?P<size>[0-9\-]+) *bytes', ret.stdout)
result = re.search('Size *= *(?P<size>[0-9\-]+) *bytes', ret.stdout)
# Cleaning any created tmp volume
ssh.run('docker volume prune --force')
ssh.close()
@@ -520,18 +628,21 @@ class Containerize():
allImagesSize['proxy'] = str(round(imageSize,1)) + ' Mbytes'
logging.info('\u001B[1m Building L2sim Proxy Image Pass\u001B[0m')
HTML.CreateHtmlTestRow('commit ' + tag, 'OK', CONST.ALL_PROCESSES_OK)
HTML.CreateHtmlNextTabHeaderTestRow(collectInfo, allImagesSize)
return True
else:
logging.error('proxy size is unknown')
allImagesSize['proxy'] = 'unknown'
logging.error('\u001B[1m Build of L2sim proxy failed\u001B[0m')
HTML.CreateHtmlTestRow('commit ' + tag, 'KO', CONST.ALL_PROCESSES_OK)
HTML.CreateHtmlTabFooter(False)
return False
def BuildRunTests(self, ctx, node, HTML):
lSourcePath = self.eNBSourceCodePath
logging.debug('Building on server: ' + node)
cmd = cls_cmd.RemoteCmd(node)
def BuildRunTests(self, HTML):
svr = self.eNB_serverId[self.eNB_instance]
lIpAddr, lSourcePath = self.GetCredentials(svr)
logging.debug('Building on server: ' + lIpAddr)
cmd = cls_cmd.RemoteCmd(lIpAddr)
cmd.cd(lSourcePath)
ret = cmd.run('hostnamectl')
@@ -542,6 +653,10 @@ class Containerize():
raise Exception("Can build unit tests only on Ubuntu server")
logging.debug('running on Ubuntu as expected')
if self.forcedWorkspaceCleanup:
cmd.run(f'sudo -S rm -Rf {lSourcePath}')
self.testCase_id = HTML.testCase_id
# check that ran-base image exists as we expect it
baseImage = 'ran-base'
baseTag = 'develop'
@@ -559,11 +674,11 @@ class Containerize():
return False
# build ran-unittests image
dockerfile = "ci-scripts/docker/Dockerfile.unittest.ubuntu"
logfile = f'{lSourcePath}/cmake_targets/log/unittest-build.log'
ret = cmd.run(f'docker build --progress=plain --tag ran-unittests:{baseTag} --file {dockerfile} . &> {logfile}')
archiveArtifact(cmd, ctx, logfile)
dockerfile = "ci-scripts/docker/Dockerfile.unittest.ubuntu22"
ret = cmd.run(f'docker build --progress=plain --tag ran-unittests:{baseTag} --file {dockerfile} . &> {lSourcePath}/cmake_targets/log/unittest-build.log')
if ret.returncode != 0:
build_log_name = f'build_log_{self.testCase_id}'
CopyLogsToExecutor(cmd, lSourcePath, build_log_name)
logging.error(f'Cannot build unit tests')
HTML.CreateHtmlTestRow("Unit test build failed", 'KO', [dockerfile])
HTML.CreateHtmlTabFooter(False)
@@ -572,15 +687,10 @@ class Containerize():
HTML.CreateHtmlTestRowQueue("Build unit tests", 'OK', [dockerfile])
# it worked, build and execute tests, and close connection
# I would like to run it with --rm and mount the ctest result directory to avoid 'docker cp'
# below, but then permissions are messed up and we can't remove the directory without sudo
# making the next pipeline fail
ret = cmd.run(f'docker run -a STDOUT --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --name ran-unittests ran-unittests:{baseTag} ctest --no-label-summary -j$(nproc)')
cmd.run('docker cp ran-unittests:/oai-ran/build/Testing/Temporary/LastTest.log .')
archiveArtifact(cmd, ctx, f'{lSourcePath}/LastTest.log')
cmd.run('docker cp ran-unittests:/oai-ran/build/Testing/Temporary/LastTestsFailed.log .')
archiveArtifact(cmd, ctx, f'{lSourcePath}/LastTestsFailed.log')
cmd.run('docker rm ran-unittests')
ret = cmd.run(f'docker run -a STDOUT --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --rm ran-unittests:{baseTag} ctest --no-label-summary -j$(nproc)')
cmd.run(f'docker rmi ran-unittests:{baseTag}')
build_log_name = f'build_log_{self.testCase_id}'
CopyLogsToExecutor(cmd, lSourcePath, build_log_name)
cmd.close()
if ret.returncode == 0:
@@ -592,10 +702,10 @@ class Containerize():
HTML.CreateHtmlTabFooter(False)
return False
def Push_Image_to_Local_Registry(self, node, HTML, tag_prefix=""):
lSourcePath = self.eNBSourceCodePath
logging.debug('Pushing images to server: ' + node)
ssh = cls_cmd.getConnection(node)
def Push_Image_to_Local_Registry(self, HTML, svr_id, tag_prefix=""):
lIpAddr, lSourcePath = self.GetCredentials(svr_id)
logging.debug('Pushing images to server: ' + lIpAddr)
ssh = cls_cmd.getConnection(lIpAddr)
imagePrefix = 'porcepix.sboai.cs.eurecom.fr'
ret = ssh.run(f'docker login -u oaicicd -p oaicicd {imagePrefix}')
if ret.returncode != 0:
@@ -667,26 +777,28 @@ class Containerize():
msg = "Pulled Images:\n" + '\n'.join(pulled_images)
return True, msg
def Pull_Image_from_Registry(self, HTML, node, images, tag=None, tag_prefix="", registry="porcepix.sboai.cs.eurecom.fr", username="oaicicd", password="oaicicd"):
logging.debug(f'\u001B[1m Pulling image(s) on server: {node}\u001B[0m')
def Pull_Image_from_Registry(self, HTML, svr_id, images, tag=None, tag_prefix="", registry="porcepix.sboai.cs.eurecom.fr", username="oaicicd", password="oaicicd"):
lIpAddr, lSourcePath = self.GetCredentials(svr_id)
logging.debug('\u001B[1m Pulling image(s) on server: ' + lIpAddr + '\u001B[0m')
if not tag:
tag = CreateTag(self.ranCommitID, self.ranBranch, self.ranAllowMerge)
with cls_cmd.getConnection(node) as cmd:
with cls_cmd.getConnection(lIpAddr) as cmd:
success, msg = Containerize.Pull_Image(cmd, images, tag, tag_prefix, registry, username, password)
param = f"on node {node}"
param = f"on node {lIpAddr}"
if success:
HTML.CreateHtmlTestRowQueue(param, 'OK', [msg])
else:
HTML.CreateHtmlTestRowQueue(param, 'KO', [msg])
return success
def Clean_Test_Server_Images(self, HTML, node, images, tag=None):
logging.debug(f'\u001B[1m Cleaning image(s) from server: {node}\u001B[0m')
def Clean_Test_Server_Images(self, HTML, svr_id, images, tag=None):
lIpAddr, lSourcePath = self.GetCredentials(svr_id)
logging.debug(f'\u001B[1m Cleaning image(s) from server: {lIpAddr}\u001B[0m')
if not tag:
tag = CreateTag(self.ranCommitID, self.ranBranch, self.ranAllowMerge)
status = True
with cls_cmd.getConnection(node) as myCmd:
with cls_cmd.getConnection(lIpAddr) as myCmd:
removed_images = []
for image in images:
fullImage = f"oai-ci/{image}:{tag}"
@@ -697,28 +809,34 @@ class Containerize():
msg = "Removed Images:\n" + '\n'.join(removed_images)
s = 'OK' if status else 'KO'
param = f"on node {node}"
param = f"on node {lIpAddr}"
HTML.CreateHtmlTestRowQueue(param, s, [msg])
return status
def Create_Workspace(self, node, HTML):
lSourcePath = self.eNBSourceCodePath
success = CreateWorkspace(node, lSourcePath, self.ranRepository, self.ranCommitID, self.ranTargetBranch, self.ranAllowMerge)
def Create_Workspace(self,HTML):
svr = self.eNB_serverId[self.eNB_instance]
lIpAddr, lSourcePath = self.GetCredentials(svr)
success = CreateWorkspace(lIpAddr, lSourcePath, self.ranRepository, self.ranCommitID, self.ranTargetBranch, self.ranAllowMerge)
if success:
HTML.CreateHtmlTestRowQueue('N/A', 'OK', [f"created workspace {lSourcePath}"])
else:
HTML.CreateHtmlTestRowQueue('N/A', 'KO', ["cannot create workspace"])
return success
def DeployObject(self, ctx, node, HTML):
def DeployObject(self, HTML):
svr = self.eNB_serverId[self.eNB_instance]
num_attempts = self.num_attempts
lSourcePath = self.eNBSourceCodePath
logging.debug(f'Deploying OAI Object on server: {node}')
yaml = self.yamlPath.strip('/')
lIpAddr, lSourcePath = self.GetCredentials(svr)
logging.debug(f'Deploying OAI Object on server: {lIpAddr}')
yaml = self.yamlPath[self.eNB_instance].strip('/')
# creating the log folder by default
local_dir = f"{os.getcwd()}/../cmake_targets/log/{yaml.split('/')[-1]}"
os.system(f'mkdir -p {local_dir}')
wd = f'{lSourcePath}/{yaml}'
wd_yaml = f'{wd}/docker-compose.y*ml'
with cls_cmd.getConnection(node) as ssh:
services = GetServices(ssh, self.services, wd_yaml)
yaml_dir = yaml.split('/')[-1]
with cls_cmd.getConnection(lIpAddr) as ssh:
services = GetServices(ssh, self.services[self.eNB_instance], wd_yaml)
if services == [] or services == ' ' or services == None:
msg = 'Cannot determine services to start'
logging.error(msg)
@@ -743,7 +861,7 @@ class Containerize():
logging.warning(warning_msg)
HTML.CreateHtmlTestRowQueue('N/A', 'NOK', [warning_msg])
for svc in services.split():
CopyinServiceLog(ssh, lSourcePath, svc, wd_yaml, ctx)
CopyinServiceLog(ssh, lSourcePath, yaml_dir, svc, wd_yaml, f'{svc}-{HTML.testCase_id}-attempt{attempt}.log')
ssh.run(f'docker compose -f {wd_yaml} down -- {services}')
imagesInfo = info.stdout.splitlines()[1:]
logging.debug(f'{info.stdout.splitlines()[1:]}')
@@ -753,19 +871,21 @@ class Containerize():
HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['\n'.join(imagesInfo)])
return deployed
def UndeployObject(self, ctx, node, HTML, RAN):
lSourcePath = self.eNBSourceCodePath
logging.debug(f'\u001B[1m Undeploying OAI Object from server: {node}\u001B[0m')
yaml = self.yamlPath.strip('/')
def UndeployObject(self, HTML, RAN):
svr = self.eNB_serverId[self.eNB_instance]
lIpAddr, lSourcePath = self.GetCredentials(svr)
logging.debug(f'\u001B[1m Undeploying OAI Object from server: {lIpAddr}\u001B[0m')
yaml = self.yamlPath[self.eNB_instance].strip('/')
wd = f'{lSourcePath}/{yaml}'
with cls_cmd.getConnection(node) as ssh:
yaml_dir = yaml.split('/')[-1]
with cls_cmd.getConnection(lIpAddr) as ssh:
ExistEnvFilePrint(ssh, wd)
services = GetRunningServices(ssh, f"{wd}/docker-compose.y*ml")
copyin_res = None
if services is not None:
all_serv = " ".join([s for s, _ in services])
ssh.run(f'docker compose -f {wd}/docker-compose.y*ml stop -- {all_serv}')
copyin_res = [CopyinServiceLog(ssh, lSourcePath, s, f"{wd}/docker-compose.y*ml", ctx) for s, _ in services]
copyin_res = all(CopyinServiceLog(ssh, lSourcePath, yaml_dir, s, f"{wd}/docker-compose.y*ml", f'{s}-{HTML.testCase_id}.log') for s, c in services)
else:
logging.warning('could not identify services to stop => no log file')
ssh.run(f'docker compose -f {wd}/docker-compose.y*ml down -v')
@@ -774,7 +894,7 @@ class Containerize():
HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['Could not copy logfile(s)'])
return False
else:
log_results = [CheckLogs(self, f, HTML, RAN) for f in copyin_res]
log_results = [CheckLogs(self, yaml_dir, s, HTML, RAN) for s, _ in services]
success = all(log_results)
if success:
logging.info('\u001B[1m Undeploying OAI Object Pass\u001B[0m')

View File

@@ -29,7 +29,6 @@ import yaml
import re
import cls_cmd
from cls_ci_helper import archiveArtifact
def listify(s):
if s is None:
@@ -115,7 +114,8 @@ class CoreNetwork:
logging.info(f'deployed core network {self}, pingable IP address {ip}')
return True, output
def _collect_logs(self, ctx):
def _collect_logs(self, log_dir):
logging.info(f'collecting logs into (local) {log_dir}')
remote_dir = "/tmp/cn-undeploy-logs"
with cls_cmd.getConnection(self._host) as c:
# create a directory for log collection
@@ -134,17 +134,18 @@ class CoreNetwork:
logging.error("cannot enumerate log files")
return []
log_files = []
# copy them to the executor one by one
# copy them to the executor one by one, and store in log_dir
for f in ret.stdout.split("\n"):
name = archiveArtifact(c, ctx, f)
log_files.append(name)
l = f.replace(remote_dir, log_dir)
c.copyin(f, l)
log_files.append(l)
c.run(f'rm -rf {remote_dir}')
return log_files
def undeploy(self, ctx=None):
def undeploy(self, log_dir=None):
log_files = []
if ctx is not None:
log_files = self._collect_logs(ctx)
if log_dir is not None:
log_files = self._collect_logs(log_dir)
else:
logging.warning("no directory for log collection specified, cannot retrieve core network logs")
logging.info(f'undeploy core network {self}')

View File

@@ -32,7 +32,6 @@ import re
import yaml
import cls_cmd
from cls_ci_helper import archiveArtifact
class Module_UE:
@@ -60,31 +59,26 @@ class Module_UE:
}
self.interface = m.get('IF')
self.MTU = m.get('MTU')
self.trace = m.get('trace') == True
self.logStore = m.get('LogStore')
self.cmd_prefix = m.get('CmdPrefix')
logging.info(f'initialized UE {self} from {filename}')
t = m.get('Tracing')
self.trace = t is not None
if self.trace:
if t.get('Start') is None or t.get('Stop') is None or t.get('Collect')is None :
raise ValueError("need to have Start/Stop/Collect for tracing")
self.cmd_dict["traceStart"] = t.get('Start')
self.cmd_dict["traceStop"] = t.get('Stop')
self._logCollect = t.get('Collect')
if "%%log_dir%%" not in self._logCollect:
raise ValueError(f"(At least one) LogCollect expression for {module_name} must contain \"%%log_dir%%\"")
def __str__(self):
return f"{self.module_name}@{self.host}"
return f"{self.module_name}@{self.host} [IP: {self.getIP()}]"
def __repr__(self):
return self.__str__()
def _command(self, cmd, silent=False, reportNonZero=True):
def _command(self, cmd, silent = False):
if cmd is None:
raise Exception("no command provided")
with cls_cmd.getConnection(self.host) as c:
response = c.run(cmd, silent=silent, reportNonZero=reportNonZero)
if self.host == "" or self.host == "localhost":
c = cls_cmd.LocalCmd()
else:
c = cls_cmd.RemoteCmd(self.host)
response = c.run(cmd, silent=silent)
c.close()
return response
#-----------------$
@@ -92,23 +86,25 @@ class Module_UE:
#-----------------$
def initialize(self):
if self.trace:
raise Exception("UE tracing not implemented yet")
self._enableTrace()
# we first terminate to make sure the UE has been stopped
if self.cmd_dict["detach"]:
self._command(self.cmd_dict["detach"], silent=True)
self._command(self.cmd_dict["terminate"], silent=True)
ret = self._command(self.cmd_dict["initialize"])
logging.info(f'For command: {ret.args} | return output: {ret.stdout} | Code: {ret.returncode}')
if self.trace:
self._enableTrace()
# Here each UE returns differently for the successful initialization, requires check based on UE
return ret.returncode == 0
def terminate(self, ctx=None):
def terminate(self):
self._command(self.cmd_dict["terminate"])
if self.trace and ctx is not None:
if self.trace:
raise Exception("UE tracing not implemented yet")
self._disableTrace()
return self._collectTrace(ctx)
return self._logCollect()
return None
def attach(self, attach_tries = 4, attach_timeout = 60):
@@ -117,11 +113,10 @@ class Module_UE:
self._command(self.cmd_dict["attach"])
timeout = attach_timeout
logging.debug("Waiting for IP address to be assigned")
ip = self.getIP(silent=False, reportNonZero=True)
while timeout > 0 and not ip:
time.sleep(1)
timeout -= 1
ip = self.getIP(silent=True, reportNonZero=False)
time.sleep(5)
timeout -= 5
ip = self.getIP()
if ip:
break
logging.warning(f"UE did not receive IP address after {attach_timeout} s, detaching")
@@ -165,8 +160,8 @@ class Module_UE:
logging.error(message)
return False
def getIP(self, silent=True, reportNonZero=True):
output = self._command(self.cmd_dict["getNetwork"], silent=silent, reportNonZero=reportNonZero)
def getIP(self):
output = self._command(self.cmd_dict["getNetwork"], silent=True)
result = re.search(r'inet (?P<ip>[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)', output.stdout)
if result and result.group('ip'):
ip = result.group('ip')
@@ -196,33 +191,10 @@ class Module_UE:
return self.cmd_prefix if self.cmd_prefix else ""
def _enableTrace(self):
logging.info(f'UE {self}: start UE tracing')
self._command(self.cmd_dict["traceStart"])
raise Exception("not implemented")
def _disableTrace(self):
logging.info(f'UE {self}: stop UE tracing')
self._command(self.cmd_dict["traceStop"])
raise Exception("not implemented")
def _collectTrace(self, ctx):
remote_dir = "/tmp/ue-trace-logs"
with cls_cmd.getConnection(self.host) as c:
# create a directory for log collection
c.run(f'rm -rf {remote_dir}')
ret = c.run(f'mkdir {remote_dir}')
if ret.returncode != 0:
logging.error("cannot create directory for log collection")
return []
log_cmd = self._logCollect.replace('%%log_dir%%', remote_dir)
self._command(log_cmd)
# enumerate collected files
ret = c.run(f'ls {remote_dir}/*')
if ret.returncode != 0:
logging.error("cannot enumerate log files")
return []
log_files = []
# copy them to the executor one by one, and store in log_dir
for f in ret.stdout.split("\n"):
name = archiveArtifact(c, ctx, f)
log_files.append(name)
c.run(f'rm -rf {remote_dir}')
return log_files
def _logCollect(self):
raise Exception("not implemented")

View File

@@ -28,24 +28,26 @@ import cls_cmd
import cls_oai_html
import cls_analysis
import constants as CONST
from cls_ci_helper import archiveArtifact
DPDK_PATH = '/opt/dpdk-t2-22.11.0'
LOG_PATH_PHYSIM = 'phy_sim_logs'
class Native():
def Build(ctx, node, test_case, HTML, directory, options):
logging.debug(f'Building on server: {node}')
def Build(test_case, HTML, host, directory, options):
logging.debug(f'Building on server: {host}')
HTML.testCase_id = test_case
with cls_cmd.getConnection(node) as ssh:
with cls_cmd.getConnection(host) as ssh:
base = f"{directory}/cmake_targets"
ret = ssh.run(f"C_INCLUDE_PATH={DPDK_PATH}/include/ PKG_CONFIG_PATH={DPDK_PATH}/lib64/pkgconfig/ {base}/build_oai {options} > {base}/build_oai.log", timeout=900)
ret = ssh.run(f"{base}/build_oai {options} > {base}/log/build_oai.log", timeout=900)
success = ret.returncode == 0
logs = ssh.run(f"cat {base}/build_oai.log", silent=True)
logging.debug(f"build finished with code {ret.returncode}")
logs = ssh.run(f"cat {base}/log/build_oai.log", silent=True)
logging.debug(f"build finished with code {ret.returncode}, output:\n{logs.stdout}")
archiveArtifact(ssh, ctx, f'{base}/build_oai.log')
# create log directory, and copy build logs
target = f"{base}/build_log_{test_case}/"
ssh.run(f"mkdir -p {target}")
ssh.run(f"mv {base}/log/* {target}")
# check if build artifacts are there
# NOTE: build_oai should fail with exit code if it could not build, but it does not
@@ -65,14 +67,15 @@ class Native():
HTML.CreateHtmlTestRow(options, 'KO', CONST.ALL_PROCESSES_OK)
return success
def Run_Physim(ctx, HTML, host, directory, options, physim_test, threshold):
def Run_Physim(HTML, host, directory, options, physim_test, threshold):
logging.debug(f'Runnin {physim_test} on server: {host}')
workSpacePath = f'{directory}/cmake_targets'
runLogFile=f'{workSpacePath}/physim.log'
os.system(f'mkdir -p ./{LOG_PATH_PHYSIM}')
runLogFile=f'physim_{HTML.testCase_id}.log'
with cls_cmd.getConnection(host) as cmd:
cmd.run(f'sudo LD_LIBRARY_PATH=.:{DPDK_PATH}/lib64/ {workSpacePath}/ran_build/build/{physim_test} {options} > {runLogFile} 2>&1')
physim_file = archiveArtifact(cmd, ctx, runLogFile)
success, msg = cls_analysis.Analysis.analyze_physim(physim_file, physim_test, options, threshold)
cmd.run(f'sudo {workSpacePath}/ran_build/build/{physim_test} {options} >> {workSpacePath}/{runLogFile}')
cmd.copyin(src=f'{workSpacePath}/{runLogFile}', tgt=f'{LOG_PATH_PHYSIM}/{runLogFile}')
success, msg = cls_analysis.Analysis.analyze_physim(f'{LOG_PATH_PHYSIM}/{runLogFile}', physim_test, options, threshold)
if success:
HTML.CreateHtmlTestRowQueue(options, 'OK', [msg])
else:

View File

@@ -70,6 +70,16 @@ class HTMLManagement():
self.testCase_id = ''
self.desc = ''
self.OsVersion = ['', '']
self.KernelVersion = ['', '']
self.UhdVersion = ['', '']
self.UsrpBoard = ['', '']
self.CpuNb = ['', '']
self.CpuModel = ['', '']
self.CpuMHz = ['', '']
#-----------------------------------------------------------
# HTML structure creation functions
#-----------------------------------------------------------
@@ -332,6 +342,58 @@ class HTMLManagement():
self.htmlFile.write(' </tr>\n')
self.htmlFile.close()
def CreateHtmlNextTabHeaderTestRow(self, collectInfo, allImagesSize, machine='eNB'):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
self.htmlFile = open('test_results.html', 'a')
if bool(collectInfo) == False:
self.htmlFile.write(' <tr bgcolor = "red" >\n')
self.htmlFile.write(' <td colspan="6"><b> ----IMAGES BUILDING FAILED - Unable to recover the image logs ---- </b></td>\n')
self.htmlFile.write(' </tr>\n')
else:
for image in collectInfo:
files = collectInfo[image]
# TabHeader for image logs on built shared and target images
if allImagesSize[image].count('unknown') > 0:
self.htmlFile.write(' <tr bgcolor = "orange" >\n')
elif allImagesSize[image].count('Build Failed') > 0:
self.htmlFile.write(' <tr bgcolor = "red" >\n')
else:
self.htmlFile.write(' <tr bgcolor = "#F0F0F0" >\n')
self.htmlFile.write(' <td colspan="6"><b> ---- ' + image + ' IMAGE STATUS ----> Size ' + allImagesSize[image] + ' </b></td>\n')
self.htmlFile.write(' </tr>\n')
self.htmlFile.write(' <tr bgcolor = "#33CCFF" >\n')
self.htmlFile.write(' <th colspan="2">Element</th>\n')
self.htmlFile.write(' <th>Nb Errors</th>\n')
self.htmlFile.write(' <th>Nb Warnings</th>\n')
self.htmlFile.write(' <th colspan="2">Status</th>\n')
self.htmlFile.write(' </tr>\n')
for fil in files:
parameters = files[fil]
# TestRow for image logs on built shared and target images
self.htmlFile.write(' <tr>\n')
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" >' + fil + ' </td>\n')
if (parameters['errors'] == 0):
self.htmlFile.write(' <td bgcolor = "green" >' + str(parameters['errors']) + '</td>\n')
else:
self.htmlFile.write(' <td bgcolor = "red" >' + str(parameters['errors']) + '</td>\n')
if (parameters['errors'] > 0):
self.htmlFile.write(' <td bgcolor = "red" >' + str(parameters['warnings']) + '</td>\n')
elif (parameters['warnings'] == 0):
self.htmlFile.write(' <td bgcolor = "green" >' + str(parameters['warnings']) + '</td>\n')
else:
self.htmlFile.write(' <td bgcolor = "orange" >' + str(parameters['warnings']) + '</td>\n')
if (parameters['errors'] == 0) and (parameters['warnings'] == 0):
self.htmlFile.write(' <th colspan="2" bgcolor = "green" ><font color="white">OK </font></th>\n')
elif (parameters['errors'] == 0):
self.htmlFile.write(' <th colspan="2" bgcolor = "orange" ><font color="white">OK </font></th>\n')
else:
self.htmlFile.write(' <th colspan="2" bgcolor = "red" > NOT OK </th>\n')
self.htmlFile.write(' </tr>\n')
self.htmlFile.close()
#for the moment it is limited to 4 columns, to be made generic later
def CreateHtmlDataLogTable(self, DataLog):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):

View File

@@ -47,7 +47,6 @@ import constants as CONST
import cls_module
import cls_corenetwork
import cls_cmd
from cls_ci_helper import archiveArtifact
#-----------------------------------------------------------
# Helper functions used here and in other classes
@@ -84,29 +83,6 @@ def Iperf_ComputeTime(args):
raise Exception('Iperf time not found!')
return int(result.group('iperf_time'))
def convert_to_mbps(value, magnitude):
value = float(value)
if magnitude == 'K' or magnitude == 'k':
return value / 1000
elif magnitude == 'M':
return value
elif magnitude == 'G':
return value * 1000
else:
return value
def extract_iperf_data(res):
if not res:
return None
bitrate_val = res.group('bitrate')
magnitude = res.group('magnitude')
return {
'bitrate_mbps': convert_to_mbps(bitrate_val, magnitude),
'jitter': res.group('jitter'),
'packetloss': res.group('packetloss'),
'bitrate_disp': f'{float(bitrate_val):.2f} {magnitude}bps'
}
def Iperf_analyzeV3TCPJson(filename, iperf_tcp_rate_target):
try:
with open(filename) as f:
@@ -154,36 +130,53 @@ def Iperf_analyzeV3BIDIRJson(filename):
return (True, msg)
def Iperf_analyzeV3UDP(filename, iperf_bitrate_threshold, iperf_packetloss_threshold, target_bitrate):
if not os.path.isfile(filename):
if (not os.path.isfile(filename)):
return (False, 'Iperf3 UDP: Log file not present')
if os.path.getsize(filename) == 0:
if (os.path.getsize(filename)==0):
return (False, 'Iperf3 UDP: Log file is empty')
sender_data = None
receiver_data = None
sender_bitrate = None
receiver_bitrate = None
with open(filename, 'r') as server_file:
for line in server_file:
res_sender = re.search(r'(?P<bitrate>[0-9\.]+)\s+(?P<magnitude>[kKMG]?)bits\/sec\s+(?P<jitter>[0-9\.]+\s+ms)\s+(?P<lostPack>-?\d+)/(?P<sentPack>-?\d+)\s+\((?P<packetloss>[0-9\.eE\-\+]+).*?\s+(sender)', line)
res_receiver = re.search(r'(?P<bitrate>[0-9\.]+)\s+(?P<magnitude>[kKMG]?)bits\/sec\s+(?P<jitter>[0-9\.]+\s+ms)\s+(?P<lostPack>-?\d+)/(?P<receivedPack>-?\d+)\s+\((?P<packetloss>[0-9\.eE\-\+]+)%\).*?(receiver)', line)
if res_sender:
sender_data = extract_iperf_data(res_sender)
if res_receiver:
receiver_data = extract_iperf_data(res_receiver)
if not sender_data or not receiver_data:
return (False, 'Could not analyze iperf report')
for line in server_file.readlines():
res_sender = re.search(r'(?P<bitrate>[0-9\.]+)\s+(?P<unit>[KMG]?bits\/sec)\s+(?P<jitter>[0-9\.]+\s+ms)\s+(?P<lostPack>-?\d+)/(?P<sentPack>-?\d+) \((?P<lost>[0-9\.]+).*?\s+(sender)', line)
res_receiver = re.search(r'(?P<bitrate>[0-9\.]+)\s+(?P<unit>[KMG]?bits\/sec)\s+(?P<jitter>[0-9\.]+\s+ms)\s+(?P<lostPack>-?\d+)/(?P<receivedPack>-?\d+)\s+\((?P<lost>[0-9\.]+)%\).*?(receiver)', line)
if res_sender is not None:
sender_bitrate = res_sender.group('bitrate')
sender_unit = res_sender.group('unit')
sender_jitter = res_sender.group('jitter')
sender_lostPack = res_sender.group('lostPack')
sender_sentPack = res_sender.group('sentPack')
sender_packetloss = res_sender.group('lost')
if res_receiver is not None:
receiver_bitrate = res_receiver.group('bitrate')
receiver_unit = res_receiver.group('unit')
receiver_jitter = res_receiver.group('jitter')
receiver_lostPack = res_receiver.group('lostPack')
receiver_receivedPack = res_receiver.group('receivedPack')
receiver_packetloss = res_receiver.group('lost')
br_perf = 100 * receiver_data['bitrate_mbps'] / float(target_bitrate)
br_perf_str = f'{br_perf:.2f}%'
req_msg = f"Sender Bitrate : {sender_data['bitrate_disp']}"
bir_msg = f"Receiver Bitrate: {receiver_data['bitrate_disp']} ({br_perf_str})"
jit_msg = f"Jitter : {receiver_data['jitter']}"
pal_msg = f"Packet Loss : {receiver_data['packetloss']}%"
if br_perf < float(iperf_bitrate_threshold):
bir_msg += f' (too low! < {iperf_bitrate_threshold}%)'
if float(receiver_data['packetloss']) > float(iperf_packetloss_threshold):
pal_msg += f' (too high! > {iperf_packetloss_threshold}%)'
result = br_perf >= float(iperf_bitrate_threshold) and float(receiver_data['packetloss']) <= float(iperf_packetloss_threshold)
return (result, f'{req_msg}\n{bir_msg}\n{jit_msg}\n{pal_msg}')
if receiver_bitrate is not None and sender_bitrate is not None:
if sender_unit == 'Kbits/sec':
sender_bitrate = float(sender_bitrate) / 1000
if receiver_unit == 'Kbits/sec':
receiver_bitrate = float(receiver_bitrate) / 1000
br_perf = 100 * float(receiver_bitrate) / float(target_bitrate)
br_perf = '%.2f ' % br_perf
sender_bitrate = '%.2f ' % float(sender_bitrate)
receiver_bitrate = '%.2f ' % float(receiver_bitrate)
req_msg = f'Sender Bitrate : {sender_bitrate} Mbps'
bir_msg = f'Receiver Bitrate: {receiver_bitrate} Mbps'
brl_msg = f'{br_perf}%'
jit_msg = f'Jitter : {receiver_jitter}'
pal_msg = f'Packet Loss : {receiver_packetloss} %'
if float(br_perf) < float(iperf_bitrate_threshold):
brl_msg = f'too low! < {iperf_bitrate_threshold}%'
if float(receiver_packetloss) > float(iperf_packetloss_threshold):
pal_msg += f' (too high! > {iperf_packetloss_threshold}%)'
result = float(br_perf) >= float(iperf_bitrate_threshold) and float(receiver_packetloss) <= float(iperf_packetloss_threshold)
return (result, f'{req_msg}\n{bir_msg} ({brl_msg})\n{jit_msg}\n{pal_msg}')
else:
return (False, 'Could not analyze iperf report')
def Iperf_analyzeV2UDP(server_filename, iperf_bitrate_threshold, iperf_packetloss_threshold, target_bitrate):
result = None
@@ -197,9 +190,12 @@ def Iperf_analyzeV2UDP(server_filename, iperf_bitrate_threshold, iperf_packetlos
result = re.search(statusTemplate, str(line)) or result
if result is None:
return (False, 'Could not parse server report!')
bitrate_val = float(result.group('bitrate'))
magnitude = result.group('magnitude')
bitrate = convert_to_mbps(bitrate_val, magnitude)
bitrate = float(result.group('bitrate'))
magn = result.group('magnitude')
if magn == "k" or magn == "K":
bitrate /= 1000
elif magn == "G": # we assume bitrate in Mbps, therefore it must be G now
bitrate *= 1000
jitter = float(result.group('jitter'))
packetloss = float(result.group('packetloss'))
br_perf = float(bitrate)/float(target_bitrate) * 100
@@ -244,7 +240,6 @@ def Custom_Script(HTML, node, script):
return status == 'OK' or status == 'Warning'
def IdleSleep(HTML, idle_sleep_time):
logging.debug(f"sleep for {idle_sleep_time} seconds")
time.sleep(idle_sleep_time)
HTML.CreateHtmlTestRow(f"{idle_sleep_time} sec", 'OK', CONST.ALL_PROCESSES_OK)
return True
@@ -261,7 +256,9 @@ class OaiCiTest():
self.ranAllowMerge = False
self.ranTargetBranch = ''
self.testCase_id = ''
self.testXMLfiles = []
self.desc = ''
self.ping_args = ''
self.ping_packetloss_threshold = ''
self.ping_rttavg_threshold =''
@@ -355,7 +352,7 @@ class OaiCiTest():
HTML.CreateHtmlTestRowQueue('NA', 'OK', messages)
return True
def Ping_common(self, ctx, cn, ue):
def Ping_common(self, cn, ue, logPath):
ping_status = 0
ueIP = ue.getIP()
if not ueIP:
@@ -363,12 +360,13 @@ class OaiCiTest():
svrIP = cn.getIP()
if not svrIP:
return (False, f"CN {cn.getName()} has no IP address")
ping_log_file = f'/tmp/ping_{ue.getName()}.log'
ping_log_file = f'ping_{self.testCase_id}_{ue.getName()}.log'
ping_time = re.findall(r"-c *(\d+)",str(self.ping_args))
local_ping_log_file = f'{logPath}/{ping_log_file}'
if re.search('%cn_ip%', self.ping_args) or re.search(r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', self.ping_args):
raise Exception(f"ping_args should not have IP address: {self.ping_args}")
interface = f'-I {ue.getIFName()}' if ue.getIFName() else ''
ping_cmd = f'{ue.getCmdPrefix()} ping {interface} {self.ping_args} {svrIP} 2>&1 | tee {ping_log_file}'
ping_cmd = f'{ue.getCmdPrefix()} ping {interface} {self.ping_args} {svrIP} 2>&1 | tee /tmp/{ping_log_file}'
cmd = cls_cmd.getConnection(ue.getHost())
response = cmd.run(ping_cmd, timeout=int(ping_time[0])*1.5)
ue_header = f'UE {ue.getName()} ({ueIP})'
@@ -376,7 +374,8 @@ class OaiCiTest():
message = ue_header + ': ping crashed: TIMEOUT?'
return (False, message)
local_ping_log_file = archiveArtifact(cmd, ctx, ping_log_file)
#copy the ping log file to have it locally for analysis (ping stats)
cmd.copyin(src=f'/tmp/{ping_log_file}', tgt=local_ping_log_file)
cmd.close()
with open(local_ping_log_file, 'r') as f:
@@ -416,13 +415,18 @@ class OaiCiTest():
return (True, message)
def Ping(self, ctx, HTML, infra_file="ci_infra.yaml"):
def Ping(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"):
if self.ue_ids == [] or self.svr_id == None:
raise Exception("no module names in self.ue_ids or/and self.svr_id provided")
# Creating destination log folder if needed on the python executor workspace
with cls_cmd.getConnection('localhost') as local:
ymlPath = CONTAINERS.yamlPath[0].split('/')
logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}'
local.run(f'mkdir -p {logPath}', silent=True)
ues = [cls_module.Module_UE(ue_id, server_name, infra_file) for ue_id, server_name in zip(self.ue_ids, self.nodes)]
cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file)
with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor:
futures = [executor.submit(self.Ping_common, ctx, cn, ue) for ue in ues]
futures = [executor.submit(self.Ping_common, cn, ue, logPath) for ue in ues]
results = [f.result() for f in futures]
# each result in results is a tuple, first member goes to successes, second to messages
successes, messages = map(list, zip(*results))
@@ -443,7 +447,7 @@ class OaiCiTest():
HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', messages)
return success
def Iperf_Module(self, ctx, cn, ue, idx, ue_num):
def Iperf_Module(self, cn, ue, idx, ue_num, logPath):
ueIP = ue.getIP()
if not ueIP:
return (False, f"UE {ue.getName()} has no IP address")
@@ -456,7 +460,7 @@ class OaiCiTest():
serverReport = ""
udpIperf = re.search('-u', iperf_opt) is not None
bidirIperf = re.search('--bidir', iperf_opt) is not None
client_filename = f'/tmp/iperf_client_{ue.getName()}.log'
client_filename = f'iperf_client_{self.testCase_id}_{ue.getName()}.log'
if udpIperf:
target_bitrate, iperf_opt = Iperf_ComputeModifiedBW(idx, ue_num, self.iperf_profile, self.iperf_args)
# note: for UDP testing we don't want to use json report - reports 0 Mbps received bitrate
@@ -471,11 +475,14 @@ class OaiCiTest():
port = 5002 + idx
# note: some core setups start an iperf3 server automatically, indicated in ci_infra by runIperf3Server: False`
t = iperf_time * 2.5
cmd_ue.run(f'rm {client_filename}', reportNonZero=False, silent=True)
cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False, silent=True)
if cn.runIperf3Server():
cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} >> /dev/null &', timeout=t)
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t)
dest_filename = archiveArtifact(cmd_ue, ctx, client_filename)
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> /tmp/{client_filename}', timeout=t)
# note: copy iperf3 log to the current directory for log analysis and log collection
dest_filename = f'{logPath}/{client_filename}'
cmd_ue.copyin(f'/tmp/{client_filename}', dest_filename)
cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False, silent=True)
if udpIperf:
status, msg = Iperf_analyzeV3UDP(dest_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate)
elif bidirIperf:
@@ -485,15 +492,20 @@ class OaiCiTest():
return (status, f'{ue_header}\n{msg}')
def Iperf(self, ctx, HTML, infra_file="ci_infra.yaml"):
def Iperf(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"):
logging.debug(f'Iperf: iperf_args "{self.iperf_args}" iperf_packetloss_threshold "{self.iperf_packetloss_threshold}" iperf_bitrate_threshold "{self.iperf_bitrate_threshold}" iperf_profile "{self.iperf_profile}" iperf_options "{self.iperf_options}"')
if self.ue_ids == [] or self.svr_id == None:
raise Exception("no module names in self.ue_ids or/and self.svr_id provided")
# create log directory on executor node
with cls_cmd.getConnection('localhost') as local:
ymlPath = CONTAINERS.yamlPath[0].split('/')
logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}'
local.run(f'mkdir -p {logPath}', silent=True)
ues = [cls_module.Module_UE(ue_id, server_name, infra_file) for ue_id, server_name in zip(self.ue_ids, self.nodes)]
cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file)
with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor:
futures = [executor.submit(self.Iperf_Module, ctx, cn, ue, i, len(ues)) for i, ue in enumerate(ues)]
futures = [executor.submit(self.Iperf_Module, cn, ue, i, len(ues), logPath) for i, ue in enumerate(ues)]
results = [f.result() for f in futures]
# each result in results is a tuple, first member goes to successes, second to messages
successes, messages = map(list, zip(*results))
@@ -514,7 +526,7 @@ class OaiCiTest():
HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', messages)
return success
def Iperf2_Unidir(self, ctx, HTML, infra_file="ci_infra.yaml"):
def Iperf2_Unidir(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"):
if self.ue_ids == [] or self.svr_id == None or len(self.ue_ids) != 1:
raise Exception("no module names in self.ue_ids or/and self.svr_id provided, multi UE scenario not supported")
ue = cls_module.Module_UE(self.ue_ids[0].strip(),self.nodes[0].strip(), infra_file)
@@ -525,17 +537,23 @@ class OaiCiTest():
svrIP = cn.getIP()
if not svrIP:
return False
server_filename = f'/tmp/iperf_server_{ue.getName()}.log'
server_filename = f'iperf_server_{self.testCase_id}_{ue.getName()}.log'
ymlPath = CONTAINERS.yamlPath[0].split('/')
logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}'
iperf_time = Iperf_ComputeTime(self.iperf_args)
target_bitrate, iperf_opt = Iperf_ComputeModifiedBW(0, 1, self.iperf_profile, self.iperf_args)
t = iperf_time*2.5
with cls_cmd.getConnection('localhost') as local:
local.run(f'mkdir -p {logPath}')
with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(cn.getHost()) as cmd_svr:
cmd_ue.run(f'rm {server_filename}', reportNonZero=False)
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} iperf -B {ueIP} -s -u -i1 >> {server_filename} &', timeout=t)
cmd_ue.run(f'rm /tmp/{server_filename}', reportNonZero=False)
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} iperf -B {ueIP} -s -u -i1 >> /tmp/{server_filename} &', timeout=t)
cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf -c {ueIP} -B {svrIP} {iperf_opt} -i1 >> /dev/null', timeout=t)
localPath = f'{os.getcwd()}'
local = archiveArtifact(cmd_ue, ctx, server_filename)
success, msg = Iperf_analyzeV2UDP(local, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate)
# note: copy iperf2 log to the directory for log collection
cmd_ue.copyin(f'/tmp/{server_filename}', f'{logPath}/{server_filename}')
cmd_ue.run(f'rm /tmp/{server_filename}', reportNonZero=False)
success, msg = Iperf_analyzeV2UDP(f'{logPath}/{server_filename}', self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate)
ue_header = f'UE {ue.getName()} ({ueIP})'
logging.info(f'\u001B[1;37;45m iperf result for {ue_header}\u001B[0m')
for l in msg.split('\n'):
@@ -817,17 +835,17 @@ class OaiCiTest():
global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC
return global_status
def TerminateUE(self, ctx, HTML):
def TerminateUE(self, HTML):
ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids]
with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor:
futures = [executor.submit(ue.terminate, ctx) for ue in ues]
futures = [executor.submit(ue.terminate) for ue in ues]
archives = [f.result() for f in futures]
archive_info = [f'Log at: {a}' if a else 'No log available' for a in archives]
messages = [f"UE {ue.getName()}: {log}" for (ue, log) in zip(ues, archive_info)]
HTML.CreateHtmlTestRowQueue(f'N/A', 'OK', messages)
return True
def DeployCoreNetwork(cn_id, ctx, HTML):
def DeployCoreNetwork(cn_id, HTML):
core_name = cn_id.strip()
cn = cls_corenetwork.CoreNetwork(core_name)
success, output = cn.deploy()
@@ -841,11 +859,41 @@ class OaiCiTest():
HTML.CreateHtmlTestRowQueue(core_name, 'KO', [msg])
return success
def UndeployCoreNetwork(cn_id, ctx, HTML):
def UndeployCoreNetwork(cn_id, HTML):
# Ping, Iperf, DeployObject put logs into a path based on YAML. We
# can't do this here (because there is no yaml), so hardcode a path for
# "cn_logs" for the moment
logPath = f'{os.getcwd()}/../cmake_targets/log/cn_logs'
with cls_cmd.getConnection('localhost') as local:
local.run(f'mkdir -p {logPath}', silent=True)
core_name = cn_id.strip()
cn = cls_corenetwork.CoreNetwork(core_name)
logs, output = cn.undeploy(ctx=ctx)
logs, output = cn.undeploy(log_dir=logPath)
logging.info(f"undeployed core network {core_name}, logs {logs}, output:\n{output}")
message = "Log files:\n" + "\n".join([os.path.basename(l) for l in logs])
message = "Log files: " + ", ".join([os.path.basename(l) for l in logs])
HTML.CreateHtmlTestRowQueue(core_name, 'OK', [message])
return True
def LogCollectBuild(self,RAN):
# Some pipelines are using "none" IP / Credentials
# In that case, just forget about it
if RAN.eNBIPAddress == 'none':
sys.exit(0)
if (RAN.eNBIPAddress != '' and RAN.eNBUserName != '' and RAN.eNBPassword != ''):
IPAddress = RAN.eNBIPAddress
UserName = RAN.eNBUserName
Password = RAN.eNBPassword
SourceCodePath = RAN.eNBSourceCodePath
else:
sys.exit('Insufficient Parameter')
with cls_cmd.getConnection(IPAddress) as cmd:
d = f'{SourceCodePath}/cmake_targets'
cmd.run(f'rm -f {d}/build.log.zip')
cmd.run(f'cd {d} && zip -r build.log.zip build_log_*/*')
def ShowTestID(self):
logging.info(f'\u001B[1m----------------------------------------\u001B[0m')
logging.info(f'\u001B[1m Test ID: {self.testCase_id} \u001B[0m')
logging.info(f'\u001B[1m {self.desc} \u001B[0m')
logging.info(f'\u001B[1m----------------------------------------\u001B[0m')

View File

@@ -43,7 +43,6 @@ from pathlib import Path
import helpreadme as HELP
import constants as CONST
import cls_cmd
from cls_ci_helper import archiveArtifact
#-----------------------------------------------------------
# Class Declaration
@@ -76,18 +75,25 @@ class StaticCodeAnalysis():
self.ranAllowMerge = False
self.ranCommitID = ''
self.ranTargetBranch = ''
self.eNBIPAddress = ''
self.eNBUserName = ''
self.eNBPassword = ''
self.eNBSourceCodePath = ''
def CppCheckAnalysis(self, ctx, node, HTML):
def CppCheckAnalysis(self, HTML):
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
lIpAddr = self.eNBIPAddress
lUserName = self.eNBUserName
lPassWord = self.eNBPassword
lSourcePath = self.eNBSourceCodePath
if not lSourcePath or not node:
raise ValueError(f"{lSourcePath=} {node=}")
logging.debug('Building on server: ' + node)
cmd = cls_cmd.getConnection(node)
if lIpAddr == '' or lUserName == '' or lPassWord == '' or lSourcePath == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
logging.debug('Building on server: ' + lIpAddr)
cmd = cls_cmd.getConnection(lIpAddr)
self.testCase_id = HTML.testCase_id
# on RedHat/CentOS .git extension is mandatory
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
@@ -97,7 +103,7 @@ class StaticCodeAnalysis():
full_ran_repo_name = self.ranRepository + '.git'
cmd.cd(lSourcePath)
logDir = f'{lSourcePath}/cmake_targets/log'
logDir = f'{lSourcePath}/cmake_targets/build_log_{self.testCase_id}'
cmd.run(f'mkdir -p {logDir}')
cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal')
cmd.run(f'sed -e "s@xenial@bionic@" {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.xenial > {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.bionic')
@@ -106,21 +112,31 @@ class StaticCodeAnalysis():
cmd.run(f'docker build --tag oai-cppcheck:focal --file {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.focal . > {logDir}/cppcheck-focal.txt 2>&1')
cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal')
bionic = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-bionic.txt')
focal = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-focal.txt')
# Analyzing the logs
cmd.copyin(f'{logDir}/cppcheck-bionic.txt', 'cppcheck-bionic.txt')
cmd.copyin(f'{logDir}/cppcheck-focal.txt', 'cppcheck-focal.txt')
cmd.close()
CCR = CppCheckResults()
CCR_ref = CppCheckResults()
vId = 0
for variant in CCR.variants:
filename = ctx.baseFilename() + '-cppcheck-'+ variant + '.txt'
logging.info(f"will check file '{filename}'")
if not os.path.isfile(filename):
raise FileNotFoundError(f"{filename} is not a file")
else:
refAvailable = False
if self.ranAllowMerge:
refFolder = str(Path.home()) + '/cppcheck-references'
if (os.path.isfile(refFolder + '/cppcheck-'+ variant + '.txt')):
refAvailable = True
with open(refFolder + '/cppcheck-'+ variant + '.txt', 'r') as refFile:
for line in refFile:
ret = re.search(' (?P<nb_errors>[0-9\.]+) errors', str(line))
if ret is not None:
CCR_ref.nbErrors[vId] = int(ret.group('nb_errors'))
ret = re.search(' (?P<nb_warnings>[0-9\.]+) warnings', str(line))
if ret is not None:
CCR_ref.nbWarnings[vId] = int(ret.group('nb_warnings'))
if (os.path.isfile('./cppcheck-'+ variant + '.txt')):
xmlStart = False
with open(filename, 'r') as logfile:
with open('./cppcheck-'+ variant + '.txt', 'r') as logfile:
for line in logfile:
ret = re.search('cppcheck version="(?P<version>[0-9\.]+)"', str(line))
if ret is not None:
@@ -169,6 +185,26 @@ class StaticCodeAnalysis():
vMsg += ' Wrong Scanf Nb Args: ' + str(CCR.nbWrongScanfArg[vId]) + '\n'
for vLine in vMsg.split('\n'):
logging.debug(vLine)
if self.ranAllowMerge and refAvailable:
if CCR_ref.nbErrors[vId] == CCR.nbErrors[vId]:
logging.debug(' No change in number of errors')
elif CCR_ref.nbErrors[vId] > CCR.nbErrors[vId]:
logging.debug(' Good! Decrease in number of errors')
else:
logging.debug(' Bad! increase in number of errors')
if CCR_ref.nbWarnings[vId] == CCR.nbWarnings[vId]:
logging.debug(' No change in number of warnings')
elif CCR_ref.nbWarnings[vId] > CCR.nbWarnings[vId]:
logging.debug(' Good! Decrease in number of warnings')
else:
logging.debug(' Bad! increase in number of warnings')
# Create new reference file
if not self.ranAllowMerge:
refFolder = str(Path.home()) + '/cppcheck-references'
if not os.path.isdir(refFolder):
os.mkdir(refFolder)
with open(refFolder + '/cppcheck-'+ variant + '.txt', 'w') as refFile:
refFile.write(vMsg)
vId += 1
HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
@@ -177,16 +213,20 @@ class StaticCodeAnalysis():
return True
def LicenceAndFormattingCheck(self, ctx, node, HTML):
def LicenceAndFormattingCheck(self, HTML):
# Workspace is no longer recreated from scratch.
# It implies that this method shall be called last within a build pipeline
# where workspace is already created
lIpAddr = self.eNBIPAddress
lUserName = self.eNBUserName
lPassWord = self.eNBPassword
lSourcePath = self.eNBSourceCodePath
if not node or not lSourcePath:
raise ValueError(f"{lSourcePath=} {node=}")
logging.debug('Building on server: ' + node)
cmd = cls_cmd.getConnection(node)
if lIpAddr == '' or lUserName == '' or lPassWord == '' or lSourcePath == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
logging.debug('Building on server: ' + lIpAddr)
cmd = cls_cmd.getConnection(lIpAddr)
self.testCase_id = HTML.testCase_id
check_options = ''
@@ -198,20 +238,21 @@ class StaticCodeAnalysis():
else:
check_options += f' --build-arg TARGET_BRANCH={self.ranTargetBranch}'
logDir = f'{lSourcePath}/cmake_targets/log/'
logDir = f'{lSourcePath}/cmake_targets/build_log_{self.testCase_id}'
cmd.run(f'mkdir -p {logDir}')
cmd.run('docker image rm oai-formatting-check:latest')
cmd.run(f'docker build --target oai-formatting-check --tag oai-formatting-check:latest {check_options} --file {lSourcePath}/ci-scripts/docker/Dockerfile.formatting.ubuntu {lSourcePath} > {logDir}/oai-formatting-check.txt 2>&1')
cmd.run(f'docker build --target oai-formatting-check --tag oai-formatting-check:latest {check_options} --file {lSourcePath}/ci-scripts/docker/Dockerfile.formatting.bionic {lSourcePath} > {logDir}/oai-formatting-check.txt 2>&1')
cmd.run('docker image rm oai-formatting-check:latest')
cmd.run('docker image prune --force')
cmd.run('docker volume prune --force')
file = archiveArtifact(cmd, ctx, f'{logDir}/oai-formatting-check.txt')
# Analyzing the logs
cmd.copyin(f'{logDir}/oai-formatting-check.txt', 'oai-formatting-check.txt')
cmd.close()
finalStatus = 0
if (os.path.isfile(file)):
if (os.path.isfile('./oai-formatting-check.txt')):
analyzed = False
nbFilesNotFormatted = 0
listFiles = False
@@ -222,7 +263,7 @@ class StaticCodeAnalysis():
gnuGplLicenceFiles = []
suspectLicence = False
suspectLicenceFiles = []
with open(file, 'r') as logfile:
with open('./oai-formatting-check.txt', 'r') as logfile:
for line in logfile:
ret = re.search('./ci-scripts/checkCodingFormattingRules.sh', str(line))
if ret is not None:

View File

@@ -23,7 +23,9 @@ gNBs =
local_s_address = "192.168.71.150";
remote_s_address = "192.168.71.171";
local_s_portc = 501;
local_s_portd = 2153;
remote_s_portc = 500;
remote_s_portd = 2153;
# ------- SCTP definitions

View File

@@ -23,7 +23,9 @@ gNBs =
local_s_address = "127.0.0.4";
remote_s_address = "127.0.0.5";
local_s_portc = 501;
local_s_portd = 2153;
remote_s_portc = 500;
remote_s_portd = 2153;
# ------- SCTP definitions

View File

@@ -23,7 +23,9 @@ gNBs =
local_s_address = "192.168.71.150";
remote_s_address = "0.0.0.0"; # multiple DUs
local_s_portc = 501;
local_s_portd = 2152;
remote_s_portc = 500;
remote_s_portd = 2152;
# ------- SCTP definitions

View File

@@ -23,7 +23,9 @@ gNBs =
local_s_address = "127.0.0.4";
remote_s_address = "127.0.0.5";
local_s_portc = 501;
local_s_portd = 2153;
remote_s_portc = 500;
remote_s_portd = 2153;
# ------- SCTP definitions

View File

@@ -22,7 +22,9 @@ gNBs =
local_s_address = "192.168.72.161";
remote_s_address = "192.168.72.171";
local_s_portc = 501;
local_s_portd = 2153;
remote_s_portc = 500;
remote_s_portd = 2153;
# ------- SCTP definitions

View File

@@ -22,7 +22,9 @@ gNBs =
local_s_address = "127.0.0.6";
remote_s_address = "127.0.0.5";
local_s_portc = 501;
local_s_portd = 2153;
remote_s_portc = 500;
remote_s_portd = 2153;
# ------- SCTP definitions

View File

@@ -86,6 +86,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
@@ -160,7 +163,9 @@ MACRLCs = (
tr_n_preference = "f1";
local_n_address = "127.0.0.5";
remote_n_address = "127.0.0.4";
local_n_portc = 500;
local_n_portd = 2153;
remote_n_portc = 501;
remote_n_portd = 2153;
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;

View File

@@ -84,6 +84,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
@@ -165,7 +168,9 @@ MACRLCs = (
tr_n_preference = "f1";
local_n_address = "192.168.71.171";
remote_n_address = "192.168.71.150";
local_n_portc = 500;
local_n_portd = 2153;
remote_n_portc = 501;
remote_n_portd = 2153;
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;

View File

@@ -34,9 +34,11 @@ gNBs =
# downlinkConfigCommon
#frequencyInfoDL
absoluteFrequencySSB = 641280; # GSCN: 7929, Freq: 3619.200 MHz
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
absoluteFrequencySSB = 641280;
dl_frequencyBand = 78;
dl_absoluteFrequencyPointA = 640752; # 3611.280 MHz
# this is 3600 MHz
dl_absoluteFrequencyPointA = 640008;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
@@ -45,12 +47,13 @@ gNBs =
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 13750; # RBstart=0, L=51 (275*(L-1))+RBstart
# this is RBstart=27,L=48 (275*(L-1))+RBstart
initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 10;
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
@@ -65,7 +68,7 @@ gNBs =
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 13750; # RBstart=0, L=51 (275*(L-1))+RBstart
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
@@ -83,6 +86,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
@@ -146,18 +152,6 @@ gNBs =
);
servingCellConfigDedicated = ({
dl_bwp-Id_1 = 1;
dl_bwp1_locationAndBandwidth = 28875; # RBstart=0, L=106 (275*(L-1))+RBstart
dl_bwp1_subcarrierSpacing = 1;
firstActiveDownlinkBWP-Id = 1;
defaultDownlinkBWP-Id = 1;
ul_bwp-Id_1 = 1;
ul_bwp1_locationAndBandwidth = 28875; # RBstart=0, L=106 (275*(L-1))+RBstart
ul_bwp1_subcarrierSpacing = 1;
firstActiveUplinkBWP-Id = 1;
});
# ------- SCTP definitions
SCTP :
@@ -201,7 +195,9 @@ MACRLCs = (
tr_n_preference = "f1";
local_n_address = "127.0.0.5";
remote_n_address = "127.0.0.4";
local_n_portc = 500;
local_n_portd = 2153;
remote_n_portc = 501;
remote_n_portd = 2153;
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;

View File

@@ -85,6 +85,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -97,6 +97,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -84,6 +84,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -83,6 +83,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -31,11 +31,11 @@ gNBs =
# downlinkConfigCommon
#frequencyInfoDL
# this is 4412.64 MHz
absoluteFrequencySSB = 694176;
dl_frequencyBand = 79;
# this is 4400.94 MHz
dl_absoluteFrequencyPointA = 693396;
# this is 3319.68 MHz
absoluteFrequencySSB = 621312;
dl_frequencyBand = 78;
# this is 3300.6 MHz
dl_absoluteFrequencyPointA = 620040;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
@@ -55,7 +55,7 @@ gNBs =
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 79;
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
@@ -83,6 +83,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -31,11 +31,11 @@ gNBs =
# downlinkConfigCommon
#frequencyInfoDL
# this is 4435.68 MHz
absoluteFrequencySSB = 695712;
dl_frequencyBand = 79;
# this is 4400.94 MHz
dl_absoluteFrequencyPointA = 693396;
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
absoluteFrequencySSB = 621312;
dl_frequencyBand = 78;
# this is 3600 MHz
dl_absoluteFrequencyPointA = 620040;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
@@ -55,7 +55,7 @@ gNBs =
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 79;
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
@@ -83,6 +83,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -33,11 +33,11 @@ gNBs =
# downlinkConfigCommon
#frequencyInfoDL
# this is 4458.72 MHz
absoluteFrequencySSB = 697248;
dl_frequencyBand = 79;
# this is 4400.94 MHz
dl_absoluteFrequencyPointA = 693396;
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
absoluteFrequencySSB = 621312;
dl_frequencyBand = 78;
# this is 3600 MHz
dl_absoluteFrequencyPointA = 620040;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
@@ -57,7 +57,7 @@ gNBs =
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 79;
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
@@ -85,6 +85,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -78,6 +78,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -83,6 +83,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -16,6 +16,7 @@ gNBs =
////////// Physical parameters:
sib1_tda = 5;
min_rxtxtime = 6;
num_dlharq = 32;
num_ulharq = 32;
@@ -84,6 +85,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
@@ -146,8 +150,6 @@ gNBs =
#ext2
#ntn_Config_r17
# This can be of values {s5, s10, s15, s20, s25, s30, s35, s40, s45, s50, s55, s60, s120, s180, s240, s900}
ntn-UlSyncValidityDuration-r17 = 5;
cellSpecificKoffset_r17 = 40;
ta-Common-r17 = 4627000; # 18.84 ms
ta-CommonDrift-r17 = -230000; # -46 µs/s

View File

@@ -16,6 +16,7 @@ gNBs =
////////// Physical parameters:
sib1_tda = 5;
min_rxtxtime = 6;
disable_harq = 1;
cu_sibs = [2];
@@ -83,6 +84,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -82,6 +82,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -81,6 +81,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -86,6 +86,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -84,6 +84,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 2;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -79,6 +79,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -86,6 +86,9 @@ gNBs = (
# powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
# ra_ReponseWindow
# 1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
# ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
# 1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -35,8 +35,8 @@ gNBs =
# downlinkConfigCommon
#frequencyInfoDL
dl_frequencyBand = 78;
absoluteFrequencySSB = 628320;
dl_absoluteFrequencyPointA = 627062;
absoluteFrequencySSB = 634080;
dl_absoluteFrequencyPointA = 632808;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
@@ -84,6 +84,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
@@ -194,7 +197,7 @@ RUs = (
local_rf = "yes";
nb_tx = 1;
nb_rx = 1;
att_tx = 10;
att_tx = 0;
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;

View File

@@ -86,6 +86,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -80,6 +80,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -82,6 +82,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -72,6 +72,9 @@ gNBs:
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep: 1
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow: 5
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR: 4

View File

@@ -85,6 +85,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -78,6 +78,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -98,6 +98,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -99,6 +99,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -80,6 +80,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -87,6 +87,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -83,6 +83,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -87,6 +87,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -25,7 +25,6 @@ gNBs =
pusch_AntennaPorts = 1;
do_CSIRS = 1;
do_SRS = 0;
uess_agg_levels = [2, 2, 2, 0, 0];
servingCellConfigCommon = (
{
@@ -35,11 +34,11 @@ gNBs =
# downlinkConfigCommon
#frequencyInfoDL
# this is 3410.400 MHz
absoluteFrequencySSB = 627360;
# this is 3469.44 MHz => 301 REs from PointA 25 PRBs + 1 RE
absoluteFrequencySSB = 631296;
dl_frequencyBand = 78;
# this is 3401.220 MHz
dl_absoluteFrequencyPointA = 626748;
# this is 3469.44 - (51*12*30e-3/2) = 3460.26 MHz
dl_absoluteFrequencyPointA = 630684;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
@@ -88,6 +87,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
@@ -151,11 +153,6 @@ gNBs =
);
RedCap: {
cellBarredRedCap1Rx_r17 = 1;
cellBarredRedCap2Rx_r17 = 1;
intraFreqReselectionRedCap_r17 = 0;
}
# ------- SCTP definitions
SCTP :

View File

@@ -78,6 +78,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -83,6 +83,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 2;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -83,6 +83,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -82,6 +82,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -83,6 +83,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -98,6 +98,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;

View File

@@ -94,6 +94,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;

View File

@@ -103,6 +103,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;

View File

@@ -77,6 +77,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -82,6 +82,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -81,6 +81,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -80,6 +80,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -79,6 +79,9 @@ gNBs =
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;

View File

@@ -30,8 +30,8 @@ FROM ubuntu:xenial AS oai-cppcheck
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get upgrade --yes && \
apt-get install --yes \
DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \
DEBIAN_FRONTEND=noninteractive apt-get install --yes \
build-essential \
vim \
cppcheck

View File

@@ -21,11 +21,11 @@
#---------------------------------------------------------------------
#
# Dockerfile for the Open-Air-Interface BUILD service
# Valid for Ubuntu 24.04
# Valid for Ubuntu 22.04
#
#---------------------------------------------------------------------
FROM ubuntu:noble AS oai-formatting-check
FROM ubuntu:bionic AS oai-formatting-check
ARG MERGE_REQUEST
ARG SRC_BRANCH
@@ -33,8 +33,8 @@ ARG TARGET_BRANCH
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get upgrade --yes && \
apt-get install --yes \
DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \
DEBIAN_FRONTEND=noninteractive apt-get install --yes \
gawk \
git

View File

@@ -21,7 +21,7 @@
#---------------------------------------------------------------------
#
# Dockerfile for the Open-Air-Interface BUILD service
# Valid for Ubuntu 24.04
# Valid for Ubuntu 22.04
#
#---------------------------------------------------------------------

View File

@@ -52,7 +52,10 @@ def GitSrvHelp(repository,branch,commit,mergeallow,targetbranch):
print(' --ranAllowMerge=[Allow Merge Request (with target branch) (true or false)] -- ' + mergeallow)
print(' --ranTargetBranch=[Target Branch in case of a Merge Request] -- ' + targetbranch)
def eNBSrvHelp(sourcepath):
def eNBSrvHelp(ipaddr, username, password, sourcepath):
print(' --eNBIPAddress=[eNB\'s IP Address] -- ' + ipaddr)
print(' --eNBUserName=[eNB\'s Login User Name] -- ' + username)
print(' --eNBPassword=[eNB\'s Login Password] -- ' + password)
print(' --eNBSourceCodePath=[eNB\'s Source Code Path] -- ' + sourcepath)
def XmlHelp(filename):

View File

@@ -43,10 +43,8 @@ import cls_containerize #class Containerize for all container-based operations
import cls_static_code_analysis #class for static code analysis
import cls_cluster # class for building/deploying on cluster
import cls_native # class for all native/source-based operations
from cls_ci_helper import TestCaseCtx
import ran
import cls_cmd
import cls_oai_html
@@ -93,7 +91,7 @@ def AssignParams(params_dict):
setattr(RAN, key, value)
setattr(HTML, key, value)
def ExecuteActionWithParam(action, ctx):
def ExecuteActionWithParam(action):
global RAN
global HTML
global CONTAINERS
@@ -102,46 +100,98 @@ def ExecuteActionWithParam(action, ctx):
if action == 'Build_eNB' or action == 'Build_Image' or action == 'Build_Proxy' or action == "Build_Cluster_Image" or action == "Build_Run_Tests":
RAN.Build_eNB_args=test.findtext('Build_eNB_args')
CONTAINERS.imageKind=test.findtext('kind')
node = test.findtext('node')
forced_workspace_cleanup = test.findtext('forced_workspace_cleanup')
RAN.Build_eNB_forced_workspace_cleanup=False
CONTAINERS.forcedWorkspaceCleanup=False
CLUSTER.forcedWorkspaceCleanup = False
if forced_workspace_cleanup is not None and re.match('true', forced_workspace_cleanup, re.IGNORECASE):
RAN.Build_eNB_forced_workspace_cleanup = True
CONTAINERS.forcedWorkspaceCleanup = True
CLUSTER.forcedWorkspaceCleanup = True
eNB_instance=test.findtext('eNB_instance')
if (eNB_instance is None):
RAN.eNB_instance=0
CONTAINERS.eNB_instance=0
else:
RAN.eNB_instance=int(eNB_instance)
CONTAINERS.eNB_instance=int(eNB_instance)
eNB_serverId=test.findtext('eNB_serverId')
if (eNB_serverId is None):
RAN.eNB_serverId[RAN.eNB_instance]='0'
CONTAINERS.eNB_serverId[RAN.eNB_instance]='0'
else:
RAN.eNB_serverId[RAN.eNB_instance]=eNB_serverId
CONTAINERS.eNB_serverId[CONTAINERS.eNB_instance]=eNB_serverId
xmlBgBuildField = test.findtext('backgroundBuild')
if (xmlBgBuildField is None):
RAN.backgroundBuild=False
else:
if re.match('true', xmlBgBuildField, re.IGNORECASE):
RAN.backgroundBuild=True
else:
RAN.backgroundBuild=False
proxy_commit = test.findtext('proxy_commit')
if proxy_commit is not None:
CONTAINERS.proxyCommit = proxy_commit
if action == 'Build_eNB':
success = cls_native.Native.Build(ctx, node, HTML.testCase_id, HTML, RAN.eNBSourceCodePath, RAN.Build_eNB_args)
success = cls_native.Native.Build(HTML.testCase_id, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, RAN.Build_eNB_args)
elif action == 'Build_Image':
success = CONTAINERS.BuildImage(ctx, node, HTML)
success = CONTAINERS.BuildImage(HTML)
elif action == 'Build_Proxy':
success = CONTAINERS.BuildProxy(ctx, node, HTML)
success = CONTAINERS.BuildProxy(HTML)
elif action == 'Build_Cluster_Image':
success = CLUSTER.BuildClusterImage(ctx, node, HTML)
success = CLUSTER.BuildClusterImage(HTML)
elif action == 'Build_Run_Tests':
success = CONTAINERS.BuildRunTests(ctx, node, HTML)
success = CONTAINERS.BuildRunTests(HTML)
elif action == 'Initialize_eNB':
node = test.findtext('node')
datalog_rt_stats_file=test.findtext('rt_stats_cfg')
if datalog_rt_stats_file is None:
RAN.datalog_rt_stats_file='datalog_rt_stats.default.yaml'
else:
RAN.datalog_rt_stats_file=datalog_rt_stats_file
RAN.Initialize_eNB_args=test.findtext('Initialize_eNB_args')
USRPIPAddress = test.findtext('USRP_IPAddress') or ''
eNB_instance=test.findtext('eNB_instance')
USRPIPAddress=test.findtext('USRP_IPAddress')
if USRPIPAddress is None:
RAN.USRPIPAddress=''
else:
RAN.USRPIPAddress=USRPIPAddress
if (eNB_instance is None):
RAN.eNB_instance=0
else:
RAN.eNB_instance=int(eNB_instance)
eNB_serverId=test.findtext('eNB_serverId')
if (eNB_serverId is None):
RAN.eNB_serverId[RAN.eNB_instance]='0'
else:
RAN.eNB_serverId[RAN.eNB_instance]=eNB_serverId
#local variable air_interface
air_interface = test.findtext('air_interface')
if (air_interface is None) or (air_interface.lower() not in ['nr','lte']):
RAN.air_interface = 'lte-softmodem'
RAN.air_interface[RAN.eNB_instance] = 'lte-softmodem'
else:
RAN.air_interface = air_interface.lower() +'-softmodem'
RAN.air_interface[RAN.eNB_instance] = air_interface.lower() +'-softmodem'
cmd_prefix = test.findtext('cmd_prefix')
if cmd_prefix is not None: RAN.cmd_prefix = cmd_prefix
success = RAN.InitializeeNB(ctx, node, HTML)
success = RAN.InitializeeNB(HTML)
elif action == 'Terminate_eNB':
node = test.findtext('node')
eNB_instance=test.findtext('eNB_instance')
if (eNB_instance is None):
RAN.eNB_instance=0
else:
RAN.eNB_instance=int(eNB_instance)
eNB_serverId=test.findtext('eNB_serverId')
if (eNB_serverId is None):
RAN.eNB_serverId[RAN.eNB_instance]='0'
else:
RAN.eNB_serverId[RAN.eNB_instance]=eNB_serverId
#retx checkers
string_field = test.findtext('d_retx_th')
string_field=test.findtext('d_retx_th')
if (string_field is not None):
RAN.ran_checkers['d_retx_th'] = [float(x) for x in string_field.split(',')]
string_field=test.findtext('u_retx_th')
@@ -151,10 +201,10 @@ def ExecuteActionWithParam(action, ctx):
#local variable air_interface
air_interface = test.findtext('air_interface')
if (air_interface is None) or (air_interface.lower() not in ['nr','lte']):
RAN.air_interface = 'lte-softmodem'
RAN.air_interface[RAN.eNB_instance] = 'lte-softmodem'
else:
RAN.air_interface = air_interface.lower() +'-softmodem'
success = RAN.TerminateeNB(ctx, node, HTML)
RAN.air_interface[RAN.eNB_instance] = air_interface.lower() +'-softmodem'
success = RAN.TerminateeNB(HTML)
elif action == 'Initialize_UE' or action == 'Attach_UE' or action == 'Detach_UE' or action == 'Terminate_UE' or action == 'CheckStatusUE' or action == 'DataEnable_UE' or action == 'DataDisable_UE':
CiTestObj.ue_ids = test.findtext('id').split(' ')
@@ -176,7 +226,7 @@ def ExecuteActionWithParam(action, ctx):
elif action == 'Detach_UE':
success = CiTestObj.DetachUE(HTML)
elif action == 'Terminate_UE':
success = CiTestObj.TerminateUE(ctx, HTML)
success = CiTestObj.TerminateUE(HTML)
elif action == 'CheckStatusUE':
success = CiTestObj.CheckStatusUE(HTML)
elif action == 'DataEnable_UE':
@@ -203,7 +253,7 @@ def ExecuteActionWithParam(action, ctx):
else:
CiTestObj.nodes = [None] * len(CiTestObj.ue_ids)
ping_rttavg_threshold = test.findtext('ping_rttavg_threshold') or ''
success = CiTestObj.Ping(ctx, HTML)
success = CiTestObj.Ping(HTML, CONTAINERS)
elif action == 'Iperf' or action == 'Iperf2_Unidir':
CiTestObj.iperf_args = test.findtext('iperf_args')
@@ -234,9 +284,9 @@ def ExecuteActionWithParam(action, ctx):
logging.error('test-case has wrong option ' + CiTestObj.iperf_options)
CiTestObj.iperf_options = 'check'
if action == 'Iperf':
success = CiTestObj.Iperf(ctx, HTML)
success = CiTestObj.Iperf(HTML, CONTAINERS)
elif action == 'Iperf2_Unidir':
success = CiTestObj.Iperf2_Unidir(ctx, HTML)
success = CiTestObj.Iperf2_Unidir(HTML, CONTAINERS)
elif action == 'IdleSleep':
st = test.findtext('idle_sleep_time_in_sec') or "5"
@@ -244,61 +294,71 @@ def ExecuteActionWithParam(action, ctx):
elif action == 'Deploy_Run_PhySim':
oc_release = test.findtext('oc_release')
node = test.findtext('node') or None
success = CLUSTER.deploy_oc_physim(ctx, HTML, oc_release, node)
svr_id = test.findtext('svr_id') or None
success = CLUSTER.deploy_oc_physim(HTML, oc_release, svr_id)
elif action == 'DeployCoreNetwork' or action == 'UndeployCoreNetwork':
cn_id = test.findtext('cn_id')
core_op = getattr(cls_oaicitest.OaiCiTest, action)
success = core_op(cn_id, ctx, HTML)
success = core_op(cn_id, HTML)
elif action == 'Deploy_Object' or action == 'Undeploy_Object' or action == "Create_Workspace":
node = test.findtext('node')
CONTAINERS.yamlPath = test.findtext('yaml_path')
eNB_instance=test.findtext('eNB_instance')
if (eNB_instance is None):
CONTAINERS.eNB_instance=0
else:
CONTAINERS.eNB_instance=int(eNB_instance)
eNB_serverId=test.findtext('eNB_serverId')
if (eNB_serverId is None):
CONTAINERS.eNB_serverId[CONTAINERS.eNB_instance]='0'
else:
CONTAINERS.eNB_serverId[CONTAINERS.eNB_instance]=eNB_serverId
string_field = test.findtext('yaml_path')
if (string_field is not None):
CONTAINERS.yamlPath[CONTAINERS.eNB_instance] = string_field
string_field=test.findtext('d_retx_th')
if (string_field is not None):
CONTAINERS.ran_checkers['d_retx_th'] = [float(x) for x in string_field.split(',')]
string_field=test.findtext('u_retx_th')
if (string_field is not None):
CONTAINERS.ran_checkers['u_retx_th'] = [float(x) for x in string_field.split(',')]
CONTAINERS.services = test.findtext('services')
string_field = test.findtext('services')
if string_field is not None:
CONTAINERS.services[CONTAINERS.eNB_instance] = string_field
CONTAINERS.num_attempts = int(test.findtext('num_attempts') or 1)
CONTAINERS.deploymentTag = cls_containerize.CreateTag(CONTAINERS.ranCommitID, CONTAINERS.ranBranch, CONTAINERS.ranAllowMerge)
if action == 'Deploy_Object':
success = CONTAINERS.DeployObject(ctx, node, HTML)
success = CONTAINERS.DeployObject(HTML)
elif action == 'Undeploy_Object':
success = CONTAINERS.UndeployObject(ctx, node, HTML, RAN)
success = CONTAINERS.UndeployObject(HTML, RAN)
elif action == 'Create_Workspace':
if force_local:
# Do not create a working directory when running locally. Current repo directory will be used
return True
success = CONTAINERS.Create_Workspace(node, HTML)
success = CONTAINERS.Create_Workspace(HTML)
elif action == 'Run_Physim':
physim_options = test.findtext('physim_run_args')
physim_test = test.findtext('physim_test')
physim_threshold = test.findtext('physim_time_threshold') or 'inf'
node = test.findtext('node')
success = cls_native.Native.Run_Physim(ctx, HTML, node, RAN.eNBSourceCodePath, physim_options, physim_test, physim_threshold)
success = cls_native.Native.Run_Physim(HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, physim_options, physim_test, physim_threshold)
elif action == 'LicenceAndFormattingCheck':
node = test.findtext('node')
success = SCA.LicenceAndFormattingCheck(ctx, node, HTML)
success = SCA.LicenceAndFormattingCheck(HTML)
elif action == 'Cppcheck_Analysis':
node = test.findtext('node')
success = SCA.CppCheckAnalysis(ctx, node, HTML)
success = SCA.CppCheckAnalysis(HTML)
elif action == 'Push_Local_Registry':
node = test.findtext('node')
svr_id = test.findtext('svr_id')
tag_prefix = test.findtext('tag_prefix') or ""
success = CONTAINERS.Push_Image_to_Local_Registry(node, HTML, tag_prefix)
success = CONTAINERS.Push_Image_to_Local_Registry(HTML, svr_id, tag_prefix)
elif action == 'Pull_Local_Registry' or action == 'Clean_Test_Server_Images':
if force_local:
# Do not pull or remove images when running locally. User is supposed to handle image creation & cleanup
return True
node = test.findtext('node')
svr_id = test.findtext('svr_id')
tag_prefix = test.findtext('tag_prefix') or ""
images = test.findtext('images').split()
# hack: for FlexRIC, we need to overwrite the tag to use
@@ -306,9 +366,9 @@ def ExecuteActionWithParam(action, ctx):
if len(images) == 1 and images[0] == "oai-flexric":
tag = CONTAINERS.flexricTag
if action == "Pull_Local_Registry":
success = CONTAINERS.Pull_Image_from_Registry(HTML, node, images, tag=tag, tag_prefix=tag_prefix)
success = CONTAINERS.Pull_Image_from_Registry(HTML, svr_id, images, tag=tag, tag_prefix=tag_prefix)
if action == "Clean_Test_Server_Images":
success = CONTAINERS.Clean_Test_Server_Images(HTML, node, images, tag=tag)
success = CONTAINERS.Clean_Test_Server_Images(HTML, svr_id, images, tag=tag)
elif action == 'Custom_Command':
node = test.findtext('node')
@@ -347,11 +407,10 @@ def test_in_list(test, list):
def receive_signal(signum, frame):
sys.exit(1)
def ShowTestID(ctx, desc):
logging.info(f'\u001B[1m----------------------------------------\u001B[0m')
logging.info(f'\u001B[1m Test ID: {ctx.test_id} (#{ctx.count}) \u001B[0m')
logging.info(f'\u001B[1m {desc} \u001B[0m')
logging.info(f'\u001B[1m----------------------------------------\u001B[0m')
#-----------------------------------------------------------
# MAIN PART
@@ -407,7 +466,15 @@ if py_param_file_present == True:
cwd = os.getcwd()
if re.match('^TerminateeNB$', mode, re.IGNORECASE):
logging.warning("Option TerminateeNB ignored")
if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
if RAN.eNBIPAddress == 'none':
sys.exit(0)
RAN.eNB_instance=0
RAN.eNB_serverId[0]='0'
RAN.eNBSourceCodePath='/tmp/'
RAN.TerminateeNB(HTML)
elif re.match('^TerminateHSS$', mode, re.IGNORECASE):
logging.warning("Option TerminateHSS ignored")
elif re.match('^TerminateMME$', mode, re.IGNORECASE):
@@ -415,9 +482,14 @@ elif re.match('^TerminateMME$', mode, re.IGNORECASE):
elif re.match('^TerminateSPGW$', mode, re.IGNORECASE):
logging.warning("Option TerminateSPGW ignored")
elif re.match('^LogCollectBuild$', mode, re.IGNORECASE):
logging.warning("Option LogCollectBuild ignored")
if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '':
logging.warning("nothing to collect (eNBIPAddress/eNBUserName/eNBPassword/eNBSourceCodePath is '')")
sys.exit(0)
if RAN.eNBIPAddress == 'none':
sys.exit(0)
CiTestObj.LogCollectBuild(RAN)
elif re.match('^LogCollecteNB$', mode, re.IGNORECASE):
if RAN.eNBSourceCodePath == '':
if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
if os.path.isdir('cmake_targets/log'):
@@ -429,6 +501,7 @@ elif re.match('^LogCollecteNB$', mode, re.IGNORECASE):
logging.error("Command '{}' returned non-zero exit status {}.".format(e.cmd, e.returncode))
logging.error("Error output:\n{}".format(e.output))
sys.exit(0)
RAN.LogCollecteNB()
elif re.match('^LogCollectHSS$', mode, re.IGNORECASE):
logging.warning("Option LogCollectHSS ignored")
elif re.match('^LogCollectMME$', mode, re.IGNORECASE):
@@ -473,12 +546,12 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
logging.info('\u001B[1m Starting Scenario: ' + CiTestObj.testXMLfiles[0] + '\u001B[0m')
logging.info('\u001B[1m----------------------------------------\u001B[0m')
if re.match('^TesteNB$', mode, re.IGNORECASE):
if RAN.ranRepository == '' or RAN.ranBranch == '' or RAN.eNBSourceCodePath == '':
if RAN.eNBIPAddress == '' or RAN.ranRepository == '' or RAN.ranBranch == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '':
HELP.GenericHelp(CONST.Version)
if RAN.ranRepository == '':
HELP.GitSrvHelp(RAN.ranRepository, RAN.ranBranch, RAN.ranCommitID, RAN.ranAllowMerge, RAN.ranTargetBranch)
if RAN.eNBSourceCodePath == '':
HELP.eNBSrvHelp(RAN.eNBSourceCodePath)
if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '':
HELP.eNBSrvHelp(RAN.eNBIPAddress, RAN.eNBUserName, RAN.eNBPassword, RAN.eNBSourceCodePath)
sys.exit('Insufficient Parameter')
else:
if CiTestObj.ranRepository == '' or CiTestObj.ranBranch == '':
@@ -492,16 +565,6 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
else:
xml_test_file = cwd + "/" + CiTestObj.testXMLfiles[0]
# directory where all log artifacts will be placed
logPath = f"{cwd}/../cmake_targets/log/{CiTestObj.testXMLfiles[0].split('/')[-1]}.d"
# we run from within ci-scripts, but the logPath is absolute, so replace
# the ci-scripts/..; if it does not exist, nothing will happen
logPath = logPath.replace(r'/ci-scripts/..', '')
logging.info(f"placing all artifacts for this run in {logPath}/")
with cls_cmd.LocalCmd() as c:
c.run(f"rm -rf {logPath}")
c.run(f"mkdir -p {logPath}")
xmlTree = ET.parse(xml_test_file)
xmlRoot = xmlTree.getroot()
@@ -552,32 +615,29 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
task_set_succeeded = True
HTML.startTime=int(round(time.time() * 1000))
i = 0
for test_case_id in todo_tests:
for test in all_tests:
id = test.get('id')
if test_case_id != id:
continue
i += 1
CiTestObj.testCase_id = id
ctx = TestCaseCtx(i, int(id), logPath)
HTML.testCase_id=CiTestObj.testCase_id
desc = test.findtext('desc')
CiTestObj.desc = test.findtext('desc')
always_exec = test.findtext('always_exec') in ['True', 'true', 'Yes', 'yes']
may_fail = test.findtext('may_fail') in ['True', 'true', 'Yes', 'yes']
HTML.desc = desc
HTML.desc=CiTestObj.desc
action = test.findtext('class')
if (CheckClassValidity(xml_class_list, action, id) == False):
task_set_succeeded = False
continue
ShowTestID(ctx, desc)
CiTestObj.ShowTestID()
if not task_set_succeeded and not always_exec:
msg = f"skipping test due to prior error"
logging.warning(msg)
HTML.CreateHtmlTestRowQueue(msg, "SKIP", [])
break
try:
test_succeeded = ExecuteActionWithParam(action, ctx)
test_succeeded = ExecuteActionWithParam(action)
if not test_succeeded and may_fail:
logging.warning(f"test ID {test_case_id} action {action} may or may not fail, proceeding despite error")
elif not test_succeeded:

View File

@@ -43,10 +43,9 @@ import cls_cmd
#-----------------------------------------------------------
# OAI Testing modules
#-----------------------------------------------------------
import cls_cmd
import sshconnection as SSH
import helpreadme as HELP
import constants as CONST
from cls_ci_helper import archiveArtifact
#-----------------------------------------------------------
# Class Declaration
@@ -60,14 +59,33 @@ class RANManagement():
self.ranAllowMerge = False
self.ranCommitID = ''
self.ranTargetBranch = ''
self.eNBIPAddress = ''
self.eNBUserName = ''
self.eNBPassword = ''
self.eNBSourceCodePath = ''
self.eNB1IPAddress = ''
self.eNB1UserName = ''
self.eNB1Password = ''
self.eNB1SourceCodePath = ''
self.eNB2IPAddress = ''
self.eNB2UserName = ''
self.eNB2Password = ''
self.eNB2SourceCodePath = ''
self.Build_eNB_args = ''
self.backgroundBuild = False
self.backgroundBuildTestId = ['', '', '']
self.Build_eNB_forced_workspace_cleanup = False
self.Initialize_eNB_args = ''
self.imageKind = ''
self.air_interface = ''
self.air_interface = ['', '', ''] #changed from 'lte' to '' may lead to side effects in main
self.eNB_instance = 0
self.eNB_serverId = ['', '', '']
self.eNBLogFiles = ['', '', '']
self.eNBOptions = ['', '', '']
self.eNBmbmsEnables = [False, False, False]
self.eNBstatuses = [-1, -1, -1]
self.testCase_id = ''
self.epcPcapFile = ''
self.runtime_stats= ''
self.datalog_rt_stats={}
self.datalog_rt_stats_file='datalog_rt_stats.default.yaml'
@@ -83,98 +101,324 @@ class RANManagement():
# RAN management functions
#-----------------------------------------------------------
def InitializeeNB(self, ctx, node, HTML):
if not node:
raise ValueError(f"{node=}")
logging.debug('Starting eNB/gNB on server: ' + node)
def InitializeeNB(self, HTML):
if self.eNB_serverId[self.eNB_instance] == '0':
lIpAddr = self.eNBIPAddress
lUserName = self.eNBUserName
lPassWord = self.eNBPassword
lSourcePath = self.eNBSourceCodePath
elif self.eNB_serverId[self.eNB_instance] == '1':
lIpAddr = self.eNB1IPAddress
lUserName = self.eNB1UserName
lPassWord = self.eNB1Password
lSourcePath = self.eNB1SourceCodePath
elif self.eNB_serverId[self.eNB_instance] == '2':
lIpAddr = self.eNB2IPAddress
lUserName = self.eNB2UserName
lPassWord = self.eNB2Password
lSourcePath = self.eNB2SourceCodePath
if lIpAddr == '' or lUserName == '' or lPassWord == '' or lSourcePath == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
logging.debug('Starting eNB/gNB on server: ' + lIpAddr)
self.testCase_id = HTML.testCase_id
lSourcePath = self.eNBSourceCodePath
cmd = cls_cmd.getConnection(node)
mySSH = SSH.SSHConnection()
cwd = os.getcwd()
mySSH.open(lIpAddr, lUserName, lPassWord)
mySSH.command('cd ' + lSourcePath, '\$', 5)
# Initialize_eNB_args usually start with -O and followed by the location in repository
full_config_file = self.Initialize_eNB_args.replace('-O ','')
extra_options = ''
extIdx = full_config_file.find('.conf')
if (extIdx <= 0):
raise ValueError(f"no config file in {self.Initialize_eNB_args}")
extra_options = full_config_file[extIdx + 5:]
full_config_file = full_config_file[:extIdx + 5]
config_path, config_file = os.path.split(full_config_file)
logfile = f'{lSourcePath}/cmake_targets/enb.log'
cmd.cd(f"{lSourcePath}/cmake_targets/") # important: set wd so nrL1_stats.log etc are logged here
cmd.run(f'sudo -E stdbuf -o0 {self.cmd_prefix} {lSourcePath}/cmake_targets/ran_build/build/{self.air_interface} -O {lSourcePath}/{full_config_file} {extra_options} > {logfile} 2>&1 &')
if extra_options != '':
self.eNBOptions = extra_options
enbDidSync = False
for _ in range(10):
time.sleep(5)
ret = cmd.run(f'grep --text -E --color=never -i "wait|sync|Starting|Started" {logfile}', reportNonZero=False)
result = re.search('got sync|Starting F1AP at CU', ret.stdout)
if (extIdx > 0):
extra_options = full_config_file[extIdx + 5:]
# if tracer options is on, compiling and running T Tracer
result = re.search('T_stdout', str(extra_options))
if result is not None:
enbDidSync = True
break
if not enbDidSync:
cmd.run(f'sudo killall -9 {self.air_interface}') # in case it did not stop automatically
archiveArtifact(cmd, ctx, logfile)
cmd.close()
msg = f'{self.cmd_prefix} {self.air_interface} -O {config_file} {extra_options}'
if enbDidSync:
logging.debug('\u001B[1m Initialize eNB/gNB Completed\u001B[0m')
HTML.CreateHtmlTestRowQueue(msg, 'OK', [])
logging.debug('\u001B[1m Compiling and launching T Tracer\u001B[0m')
mySSH.command('cd common/utils/T/tracer', '\$', 5)
mySSH.command('make', '\$', 10)
mySSH.command('echo $USER; nohup ./record -d ../T_messages.txt -o ' + lSourcePath + '/cmake_targets/enb_' + self.testCase_id + '_record.raw -ON -off VCD -off HEAVY -off LEGACY_GROUP_TRACE -off LEGACY_GROUP_DEBUG > ' + lSourcePath + '/cmake_targets/enb_' + self.testCase_id + '_record.log 2>&1 &', lUserName, 5)
mySSH.command('cd ' + lSourcePath, '\$', 5)
full_config_file = full_config_file[:extIdx + 5]
config_path, config_file = os.path.split(full_config_file)
else:
logging.error('\u001B[1;37;41m eNB/gNB logging system did not show got sync! \u001B[0m')
HTML.CreateHtmlTestRowQueue(msg, 'KO', [])
sys.exit('Insufficient Parameter')
ci_full_config_file = config_path + '/ci-' + config_file
rruCheck = False
result = re.search('^rru|^rcc|^du.band', str(config_file))
if result is not None:
rruCheck = True
mySSH.command('cp ' + full_config_file + ' ' + ci_full_config_file, '\$', 5)
mySSH.command('sed -i -e \'s/CI_ENB_IP_ADDR/' + lIpAddr + '/\' ' + ci_full_config_file, '\$', 2);
mySSH.command('sed -i -e \'s/CI_GNB_IP_ADDR/' + lIpAddr + '/\' ' + ci_full_config_file, '\$', 2);
mySSH.command('sed -i -e \'s/CI_RCC_IP_ADDR/' + self.eNBIPAddress + '/\' ' + ci_full_config_file, '\$', 2);
mySSH.command('sed -i -e \'s/CI_RRU1_IP_ADDR/' + self.eNB1IPAddress + '/\' ' + ci_full_config_file, '\$', 2);
mySSH.command('sed -i -e \'s/CI_RRU2_IP_ADDR/' + self.eNB2IPAddress + '/\' ' + ci_full_config_file, '\$', 2);
mySSH.command('sed -i -e \'s/CI_FR1_CTL_ENB_IP_ADDR/' + self.eNBIPAddress + '/\' ' + ci_full_config_file, '\$', 2);
self.eNBmbmsEnables[int(self.eNB_instance)] = False
mySSH.command('grep --colour=never enable_enb_m2 ' + ci_full_config_file, '\$', 2);
result = re.search('yes', mySSH.getBefore())
if result is not None:
self.eNBmbmsEnables[int(self.eNB_instance)] = True
logging.debug('\u001B[1m MBMS is enabled on this eNB\u001B[0m')
result = re.search('noS1', str(self.Initialize_eNB_args))
eNBinNoS1 = False
if result is not None:
eNBinNoS1 = True
logging.debug('\u001B[1m eNB is in noS1 configuration \u001B[0m')
# Launch eNB with the modified config file
mySSH.command('source oaienv', '\$', 5)
mySSH.command('cd cmake_targets', '\$', 5)
if self.air_interface[self.eNB_instance] == 'nr-softmodem':
mySSH.command('if [ -e rbconfig.raw ]; then echo ' + lPassWord + ' | sudo -S rm rbconfig.raw; fi', '\$', 5)
mySSH.command('if [ -e reconfig.raw ]; then echo ' + lPassWord + ' | sudo -S rm reconfig.raw; fi', '\$', 5)
# NOTE: WE SHALL do a check if the executable is present (in case build went wrong)
#hack UHD_RFNOC_DIR variable for gNB / N310 on RHEL8 server:
#if the USRP address is in the xml then we are using an eth USRP (N3xx)
if self.air_interface[self.eNB_instance] == 'lte-softmodem':
gNB = False
else:
gNB = True
mySSH.command(f'echo "ulimit -c unlimited && {self.cmd_prefix} ./ran_build/build/{self.air_interface[self.eNB_instance]} -O {lSourcePath}/{ci_full_config_file} {extra_options}" > ./my-lte-softmodem-run{self.eNB_instance}.sh', '\$', 5)
mySSH.command('chmod 775 ./my-lte-softmodem-run' + str(self.eNB_instance) + '.sh', '\$', 5)
mySSH.command('echo ' + lPassWord + ' | sudo -S rm -Rf enb_' + self.testCase_id + '.log', '\$', 5)
mySSH.command('echo $USER; nohup sudo -E stdbuf -o0 ./my-lte-softmodem-run' + str(self.eNB_instance) + '.sh > ' + lSourcePath + '/cmake_targets/enb_' + self.testCase_id + '.log 2>&1 &', lUserName, 10)
self.eNBLogFiles[int(self.eNB_instance)] = 'enb_' + self.testCase_id + '.log'
if extra_options != '':
self.eNBOptions[int(self.eNB_instance)] = extra_options
time.sleep(6)
doLoop = True
loopCounter = 20
enbDidSync = False
while (doLoop):
loopCounter = loopCounter - 1
if (loopCounter == 0):
# In case of T tracer recording, we may need to kill it
result = re.search('T_stdout', str(self.Initialize_eNB_args))
if result is not None:
mySSH.command('killall --signal SIGKILL record', '\$', 5)
mySSH.close()
doLoop = False
logging.error('\u001B[1;37;41m eNB/gNB logging system did not show got sync! \u001B[0m')
HTML.CreateHtmlTestRow(self.air_interface[self.eNB_instance] + ' -O ' + config_file + extra_options, 'KO', CONST.ALL_PROCESSES_OK)
return False
else:
mySSH.command('stdbuf -o0 cat enb_' + self.testCase_id + '.log | grep -E --text --color=never -i "wait|sync|Starting|Started"', '\$', 4)
if rruCheck:
result = re.search('wait RUs', mySSH.getBefore())
else:
result = re.search('got sync|Starting F1AP at CU', mySSH.getBefore())
if result is None:
time.sleep(6)
else:
doLoop = False
enbDidSync = True
time.sleep(10)
rruCheck = False
result = re.search('^rru|^du.band', str(config_file))
if result is not None:
rruCheck = True
if enbDidSync and eNBinNoS1 and not rruCheck:
mySSH.command('ifconfig oaitun_enb1', '\$', 4)
mySSH.command('ifconfig oaitun_enb1', '\$', 4)
result = re.search('inet addr:1|inet 1', mySSH.getBefore())
if result is not None:
logging.debug('\u001B[1m oaitun_enb1 interface is mounted and configured\u001B[0m')
else:
logging.error('\u001B[1m oaitun_enb1 interface is either NOT mounted or NOT configured\u001B[0m')
if self.eNBmbmsEnables[int(self.eNB_instance)]:
mySSH.command('ifconfig oaitun_enm1', '\$', 4)
result = re.search('inet addr', mySSH.getBefore())
if result is not None:
logging.debug('\u001B[1m oaitun_enm1 interface is mounted and configured\u001B[0m')
else:
logging.error('\u001B[1m oaitun_enm1 interface is either NOT mounted or NOT configured\u001B[0m')
if enbDidSync:
self.eNBstatuses[int(self.eNB_instance)] = int(self.eNB_serverId[self.eNB_instance])
mySSH.close()
HTML.CreateHtmlTestRow(f'{self.cmd_prefix} {self.air_interface[self.eNB_instance]} -O {config_file} {extra_options}', 'OK', CONST.ALL_PROCESSES_OK)
logging.debug('\u001B[1m Initialize eNB/gNB Completed\u001B[0m')
return enbDidSync
def TerminateeNB(self, ctx, node, HTML):
logging.debug('Stopping eNB/gNB on server: ' + node)
lSourcePath = self.eNBSourceCodePath
cmd = cls_cmd.getConnection(node)
ret = cmd.run('ps -aux | grep --color=never -e softmodem | grep -v grep')
result = re.search('-softmodem', ret.stdout)
if result is not None:
cmd.run('sudo -S killall --signal SIGINT -r .*-softmodem')
time.sleep(6)
ret = cmd.run('ps -aux | grep --color=never -e softmodem | grep -v grep')
result = re.search('-softmodem', ret.stdout)
if result is not None:
cmd.run('sudo -S killall --signal SIGKILL -r .*-softmodem')
time.sleep(5)
# see InitializeeNB()
logfile = f'{lSourcePath}/cmake_targets/enb.log'
logdir = os.path.dirname(logfile)
file = archiveArtifact(cmd, ctx, logfile)
archiveArtifact(cmd, ctx, f"{logdir}/nrL1_stats.log")
archiveArtifact(cmd, ctx, f"{logdir}/nrMAC_stats.log")
cmd.close()
if file is None:
logging.debug('\u001B[1;37;41m Could not copy xNB logfile to analyze it! \u001B[0m')
msg = 'Could not copy xNB logfile to analyze it!'
HTML.CreateHtmlTestRowQueue('N/A', 'KO', [msg])
return False
logging.debug('\u001B[1m Analyzing xNB logfile \u001B[0m ' + file)
logStatus = self.AnalyzeLogFile_eNB(file, HTML, self.ran_checkers)
if logStatus < 0:
HTML.CreateHtmlTestRow('N/A', 'KO', logStatus)
def CheckeNBProcess(self, status_queue):
# At least the instance 0 SHALL be on!
if self.eNBstatuses[0] == 0:
lIpAddr = self.eNBIPAddress
lUserName = self.eNBUserName
lPassWord = self.eNBPassword
elif self.eNBstatuses[0] == 1:
lIpAddr = self.eNB1IPAddress
lUserName = self.eNB1UserName
lPassWord = self.eNB1Password
elif self.eNBstatuses[0] == 2:
lIpAddr = self.eNB2IPAddress
lUserName = self.eNB2UserName
lPassWord = self.eNB2Password
else:
lIpAddr = self.eNBIPAddress
lUserName = self.eNBUserName
lPassWord = self.eNBPassword
mySSH = SSH.SSHConnection()
mySSH.open(lIpAddr, lUserName, lPassWord)
if self.air_interface[self.eNB_instance] == '':
pattern = 'softmodem'
else:
pattern = self.air_interface[self.eNB_instance]
mySSH.command('stdbuf -o0 ps -aux | grep --color=never ' + pattern + ' | grep -v grep', '\$', 5)
result = re.search(pattern, mySSH.getBefore())
success = result is not None
if not success:
logging.debug('\u001B[1;37;41m eNB Process Not Found! \u001B[0m')
status_queue.put(CONST.ENB_PROCESS_FAILED)
else:
status_queue.put(CONST.ENB_PROCESS_OK)
mySSH.close()
return success
def TerminateeNB(self, HTML):
if self.eNB_serverId[self.eNB_instance] == '0':
lIpAddr = self.eNBIPAddress
lUserName = self.eNBUserName
lPassWord = self.eNBPassword
lSourcePath = self.eNBSourceCodePath
elif self.eNB_serverId[self.eNB_instance] == '1':
lIpAddr = self.eNB1IPAddress
lUserName = self.eNB1UserName
lPassWord = self.eNB1Password
lSourcePath = self.eNB1SourceCodePath
elif self.eNB_serverId[self.eNB_instance] == '2':
lIpAddr = self.eNB2IPAddress
lUserName = self.eNB2UserName
lPassWord = self.eNB2Password
lSourcePath = self.eNB2SourceCodePath
if lIpAddr == '' or lUserName == '' or lPassWord == '' or lSourcePath == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
logging.debug('Stopping eNB/gNB on server: ' + lIpAddr)
mySSH = SSH.SSHConnection()
mySSH.open(lIpAddr, lUserName, lPassWord)
mySSH.command('cd ' + lSourcePath + '/cmake_targets', '\$', 5)
if self.air_interface[self.eNB_instance] == 'lte-softmodem':
nodeB_prefix = 'e'
else:
nodeB_prefix = 'g'
mySSH.command('stdbuf -o0 ps -aux | grep --color=never -e softmodem | grep -v grep', '\$', 5)
result = re.search('-softmodem', mySSH.getBefore())
if result is not None:
mySSH.command('echo ' + lPassWord + ' | sudo -S killall --signal SIGINT -r .*-softmodem || true', '\$', 5)
time.sleep(10)
mySSH.command('stdbuf -o0 ps -aux | grep --color=never -e softmodem | grep -v grep', '\$', 5)
result = re.search('-softmodem', mySSH.getBefore())
if result is not None:
mySSH.command('echo ' + lPassWord + ' | sudo -S killall --signal SIGKILL -r .*-softmodem || true', '\$', 5)
time.sleep(5)
mySSH.command('rm -f my-lte-softmodem-run' + str(self.eNB_instance) + '.sh', '\$', 5)
#stopping tshark (valid if eNB and enabled in xml, will not harm otherwise)
logging.debug('\u001B[1m Stopping tshark on xNB \u001B[0m')
mySSH.command('echo ' + lPassWord + ' | sudo -S killall --signal SIGKILL tshark', '\$', 5)
time.sleep(1)
mySSH.close()
# if T tracer was run with option 0 (no logs), analyze logs
# from textlog, otherwise do normal analysis (e.g., option 2)
result = re.search('T_stdout 0', str(self.Initialize_eNB_args))
if (result is not None):
logging.debug('\u001B[1m Replaying RAW record file\u001B[0m')
mySSH.open(lIpAddr, lUserName, lPassWord)
mySSH.command('cd ' + lSourcePath + '/common/utils/T/tracer/', '\$', 5)
enbLogFile = self.eNBLogFiles[int(self.eNB_instance)]
raw_record_file = enbLogFile.replace('.log', '_record.raw')
replay_log_file = enbLogFile.replace('.log', '_replay.log')
extracted_txt_file = enbLogFile.replace('.log', '_extracted_messages.txt')
extracted_log_file = enbLogFile.replace('.log', '_extracted_messages.log')
mySSH.command('./extract_config -i ' + lSourcePath + '/cmake_targets/' + raw_record_file + ' > ' + lSourcePath + '/cmake_targets/' + extracted_txt_file, '\$', 5)
mySSH.command('echo $USER; nohup ./replay -i ' + lSourcePath + '/cmake_targets/' + raw_record_file + ' > ' + lSourcePath + '/cmake_targets/' + replay_log_file + ' 2>&1 &', lUserName, 5)
mySSH.command('./textlog -d ' + lSourcePath + '/cmake_targets/' + extracted_txt_file + ' -no-gui -ON -full > ' + lSourcePath + '/cmake_targets/' + extracted_log_file, '\$', 5)
mySSH.close()
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/' + extracted_log_file, '.')
logging.debug('\u001B[1m Analyzing eNB replay logfile \u001B[0m')
logStatus = self.AnalyzeLogFile_eNB(extracted_log_file, HTML, self.ran_checkers)
HTML.CreateHtmlTestRow(self.runtime_stats, 'OK', CONST.ALL_PROCESSES_OK)
self.eNBLogFiles[int(self.eNB_instance)] = ''
return True
else:
analyzeFile = False
if self.eNBLogFiles[int(self.eNB_instance)] != '':
analyzeFile = True
fileToAnalyze = self.eNBLogFiles[int(self.eNB_instance)]
self.eNBLogFiles[int(self.eNB_instance)] = ''
if analyzeFile:
#*stats.log files + pickle + png
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*stats.log', '.')
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*.pickle', '.')
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*.png', '.')
#
copyin_res = mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/' + fileToAnalyze, '.')
if (copyin_res == -1):
logging.debug('\u001B[1;37;41m Could not copy ' + nodeB_prefix + 'NB logfile to analyze it! \u001B[0m')
HTML.htmleNBFailureMsg='Could not copy ' + nodeB_prefix + 'NB logfile to analyze it!'
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE)
self.eNBmbmsEnables[int(self.eNB_instance)] = False
return False
if self.eNB_serverId[self.eNB_instance] != '0':
#*stats.log files + pickle + png
#debug / tentative
mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './nrL1_stats.log', self.eNBSourceCodePath + '/cmake_targets/')
mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './nrMAC_stats.log', self.eNBSourceCodePath + '/cmake_targets/')
mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './' + fileToAnalyze, self.eNBSourceCodePath + '/cmake_targets/')
logging.debug('\u001B[1m Analyzing ' + nodeB_prefix + 'NB logfile \u001B[0m ' + fileToAnalyze)
logStatus = self.AnalyzeLogFile_eNB(fileToAnalyze, HTML, self.ran_checkers)
if (logStatus < 0):
HTML.CreateHtmlTestRow('N/A', 'KO', logStatus)
#display rt stats for gNB only
if len(self.datalog_rt_stats)!=0 and nodeB_prefix == 'g':
HTML.CreateHtmlDataLogTable(self.datalog_rt_stats)
self.eNBmbmsEnables[int(self.eNB_instance)] = False
return False
else:
HTML.CreateHtmlTestRow(self.runtime_stats, 'OK', CONST.ALL_PROCESSES_OK)
else:
HTML.CreateHtmlTestRow(self.runtime_stats, 'OK', CONST.ALL_PROCESSES_OK)
#display rt stats for gNB only
if len(self.datalog_rt_stats) != 0:
if len(self.datalog_rt_stats)!=0 and nodeB_prefix == 'g':
HTML.CreateHtmlDataLogTable(self.datalog_rt_stats)
self.eNBmbmsEnables[int(self.eNB_instance)] = False
self.eNBstatuses[int(self.eNB_instance)] = -1
return True
return logStatus >= 0
def LogCollecteNB(self):
mySSH = SSH.SSHConnection()
# Copying back to xNB server any log from all the runs.
# Should also contains ping and iperf logs
absPath = os.path.abspath('.')
if absPath.count('ci-scripts') == 0:
os.chdir('./ci-scripts')
for x in os.listdir():
if x.endswith('.log') or x.endswith('.log.png'):
mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, x, self.eNBSourceCodePath + '/cmake_targets/', silent=True, ignorePermDenied=True)
# Back to normal
mySSH.open(self.eNBIPAddress, self.eNBUserName, self.eNBPassword)
mySSH.command('cd ' + self.eNBSourceCodePath, '\$', 5)
mySSH.command('cd cmake_targets', '\$', 5)
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S mv /tmp/enb_*.pcap .','\$',20)
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S mv /tmp/gnb_*.pcap .','\$',20)
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S rm -f enb.log.zip', '\$', 5)
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S zip enb.log.zip *.log enb_*record.raw enb_*.pcap gnb_*.pcap enb_*txt physim_*.log *stats.log *monitor.pickle *monitor*.png ping*.log* iperf*.log log/*/*.log log/*/*.pcap', '\$', 60)
result = re.search('core.\d+', mySSH.getBefore())
if result is not None:
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S zip enb.log.zip core* ran_build/build/{lte,nr}-softmodem', '\$', 60) # add core and executable to zip
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S rm enb*.log core* enb_*record.raw enb_*.pcap gnb_*.pcap enb_*txt physim_*.log *stats.log *monitor.pickle *monitor*.png ping*.log* iperf*.log log/*/*.log log/*/*.pcap', '\$', 15)
mySSH.close()
def _analyzeUeRetx(self, rounds, checkers, regex):
if len(rounds) == 0 or len(checkers) == 0:
@@ -321,16 +565,16 @@ class RANManagement():
X2HO_state = CONST.X2_HO_REQ_STATE__IDLE
X2HO_outNbProcedures += 1
if self.eNBOptions[0] != '':
res1 = re.search('max_rxgain (?P<requested_option>[0-9]+)', self.eNBOptions[0])
if self.eNBOptions[int(self.eNB_instance)] != '':
res1 = re.search('max_rxgain (?P<requested_option>[0-9]+)', self.eNBOptions[int(self.eNB_instance)])
res2 = re.search('max_rxgain (?P<applied_option>[0-9]+)', str(line))
if res1 is not None and res2 is not None:
requested_option = int(res1.group('requested_option'))
applied_option = int(res2.group('applied_option'))
if requested_option == applied_option:
htmleNBFailureMsg += '<span class="glyphicon glyphicon-ok-circle"></span> Command line option(s) correctly applied <span class="glyphicon glyphicon-arrow-right"></span> ' + self.eNBOptions[0] + '\n\n'
htmleNBFailureMsg += '<span class="glyphicon glyphicon-ok-circle"></span> Command line option(s) correctly applied <span class="glyphicon glyphicon-arrow-right"></span> ' + self.eNBOptions[int(self.eNB_instance)] + '\n\n'
else:
htmleNBFailureMsg += '<span class="glyphicon glyphicon-ban-circle"></span> Command line option(s) NOT applied <span class="glyphicon glyphicon-arrow-right"></span> ' + self.eNBOptions[0] + '\n\n'
htmleNBFailureMsg += '<span class="glyphicon glyphicon-ban-circle"></span> Command line option(s) NOT applied <span class="glyphicon glyphicon-arrow-right"></span> ' + self.eNBOptions[int(self.eNB_instance)] + '\n\n'
result = re.search('Exiting OAI softmodem|Caught SIGTERM, shutting down', str(line))
if result is not None:
exitSignalReceived = True
@@ -417,7 +661,7 @@ class RANManagement():
result = re.search('dropping, not enough RBs', str(line))
if result is not None:
dropNotEnoughRBs += 1
if self.eNBmbmsEnables[0]:
if self.eNBmbmsEnables[int(self.eNB_instance)]:
result = re.search('MBMS USER-PLANE.*Requesting.*bytes from RLC', str(line))
if result is not None:
mbmsRequestMsg += 1
@@ -490,16 +734,11 @@ class RANManagement():
datalog_rt_stats = yaml.load(f,Loader=yaml.FullLoader)
rt_keys = datalog_rt_stats['Ref'] #we use the keys from the Ref field
# nrL1_stats.log/nrMAC_stats.log should be in the same directory as main log file
# currently the link is only implicit as below based on pattern matching
# I will rework this to give the file explicitly
l1_stats_fn = re.sub(r'-enb.log$', '-nrL1_stats.log', eNBlogFile)
mac_stats_fn = re.sub(r'-enb.log$', '-nrMAC_stats.log', eNBlogFile)
if os.path.isfile(l1_stats_fn) and os.path.isfile(mac_stats_fn):
if os.path.isfile('./nrL1_stats.log') and os.path.isfile('./nrMAC_stats.log'):
# don't use CI-nrL1_stats.log, as this will increase the processing time for
# no reason, we just need the last occurence
nrL1_stats = open(l1_stats_fn, 'r')
nrMAC_stats = open(mac_stats_fn, 'r')
nrL1_stats = open('./nrL1_stats.log', 'r')
nrMAC_stats = open('./nrMAC_stats.log', 'r')
for line in nrL1_stats.readlines():
for k in rt_keys:
result = re.search(k, line)
@@ -519,14 +758,14 @@ class RANManagement():
nrL1_stats.close()
nrMAC_stats.close()
else:
logging.debug(f"NR Stats files for RT analysis not found: {l1_stats_fn}, {mac_stats_fn}")
logging.debug("NR Stats files for RT analysis not found")
#stdout log file and stat log files analysis completed
logging.debug(' File analysis (stdout, stats) completed')
#post processing depending on the node type
if not nodeB_prefix_found:
if self.air_interface == 'lte-softmodem':
if self.air_interface[self.eNB_instance] == 'lte-softmodem':
nodeB_prefix = 'e'
else:
nodeB_prefix = 'g'
@@ -730,7 +969,7 @@ class RANManagement():
rrcMsg = ' -- ' + str(rrcReestablishReject) + ' were rejected'
logging.debug('\u001B[1;30;43m ' + rrcMsg + ' \u001B[0m')
htmleNBFailureMsg += rrcMsg + '\n'
if self.eNBmbmsEnables[0]:
if self.eNBmbmsEnables[int(self.eNB_instance)]:
if mbmsRequestMsg > 0:
rrcMsg = 'eNB requested ' + str(mbmsRequestMsg) + ' times the RLC for MBMS USER-PLANE'
logging.debug('\u001B[1;30;43m ' + rrcMsg + ' \u001B[0m')
@@ -743,8 +982,8 @@ class RANManagement():
rrcMsg = 'eNB completed ' + str(X2HO_outNbProcedures) + ' X2 Handover Release procedure(s)'
logging.debug('\u001B[1;30;43m ' + rrcMsg + ' \u001B[0m')
htmleNBFailureMsg += rrcMsg + '\n'
if self.eNBOptions[0] != '':
res1 = re.search('drx_Config_present prSetup', self.eNBOptions[0])
if self.eNBOptions[int(self.eNB_instance)] != '':
res1 = re.search('drx_Config_present prSetup', self.eNBOptions[int(self.eNB_instance)])
if res1 is not None:
if cdrxActivationMessageCount > 0:
rrcMsg = 'eNB activated the CDRX Configuration for ' + str(cdrxActivationMessageCount) + ' time(s)'

View File

@@ -26,18 +26,8 @@ fi
docker tag oai-nr-ue oai-ci/oai-nr-ue:develop-${SHORT_COMMIT_SHA}
docker tag oai-gnb oai-ci/oai-gnb:develop-${SHORT_COMMIT_SHA}
python3 main.py --mode=InitiateHtml --ranRepository=NONE --ranBranch=${CURRENT_BRANCH} \
--ranCommitID=${COMMIT_SHA} --ranAllowMerge=false \
--ranTargetBranch=NONE \
--XMLTestFile=xml_files/${TESTCASE} --local
python3 main.py --mode=TesteNB --ranRepository=NONE --ranBranch=${CURRENT_BRANCH} \
--ranCommitID=${COMMIT_SHA} --ranAllowMerge=false \
--ranTargetBranch=NONE \
--ranTargetBranch=NONE --eNBIPAddress=NONE --eNBUserName=NONE --eNBPassword=NONE \
--eNBSourceCodePath=${REPO_PATH} \
--XMLTestFile=${TESTCASE} --local
RET=$?
python3 main.py --mode=FinalizeHtml --local
exit ${RET}
--XMLTestFile=xml_files/${TESTCASE} --local

281
ci-scripts/sshconnection.py Normal file
View File

@@ -0,0 +1,281 @@
#/*
# * 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
# */
#---------------------------------------------------------------------
# Python for CI of OAI-eNB + COTS-UE
#
# Required Python Version
# Python 3.x
#
# Required Python Package
# pexpect
#---------------------------------------------------------------------
#-----------------------------------------------------------
# Import
#-----------------------------------------------------------
import pexpect # pexpect
import logging
import time # sleep
import re
import subprocess
import sys
#-----------------------------------------------------------
# Class Declaration
#-----------------------------------------------------------
class SSHConnection():
def __init__(self):
self.ssh = ''
self.picocom_closure = False
self.ipaddress = ''
self.username = ''
self.cmd2Results = ''
def disablePicocomClosure(self):
self.picocom_closure = False
def enablePicocomClosure(self):
self.picocom_closure = True
def open(self, ipaddress, username, password):
prompt = "\$"
count = 0
connect_status = False
while count < 4:
self.ssh = pexpect.spawn('ssh -o PubkeyAuthentication=yes {}@{}'.format(username,ipaddress))
# Longer timeout at connection due to asterix slowness
self.ssh.timeout = 25
self.sshresponse = self.ssh.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', 'Last login', pexpect.EOF, pexpect.TIMEOUT])
if self.sshresponse == 0:
self.ssh.sendline('yes')
self.sshresponse = self.ssh.expect(['password:', username + '@'])
if self.sshresponse == 0:
self.ssh.sendline(password)
self.sshresponse = self.ssh.expect([prompt, 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
if self.sshresponse == 0:
count = 10
connect_status = True
else:
logging.warning('self.sshresponse = ' + str(self.sshresponse))
elif self.sshresponse == 1:
self.ssh.sendline(password)
self.sshresponse = self.ssh.expect([prompt, 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
if self.sshresponse == 0:
count = 10
connect_status = True
else:
logging.warning('self.sshresponse = ' + str(self.sshresponse))
elif self.sshresponse == 2:
# We directly ended up on the remote server because of pubkey auth
count = 10
connect_status = True
# this expect() seems to be necessary to advance the read buffer until the prompt, or getBefore() will not return the last command
self.sshresponse = self.ssh.expect([prompt])
else:
# debug output
logging.warning(str(self.ssh.before))
logging.warning('self.sshresponse = ' + str(self.sshresponse))
# adding a tempo when failure
if not connect_status:
time.sleep(1)
count += 1
if connect_status:
self.command('unset HISTFILE', prompt, 5, silent=True)
else:
raise ConnectionError('SSH Connection Failed')
self.ipaddress = ipaddress
self.username = username
def cde_check_value(self, commandline, expected, timeout):
logging.info(commandline)
self.ssh.timeout = timeout
self.ssh.sendline(commandline)
expected.append(pexpect.EOF)
expected.append(pexpect.TIMEOUT)
self.sshresponse = self.ssh.expect(expected)
return self.sshresponse
def command(self, commandline, expectedline, timeout, silent=False, resync=False):
if not silent:
logging.info(commandline)
self.ssh.timeout = timeout
# Nasty patch when pexpect output is out of sync.
# Much pronounced when running back-to-back-back oc commands
if resync:
self.ssh.send(commandline)
self.ssh.expect([commandline, pexpect.TIMEOUT])
self.ssh.send('\r\n')
self.sshresponse = self.ssh.expect([expectedline, pexpect.EOF, pexpect.TIMEOUT])
else:
self.ssh.sendline(commandline)
self.sshresponse = self.ssh.expect([expectedline, pexpect.EOF, pexpect.TIMEOUT])
if self.sshresponse == 0:
return 0
elif self.sshresponse == 1:
logging.error('\u001B[1;37;41m Unexpected EOF \u001B[0m')
logging.error('Expected Line : ' + expectedline)
logging.error(str(self.ssh.before))
raise ConnectionError(self.sshresponse)
elif self.sshresponse == 2:
logging.error('\u001B[1;37;41m Unexpected TIMEOUT \u001B[0m')
logging.error('Expected Line : ' + expectedline)
result = re.search('ping |iperf |picocom', str(commandline))
if result is None:
logging.warning(str(self.ssh.before))
raise ConnectionError(self.sshresponse)
else:
return -1
else:
logging.error('\u001B[1;37;41m Unexpected Others \u001B[0m')
logging.error('Expected Line : ' + expectedline)
raise ConnectionError(self.sshresponse)
def command2(self, commandline, timeout, silent=False):
if not silent:
logging.info(commandline)
self.cmd2Results = ''
noHistoryCmd = 'unset HISTFILE; ' + commandline
myHost = self.username + '@' + self.ipaddress
# CAUTION: THIS METHOD IMPLIES THAT THERE ARE VALID SSH KEYS
# BETWEEN THE PYTHON EXECUTOR NODE AND THE REMOTE HOST
# OTHERWISE IT WON'T WORK
lSsh = subprocess.Popen(["ssh", "%s" % myHost, noHistoryCmd],shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
self.cmd2Results = str(lSsh.stdout.readlines())
def command3(self, commandline, timeout, silent=False):
if not silent:
logging.info(commandline)
self.cmd2Results = ''
noHistoryCmd = 'unset HISTFILE; ' + commandline
myHost = self.username + '@' + self.ipaddress
# CAUTION: THIS METHOD IMPLIES THAT THERE ARE VALID SSH KEYS
# BETWEEN THE PYTHON EXECUTOR NODE AND THE REMOTE HOST
# OTHERWISE IT WON'T WORK
lSsh = subprocess.Popen(["ssh", "%s" % myHost, noHistoryCmd],shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
return lSsh.stdout.readlines()
def close(self):
self.ssh.timeout = 5
self.ssh.sendline('exit')
self.sshresponse = self.ssh.expect([pexpect.EOF, pexpect.TIMEOUT])
self.ipaddress = ''
self.username = ''
if self.sshresponse == 0:
pass
elif self.sshresponse == 1:
if not self.picocom_closure:
logging.warning('\u001B[1;37;41m Unexpected TIMEOUT during closing\u001B[0m')
else:
logging.warning('\u001B[1;37;41m Unexpected Others during closing\u001B[0m')
def copyin(self, ipaddress, username, password, source, destination):
count = 0
copy_status = False
logging.info('scp -r '+ username + '@' + ipaddress + ':' + source + ' ' + destination)
while count < 10:
scp_spawn = pexpect.spawn('scp -r '+ username + '@' + ipaddress + ':' + source + ' ' + destination, timeout = 100)
scp_response = scp_spawn.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', pexpect.EOF, pexpect.TIMEOUT])
if scp_response == 0:
scp_spawn.sendline('yes')
scp_spawn.expect('password:')
scp_spawn.sendline(password)
scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
if scp_response == 0:
count = 10
copy_status = True
else:
logging.warning('1 - scp_response = ' + str(scp_response))
elif scp_response == 1:
scp_spawn.sendline(password)
scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
if scp_response == 0 or scp_response == 3:
count = 10
copy_status = True
else:
logging.warning('2 - scp_response = ' + str(scp_response))
elif scp_response == 2:
count = 10
copy_status = True
else:
logging.warning('3 - scp_response = ' + str(scp_response))
# adding a tempo when failure
if not copy_status:
time.sleep(1)
count += 1
if copy_status:
return 0
else:
return -1
def copyout(self, ipaddress, username, password, source, destination, silent=False, ignorePermDenied=False):
count = 0
copy_status = False
if not silent:
logging.info('scp -r ' + source + ' ' + username + '@' + ipaddress + ':' + destination)
while count < 4:
scp_spawn = pexpect.spawn('scp -r ' + source + ' ' + username + '@' + ipaddress + ':' + destination, timeout = 100)
scp_response = scp_spawn.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', pexpect.EOF, pexpect.TIMEOUT])
if scp_response == 0:
scp_spawn.sendline('yes')
scp_spawn.expect('password:')
scp_spawn.sendline(password)
scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
if scp_response == 0:
count = 10
copy_status = True
elif scp_response == 1 and ignorePermDenied:
logging.warning(f'copyout(): permission denied, not copying file ({source})')
count = 10
copy_status = True
else:
logging.warning('1 - scp_response = ' + str(scp_response))
elif scp_response == 1:
scp_spawn.sendline(password)
scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
if scp_response == 0 or scp_response == 3:
count = 10
copy_status = True
elif scp_response == 1 and ignorePermDenied:
logging.warning(f'copyout(): permission denied, not copying file ({source})')
count = 10
copy_status = True
else:
logging.warning('2 - scp_response = ' + str(scp_response))
elif scp_response == 2:
count = 10
copy_status = True
else:
logging.warning('3 - scp_response = ' + str(scp_response))
# adding a tempo when failure
if not copy_status:
time.sleep(1)
count += 1
if copy_status:
pass
else:
raise ConnectionError('SCP failed')
def getBefore(self):
return self.ssh.before.decode('utf-8')

View File

@@ -11,7 +11,6 @@ import tempfile
sys.path.append('./') # to find OAI imports below
import cls_oai_html
import cls_containerize
from cls_ci_helper import TestCaseCtx
import cls_cmd
class TestBuild(unittest.TestCase):
@@ -19,21 +18,22 @@ class TestBuild(unittest.TestCase):
self.html = cls_oai_html.HTMLManagement()
self.html.testCase_id = "000000"
self.cont = cls_containerize.Containerize()
self.cont.eNB_serverId[0] = '0'
self.cont.eNBIPAddress = 'localhost'
self.cont.eNBUserName = None
self.cont.eNBPassword = None
self._d = tempfile.mkdtemp()
logging.warning(f"temporary directory: {self._d}")
self.node = 'localhost'
self.cont.eNBSourceCodePath = self._d
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
def tearDown(self):
logging.warning(f"removing directory contents")
with cls_cmd.getConnection(None) as cmd:
cmd.run(f"rm -rf {self._d}")
cmd.run(f'rm -rf {self.ctx.logPath}')
def test_build_proxy(self):
self.cont.proxyCommit = "b64d9bce986b38ca59e8582864ade3fcdd05c0dc"
success = self.cont.BuildProxy(self.ctx, self.node, self.html)
success = self.cont.BuildProxy(self.html)
self.assertTrue(success)
if __name__ == '__main__':

View File

@@ -1,34 +0,0 @@
test:
Host: localhost
InitScript: ""
TermScript: ""
AttachScript: "true"
DetachScript: "true"
NetworkScript: ip a show dev lo
IF: lo
MTU: 65536
test-fail:
Host: localhost
InitScript: ""
TermScript: ""
AttachScript: "false"
DetachScript: "true"
NetworkScript: ip a show dev TESTDEVICEDOESNOTEXIST
IF: TESTDEVICEDOESNOTEXIST
MTU: 123
test-trace:
Host: localhost
InitScript: ""
TermScript: ""
AttachScript: "true"
DetachScript: "true"
Tracing:
# tttf: temp-trace-test-file
Start: "rm -f /tmp/tttf; for i in {1..8}; do echo $i >> /tmp/tttf; sleep 1; done &"
Stop: "while [[ $(wc -l /tmp/tttf) < 4 ]]; do sleep 1; done"
Collect: "cp /tmp/tttf %%log_dir%%"
NetworkScript: ip a show dev lo
IF: lo
MTU: 65536

View File

@@ -6,22 +6,14 @@ logging.basicConfig(
format="[%(asctime)s] %(levelname)8s: %(message)s"
)
import os
import tempfile
import unittest
sys.path.append('./') # to find OAI imports below
import cls_corenetwork
from cls_ci_helper import TestCaseCtx
import cls_cmd
class TestCoreNetwork(unittest.TestCase):
def setUp(self):
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
def tearDown(self):
with cls_cmd.LocalCmd() as c:
c.run(f'rm -rf {self.ctx.logPath}')
def test_simple_core(self):
c = cls_corenetwork.CoreNetwork("test", filename="tests/config/test_core_infra.yaml")
success, output = c.deploy()
@@ -30,14 +22,14 @@ class TestCoreNetwork(unittest.TestCase):
# should not have it set in config
self.assertTrue(c.runIperf3Server())
self.assertEqual(c.getIP(), "127.0.0.1")
log_files, output = c.undeploy(self.ctx)
log_files, output = c.undeploy(log_dir="/tmp")
self.assertEqual(output, "undeploy")
with cls_cmd.LocalCmd() as cmd:
# there must be one log file for this (test) core
self.assertEqual(len(log_files), 1)
# undeploy uses archiveArtifact(), which writes to {prefix}-logs
# test core writes to %%log_dir%%/logs, which expands to /tmp/logs
l = log_files[0]
self.assertEqual(l, f"{self.ctx.baseFilename()}-logs")
self.assertEqual(l, "/tmp/logs")
ret = cmd.run(f"cat {l}")
self.assertEqual(ret.returncode, 0) # command must succeed
self.assertEqual(ret.stdout, "logs") # output should be "logs"
@@ -47,14 +39,14 @@ class TestCoreNetwork(unittest.TestCase):
success, _ = c.deploy()
self.assertTrue(success)
self.assertFalse(c.runIperf3Server())
c.undeploy(None)
c.undeploy(log_dir="/tmp")
def test_core_fail(self):
c = cls_corenetwork.CoreNetwork("test_fail", filename="tests/config/test_core_infra.yaml")
success, _ = c.deploy()
self.assertFalse(success)
# undeployment should still work
c.undeploy(None)
c.undeploy(log_dir="/tmp")
def test_core_script(self):
c = cls_corenetwork.CoreNetwork("test_script", filename="tests/config/test_core_infra.yaml")

View File

@@ -6,7 +6,6 @@ logging.basicConfig(
format="[%(asctime)s] %(levelname)8s: %(message)s"
)
import os
import tempfile
os.system(f'rm -rf cmake_targets')
os.system(f'mkdir -p cmake_targets/log')
import unittest
@@ -15,7 +14,6 @@ sys.path.append('./') # to find OAI imports below
import cls_oai_html
import cls_oaicitest
import cls_containerize
from cls_ci_helper import TestCaseCtx
import ran
import cls_cmd
@@ -40,83 +38,72 @@ class TestDeploymentMethods(unittest.TestCase):
self.ci = cls_oaicitest.OaiCiTest()
self.cont = cls_containerize.Containerize()
self.ran = ran.RANManagement()
self.cont.yamlPath = ''
self.cont.yamlPath[0] = ''
self.cont.ranAllowMerge = True
self.cont.ranBranch = ''
self.cont.ranCommitID = ''
self.cont.eNB_serverId[0] = '0'
self.cont.eNBIPAddress = 'localhost'
self.cont.eNBUserName = None
self.cont.eNBPassword = None
self.cont.eNBSourceCodePath = os.getcwd()
self.cont.num_attempts = 3
self.node = 'localhost'
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
def tearDown(self):
with cls_cmd.LocalCmd() as c:
c.run(f'rm -rf {self.ctx.logPath}')
def test_deploy(self):
self.cont.yamlPath = 'tests/simple-dep/'
self.cont.yamlPath[0] = 'tests/simple-dep/'
self.cont.deploymentTag = "noble"
deploy = self.cont.DeployObject(self.ctx, self.node, self.html)
undeploy = self.cont.UndeployObject(self.ctx, self.node, self.html, self.ran)
deploy = self.cont.DeployObject(self.html)
undeploy = self.cont.UndeployObject(self.html, self.ran)
self.assertTrue(deploy)
self.assertTrue(undeploy)
def test_deployfails(self):
# fails reliably
old = self.cont.yamlPath
self.cont.yamlPath = 'tests/simple-fail/'
deploy = self.cont.DeployObject(self.ctx, self.node, self.html)
self.cont.UndeployObject(self.ctx, self.node, self.html, self.ran)
self.cont.yamlPath[0] = 'tests/simple-fail/'
deploy = self.cont.DeployObject(self.html)
self.cont.UndeployObject(self.html, self.ran)
self.assertFalse(deploy)
self.cont.yamlPath = old
def test_deployfails_2svc(self):
# fails reliably
old = self.cont.yamlPath
self.cont.yamlPath = 'tests/simple-fail-2svc/'
deploy = self.cont.DeployObject(self.ctx, self.node, self.html)
self.cont.UndeployObject(self.ctx, self.node, self.html, self.ran)
self.cont.yamlPath[0] = 'tests/simple-fail-2svc/'
deploy = self.cont.DeployObject(self.html)
self.cont.UndeployObject(self.html, self.ran)
self.assertFalse(deploy)
self.cont.yamlPath = old
def test_deploy_ran(self):
self.cont.yamlPath = 'yaml_files/5g_rfsimulator_tdd_dora'
self.cont.services = "oai-gnb"
self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora'
self.cont.services[0] = "oai-gnb"
self.cont.deploymentTag = 'develop-12345678'
deploy = self.cont.DeployObject(self.ctx, self.node, self.html)
undeploy = self.cont.UndeployObject(self.ctx, self.node, self.html, self.ran)
deploy = self.cont.DeployObject(self.html)
undeploy = self.cont.UndeployObject(self.html, self.ran)
self.assertTrue(deploy)
self.assertTrue(undeploy)
def test_deploy_multiran(self):
self.cont.yamlPath = 'yaml_files/5g_rfsimulator_tdd_dora'
self.cont.services = "oai-gnb oai-nr-ue"
self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora'
self.cont.services[0] = "oai-gnb oai-nr-ue"
self.cont.deploymentTag = 'develop-12345678'
deploy = self.cont.DeployObject(self.ctx, self.node, self.html)
undeploy = self.cont.UndeployObject(self.ctx, self.node, self.html, self.ran)
deploy = self.cont.DeployObject(self.html)
undeploy = self.cont.UndeployObject(self.html, self.ran)
self.assertTrue(deploy)
self.assertTrue(undeploy)
def test_deploy_staged(self):
self.cont.yamlPath = 'yaml_files/5g_rfsimulator_tdd_dora'
self.cont.services = "oai-gnb"
self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora'
self.cont.services[0] = "oai-gnb"
self.cont.deploymentTag = 'develop-12345678'
deploy1 = self.cont.DeployObject(self.ctx, self.node, self.html)
self.cont.services = "oai-nr-ue"
deploy2 = self.cont.DeployObject(self.ctx, self.node, self.html)
undeploy = self.cont.UndeployObject(self.ctx, self.node, self.html, self.ran)
deploy1 = self.cont.DeployObject(self.html)
self.cont.services[0] = "oai-nr-ue"
deploy2 = self.cont.DeployObject(self.html)
undeploy = self.cont.UndeployObject(self.html, self.ran)
self.assertTrue(deploy1)
self.assertTrue(deploy2)
self.assertTrue(undeploy)
def test_create_workspace(self):
self.cont.eNBSourceCodePath = tempfile.mkdtemp()
self.cont.ranRepository = "https://gitlab.eurecom.fr/oai/openairinterface5g.git"
self.cont.ranCommitID = "05f9c975eeecbca1bdff5940affad44465f1301f"
self.cont.ranBranch = "develop"
ws = self.cont.Create_Workspace(self.node, self.html)
with cls_cmd.LocalCmd() as cmd:
cmd.run(f"rm -rf {self.cont.eNBSourceCodePath}")
self.assertTrue(ws)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,4 +1,4 @@
Sender Bitrate : 524.00 Kbps
Receiver Bitrate: 524.00 Kbps (52.40%) (too low! < 99%)
Sender Bitrate : 0.52 Mbps
Receiver Bitrate: 0.52 Mbps (too low! < 99%)
Jitter : 0.026 ms
Packet Loss : 10% (too high! > 0%)
Packet Loss : 10 % (too high! > 0%)

View File

@@ -1,4 +1,4 @@
Sender Bitrate : 0.00 bps
Receiver Bitrate: 3.98 Mbps (4.97%) (too low! < 99%)
Sender Bitrate : 0.00 Mbps
Receiver Bitrate: 3.98 Mbps (too low! < 99%)
Jitter : 3.847 ms
Packet Loss : 0.038% (too high! > 0%)
Packet Loss : 0.038 % (too high! > 0%)

View File

@@ -1,4 +1,4 @@
Sender Bitrate : 1.03 Mbps
Receiver Bitrate: 0.00 bps (0.00%) (too low! < 99%)
Sender Bitrate : 1.03 Mbps
Receiver Bitrate: 0.00 Mbps (too low! < 99%)
Jitter : 0.000 ms
Packet Loss : 0%
Packet Loss : 0 %

View File

@@ -1,4 +1,4 @@
Sender Bitrate : 996.00 Kbps
Receiver Bitrate: 996.00 Kbps (99.60%)
Sender Bitrate : 1.00 Mbps
Receiver Bitrate: 1.00 Mbps (99.60 %)
Jitter : 0.029 ms
Packet Loss : 0%
Packet Loss : 0 %

View File

@@ -1,4 +1,4 @@
Sender Bitrate : 4.67 Mbps
Receiver Bitrate: 4.67 Mbps (100.00%)
Sender Bitrate : 4.67 Mbps
Receiver Bitrate: 4.67 Mbps (100.00 %)
Jitter : 3.413 ms
Packet Loss : 0%
Packet Loss : 0 %

View File

@@ -1,64 +0,0 @@
import sys
import logging
import tempfile
logging.basicConfig(
level=logging.DEBUG,
stream=sys.stdout,
format="[%(asctime)s] %(levelname)8s: %(message)s"
)
import os
import unittest
sys.path.append('./') # to find OAI imports below
import cls_module
from cls_ci_helper import TestCaseCtx
import cls_cmd
class TestModule(unittest.TestCase):
def setUp(self):
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
def tearDown(self):
with cls_cmd.LocalCmd() as c:
c.run(f'rm -rf {self.ctx.logPath}')
def test_simple_module(self):
c = cls_module.Module_UE("test", filename="tests/config/test_module_infra.yaml")
self.assertFalse(c.trace)
success = c.initialize()
self.assertTrue(success)
ip = c.attach()
self.assertEqual(ip, "127.0.0.1")
self.assertTrue(c.checkMTU())
c.detach()
logs = c.terminate()
self.assertEqual(logs, None) # no tracing
@unittest.skip("this test takes long: it verifies the UE cannot attach")
def test_simple_fail(self):
c = cls_module.Module_UE("test-fail", filename="tests/config/test_module_infra.yaml")
success = c.initialize()
self.assertTrue(success)
ip = c.attach()
self.assertEqual(ip, None)
self.assertFalse(c.checkMTU())
c.detach()
logs = c.terminate()
self.assertEqual(logs, None) # no tracing
def test_simple_trace(self):
c = cls_module.Module_UE("test-trace", filename="tests/config/test_module_infra.yaml")
self.assertTrue(c.trace)
success = c.initialize()
self.assertTrue(success)
ip = c.attach()
self.assertEqual(ip, "127.0.0.1")
self.assertTrue(c.checkMTU())
c.detach()
tmp = tempfile.mkdtemp()
log_file = c.terminate(self.ctx)
# undeploy uses archiveArtifact(), which writes to {prefix}-logs
self.assertEqual(log_file, [f"{self.ctx.baseFilename()}-tttf"]) # matches test-trace UE collection file
if __name__ == '__main__':
unittest.main()

View File

@@ -9,13 +9,11 @@ import os
os.system(f'rm -rf cmake_targets')
os.system(f'mkdir -p cmake_targets/log')
import unittest
import tempfile
sys.path.append('./') # to find OAI imports below
import cls_oai_html
from cls_ci_helper import TestCaseCtx
import cls_oaicitest
import cls_cmd
import cls_containerize
class TestPingIperf(unittest.TestCase):
def setUp(self):
@@ -24,10 +22,7 @@ class TestPingIperf(unittest.TestCase):
self.ci = cls_oaicitest.OaiCiTest()
self.ci.ue_ids = ["test"]
self.ci.nodes = ["localhost"]
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
def tearDown(self):
with cls_cmd.LocalCmd() as c:
c.run(f'rm -rf {self.ctx.logPath}')
self.cont = cls_containerize.Containerize()
def test_ping(self):
self.ci.ping_args = "-c3"
@@ -35,7 +30,7 @@ class TestPingIperf(unittest.TestCase):
self.ci.svr_id = "test"
infra_file = "tests/config/infra_ping_iperf.yaml"
# TODO Should need nothing but options and UE(s) to use
success = self.ci.Ping(self.ctx, self.html, infra_file=infra_file)
success = self.ci.Ping(self.html, self.cont, infra_file=infra_file)
self.assertTrue(success)
def test_iperf(self):
@@ -50,7 +45,7 @@ class TestPingIperf(unittest.TestCase):
self.ci.iperf_profile = "balanced"
infra_file = "tests/config/infra_ping_iperf.yaml"
# TODO Should need nothing but options and UE(s) to use
success = self.ci.Iperf(self.ctx, self.html, infra_file=infra_file)
success = self.ci.Iperf(self.html, self.cont, infra_file=infra_file)
self.assertTrue(success)
def test_iperf2_unidir(self):
@@ -62,22 +57,7 @@ class TestPingIperf(unittest.TestCase):
self.ci.iperf_profile = "balanced"
infra_file = "tests/config/infra_ping_iperf.yaml"
# TODO Should need nothing but options and UE(s) to use
success = self.ci.Iperf2_Unidir(self.ctx, self.html, infra_file=infra_file)
self.assertTrue(success)
def test_iperf_highrate(self):
# note: needs to be five seconds because Iperf() adds -O 3, so if it is
# too short, the server is terminated before the client loaded
# everything
self.ci.iperf_args = "-u -t 5 -b 1000M -R -O 0"
self.ci.svr_id = "test"
self.ci.svr_node = "localhost"
self.ci.iperf_packetloss_threshold = "0"
self.ci.iperf_bitrate_threshold = "0"
self.ci.iperf_profile = "balanced"
infra_file = "tests/config/infra_ping_iperf.yaml"
# TODO Should need nothing but options and UE(s) to use
success = self.ci.Iperf(self.ctx, self.html, infra_file=infra_file)
success = self.ci.Iperf2_Unidir(self.html, self.cont, infra_file=infra_file)
self.assertTrue(success)
if __name__ == '__main__':

View File

@@ -20,6 +20,7 @@ class TestDeploymentMethods(unittest.TestCase):
self.html = cls_oai_html.HTMLManagement()
self.html.testCaseId = "000000"
self.cont = cls_containerize.Containerize()
self.cont.eNBIPAddress = 'localhost'
self.cont.eNBSourceCodePath = os.getcwd()
def test_pull_clean_local_reg(self):
@@ -31,21 +32,21 @@ class TestDeploymentMethods(unittest.TestCase):
ret = cmd.run(f"ping -c1 -w1 {registry}")
if ret.returncode != 0: # could not ping once -> skip test
self.skipTest(f"test_pull_clean_local_reg: could not reach {registry} (run inside sboai)")
node = 'localhost'
svr_id = '0'
images = ["oai-gnb"]
tag = "develop"
pull = self.cont.Pull_Image_from_Registry(self.html, node, images, tag=tag)
clean = self.cont.Clean_Test_Server_Images(self.html, node, images, tag=tag)
pull = self.cont.Pull_Image_from_Registry(self.html, svr_id, images, tag=tag)
clean = self.cont.Clean_Test_Server_Images(self.html, svr_id, images, tag=tag)
self.assertTrue(pull)
self.assertTrue(clean)
def test_pull_clean_docker_hub(self):
node = 'localhost'
svr_id = '0'
r = "docker.io"
images = ["hello-world"]
tag = "latest"
pull = self.cont.Pull_Image_from_Registry(self.html, node, images, tag=tag, registry=r, username=None, password=None)
clean = self.cont.Clean_Test_Server_Images(self.html, node, images, tag=tag)
pull = self.cont.Pull_Image_from_Registry(self.html, svr_id, images, tag=tag, registry=r, username=None, password=None)
clean = self.cont.Clean_Test_Server_Images(self.html, svr_id, images, tag=tag)
self.assertTrue(pull)
self.assertTrue(clean)

View File

@@ -22,6 +22,9 @@ python3 main.py \
--ranCommitID=${commit} \
--ranAllowMerge=true \
--ranTargetBranch=develop \
--eNBIPAddress=localhost \
--eNBUserName=NONE \
--eNBPassword=NONE \
--eNBSourceCodePath=NONE \
--XMLTestFile=tests/test-runner/test.xml

View File

@@ -32,12 +32,10 @@
<testCase id="000002">
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<node>poseidon</node>
</testCase>
<testCase id="000001">
<class>Build_Cluster_Image</class>
<desc>Build Images on OpenShift Cluster</desc>
<node>poseidon</node>
</testCase>
</testCaseList>

View File

@@ -0,0 +1,47 @@
<!--
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
-->
<testCaseList>
<htmlTabRef>l2sim-4glte-proxy-build</htmlTabRef>
<htmlTabName>Build L2sim proxy image</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<TestCaseRequestedList>
100001
000001
</TestCaseRequestedList>
<TestCaseExclusionList></TestCaseExclusionList>
<testCase id="100001">
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<eNB_instance>1</eNB_instance>
<eNB_serverId>1</eNB_serverId>
</testCase>
<testCase id="000001">
<class>Build_Proxy</class>
<desc>Build L2sim Proxy Image</desc>
<eNB_instance>1</eNB_instance>
<eNB_serverId>1</eNB_serverId>
<forced_workspace_cleanup>True</forced_workspace_cleanup>
<proxy_commit>b64d9bce986b38ca59e8582864ade3fcdd05c0dc</proxy_commit>
</testCase>
</testCaseList>

Some files were not shown because too many files have changed in this diff Show More