#/* # * 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(' Test Results for TEMPLATE_JOB_NAME job build #TEMPLATE_BUILD_ID\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(' \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(' \n') self.htmlFile.write(' Job Summary -- Job: TEMPLATE_JOB_NAME -- Build-ID: TEMPLATE_BUILD_ID\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(' \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(' \n') self.htmlFile.write(' \n') if (self.ranAllowMerge): self.htmlFile.write(' \n') else: self.htmlFile.write(' \n') self.htmlFile.write(' \n') self.htmlFile.write(' \n') if (self.ranAllowMerge): self.htmlFile.write(' \n') else: self.htmlFile.write(' \n') self.htmlFile.write(' \n') self.htmlFile.write(' \n') self.htmlFile.write(' \n') if (self.ranAllowMerge): self.htmlFile.write(' \n') else: self.htmlFile.write(' \n') self.htmlFile.write(' \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(' \n') else: self.htmlFile.write(' \n') self.htmlFile.write(' \n') self.htmlFile.write(' \n') if (self.ranAllowMerge): self.htmlFile.write(' \n') self.htmlFile.write(' \n') if (self.ranTargetBranch == ''): self.htmlFile.write(' \n') else: self.htmlFile.write(' \n') self.htmlFile.write(' \n') self.htmlFile.write('
Build Start Time (UTC) TEMPLATE_BUILD_TIME
GIT Repository ' + self.ranRepository + '
Job Trigger Merge-RequestPush to Branch
Source Branch Branch' + self.ranBranch + '
Source Commit ID Commit ID ' + self.ranCommitID + '
Source Commit Message Commit Message ' + commit_message + '
Target Branch develop' + self.ranTargetBranch + '
\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(' \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() 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(' \n') if passStatus: self.htmlFile.write(' \n') else: self.htmlFile.write(' \n') self.htmlFile.write(' \n') self.htmlFile.write('
Relative Time (s)Test IdTest DescTest OptionsTest StatusInfo
Final Tab StatusPASS FAIL
\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(' \n') if passStatus: self.htmlFile.write(' \n') else: self.htmlFile.write(' \n') self.htmlFile.write(' \n') self.htmlFile.write('
Final StatusPASS FAIL
\n') self.htmlFile.write('

\n') self.htmlFile.write('
End of Test Report -- Copyright 2018 OpenAirInterface. All Rights Reserved.
\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()