CI: Add functions for OC Physim deployment and analysis
This commit is contained in:
@@ -20,8 +20,11 @@
|
|||||||
# */
|
# */
|
||||||
#---------------------------------------------------------------------
|
#---------------------------------------------------------------------
|
||||||
|
|
||||||
import logging
|
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import json
|
||||||
|
|
||||||
# Define the mapping of physim_test values to search patterns
|
# Define the mapping of physim_test values to search patterns
|
||||||
PHYSIM_PATTERN_MAPPING = {
|
PHYSIM_PATTERN_MAPPING = {
|
||||||
@@ -80,3 +83,65 @@ class Analysis():
|
|||||||
msg = f'{time1_match.group(1)}: {time1_match.group(2)} us\n{time2_match.group(1)}: {time2_match.group(2)} us exceeds a limit of {threshold} us'
|
msg = f'{time1_match.group(1)}: {time1_match.group(2)} us\n{time2_match.group(1)}: {time2_match.group(2)} us exceeds a limit of {threshold} us'
|
||||||
|
|
||||||
return success,msg
|
return success,msg
|
||||||
|
|
||||||
|
def analyze_oc_physim(result_junit, details_json):
|
||||||
|
try:
|
||||||
|
tree = ET.parse(result_junit)
|
||||||
|
root = tree.getroot()
|
||||||
|
nb_tests = int(root.attrib["tests"])
|
||||||
|
nb_failed = int(root.attrib["failures"])
|
||||||
|
except ET.ParseError as e:
|
||||||
|
return False, False, f'Could not parse XML log file {result_junit}: {e}'
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
return False, False, f'JUnit XML log file {result_junit} not found: {e}'
|
||||||
|
except Exception as e:
|
||||||
|
return False, False, f'While parsing JUnit XML log file: exception: {e}'
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(details_json) as f:
|
||||||
|
j = json.load(f)
|
||||||
|
# prepare JSON for easier access of strings
|
||||||
|
json_test_desc = {}
|
||||||
|
for e in j["tests"]:
|
||||||
|
json_test_desc[e["name"]] = e
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return False, False, f'Could not decode JSON log file {details_json}: {e}'
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
return False, False, f'Physim JSON log file {details_json} not found: {e}'
|
||||||
|
except Exception as e:
|
||||||
|
return False, False, f'While parsing physim JSON log file: exception: {e}'
|
||||||
|
|
||||||
|
test_result = {}
|
||||||
|
for test in root: # for each test
|
||||||
|
test_name = test.attrib["name"]
|
||||||
|
test_exec = json_test_desc[test_name]["properties"][1]["value"][0]
|
||||||
|
desc = json_test_desc[test_name]["properties"][1]["value"][1]
|
||||||
|
# get runtime and checks
|
||||||
|
test_check = test.attrib["status"] == "run"
|
||||||
|
time = round(float(test.attrib["time"]), 1)
|
||||||
|
time_check = time < 150
|
||||||
|
output = test.findtext("system-out")
|
||||||
|
output_check = "exceeds the threshold" not in output
|
||||||
|
# collect logs
|
||||||
|
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)
|
||||||
|
# prepare result and info
|
||||||
|
info = f"Runtime: {f'{time:.3f}'[:5]} s"
|
||||||
|
resultstr = 'PASS' if (test_check and time_check and output_check) else 'FAIL'
|
||||||
|
if test_check:
|
||||||
|
if not output_check:
|
||||||
|
info += " Test log exceeds maximal allowed length 100 kB"
|
||||||
|
if not time_check:
|
||||||
|
info += " Test exceeds 150s"
|
||||||
|
if not (time_check and output_check):
|
||||||
|
nb_failed += 1 # time threshold/output length error, not counted for by ctest as of now
|
||||||
|
test_result[test_name] = [desc, info, resultstr]
|
||||||
|
|
||||||
|
test_summary = {}
|
||||||
|
test_summary['Nbtests'] = nb_tests
|
||||||
|
test_summary['Nbpass'] = nb_tests - nb_failed
|
||||||
|
test_summary['Nbfail'] = nb_failed
|
||||||
|
return nb_failed == 0, test_summary, test_result
|
||||||
|
|||||||
@@ -31,8 +31,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
import cls_oai_html
|
import cls_oai_html
|
||||||
|
import cls_analysis
|
||||||
import constants as CONST
|
import constants as CONST
|
||||||
import helpreadme as HELP
|
import helpreadme as HELP
|
||||||
import cls_containerize
|
import cls_containerize
|
||||||
@@ -457,3 +459,38 @@ class Cluster:
|
|||||||
HTML.CreateHtmlNextTabHeaderTestRow(collectInfo, imageSize)
|
HTML.CreateHtmlNextTabHeaderTestRow(collectInfo, imageSize)
|
||||||
|
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
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: {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(svr_id, script, 600, options)
|
||||||
|
logging.debug(f'"{script}" finished with code {ret.returncode}, output:\n{ret.stdout}')
|
||||||
|
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.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:
|
||||||
|
HTML.CreateHtmlTestRowQueue('Physical simulator failed', 'KO', [test_result])
|
||||||
|
logging.error('\u001B[1m Physical Simulator Fail\u001B[0m')
|
||||||
|
return test_status
|
||||||
|
|||||||
+12
-10
@@ -564,13 +564,13 @@ class HTMLManagement():
|
|||||||
self.htmlFile.write(' <th>Nb Pass</th>\n')
|
self.htmlFile.write(' <th>Nb Pass</th>\n')
|
||||||
self.htmlFile.write(' </tr>\n')
|
self.htmlFile.write(' </tr>\n')
|
||||||
self.htmlFile.write(' <tr>\n')
|
self.htmlFile.write(' <tr>\n')
|
||||||
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" > physim_test.txt </td>\n')
|
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" > physim_log.txt </td>\n')
|
||||||
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" >' + str(testSummary['Nbtests']) + ' </td>\n')
|
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" >' + str(testSummary['Nbtests']) + ' </td>\n')
|
||||||
if testSummary['Nbfail'] == 0:
|
if testSummary['Nbfail'] == 0:
|
||||||
self.htmlFile.write(' <td bgcolor = "lightcyan" >' + str(testSummary['Nbfail']) + ' </td>\n')
|
self.htmlFile.write(' <td bgcolor = "lightcyan" >' + str(testSummary['Nbfail']) + '</td>\n')
|
||||||
else:
|
else:
|
||||||
self.htmlFile.write(' <td bgcolor = "red" >' + str(testSummary['Nbfail']) + ' </td>\n')
|
self.htmlFile.write(' <td bgcolor = "red" ><font color="white">' + str(testSummary['Nbfail']) + '</font></td>\n')
|
||||||
self.htmlFile.write(' <td gcolor = "lightcyan" >' + str(testSummary['Nbpass']) + ' </td>\n')
|
self.htmlFile.write(' <td bgcolor = "lightcyan" >' + str(testSummary['Nbpass']) + ' </td>\n')
|
||||||
self.htmlFile.write(' </tr>\n')
|
self.htmlFile.write(' </tr>\n')
|
||||||
self.htmlFile.write(' <tr bgcolor = "#F0F0F0" >\n')
|
self.htmlFile.write(' <tr bgcolor = "#F0F0F0" >\n')
|
||||||
self.htmlFile.write(' <td colspan="6"><b> ---- PHYSIM TEST DETAIL INFO---- </b></td>\n')
|
self.htmlFile.write(' <td colspan="6"><b> ---- PHYSIM TEST DETAIL INFO---- </b></td>\n')
|
||||||
@@ -578,22 +578,24 @@ class HTMLManagement():
|
|||||||
self.htmlFile.write(' <tr bgcolor = "#33CCFF" >\n')
|
self.htmlFile.write(' <tr bgcolor = "#33CCFF" >\n')
|
||||||
self.htmlFile.write(' <th colspan="2">Test Name</th>\n')
|
self.htmlFile.write(' <th colspan="2">Test Name</th>\n')
|
||||||
self.htmlFile.write(' <th colspan="2">Test Description</th>\n')
|
self.htmlFile.write(' <th colspan="2">Test Description</th>\n')
|
||||||
self.htmlFile.write(' <th colspan="2">Result</th>\n')
|
self.htmlFile.write(' <th>Test Status</th>\n')
|
||||||
|
self.htmlFile.write(' <th>Info</th>\n')
|
||||||
self.htmlFile.write(' </tr>\n')
|
self.htmlFile.write(' </tr>\n')
|
||||||
y = ''
|
y = ''
|
||||||
for key, value in testResult.items():
|
for key, value in testResult.items():
|
||||||
x = key.split(".")
|
x = key.split(".")
|
||||||
if x[0] != y:
|
if x[2] != y:
|
||||||
self.htmlFile.write(' <tr bgcolor = "lightgreen" >\n')
|
self.htmlFile.write(' <tr bgcolor = "lightgreen" >\n')
|
||||||
self.htmlFile.write(' <td style="text-align: center;" colspan="6"><b>"' + x[0] + '" series </b></td>\n')
|
self.htmlFile.write(' <td style="text-align: center;" colspan="6"><b>"' + x[2] + '" series </b></td>\n')
|
||||||
self.htmlFile.write(' </tr>\n')
|
self.htmlFile.write(' </tr>\n')
|
||||||
y = x[0]
|
y = x[2]
|
||||||
self.htmlFile.write(' <tr>\n')
|
self.htmlFile.write(' <tr>\n')
|
||||||
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" >' + key + ' </td>\n')
|
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" >' + key + ' </td>\n')
|
||||||
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" >' + value[0] + '</td>\n')
|
self.htmlFile.write(' <td colspan="2" bgcolor = "lightcyan" >' + value[0] + '</td>\n')
|
||||||
if 'PASS' in value:
|
if 'PASS' in value:
|
||||||
self.htmlFile.write(' <td colspan="2" bgcolor = "green" >' + value[1] + '</td>\n')
|
self.htmlFile.write(' <td bgcolor = "green" ><font color="white"><b>' + value[2] + '</b></font></td>\n')
|
||||||
else:
|
else:
|
||||||
self.htmlFile.write(' <td colspan="2" bgcolor = "red" >' + value[1] + '</td>\n')
|
self.htmlFile.write(' <td bgcolor = "red" ><font color="white"><b>' + value[2] + '</b></font></td>\n')
|
||||||
|
self.htmlFile.write(' <td bgcolor = "lightcyan">' + value[1] + '</td>\n')
|
||||||
|
|
||||||
self.htmlFile.close()
|
self.htmlFile.close()
|
||||||
|
|||||||
Executable
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
function die() { echo $@; exit 1; }
|
||||||
|
[ $# -eq 4 ] || die "usage: $0 <namespace> <release> <image tag> <oai directory>"
|
||||||
|
|
||||||
|
OC_NS=${1}
|
||||||
|
OC_RELEASE=${2}
|
||||||
|
IMG_TAG=${3}
|
||||||
|
OAI_DIR=${4}
|
||||||
|
|
||||||
|
cat /opt/oc-password | oc login -u oaicicd --server https://api.oai.cs.eurecom.fr:6443 > /dev/null
|
||||||
|
oc project ${OC_NS} > /dev/null
|
||||||
|
oc tag oaicicd-ran/oai-physim:${IMG_TAG} ${OC_NS}/oai-physim:${IMG_TAG}
|
||||||
|
helm install ${OC_RELEASE} ${OAI_DIR}/charts/${OC_RELEASE} --set global.image.version=${IMG_TAG} --wait
|
||||||
|
POD_ID=$(oc get pods | grep oai-${OC_RELEASE} | awk '{print $1}')
|
||||||
|
sleep 10
|
||||||
|
echo "Monitoring logs for 'FINISHED' in pod '$POD_ID'"
|
||||||
|
oc logs -f -n ${OC_NS} "$POD_ID" | while read -r line; do
|
||||||
|
if [[ "$line" == *"FINISHED"* ]]; then
|
||||||
|
echo "'FINISHED' detected in logs. Copying logs..."
|
||||||
|
oc logs -n ${OC_NS} "$POD_ID" >> ${OAI_DIR}/ci-scripts/physim_log.txt
|
||||||
|
oc describe pod $POD_ID >> ${OAI_DIR}/ci-scripts/physim_pods_summary.txt
|
||||||
|
oc cp "$POD_ID":/opt/oai-physim/Testing/Temporary/LastTestsFailed.log ${OAI_DIR}/ci-scripts/LastTestsFailed.log
|
||||||
|
oc cp "$POD_ID":/opt/oai-physim/${OC_RELEASE}-tests.json ${OAI_DIR}/ci-scripts/${OC_RELEASE}-tests.json
|
||||||
|
oc cp "$POD_ID":/opt/oai-physim/${OC_RELEASE}-run.xml ${OAI_DIR}/ci-scripts/${OC_RELEASE}-run.xml
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
helm uninstall ${OC_RELEASE} --wait
|
||||||
|
oc delete istag oai-physim:${IMG_TAG} -n ${OC_NS}
|
||||||
|
oc logout > /dev/null
|
||||||
Reference in New Issue
Block a user