Merge remote-tracking branch 'origin/physim-deploy-handle-error-cases' into develop-NR_SA_F1AP_5GRECORDS
This commit is contained in:
@@ -162,7 +162,7 @@ class Containerize():
|
|||||||
# on RedHat/CentOS .git extension is mandatory
|
# on RedHat/CentOS .git extension is mandatory
|
||||||
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
full_ran_repo_name = self.ranRepository
|
full_ran_repo_name = self.ranRepository.replace('git/', 'git')
|
||||||
else:
|
else:
|
||||||
full_ran_repo_name = self.ranRepository + '.git'
|
full_ran_repo_name = self.ranRepository + '.git'
|
||||||
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
|
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
|
||||||
@@ -176,7 +176,7 @@ class Containerize():
|
|||||||
mySSH.command('mkdir -p cmake_targets/log', '\$', 5)
|
mySSH.command('mkdir -p cmake_targets/log', '\$', 5)
|
||||||
# if the commit ID is provided use it to point to it
|
# if the commit ID is provided use it to point to it
|
||||||
if self.ranCommitID != '':
|
if self.ranCommitID != '':
|
||||||
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 5)
|
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 30)
|
||||||
# if the branch is not develop, then it is a merge request and we need to do
|
# if the branch is not develop, then it is a merge request and we need to do
|
||||||
# the potential merge. Note that merge conflicts should already been checked earlier
|
# the potential merge. Note that merge conflicts should already been checked earlier
|
||||||
imageTag = 'develop'
|
imageTag = 'develop'
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class OaiCiTest():
|
|||||||
ue_prefix = ''
|
ue_prefix = ''
|
||||||
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
full_ran_repo_name = self.ranRepository
|
full_ran_repo_name = self.ranRepository.replace('git/', 'git')
|
||||||
else:
|
else:
|
||||||
full_ran_repo_name = self.ranRepository + '.git'
|
full_ran_repo_name = self.ranRepository + '.git'
|
||||||
SSH.command('mkdir -p ' + self.UESourceCodePath, '\$', 5)
|
SSH.command('mkdir -p ' + self.UESourceCodePath, '\$', 5)
|
||||||
@@ -220,7 +220,7 @@ class OaiCiTest():
|
|||||||
|
|
||||||
# if the commit ID is provided use it to point to it
|
# if the commit ID is provided use it to point to it
|
||||||
if self.ranCommitID != '':
|
if self.ranCommitID != '':
|
||||||
SSH.command('git checkout -f ' + self.ranCommitID, '\$', 5)
|
SSH.command('git checkout -f ' + self.ranCommitID, '\$', 30)
|
||||||
# if the branch is not develop, then it is a merge request and we need to do
|
# if the branch is not develop, then it is a merge request and we need to do
|
||||||
# the potential merge. Note that merge conflicts should already been checked earlier
|
# the potential merge. Note that merge conflicts should already been checked earlier
|
||||||
if self.ranAllowMerge:
|
if self.ranAllowMerge:
|
||||||
|
|||||||
+191
-191
@@ -1,191 +1,191 @@
|
|||||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||||
# * contributor license agreements. See the NOTICE file distributed with
|
# * contributor license agreements. See the NOTICE file distributed with
|
||||||
# * this work for additional information regarding copyright ownership.
|
# * this work for additional information regarding copyright ownership.
|
||||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
# * 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
|
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||||
# * except in compliance with the License.
|
# * except in compliance with the License.
|
||||||
# * You may obtain a copy of the License at
|
# * You may obtain a copy of the License at
|
||||||
# *
|
# *
|
||||||
# * http://www.openairinterface.org/?page_id=698
|
# * http://www.openairinterface.org/?page_id=698
|
||||||
# *
|
# *
|
||||||
# * Unless required by applicable law or agreed to in writing, software
|
# * Unless required by applicable law or agreed to in writing, software
|
||||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# * See the License for the specific language governing permissions and
|
# * See the License for the specific language governing permissions and
|
||||||
# * limitations under the License.
|
# * limitations under the License.
|
||||||
# *-------------------------------------------------------------------------------
|
# *-------------------------------------------------------------------------------
|
||||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||||
# * contact@openairinterface.org
|
# * contact@openairinterface.org
|
||||||
# */
|
# */
|
||||||
#---------------------------------------------------------------------
|
#---------------------------------------------------------------------
|
||||||
# Python for CI of OAI-eNB + COTS-UE
|
# Python for CI of OAI-eNB + COTS-UE
|
||||||
#
|
#
|
||||||
# Required Python Version
|
# Required Python Version
|
||||||
# Python 3.x
|
# Python 3.x
|
||||||
#
|
#
|
||||||
# Required Python Package
|
# Required Python Package
|
||||||
# pexpect
|
# pexpect
|
||||||
#---------------------------------------------------------------------
|
#---------------------------------------------------------------------
|
||||||
|
|
||||||
#to use logging.info()
|
#to use logging.info()
|
||||||
import logging
|
import logging
|
||||||
#to create a SSH object locally in the methods
|
#to create a SSH object locally in the methods
|
||||||
import sshconnection
|
import sshconnection
|
||||||
#to update the HTML object
|
#to update the HTML object
|
||||||
import html
|
import html
|
||||||
from multiprocessing import SimpleQueue
|
from multiprocessing import SimpleQueue
|
||||||
#for log folder maintenance
|
#for log folder maintenance
|
||||||
import os
|
import os
|
||||||
|
|
||||||
class PhySim:
|
class PhySim:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.buildargs = ""
|
self.buildargs = ""
|
||||||
self.runargs = ""
|
self.runargs = ""
|
||||||
self.eNBIpAddr = ""
|
self.eNBIpAddr = ""
|
||||||
self.eNBUserName = ""
|
self.eNBUserName = ""
|
||||||
self.eNBPassWord = ""
|
self.eNBPassWord = ""
|
||||||
self.eNBSourceCodePath = ""
|
self.eNBSourceCodePath = ""
|
||||||
self.ranRepository = ""
|
self.ranRepository = ""
|
||||||
self.ranBranch = ""
|
self.ranBranch = ""
|
||||||
self.ranCommitID= ""
|
self.ranCommitID= ""
|
||||||
self.ranAllowMerge= ""
|
self.ranAllowMerge= ""
|
||||||
self.ranTargetBranch= ""
|
self.ranTargetBranch= ""
|
||||||
self.exitStatus=0
|
self.exitStatus=0
|
||||||
self.forced_workspace_cleanup=False
|
self.forced_workspace_cleanup=False
|
||||||
#private attributes
|
#private attributes
|
||||||
self.__workSpacePath=''
|
self.__workSpacePath=''
|
||||||
self.__buildLogFile='compile_phy_sim.log'
|
self.__buildLogFile='compile_phy_sim.log'
|
||||||
self.__runLogFile=''
|
self.__runLogFile=''
|
||||||
self.__runResults=[]
|
self.__runResults=[]
|
||||||
self.__runLogPath='phy_sim_logs'
|
self.__runLogPath='phy_sim_logs'
|
||||||
|
|
||||||
|
|
||||||
#-----------------
|
#-----------------
|
||||||
#PRIVATE Methods
|
#PRIVATE Methods
|
||||||
#-----------------
|
#-----------------
|
||||||
|
|
||||||
def __CheckResults_PhySim(self,HTML,CONST,testcase_id):
|
def __CheckResults_PhySim(self,HTML,CONST,testcase_id):
|
||||||
mySSH = sshconnection.SSHConnection()
|
mySSH = sshconnection.SSHConnection()
|
||||||
mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
|
mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
|
||||||
#retrieve run log file and store it locally$
|
#retrieve run log file and store it locally$
|
||||||
mySSH.copyin(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord, self.__workSpacePath+self.__runLogFile, '.')
|
mySSH.copyin(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord, self.__workSpacePath+self.__runLogFile, '.')
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
#parse results looking for Encoding and Decoding mean values
|
#parse results looking for Encoding and Decoding mean values
|
||||||
self.__runResults=[]
|
self.__runResults=[]
|
||||||
with open(self.__runLogFile) as f:
|
with open(self.__runLogFile) as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
if 'mean' in line:
|
if 'mean' in line:
|
||||||
self.__runResults.append(line)
|
self.__runResults.append(line)
|
||||||
#the values are appended for each mean value (2), so we take these 2 values from the list
|
#the values are appended for each mean value (2), so we take these 2 values from the list
|
||||||
info=self.__runResults[0]+self.__runResults[1]
|
info=self.__runResults[0]+self.__runResults[1]
|
||||||
|
|
||||||
#once parsed move the local logfile to its folder for tidiness
|
#once parsed move the local logfile to its folder for tidiness
|
||||||
os.system('mv '+self.__runLogFile+' '+ self.__runLogPath+'/.')
|
os.system('mv '+self.__runLogFile+' '+ self.__runLogPath+'/.')
|
||||||
|
|
||||||
#updating the HTML with results
|
#updating the HTML with results
|
||||||
html_cell = '<pre style="background-color:white">' + info + '</pre>'
|
html_cell = '<pre style="background-color:white">' + info + '</pre>'
|
||||||
html_queue=SimpleQueue()
|
html_queue=SimpleQueue()
|
||||||
html_queue.put(html_cell)
|
html_queue.put(html_cell)
|
||||||
HTML.CreateHtmlTestRowQueue(self.runargs, 'OK', 1, html_queue)
|
HTML.CreateHtmlTestRowQueue(self.runargs, 'OK', 1, html_queue)
|
||||||
return HTML
|
return HTML
|
||||||
|
|
||||||
|
|
||||||
def __CheckBuild_PhySim(self, HTML, CONST):
|
def __CheckBuild_PhySim(self, HTML, CONST):
|
||||||
self.__workSpacePath=self.eNBSourceCodePath+'/cmake_targets/'
|
self.__workSpacePath=self.eNBSourceCodePath+'/cmake_targets/'
|
||||||
mySSH = sshconnection.SSHConnection()
|
mySSH = sshconnection.SSHConnection()
|
||||||
mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
|
mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
|
||||||
#retrieve compile log file and store it locally
|
#retrieve compile log file and store it locally
|
||||||
mySSH.copyin(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord, self.__workSpacePath+self.__buildLogFile, '.')
|
mySSH.copyin(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord, self.__workSpacePath+self.__buildLogFile, '.')
|
||||||
#delete older run log file
|
#delete older run log file
|
||||||
mySSH.command('rm ' + self.__workSpacePath+self.__runLogFile, '\$', 5)
|
mySSH.command('rm ' + self.__workSpacePath+self.__runLogFile, '\$', 5)
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
#check build result from local compile log file
|
#check build result from local compile log file
|
||||||
buildStatus=False
|
buildStatus=False
|
||||||
with open(self.__buildLogFile) as f:
|
with open(self.__buildLogFile) as f:
|
||||||
#nr_prachsim is the last compile step
|
#nr_prachsim is the last compile step
|
||||||
if 'nr_prachsim compiled' in f.read():
|
if 'nr_prachsim compiled' in f.read():
|
||||||
buildStatus=True
|
buildStatus=True
|
||||||
#update HTML based on build status
|
#update HTML based on build status
|
||||||
if buildStatus:
|
if buildStatus:
|
||||||
HTML.CreateHtmlTestRow(self.buildargs, 'OK', CONST.ALL_PROCESSES_OK, 'LDPC')
|
HTML.CreateHtmlTestRow(self.buildargs, 'OK', CONST.ALL_PROCESSES_OK, 'LDPC')
|
||||||
self.exitStatus=0
|
self.exitStatus=0
|
||||||
else:
|
else:
|
||||||
logging.error('\u001B[1m Building Physical Simulators Failed\u001B[0m')
|
logging.error('\u001B[1m Building Physical Simulators Failed\u001B[0m')
|
||||||
HTML.CreateHtmlTestRow(self.buildargs, 'KO', CONST.ALL_PROCESSES_OK, 'LDPC')
|
HTML.CreateHtmlTestRow(self.buildargs, 'KO', CONST.ALL_PROCESSES_OK, 'LDPC')
|
||||||
HTML.CreateHtmlTabFooter(False)
|
HTML.CreateHtmlTabFooter(False)
|
||||||
#exitStatus=1 will do a sys.exit in main
|
#exitStatus=1 will do a sys.exit in main
|
||||||
self.exitStatus=1
|
self.exitStatus=1
|
||||||
return HTML
|
return HTML
|
||||||
|
|
||||||
|
|
||||||
#-----------------$
|
#-----------------$
|
||||||
#PUBLIC Methods$
|
#PUBLIC Methods$
|
||||||
#-----------------$
|
#-----------------$
|
||||||
|
|
||||||
def Build_PhySim(self,htmlObj,constObj):
|
def Build_PhySim(self,htmlObj,constObj):
|
||||||
mySSH = sshconnection.SSHConnection()
|
mySSH = sshconnection.SSHConnection()
|
||||||
mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
|
mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
|
||||||
|
|
||||||
#create working dir
|
#create working dir
|
||||||
mySSH.command('mkdir -p ' + self.eNBSourceCodePath, '\$', 5)
|
mySSH.command('mkdir -p ' + self.eNBSourceCodePath, '\$', 5)
|
||||||
mySSH.command('cd ' + self.eNBSourceCodePath, '\$', 5)
|
mySSH.command('cd ' + self.eNBSourceCodePath, '\$', 5)
|
||||||
|
|
||||||
if not self.ranRepository.lower().endswith('.git'):
|
if not self.ranRepository.lower().endswith('.git'):
|
||||||
self.ranRepository+='.git'
|
self.ranRepository+='.git'
|
||||||
|
|
||||||
#git clone
|
#git clone
|
||||||
mySSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone ' + self.ranRepository + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600)
|
mySSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone ' + self.ranRepository + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600)
|
||||||
#git config
|
#git config
|
||||||
mySSH.command('git config user.email "jenkins@openairinterface.org"', '\$', 5)
|
mySSH.command('git config user.email "jenkins@openairinterface.org"', '\$', 5)
|
||||||
mySSH.command('git config user.name "OAI Jenkins"', '\$', 5)
|
mySSH.command('git config user.name "OAI Jenkins"', '\$', 5)
|
||||||
|
|
||||||
#git clean depending on self.forced_workspace_cleanup captured in xml
|
#git clean depending on self.forced_workspace_cleanup captured in xml
|
||||||
if self.forced_workspace_cleanup==True:
|
if self.forced_workspace_cleanup==True:
|
||||||
logging.info('Cleaning workspace ...')
|
logging.info('Cleaning workspace ...')
|
||||||
mySSH.command('echo ' + self.eNBPassWord + ' | sudo -S git clean -x -d -ff', '\$', 30)
|
mySSH.command('echo ' + self.eNBPassWord + ' | sudo -S git clean -x -d -ff', '\$', 30)
|
||||||
else:
|
else:
|
||||||
logging.info('Workspace cleaning was disabled')
|
logging.info('Workspace cleaning was disabled')
|
||||||
|
|
||||||
# if the commit ID is provided, use it to point to it
|
# if the commit ID is provided, use it to point to it
|
||||||
if self.ranCommitID != '':
|
if self.ranCommitID != '':
|
||||||
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 5)
|
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 30)
|
||||||
# if the branch is not develop, then it is a merge request and we need to do
|
# if the branch is not develop, then it is a merge request and we need to do
|
||||||
# the potential merge. Note that merge conflicts should have already been checked earlier
|
# the potential merge. Note that merge conflicts should have already been checked earlier
|
||||||
if (self.ranAllowMerge):
|
if (self.ranAllowMerge):
|
||||||
if self.ranTargetBranch == '':
|
if self.ranTargetBranch == '':
|
||||||
if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'):
|
if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'):
|
||||||
mySSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5)
|
mySSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5)
|
||||||
else:
|
else:
|
||||||
logging.info('Merging with the target branch: ' + self.ranTargetBranch)
|
logging.info('Merging with the target branch: ' + self.ranTargetBranch)
|
||||||
mySSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5)
|
mySSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5)
|
||||||
|
|
||||||
#build
|
#build
|
||||||
mySSH.command('source oaienv', '\$', 5)
|
mySSH.command('source oaienv', '\$', 5)
|
||||||
mySSH.command('cd cmake_targets', '\$', 5)
|
mySSH.command('cd cmake_targets', '\$', 5)
|
||||||
mySSH.command('mkdir -p log', '\$', 5)
|
mySSH.command('mkdir -p log', '\$', 5)
|
||||||
mySSH.command('chmod 777 log', '\$', 5)
|
mySSH.command('chmod 777 log', '\$', 5)
|
||||||
mySSH.command('stdbuf -o0 ./build_oai ' + self.buildargs + ' 2>&1 | stdbuf -o0 tee ' + self.__buildLogFile, 'Bypassing the Tests|build have failed', 1500)
|
mySSH.command('stdbuf -o0 ./build_oai ' + self.buildargs + ' 2>&1 | stdbuf -o0 tee ' + self.__buildLogFile, 'Bypassing the Tests|build have failed', 1500)
|
||||||
|
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
#check build status and update HTML object
|
#check build status and update HTML object
|
||||||
lHTML = html.HTMLManagement()
|
lHTML = html.HTMLManagement()
|
||||||
lHTML=self.__CheckBuild_PhySim(htmlObj,constObj)
|
lHTML=self.__CheckBuild_PhySim(htmlObj,constObj)
|
||||||
return lHTML
|
return lHTML
|
||||||
|
|
||||||
|
|
||||||
def Run_PhySim(self,htmlObj,constObj,testcase_id):
|
def Run_PhySim(self,htmlObj,constObj,testcase_id):
|
||||||
#create run logs folder locally
|
#create run logs folder locally
|
||||||
os.system('mkdir -p ./'+self.__runLogPath)
|
os.system('mkdir -p ./'+self.__runLogPath)
|
||||||
#log file is tc_<testcase_id>.log remotely
|
#log file is tc_<testcase_id>.log remotely
|
||||||
self.__runLogFile='physim_'+str(testcase_id)+'.log'
|
self.__runLogFile='physim_'+str(testcase_id)+'.log'
|
||||||
#open a session for test run
|
#open a session for test run
|
||||||
mySSH = sshconnection.SSHConnection()
|
mySSH = sshconnection.SSHConnection()
|
||||||
mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
|
mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
|
||||||
mySSH.command('cd '+self.__workSpacePath,'\$',5)
|
mySSH.command('cd '+self.__workSpacePath,'\$',5)
|
||||||
#run and redirect the results to a log file
|
#run and redirect the results to a log file
|
||||||
mySSH.command(self.__workSpacePath+'phy_simulators/build/ldpctest ' + self.runargs + ' >> '+self.__runLogFile, '\$', 30)
|
mySSH.command(self.__workSpacePath+'phy_simulators/build/ldpctest ' + self.runargs + ' >> '+self.__runLogFile, '\$', 30)
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
#return updated HTML to main
|
#return updated HTML to main
|
||||||
lHTML = html.HTMLManagement()
|
lHTML = html.HTMLManagement()
|
||||||
lHTML=self.__CheckResults_PhySim(htmlObj,constObj,testcase_id)
|
lHTML=self.__CheckResults_PhySim(htmlObj,constObj,testcase_id)
|
||||||
return lHTML
|
return lHTML
|
||||||
|
|||||||
+59
-17
@@ -64,7 +64,7 @@ class PhySim:
|
|||||||
#PUBLIC Methods$
|
#PUBLIC Methods$
|
||||||
#-----------------$
|
#-----------------$
|
||||||
|
|
||||||
def Deploy_PhySim(self, HTML):
|
def Deploy_PhySim(self, HTML, RAN):
|
||||||
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
|
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
|
||||||
HELP.GenericHelp(CONST.Version)
|
HELP.GenericHelp(CONST.Version)
|
||||||
sys.exit('Insufficient Parameter')
|
sys.exit('Insufficient Parameter')
|
||||||
@@ -88,9 +88,10 @@ class PhySim:
|
|||||||
# on RedHat/CentOS .git extension is mandatory
|
# on RedHat/CentOS .git extension is mandatory
|
||||||
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
full_ran_repo_name = self.ranRepository
|
full_ran_repo_name = self.ranRepository.replace('git/', 'git')
|
||||||
else:
|
else:
|
||||||
full_ran_repo_name = self.ranRepository + '.git'
|
full_ran_repo_name = self.ranRepository + '.git'
|
||||||
|
mySSH.command('echo ' + lPassWord + ' | sudo rm -Rf ' + lSourcePath, '\$', 30)
|
||||||
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
|
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
|
||||||
mySSH.command('cd ' + lSourcePath, '\$', 5)
|
mySSH.command('cd ' + lSourcePath, '\$', 5)
|
||||||
mySSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone ' + full_ran_repo_name + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600)
|
mySSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone ' + full_ran_repo_name + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600)
|
||||||
@@ -102,9 +103,15 @@ class PhySim:
|
|||||||
mySSH.command('mkdir -p cmake_targets/log', '\$', 5)
|
mySSH.command('mkdir -p cmake_targets/log', '\$', 5)
|
||||||
# if the commit ID is provided use it to point to it
|
# if the commit ID is provided use it to point to it
|
||||||
if self.ranCommitID != '':
|
if self.ranCommitID != '':
|
||||||
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 5)
|
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 30)
|
||||||
if self.ranAllowMerge:
|
if self.ranAllowMerge:
|
||||||
imageTag = "ci-temp"
|
imageTag = "ci-temp"
|
||||||
|
if self.ranTargetBranch == '':
|
||||||
|
if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'):
|
||||||
|
mySSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5)
|
||||||
|
else:
|
||||||
|
logging.debug('Merging with the target branch: ' + self.ranTargetBranch)
|
||||||
|
mySSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5)
|
||||||
else:
|
else:
|
||||||
imageTag = "develop"
|
imageTag = "develop"
|
||||||
# Check if image is exist on the Red Hat server, before pushing it to OC cluster
|
# Check if image is exist on the Red Hat server, before pushing it to OC cluster
|
||||||
@@ -112,7 +119,9 @@ class PhySim:
|
|||||||
if mySSH.getBefore().count('no such image') != 0:
|
if mySSH.getBefore().count('no such image') != 0:
|
||||||
logging.error('\u001B[1m No such image oai-physim\u001B[0m')
|
logging.error('\u001B[1m No such image oai-physim\u001B[0m')
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
sys.exit(-1)
|
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.PHYSIM_IMAGE_ABSENT)
|
||||||
|
RAN.prematureExit = True
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
result = re.search('Size *= *(?P<size>[0-9\-]+) *bytes', mySSH.getBefore())
|
result = re.search('Size *= *(?P<size>[0-9\-]+) *bytes', mySSH.getBefore())
|
||||||
if result is not None:
|
if result is not None:
|
||||||
@@ -135,14 +144,18 @@ class PhySim:
|
|||||||
if mySSH.getBefore().count('Login successful.') == 0:
|
if mySSH.getBefore().count('Login successful.') == 0:
|
||||||
logging.error('\u001B[1m OC Cluster Login Failed\u001B[0m')
|
logging.error('\u001B[1m OC Cluster Login Failed\u001B[0m')
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
sys.exit(-1)
|
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OC_LOGIN_FAIL)
|
||||||
|
RAN.prematureExit = True
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
logging.debug('\u001B[1m Login to OC Cluster Successfully\u001B[0m')
|
logging.debug('\u001B[1m Login to OC Cluster Successfully\u001B[0m')
|
||||||
mySSH.command(f'oc project {ocProjectName}', '\$', 6)
|
mySSH.command(f'oc project {ocProjectName}', '\$', 6)
|
||||||
if mySSH.getBefore().count(f'Already on project "{ocProjectName}"') == 0 and mySSH.getBefore().count(f'Now using project "{self.OCProjectName}"') == 0:
|
if mySSH.getBefore().count(f'Already on project "{ocProjectName}"') == 0 and mySSH.getBefore().count(f'Now using project "{self.OCProjectName}"') == 0:
|
||||||
logging.error(f'\u001B[1m Unable to access OC project {ocProjectName}\u001B[0m')
|
logging.error(f'\u001B[1m Unable to access OC project {ocProjectName}\u001B[0m')
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
sys.exit(-1)
|
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OC_PROJECT_FAIL)
|
||||||
|
RAN.prematureExit = True
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
logging.debug(f'\u001B[1m Now using project {ocProjectName}\u001B[0m')
|
logging.debug(f'\u001B[1m Now using project {ocProjectName}\u001B[0m')
|
||||||
|
|
||||||
@@ -151,7 +164,9 @@ class PhySim:
|
|||||||
if mySSH.getBefore().count('Login Succeeded!') == 0:
|
if mySSH.getBefore().count('Login Succeeded!') == 0:
|
||||||
logging.error('\u001B[1m Podman Login to OC Cluster Registry Failed\u001B[0m')
|
logging.error('\u001B[1m Podman Login to OC Cluster Registry Failed\u001B[0m')
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
sys.exit(-1)
|
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OC_LOGIN_FAIL)
|
||||||
|
RAN.prematureExit = True
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
logging.debug('\u001B[1m Podman Login to OC Cluster Registry Successfully\u001B[0m')
|
logging.debug('\u001B[1m Podman Login to OC Cluster Registry Successfully\u001B[0m')
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
@@ -159,7 +174,9 @@ class PhySim:
|
|||||||
if mySSH.getBefore().count('(AlreadyExists):') == 0 and mySSH.getBefore().count('created') == 0:
|
if mySSH.getBefore().count('(AlreadyExists):') == 0 and mySSH.getBefore().count('created') == 0:
|
||||||
logging.error(f'\u001B[1m Image Stream "oai-physim" Creation Failed on OC Cluster {ocProjectName}\u001B[0m')
|
logging.error(f'\u001B[1m Image Stream "oai-physim" Creation Failed on OC Cluster {ocProjectName}\u001B[0m')
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
sys.exit(-1)
|
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OC_IS_FAIL)
|
||||||
|
RAN.prematureExit = True
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
logging.debug(f'\u001B[1m Image Stream "oai-physim" created on OC project {ocProjectName}\u001B[0m')
|
logging.debug(f'\u001B[1m Image Stream "oai-physim" created on OC project {ocProjectName}\u001B[0m')
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
@@ -169,7 +186,9 @@ class PhySim:
|
|||||||
if mySSH.getBefore().count('Storing signatures') == 0:
|
if mySSH.getBefore().count('Storing signatures') == 0:
|
||||||
logging.error('\u001B[1m Image "oai-physim" push to OC Cluster Registry Failed\u001B[0m')
|
logging.error('\u001B[1m Image "oai-physim" push to OC Cluster Registry Failed\u001B[0m')
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
sys.exit(-1)
|
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OC_IS_FAIL)
|
||||||
|
RAN.prematureExit = True
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
logging.debug('\u001B[1m Image "oai-physim" push to OC Cluster Registry Successfully\u001B[0m')
|
logging.debug('\u001B[1m Image "oai-physim" push to OC Cluster Registry Successfully\u001B[0m')
|
||||||
|
|
||||||
@@ -180,9 +199,18 @@ class PhySim:
|
|||||||
if mySSH.getBefore().count('STATUS: deployed') == 0:
|
if mySSH.getBefore().count('STATUS: deployed') == 0:
|
||||||
logging.error('\u001B[1m Deploying PhySim Failed using helm chart on OC Cluster\u001B[0m')
|
logging.error('\u001B[1m Deploying PhySim Failed using helm chart on OC Cluster\u001B[0m')
|
||||||
mySSH.command('helm uninstall physim >> cmake_targets/log/physim_helm_summary.txt 2>&1', '\$', 6)
|
mySSH.command('helm uninstall physim >> cmake_targets/log/physim_helm_summary.txt 2>&1', '\$', 6)
|
||||||
|
isFinished1 = False
|
||||||
|
while(isFinished1 == False):
|
||||||
|
time.sleep(20)
|
||||||
|
mySSH.command('oc get pods -l app.kubernetes.io/instance=physim', '\$', 6, resync=True)
|
||||||
|
if re.search('No resources found', mySSH.getBefore()):
|
||||||
|
isFinished1 = True
|
||||||
|
mySSH.command(f'sudo podman rmi default-route-openshift-image-registry.apps.5glab.nsa.eurecom.fr/{self.OCProjectName}/oai-physim:{imageTag}', '\$', 6)
|
||||||
|
mySSH.command('oc delete is oai-physim', '\$', 6)
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
self.AnalyzeLogFile_phySim(HTML)
|
self.AnalyzeLogFile_phySim(HTML)
|
||||||
sys.exit(-1)
|
RAN.prematureExit = True
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
logging.debug('\u001B[1m Deployed PhySim Successfully using helm chart\u001B[0m')
|
logging.debug('\u001B[1m Deployed PhySim Successfully using helm chart\u001B[0m')
|
||||||
isRunning = False
|
isRunning = False
|
||||||
@@ -200,7 +228,18 @@ class PhySim:
|
|||||||
mySSH.command('oc get pods -l app.kubernetes.io/instance=physim 2>&1 | tee -a cmake_targets/log/physim_pods_summary.txt', '\$', 6)
|
mySSH.command('oc get pods -l app.kubernetes.io/instance=physim 2>&1 | tee -a cmake_targets/log/physim_pods_summary.txt', '\$', 6)
|
||||||
mySSH.command('helm uninstall physim >> cmake_targets/log/physim_helm_summary.txt 2>&1', '\$', 6)
|
mySSH.command('helm uninstall physim >> cmake_targets/log/physim_helm_summary.txt 2>&1', '\$', 6)
|
||||||
self.AnalyzeLogFile_phySim(HTML)
|
self.AnalyzeLogFile_phySim(HTML)
|
||||||
sys.exit(-1)
|
isFinished1 = False
|
||||||
|
while(isFinished1 == False):
|
||||||
|
time.sleep(20)
|
||||||
|
mySSH.command('oc get pods -l app.kubernetes.io/instance=physim', '\$', 6, resync=True)
|
||||||
|
if re.search('No resources found', mySSH.getBefore()):
|
||||||
|
isFinished1 = True
|
||||||
|
mySSH.command(f'sudo podman rmi default-route-openshift-image-registry.apps.5glab.nsa.eurecom.fr/{self.OCProjectName}/oai-physim:{imageTag}', '\$', 6)
|
||||||
|
mySSH.command('oc delete is oai-physim', '\$', 6)
|
||||||
|
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OC_PHYSIM_DEPLOY_FAIL)
|
||||||
|
HTML.CreateHtmlTestRowPhySimTestResult(self.testSummary,self.testResult)
|
||||||
|
RAN.prematureExit = True
|
||||||
|
return
|
||||||
# Waiting to complete the running test
|
# Waiting to complete the running test
|
||||||
count = 0
|
count = 0
|
||||||
isFinished = False
|
isFinished = False
|
||||||
@@ -240,8 +279,15 @@ class PhySim:
|
|||||||
mySSH.command('oc logout', '\$', 6)
|
mySSH.command('oc logout', '\$', 6)
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
self.AnalyzeLogFile_phySim(HTML)
|
self.AnalyzeLogFile_phySim(HTML)
|
||||||
if self.testStatus == False:
|
if self.testStatus:
|
||||||
sys.exit(-1)
|
HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
|
||||||
|
HTML.CreateHtmlTestRowPhySimTestResult(self.testSummary,self.testResult)
|
||||||
|
logging.info('\u001B[1m Physical Simulator Pass\u001B[0m')
|
||||||
|
else:
|
||||||
|
RAN.prematureExit = True
|
||||||
|
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ALL_PROCESSES_OK)
|
||||||
|
HTML.CreateHtmlTestRowPhySimTestResult(self.testSummary,self.testResult)
|
||||||
|
logging.info('\u001B[1m Physical Simulator Fail\u001B[0m')
|
||||||
|
|
||||||
def AnalyzeLogFile_phySim(self, HTML):
|
def AnalyzeLogFile_phySim(self, HTML):
|
||||||
lIpAddr = self.eNBIPAddress
|
lIpAddr = self.eNBIPAddress
|
||||||
@@ -258,7 +304,6 @@ class PhySim:
|
|||||||
os.mkdir(f'./physim_test_logs_{self.testCase_id}')
|
os.mkdir(f'./physim_test_logs_{self.testCase_id}')
|
||||||
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/physim_test_log_' + self.testCase_id + '/*', './physim_test_logs_' + self.testCase_id)
|
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/physim_test_log_' + self.testCase_id + '/*', './physim_test_logs_' + self.testCase_id)
|
||||||
mySSH.command('rm -rf ./physim_test_log_'+ self.testCase_id, '\$', 5)
|
mySSH.command('rm -rf ./physim_test_log_'+ self.testCase_id, '\$', 5)
|
||||||
mySSH.command('oc logout', '\$', 6)
|
|
||||||
mySSH.close()
|
mySSH.close()
|
||||||
# physim test log analysis
|
# physim test log analysis
|
||||||
nextt = 0
|
nextt = 0
|
||||||
@@ -289,7 +334,4 @@ class PhySim:
|
|||||||
self.testSummary['Nbfail'] = self.testCount[2]
|
self.testSummary['Nbfail'] = self.testCount[2]
|
||||||
if self.testSummary['Nbfail'] == 0:
|
if self.testSummary['Nbfail'] == 0:
|
||||||
self.testStatus = True
|
self.testStatus = True
|
||||||
HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
|
|
||||||
HTML.CreateHtmlTestRowPhySimTestResult(self.testSummary,self.testResult)
|
|
||||||
logging.info('\u001B[1m Physical Simulator Pass\u001B[0m')
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class StaticCodeAnalysis():
|
|||||||
# on RedHat/CentOS .git extension is mandatory
|
# on RedHat/CentOS .git extension is mandatory
|
||||||
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
full_ran_repo_name = self.ranRepository
|
full_ran_repo_name = self.ranRepository.replace('git/', 'git')
|
||||||
else:
|
else:
|
||||||
full_ran_repo_name = self.ranRepository + '.git'
|
full_ran_repo_name = self.ranRepository + '.git'
|
||||||
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
|
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
|
||||||
@@ -115,7 +115,7 @@ class StaticCodeAnalysis():
|
|||||||
mySSH.command('mkdir -p cmake_targets/log', '\$', 5)
|
mySSH.command('mkdir -p cmake_targets/log', '\$', 5)
|
||||||
# if the commit ID is provided use it to point to it
|
# if the commit ID is provided use it to point to it
|
||||||
if self.ranCommitID != '':
|
if self.ranCommitID != '':
|
||||||
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 5)
|
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 30)
|
||||||
|
|
||||||
mySSH.command('docker image rm oai-cppcheck:bionic oai-cppcheck:xenial || true', '\$', 60)
|
mySSH.command('docker image rm oai-cppcheck:bionic oai-cppcheck:xenial || true', '\$', 60)
|
||||||
mySSH.command('docker build --tag oai-cppcheck:xenial --file ci-scripts/docker/Dockerfile.cppcheck.xenial . > cmake_targets/log/cppcheck-xenial.txt 2>&1', '\$', 600)
|
mySSH.command('docker build --tag oai-cppcheck:xenial --file ci-scripts/docker/Dockerfile.cppcheck.xenial . > cmake_targets/log/cppcheck-xenial.txt 2>&1', '\$', 600)
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ OAI_UE_PROCESS_SEG_FAULT = -25
|
|||||||
OAI_UE_PROCESS_NO_MBMS_MSGS = -26
|
OAI_UE_PROCESS_NO_MBMS_MSGS = -26
|
||||||
OAI_UE_PROCESS_OK = +6
|
OAI_UE_PROCESS_OK = +6
|
||||||
INVALID_PARAMETER = -50
|
INVALID_PARAMETER = -50
|
||||||
|
PHYSIM_IMAGE_ABSENT = -60
|
||||||
|
OC_LOGIN_FAIL = -61
|
||||||
|
OC_PROJECT_FAIL = -62
|
||||||
|
OC_IS_FAIL = -63
|
||||||
|
OC_PHYSIM_DEPLOY_FAIL = -64
|
||||||
|
|
||||||
UE_STATUS_DETACHED = 0
|
UE_STATUS_DETACHED = 0
|
||||||
UE_STATUS_DETACHING = 1
|
UE_STATUS_DETACHING = 1
|
||||||
|
|||||||
@@ -365,6 +365,16 @@ class HTMLManagement():
|
|||||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - SPGW process not found</td>\n')
|
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - SPGW process not found</td>\n')
|
||||||
elif (processesStatus == CONST.UE_IP_ADDRESS_ISSUE):
|
elif (processesStatus == CONST.UE_IP_ADDRESS_ISSUE):
|
||||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - Could not retrieve UE IP address</td>\n')
|
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - Could not retrieve UE IP address</td>\n')
|
||||||
|
elif (processesStatus == CONST.PHYSIM_IMAGE_ABSENT):
|
||||||
|
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - No such image oai-physim</td>\n')
|
||||||
|
elif (processesStatus == CONST.OC_LOGIN_FAIL):
|
||||||
|
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - Could not log onto cluster</td>\n')
|
||||||
|
elif (processesStatus == CONST.OC_PROJECT_FAIL):
|
||||||
|
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - Could not register into cluster project</td>\n')
|
||||||
|
elif (processesStatus == CONST.OC_IS_FAIL):
|
||||||
|
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - Could not create Image Stream</td>\n')
|
||||||
|
elif (processesStatus == CONST.OC_PHYSIM_DEPLOY_FAIL):
|
||||||
|
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - Could not properly deploy physim on cluster</td>\n')
|
||||||
else:
|
else:
|
||||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >' + str(status) + '</td>\n')
|
self.htmlFile.write(' <td bgcolor = "lightcoral" >' + str(status) + '</td>\n')
|
||||||
else:
|
else:
|
||||||
|
|||||||
+1
-1
@@ -786,7 +786,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
|
|||||||
elif action == 'Cppcheck_Analysis':
|
elif action == 'Cppcheck_Analysis':
|
||||||
SCA.CppCheckAnalysis(HTML)
|
SCA.CppCheckAnalysis(HTML)
|
||||||
elif action == 'Deploy_Run_PhySim':
|
elif action == 'Deploy_Run_PhySim':
|
||||||
PHYSIM.Deploy_PhySim(HTML)
|
PHYSIM.Deploy_PhySim(HTML, RAN)
|
||||||
else:
|
else:
|
||||||
sys.exit('Invalid class (action) from xml')
|
sys.exit('Invalid class (action) from xml')
|
||||||
if not RAN.prematureExit:
|
if not RAN.prematureExit:
|
||||||
|
|||||||
+2
-2
@@ -148,7 +148,7 @@ class RANManagement():
|
|||||||
# on RedHat/CentOS .git extension is mandatory
|
# on RedHat/CentOS .git extension is mandatory
|
||||||
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
full_ran_repo_name = self.ranRepository
|
full_ran_repo_name = self.ranRepository.replace('git/', 'git')
|
||||||
else:
|
else:
|
||||||
full_ran_repo_name = self.ranRepository + '.git'
|
full_ran_repo_name = self.ranRepository + '.git'
|
||||||
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
|
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
|
||||||
@@ -191,7 +191,7 @@ class RANManagement():
|
|||||||
mySSH.command('echo ' + lPassWord + ' | sudo -S git clean -x -d -ff', '\$', 30)
|
mySSH.command('echo ' + lPassWord + ' | sudo -S git clean -x -d -ff', '\$', 30)
|
||||||
# if the commit ID is provided use it to point to it
|
# if the commit ID is provided use it to point to it
|
||||||
if self.ranCommitID != '':
|
if self.ranCommitID != '':
|
||||||
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 5)
|
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 30)
|
||||||
# if the branch is not develop, then it is a merge request and we need to do
|
# if the branch is not develop, then it is a merge request and we need to do
|
||||||
# the potential merge. Note that merge conflicts should already been checked earlier
|
# the potential merge. Note that merge conflicts should already been checked earlier
|
||||||
if (self.ranAllowMerge):
|
if (self.ranAllowMerge):
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
<kind>all</kind>
|
<kind>all</kind>
|
||||||
<eNB_instance>0</eNB_instance>
|
<eNB_instance>0</eNB_instance>
|
||||||
<eNB_serverId>0</eNB_serverId>
|
<eNB_serverId>0</eNB_serverId>
|
||||||
|
<forced_workspace_cleanup>True</forced_workspace_cleanup>
|
||||||
</testCase>
|
</testCase>
|
||||||
|
|
||||||
</testCaseList>
|
</testCaseList>
|
||||||
|
|||||||
Reference in New Issue
Block a user