#/*
# * 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 re # reg
import fileinput
import logging
import os
import time
import subprocess
import constants as CONST
#-----------------------------------------------------------
# Class Declaration
#-----------------------------------------------------------
class HTMLManagement():
def __init__(self):
self.htmlFile = ''
self.htmlHeaderCreated = False
self.htmlFooterCreated = False
self.ranRepository = ''
self.ranBranch = ''
self.ranCommitID = ''
self.ranAllowMerge = False
self.ranTargetBranch = ''
self.nbTestXMLfiles = 0
self.htmlTabRefs = []
self.htmlTabNames = []
self.htmlTabIcons = []
self.testXMLfiles = []
self.htmleNBFailureMsg = ''
self.htmlUEFailureMsg = ''
self.startTime = int(round(time.time() * 1000))
self.testCase_id = ''
self.desc = ''
self.OsVersion = ['', '']
self.KernelVersion = ['', '']
self.UhdVersion = ['', '']
self.UsrpBoard = ['', '']
self.CpuNb = ['', '']
self.CpuModel = ['', '']
self.CpuMHz = ['', '']
#-----------------------------------------------------------
# HTML structure creation functions
#-----------------------------------------------------------
def CreateHtmlHeader(self):
if (not self.htmlHeaderCreated):
logging.info('\u001B[1m----------------------------------------\u001B[0m')
logging.info('\u001B[1m Creating HTML header \u001B[0m')
logging.info('\u001B[1m----------------------------------------\u001B[0m')
self.htmlFile = open('test_results.html', 'w')
self.htmlFile.write('\n')
self.htmlFile.write('\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
TEMPLATE_STAGE_NAME
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' Build Start Time (UTC) | \n')
self.htmlFile.write(' TEMPLATE_BUILD_TIME | \n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' GIT Repository | \n')
self.htmlFile.write(' ' + self.ranRepository + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' Job Trigger | \n')
if (self.ranAllowMerge):
self.htmlFile.write(' Merge-Request | \n')
else:
self.htmlFile.write(' Push to Branch | \n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
if (self.ranAllowMerge):
self.htmlFile.write(' Source Branch | \n')
else:
self.htmlFile.write(' Branch | \n')
self.htmlFile.write(' ' + self.ranBranch + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
if (self.ranAllowMerge):
self.htmlFile.write(' Source Commit ID | \n')
else:
self.htmlFile.write(' Commit ID | \n')
self.htmlFile.write(' ' + self.ranCommitID + ' | \n')
self.htmlFile.write('
\n')
if self.ranAllowMerge != '' and self.ranCommitID != 'develop':
commit_message = subprocess.check_output("git log -n1 --pretty=format:\"%s\" " + self.ranCommitID, shell=True, universal_newlines=True)
commit_message = commit_message.strip()
self.htmlFile.write(' \n')
if (self.ranAllowMerge):
self.htmlFile.write(' Source Commit Message | \n')
else:
self.htmlFile.write(' Commit Message | \n')
self.htmlFile.write(' ' + commit_message + ' | \n')
self.htmlFile.write('
\n')
if (self.ranAllowMerge):
self.htmlFile.write(' \n')
self.htmlFile.write(' Target Branch | \n')
if (self.ranTargetBranch == ''):
self.htmlFile.write(' develop | \n')
else:
self.htmlFile.write(' ' + self.ranTargetBranch + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.close()
def CreateHtmlTabHeader(self):
if (not self.htmlHeaderCreated):
if (not os.path.isfile('test_results.html')):
self.CreateHtmlHeader('none')
self.htmlFile = open('test_results.html', 'a')
if (self.nbTestXMLfiles == 1):
self.htmlFile.write('
\n')
self.htmlFile.write('
Test Summary for ' + self.testXMLfiles[0] + '
\n')
else:
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' Relative Time (s) | \n')
self.htmlFile.write(' Test Id | \n')
self.htmlFile.write(' Test Desc | \n')
self.htmlFile.write(' Test Options | \n')
self.htmlFile.write(' Test Status | \n')
self.htmlFile.write(' Info | \n')
self.htmlFile.write('
\n')
self.htmlFile.close()
self.htmlHeaderCreated = True
def CreateHtmlTabFooter(self, passStatus):
if ((not self.htmlFooterCreated) and (self.htmlHeaderCreated)):
self.htmlFile = open('test_results.html', 'a')
self.htmlFile.write(' \n')
self.htmlFile.write(' Final Tab Status | \n')
if passStatus:
self.htmlFile.write(' PASS | \n')
else:
self.htmlFile.write(' FAIL | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.close()
time.sleep(1)
if passStatus:
cmd = "sed -i -e 's/__STATE_" + self.htmlTabNames[0] + "__//' test_results.html"
subprocess.run(cmd, shell=True)
else:
cmd = "sed -i -e 's/__STATE_" + self.htmlTabNames[0] + "__/
<\/span>/' test_results.html"
subprocess.run(cmd, shell=True)
self.htmlFooterCreated = False
def CreateHtmlFooter(self, passStatus):
if (os.path.isfile('test_results.html')):
# Tagging the 1st tab as active so it is automatically opened.
firstTabFound = False
for line in fileinput.FileInput("test_results.html", inplace=1):
if re.search('tab-pane fade', line) and not firstTabFound:
firstTabFound = True
print(line.replace('tab-pane fade', 'tab-pane fade in active'), end ='')
else:
print(line, end ='')
self.htmlFile = open('test_results.html', 'a')
self.htmlFile.write('\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' \n')
self.htmlFile.write(' Final Status | \n')
if passStatus:
self.htmlFile.write(' PASS | \n')
else:
self.htmlFile.write(' FAIL | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write('\n')
self.htmlFile.close()
def CreateHtmlTestRow(self, options, status, processesStatus, machine='eNB'):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
self.htmlFile = open('test_results.html', 'a')
currentTime = int(round(time.time() * 1000)) - self.startTime
self.htmlFile.write('
\n')
self.htmlFile.write(' ' + format(currentTime / 1000, '.1f') + ' | \n')
self.htmlFile.write(' ' + self.testCase_id + ' | \n')
self.htmlFile.write(' ' + self.desc + ' | \n')
self.htmlFile.write(' ' + str(options) + ' | \n')
if (str(status) == 'OK'):
self.htmlFile.write(' ' + str(status) + ' | \n')
elif (str(status) == 'KO'):
if (processesStatus == 0):
self.htmlFile.write(' ' + str(status) + ' | \n')
elif (processesStatus == CONST.ENB_PROCESS_FAILED):
self.htmlFile.write(' KO - eNB process not found | \n')
elif (processesStatus == CONST.OAI_UE_PROCESS_FAILED):
self.htmlFile.write(' KO - OAI UE process not found | \n')
elif (processesStatus == CONST.ENB_PROCESS_SEG_FAULT) or (processesStatus == CONST.OAI_UE_PROCESS_SEG_FAULT):
self.htmlFile.write(' KO - ' + machine + ' process ended in Segmentation Fault | \n')
elif (processesStatus == CONST.ENB_PROCESS_ASSERTION) or (processesStatus == CONST.OAI_UE_PROCESS_ASSERTION):
self.htmlFile.write(' KO - ' + machine + ' process ended in Assertion | \n')
elif (processesStatus == CONST.ENB_PROCESS_REALTIME_ISSUE):
self.htmlFile.write(' KO - ' + machine + ' process faced Real Time issue(s) | \n')
elif (processesStatus == CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE) or (processesStatus == CONST.OAI_UE_PROCESS_NOLOGFILE_TO_ANALYZE):
self.htmlFile.write(' OK? | \n')
elif (processesStatus == CONST.ENB_PROCESS_SLAVE_RRU_NOT_SYNCED):
self.htmlFile.write(' KO - ' + machine + ' Slave RRU could not synch | \n')
elif (processesStatus == CONST.OAI_UE_PROCESS_COULD_NOT_SYNC):
self.htmlFile.write(' KO - UE could not sync | \n')
elif (processesStatus == CONST.HSS_PROCESS_FAILED):
self.htmlFile.write(' KO - HSS process not found | \n')
elif (processesStatus == CONST.MME_PROCESS_FAILED):
self.htmlFile.write(' KO - MME process not found | \n')
elif (processesStatus == CONST.SPGW_PROCESS_FAILED):
self.htmlFile.write(' KO - SPGW process not found | \n')
elif (processesStatus == CONST.UE_IP_ADDRESS_ISSUE):
self.htmlFile.write(' KO - Could not retrieve UE IP address | \n')
elif (processesStatus == CONST.PHYSIM_IMAGE_ABSENT):
self.htmlFile.write(' KO - No such image oai-physim | \n')
elif (processesStatus == CONST.OC_LOGIN_FAIL):
self.htmlFile.write(' KO - Could not log onto cluster | \n')
elif (processesStatus == CONST.OC_PROJECT_FAIL):
self.htmlFile.write(' KO - Could not register into cluster project | \n')
elif (processesStatus == CONST.OC_IS_FAIL):
self.htmlFile.write(' KO - Could not create Image Stream | \n')
elif (processesStatus == CONST.OC_PHYSIM_DEPLOY_FAIL):
self.htmlFile.write(' KO - Could not properly deploy physim on cluster | \n')
else:
self.htmlFile.write(' ' + str(status) + ' | \n')
else:
self.htmlFile.write(' ' + str(status) + ' | \n')
if (len(str(self.htmleNBFailureMsg)) > 2):
cellBgColor = 'white'
result = re.search('ended with|faced real time issues', self.htmleNBFailureMsg)
if result is not None:
cellBgColor = 'red'
else:
result = re.search('showed|Reestablishment|Could not copy eNB logfile', self.htmleNBFailureMsg)
if result is not None:
cellBgColor = 'orange'
self.htmlFile.write(' ' + self.htmleNBFailureMsg + ' | \n')
self.htmleNBFailureMsg = ''
elif (len(str(self.htmlUEFailureMsg)) > 2):
cellBgColor = 'white'
result = re.search('ended with|faced real time issues', self.htmlUEFailureMsg)
if result is not None:
cellBgColor = 'red'
else:
result = re.search('showed|Could not copy UE logfile|oaitun_ue1 interface is either NOT mounted or NOT configured', self.htmlUEFailureMsg)
if result is not None:
cellBgColor = 'orange'
self.htmlFile.write(' ' + self.htmlUEFailureMsg + ' | \n')
self.htmlUEFailureMsg = ''
else:
self.htmlFile.write(' - | \n')
self.htmlFile.write('
\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('
\n')
self.htmlFile.write(' ----IMAGES BUILDING FAILED - Unable to recover the image logs ---- | \n')
self.htmlFile.write('
\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('
\n')
elif allImagesSize[image].count('Build Failed') > 0:
self.htmlFile.write('
\n')
else:
self.htmlFile.write('
\n')
self.htmlFile.write(' ---- ' + image + ' IMAGE STATUS ----> Size ' + allImagesSize[image] + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' Element | \n')
self.htmlFile.write(' Nb Errors | \n')
self.htmlFile.write(' Nb Warnings | \n')
self.htmlFile.write(' Status | \n')
self.htmlFile.write('
\n')
for fil in files:
parameters = files[fil]
# TestRow for image logs on built shared and target images
self.htmlFile.write('
\n')
self.htmlFile.write(' ' + fil + ' | \n')
if (parameters['errors'] == 0):
self.htmlFile.write(' ' + str(parameters['errors']) + ' | \n')
else:
self.htmlFile.write(' ' + str(parameters['errors']) + ' | \n')
if (parameters['errors'] > 0):
self.htmlFile.write(' ' + str(parameters['warnings']) + ' | \n')
elif (parameters['warnings'] == 0):
self.htmlFile.write(' ' + str(parameters['warnings']) + ' | \n')
else:
self.htmlFile.write(' ' + str(parameters['warnings']) + ' | \n')
if (parameters['errors'] == 0) and (parameters['warnings'] == 0):
self.htmlFile.write(' OK | \n')
elif (parameters['errors'] == 0):
self.htmlFile.write(' OK | \n')
else:
self.htmlFile.write(' NOT OK | \n')
self.htmlFile.write('
\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)):
return
self.htmlFile = open('test_results.html', 'a')
# TabHeader
self.htmlFile.write('
\n')
self.htmlFile.write(' ---- ' + DataLog['Title'] + ' ---- | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' '+ DataLog['ColNames'][0] +' | \n')
self.htmlFile.write(' ' + DataLog['ColNames'][1] + ' | \n')
self.htmlFile.write(' '+ DataLog['ColNames'][2] +' | \n')
self.htmlFile.write('
\n')
for k in DataLog['Data']:
# TestRow
avg = DataLog['Data'][k][0]
maxval = DataLog['Data'][k][1]
count = DataLog['Data'][k][2]
valnorm = float(DataLog['Data'][k][3])
dev = DataLog['DeviationThreshold'][k]
ref = DataLog['Ref'][k]
self.htmlFile.write('
\n')
self.htmlFile.write(' ' + k + ' | \n')
self.htmlFile.write(f' {avg}; {maxval}; {count} | \n')
if valnorm > 1.0 + dev or valnorm < 1.0 - dev:
self.htmlFile.write(f' {valnorm} (Avg over Ref = {avg} over {ref}; max allowed deviation = {dev}) | \n')
else:
self.htmlFile.write(f' {valnorm} (Avg over Ref = {avg} over {ref}; max allowed deviation = {dev}) | \n')
self.htmlFile.write('
\n')
self.htmlFile.close()
def CreateHtmlTestRowQueue(self, options, status, infoList):
if ((not self.htmlFooterCreated) and (self.htmlHeaderCreated)):
self.htmlFile = open('test_results.html', 'a')
currentTime = int(round(time.time() * 1000)) - self.startTime
addOrangeBK = False
self.htmlFile.write('
\n')
self.htmlFile.write(' ' + format(currentTime / 1000, '.1f') + ' | \n')
self.htmlFile.write(' ' + self.testCase_id + ' | \n')
self.htmlFile.write(' ' + self.desc + ' | \n')
self.htmlFile.write(' ' + str(options) + ' | \n')
if (str(status) == 'OK'):
self.htmlFile.write(f' {status} | \n')
elif (str(status) == 'KO'):
self.htmlFile.write(f' {status} | \n')
elif str(status) == 'SKIP':
self.htmlFile.write(f' {status} | \n')
else:
addOrangeBK = True
self.htmlFile.write(f' {status} | \n')
if (addOrangeBK):
self.htmlFile.write(' ')
else:
self.htmlFile.write(' | ')
for i in infoList: # add custom style to have elements side-by-side to reduce need for vertical space
self.htmlFile.write(f' {i} ')
self.htmlFile.write(' | ')
self.htmlFile.write('
\n')
self.htmlFile.close()
def CreateHtmlTestRowCppCheckResults(self, CCR):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
self.htmlFile = open('test_results.html', 'a')
vId = 0
for version in CCR.versions:
self.htmlFile.write('
\n')
self.htmlFile.write(' Results for cppcheck v ' + version + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' NB ERRORS | \n')
if CCR.nbErrors[vId] == 0:
myColor = 'lightgreen'
elif CCR.nbErrors[vId] < 20:
myColor = 'orange'
else:
myColor = 'lightcoral'
self.htmlFile.write(' ' + str(CCR.nbErrors[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' NB WARNINGS | \n')
if CCR.nbWarnings[vId] == 0:
myColor = 'lightgreen'
elif CCR.nbWarnings[vId] < 20:
myColor = 'orange'
else:
myColor = 'lightcoral'
self.htmlFile.write(' ' + str(CCR.nbWarnings[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' ----------------- | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Memory leak | \n')
self.htmlFile.write(' ' + str(CCR.nbMemLeaks[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Possible null pointer deference | \n')
self.htmlFile.write(' ' + str(CCR.nbNullPtrs[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Uninitialized variable | \n')
self.htmlFile.write(' ' + str(CCR.nbUninitVars[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Undefined behaviour shifting | \n')
self.htmlFile.write(' ' + str(CCR.nbTooManyBitsShift[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Signed integer overflow | \n')
self.htmlFile.write(' ' + str(CCR.nbIntegerOverflow[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Printf formatting issues | \n')
self.htmlFile.write(' ' + str(CCR.nbInvalidPrintf[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Modulo result is predetermined | \n')
self.htmlFile.write(' ' + str(CCR.nbModuloAlways[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Opposite Condition -> dead code | \n')
self.htmlFile.write(' ' + str(CCR.nbOppoInnerCondition[vId]) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' | \n')
self.htmlFile.write(' Wrong Scanf Nb Args | \n')
self.htmlFile.write(' ' + str(CCR.nbWrongScanfArg[vId]) + ' | \n')
self.htmlFile.write('
\n')
vId += 1
def CreateHtmlTestRowPhySimTestResult(self, testSummary, testResult):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
self.htmlFile = open('test_results.html', 'a')
if bool(testResult) == False and bool(testSummary) == False:
self.htmlFile.write('
\n')
self.htmlFile.write(' ----PHYSIM TESTING FAILED - Unable to recover the test logs ---- | \n')
self.htmlFile.write('
\n')
else:
# Tab header
self.htmlFile.write('
\n')
self.htmlFile.write(' ---- PHYSIM TEST SUMMARY---- | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' LogFile Name | \n')
self.htmlFile.write(' Nb Tests | \n')
self.htmlFile.write(' Nb Failure | \n')
self.htmlFile.write(' Nb Pass | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' physim_test.txt | \n')
self.htmlFile.write(' ' + str(testSummary['Nbtests']) + ' | \n')
if testSummary['Nbfail'] == 0:
self.htmlFile.write(' ' + str(testSummary['Nbfail']) + ' | \n')
else:
self.htmlFile.write(' ' + str(testSummary['Nbfail']) + ' | \n')
self.htmlFile.write(' ' + str(testSummary['Nbpass']) + ' | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' ---- PHYSIM TEST DETAIL INFO---- | \n')
self.htmlFile.write('
\n')
self.htmlFile.write('
\n')
self.htmlFile.write(' Test Name | \n')
self.htmlFile.write(' Test Description | \n')
self.htmlFile.write(' Result | \n')
self.htmlFile.write('
\n')
y = ''
for key, value in testResult.items():
x = key.split(".")
if x[0] != y:
self.htmlFile.write('
\n')
self.htmlFile.write(' "' + x[0] + '" series | \n')
self.htmlFile.write('
\n')
y = x[0]
self.htmlFile.write('
\n')
self.htmlFile.write(' ' + key + ' | \n')
self.htmlFile.write(' ' + value[0] + ' | \n')
if 'PASS' in value:
self.htmlFile.write(' ' + value[1] + ' | \n')
else:
self.htmlFile.write(' ' + value[1] + ' | \n')
self.htmlFile.close()