[0-9\.]+)', mySSH.getBefore())
if result is not None:
self.MmeIPAddress = result.group('mme_ip_addr')
@@ -182,8 +198,8 @@ class EPCManagement():
mySSH.open(self.IPAddress, self.UserName, self.Password)
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
logging.debug('Using the OAI EPC Release 14 SPGW-CUPS in Docker')
- mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "nohup tshark -i eth0 -i lo:p5c -i lo:s5c -w /tmp/spgwc_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
- mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwu-tiny /bin/bash -c "nohup tshark -i eth0 -w /tmp/spgwu_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
+ mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "nohup tshark -i eth0 -i lo:p5c -i lo:s5c -f "not port 2152" -w /tmp/spgwc_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
+ mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwu-tiny /bin/bash -c "nohup tshark -i eth0 -f "not port 2152" -w /tmp/spgwu_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
time.sleep(5)
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "nohup ./bin/oai_spgwc -o -c ./etc/spgw_c.conf > spgwc_check_run.log 2>&1"', '\$', 5)
time.sleep(5)
@@ -219,8 +235,9 @@ class EPCManagement():
sys.exit('Insufficient EPC Parameters')
mySSH = SSH.SSHConnection()
mySSH.open(self.IPAddress, self.UserName, self.Password)
+ html_cell = '\n'
if re.match('ltebox', self.Type, re.IGNORECASE):
- logging.debug('Using the sabox simulated HSS')
+ logging.debug('Using the SABOX simulated HSS')
mySSH.command('if [ -d ' + self.SourceCodePath + '/scripts ]; then echo ' + self.Password + ' | sudo -S rm -Rf ' + self.SourceCodePath + '/scripts ; fi', '\$', 5)
mySSH.command('mkdir -p ' + self.SourceCodePath + '/scripts', '\$', 5)
mySSH.command('cd /opt/hss_sim0609', '\$', 5)
@@ -229,10 +246,48 @@ class EPCManagement():
logging.debug('Using the sabox')
mySSH.command('cd /opt/ltebox/tools', '\$', 5)
mySSH.command('echo ' + self.Password + ' | sudo -S ./start_sabox', '\$', 5)
+ html_cell += 'N/A\n'
+ elif re.match('OAICN5G', self.Type, re.IGNORECASE):
+ logging.debug('Starting OAI CN5G')
+ mySSH.command('if [ -d ' + self.SourceCodePath + '/scripts ]; then echo ' + self.Password + ' | sudo -S rm -Rf ' + self.SourceCodePath + '/scripts ; fi', '\$', 5)
+ mySSH.command('mkdir -p ' + self.SourceCodePath + '/scripts', '\$', 5)
+ mySSH.command('cd /opt/oai-cn5g-fed/docker-compose', '\$', 5)
+ mySSH.command('./core-network.sh start nrf spgwu', '\$', 60)
+ time.sleep(2)
+ mySSH.command('docker-compose -p 5gcn ps -a', '\$', 60)
+ if mySSH.getBefore().count('Up (healthy)') != 6:
+ logging.error('Not all container healthy')
+ else:
+ logging.debug('OK')
+ mySSH.command('docker-compose config | grep --colour=never image', '\$', 10)
+ listOfImages = mySSH.getBefore()
+ for imageLine in listOfImages.split('\\r\\n'):
+ res1 = re.search('image: (?P[a-zA-Z0-9\-]+):(?P[a-zA-Z0-9\-]+)', str(imageLine))
+ res2 = re.search('mysql', str(imageLine))
+ if res1 is not None and res2 is None:
+ html_cell += res1.group('name') + ':' + res1.group('tag') + ' '
+ nbChars = len(res1.group('name')) + len(res1.group('tag')) + 2
+ while (nbChars < 32):
+ html_cell += ' '
+ nbChars += 1
+ mySSH.command('docker image inspect --format="Size = {{.Size}} bytes" ' + res1.group('name') + ':' + res1.group('tag'), '\$', 10)
+ res3 = re.search('Size *= *(?P[0-9\-]*) *bytes', mySSH.getBefore())
+ if res3 is not None:
+ imageSize = int(res3.group('size'))
+ imageSize = int(imageSize/(1024*1024))
+ html_cell += str(imageSize) + ' MBytes '
+ mySSH.command('docker image inspect --format="Date = {{.Created}}" ' + res1.group('name') + ':' + res1.group('tag'), '\$', 10)
+ res4 = re.search('Date *= *(?P[0-9\-]*)T', mySSH.getBefore())
+ if res4 is not None:
+ html_cell += '(' + res4.group('date') + ')'
+ html_cell += '\n'
else:
logging.error('This option should not occur!')
+ html_cell += ''
mySSH.close()
- HTML.CreateHtmlTestRow(self.Type, 'OK', CONST.ALL_PROCESSES_OK)
+ html_queue = SimpleQueue()
+ html_queue.put(html_cell)
+ HTML.CreateHtmlTestRowQueue(self.Type, 'OK', 1, html_queue)
def SetAmfIPAddress(self):
# Not an error if we don't need an 5GCN
@@ -242,6 +297,16 @@ class EPCManagement():
return
if re.match('ltebox', self.Type, re.IGNORECASE):
self.MmeIPAddress = self.IPAddress
+ elif re.match('OAICN5G', self.Type, re.IGNORECASE):
+ mySSH = SSH.SSHConnection()
+ mySSH.open(self.IPAddress, self.UserName, self.Password)
+ response=mySSH.command3('docker container ls -f name=oai-amf', 10)
+ if len(response)>1:
+ response=mySSH.command3('docker inspect --format=\'{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}\' oai-amf', 10)
+ tmp = str(response[0],'utf-8')
+ self.MmeIPAddress = tmp.rstrip()
+ logging.debug('AMF IP Address ' + self.MmeIPAddress)
+ mySSH.close()
def CheckHSSProcess(self, status_queue):
try:
@@ -273,7 +338,15 @@ class EPCManagement():
mySSH = SSH.SSHConnection()
mySSH.open(self.IPAddress, self.UserName, self.Password)
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
- mySSH.command('docker top ' + self.containerPrefix + '-oai-mme', '\$', 5)
+ self.isMagmaUsed = False
+ mySSH.command('docker ps -a', '\$', 5)
+ result = re.search('magma', mySSH.getBefore())
+ if result is not None:
+ self.isMagmaUsed = True
+ if self.isMagmaUsed:
+ mySSH.command('docker top ' + self.containerPrefix + '-magma-mme', '\$', 5)
+ else:
+ mySSH.command('docker top ' + self.containerPrefix + '-oai-mme', '\$', 5)
else:
mySSH.command('stdbuf -o0 ps -aux | grep --color=never mme | grep -v grep', '\$', 5)
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
@@ -338,7 +411,7 @@ class EPCManagement():
elif re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGINT oai_hss || true', '\$', 5)
time.sleep(2)
- mySSH.command('stdbuf -o0 ps -aux | grep hss | grep -v grep', '\$', 5)
+ mySSH.command('stdbuf -o0 ps -aux | grep --colour=never hss | grep -v grep', '\$', 5)
result = re.search('oai_hss -j', mySSH.getBefore())
if result is not None:
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGKILL oai_hss || true', '\$', 5)
@@ -346,7 +419,7 @@ class EPCManagement():
elif re.match('OAI', self.Type, re.IGNORECASE):
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGINT run_hss oai_hss || true', '\$', 5)
time.sleep(2)
- mySSH.command('stdbuf -o0 ps -aux | grep hss | grep -v grep', '\$', 5)
+ mySSH.command('stdbuf -o0 ps -aux | grep --colour=never hss | grep -v grep', '\$', 5)
result = re.search('\/bin\/bash .\/run_', mySSH.getBefore())
if result is not None:
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGKILL run_hss oai_hss || true', '\$', 5)
@@ -355,6 +428,11 @@ class EPCManagement():
mySSH.command('cd scripts', '\$', 5)
time.sleep(1)
mySSH.command('echo ' + self.Password + ' | sudo -S screen -S simulated_hss -X quit', '\$', 5)
+ time.sleep(5)
+ mySSH.command('ps aux | grep --colour=never hss_sim | grep -v grep', '\$', 5, silent=True)
+ result = re.search('hss_sim s6as diam_hss', mySSH.getBefore())
+ if result is not None:
+ mySSH.command('echo ' + self.Password + ' | sudo -S killall hss_sim', '\$', 5)
else:
logging.error('This should not happen!')
mySSH.close()
@@ -381,6 +459,7 @@ class EPCManagement():
elif re.match('ltebox', self.Type, re.IGNORECASE):
mySSH.command('cd /opt/ltebox/tools', '\$', 5)
mySSH.command('echo ' + self.Password + ' | sudo -S ./stop_mme', '\$', 5)
+ time.sleep(5)
else:
logging.error('This should not happen!')
mySSH.close()
@@ -432,7 +511,9 @@ class EPCManagement():
def Terminate5GCN(self, HTML):
mySSH = SSH.SSHConnection()
mySSH.open(self.IPAddress, self.UserName, self.Password)
+ message = ''
if re.match('ltebox', self.Type, re.IGNORECASE):
+ logging.debug('Terminating SA BOX')
mySSH.command('cd /opt/ltebox/tools', '\$', 5)
mySSH.command('echo ' + self.Password + ' | sudo -S ./stop_sabox', '\$', 5)
time.sleep(1)
@@ -440,10 +521,33 @@ class EPCManagement():
mySSH.command('cd scripts', '\$', 5)
time.sleep(1)
mySSH.command('echo ' + self.Password + ' | sudo -S screen -S simulated_5g_hss -X quit', '\$', 5)
+ elif re.match('OAICN5G', self.Type, re.IGNORECASE):
+ logging.debug('OAI CN5G Collecting Log files to workspace')
+ mySSH.command('echo ' + self.Password + ' | sudo rm -rf ' + self.SourceCodePath + '/logs', '\$', 5)
+ mySSH.command('mkdir ' + self.SourceCodePath + '/logs','\$', 5)
+ containers_list=['oai-smf','oai-spgwu','oai-amf','oai-nrf']
+ for c in containers_list:
+ mySSH.command('docker logs ' + c + ' > ' + self.SourceCodePath + '/logs/' + c + '.log', '\$', 5)
+
+ logging.debug('Terminating OAI CN5G')
+ mySSH.command('cd /opt/oai-cn5g-fed/docker-compose', '\$', 5)
+ mySSH.command('./core-network.sh stop nrf spgwu', '\$', 60)
+ mySSH.command('docker volume prune --force || true', '\$', 60)
+ time.sleep(2)
+ mySSH.command('tshark -r /tmp/oai-cn5g.pcap | egrep --colour=never "Tracking area update" ','\$', 30)
+ result = re.search('Tracking area update request', mySSH.getBefore())
+ if result is not None:
+ message = 'UE requested ' + str(mySSH.getBefore().count('Tracking area update request')) + 'Tracking area update request(s)'
+ else:
+ message = 'No Tracking area update request'
+ logging.debug(message)
else:
logging.error('This should not happen!')
mySSH.close()
- HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
+ html_queue = SimpleQueue()
+ html_cell = '' + message + '
'
+ html_queue.put(html_cell)
+ HTML.CreateHtmlTestRowQueue(self.Type, 'OK', 1, html_queue)
def DeployEpc(self, HTML):
logging.debug('Trying to deploy')
@@ -466,6 +570,12 @@ class EPCManagement():
HTML.CreateHtmlTabFooter(False)
sys.exit('docker-compose not installed on ' + self.IPAddress)
+ # Checking if it is a MAGMA deployment
+ self.isMagmaUsed = False
+ if os.path.isfile('./' + self.yamlPath + '/redis_extern.conf'):
+ self.isMagmaUsed = True
+ logging.debug('MAGMA MME is used!')
+
mySSH.command('if [ -d ' + self.SourceCodePath + '/scripts ]; then echo ' + self.Password + ' | sudo -S rm -Rf ' + self.SourceCodePath + '/scripts ; fi', '\$', 5)
mySSH.command('if [ -d ' + self.SourceCodePath + '/logs ]; then echo ' + self.Password + ' | sudo -S rm -Rf ' + self.SourceCodePath + '/logs ; fi', '\$', 5)
mySSH.command('mkdir -p ' + self.SourceCodePath + '/scripts ' + self.SourceCodePath + '/logs', '\$', 5)
@@ -477,10 +587,19 @@ class EPCManagement():
# - docker-compose config | grep container_name
mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
mySSH.copyout(self.IPAddress, self.UserName, self.Password, './' + self.yamlPath + '/docker-compose.yml', self.SourceCodePath + '/scripts')
+ if self.isMagmaUsed:
+ mySSH.copyout(self.IPAddress, self.UserName, self.Password, './' + self.yamlPath + '/entrypoint.sh', self.SourceCodePath + '/scripts')
+ mySSH.copyout(self.IPAddress, self.UserName, self.Password, './' + self.yamlPath + '/mme.conf', self.SourceCodePath + '/scripts')
+ mySSH.copyout(self.IPAddress, self.UserName, self.Password, './' + self.yamlPath + '/mme_fd.sprint.conf', self.SourceCodePath + '/scripts')
+ mySSH.copyout(self.IPAddress, self.UserName, self.Password, './' + self.yamlPath + '/redis_extern.conf', self.SourceCodePath + '/scripts')
+ mySSH.command('chmod a+x ' + self.SourceCodePath + '/scripts/entrypoint.sh', '\$', 5)
+ else:
+ mySSH.copyout(self.IPAddress, self.UserName, self.Password, './' + self.yamlPath + '/entrypoint.sh', self.SourceCodePath + '/scripts')
+ mySSH.copyout(self.IPAddress, self.UserName, self.Password, './' + self.yamlPath + '/mme.conf', self.SourceCodePath + '/scripts')
+ mySSH.command('chmod 775 entrypoint.sh', '\$', 60)
mySSH.command('wget --quiet --tries=3 --retry-connrefused https://raw.githubusercontent.com/OPENAIRINTERFACE/openair-hss/develop/src/hss_rel14/db/oai_db.cql', '\$', 30)
mySSH.command('docker-compose down', '\$', 60)
mySSH.command('docker-compose up -d db_init', '\$', 60)
-
# databases take time...
time.sleep(10)
cnt = 0
@@ -502,8 +621,12 @@ class EPCManagement():
# deploying EPC cNFs
mySSH.command('docker-compose up -d oai_spgwu', '\$', 60)
- listOfContainers = 'prod-cassandra prod-oai-hss prod-oai-mme prod-oai-spgwc prod-oai-spgwu-tiny'
- expectedHealthyContainers = 5
+ if self.isMagmaUsed:
+ listOfContainers = 'prod-cassandra prod-oai-hss prod-magma-mme prod-oai-spgwc prod-oai-spgwu-tiny prod-redis'
+ expectedHealthyContainers = 6
+ else:
+ listOfContainers = 'prod-cassandra prod-oai-hss prod-oai-mme prod-oai-spgwc prod-oai-spgwu-tiny'
+ expectedHealthyContainers = 5
# Checking for additional services
mySSH.command('docker-compose config', '\$', 5)
@@ -517,6 +640,30 @@ class EPCManagement():
listOfContainers += ' prod-trf-gen'
expectedHealthyContainers += 1
+ mySSH.command('docker-compose config | grep --colour=never image', '\$', 10)
+ html_cell = '\n'
+ listOfImages = mySSH.getBefore()
+ for imageLine in listOfImages.split('\\r\\n'):
+ res1 = re.search('image: (?P[a-zA-Z0-9\-]+):(?P[a-zA-Z0-9\-]+)', str(imageLine))
+ res2 = re.search('cassandra|redis', str(imageLine))
+ if res1 is not None and res2 is None:
+ html_cell += res1.group('name') + ':' + res1.group('tag') + ' '
+ nbChars = len(res1.group('name')) + len(res1.group('tag')) + 2
+ while (nbChars < 32):
+ html_cell += ' '
+ nbChars += 1
+ mySSH.command('docker image inspect --format="Size = {{.Size}} bytes" ' + res1.group('name') + ':' + res1.group('tag'), '\$', 10)
+ res3 = re.search('Size *= *(?P[0-9\-]*) *bytes', mySSH.getBefore())
+ if res3 is not None:
+ imageSize = int(res3.group('size'))
+ imageSize = int(imageSize/(1024*1024))
+ html_cell += str(imageSize) + ' MBytes '
+ mySSH.command('docker image inspect --format="Date = {{.Created}}" ' + res1.group('name') + ':' + res1.group('tag'), '\$', 10)
+ res4 = re.search('Date *= *(?P[0-9\-]*)T', mySSH.getBefore())
+ if res4 is not None:
+ html_cell += '(' + res4.group('date') + ')'
+ html_cell += '\n'
+ html_cell += ''
# Checking if all are healthy
cnt = 0
while (cnt < 3):
@@ -532,19 +679,24 @@ class EPCManagement():
logging.debug(' -- ' + str(healthyNb) + ' healthy container(s)')
logging.debug(' -- ' + str(unhealthyNb) + ' unhealthy container(s)')
logging.debug(' -- ' + str(startingNb) + ' still starting container(s)')
+ html_queue = SimpleQueue()
+ html_queue.put(html_cell)
if healthyNb == expectedHealthyContainers:
mySSH.command('docker exec -d prod-oai-hss /bin/bash -c "nohup tshark -i any -f \'port 9042 or port 3868\' -w /tmp/hss_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
- mySSH.command('docker exec -d prod-oai-mme /bin/bash -c "nohup tshark -i any -f \'port 3868 or port 2123 or port 36412\' -w /tmp/mme_check_run.pcap 2>&1 > /dev/null"', '\$', 10)
+ if self.isMagmaUsed:
+ mySSH.command('docker exec -d prod-magma-mme /bin/bash -c "nohup tshark -i any -f \'port 3868 or port 2123 or port 36412\' -w /tmp/mme_check_run.pcap 2>&1 > /dev/null"', '\$', 10)
+ else:
+ mySSH.command('docker exec -d prod-oai-mme /bin/bash -c "nohup tshark -i any -f \'port 3868 or port 2123 or port 36412\' -w /tmp/mme_check_run.pcap 2>&1 > /dev/null"', '\$', 10)
mySSH.command('docker exec -d prod-oai-spgwc /bin/bash -c "nohup tshark -i any -f \'port 2123 or port 8805\' -w /tmp/spgwc_check_run.pcap 2>&1 > /dev/null"', '\$', 10)
# on SPGW-U, not capturing on SGI to avoid huge file
mySSH.command('docker exec -d prod-oai-spgwu-tiny /bin/bash -c "nohup tshark -i any -f \'port 8805\' -w /tmp/spgwu_check_run.pcap 2>&1 > /dev/null"', '\$', 10)
mySSH.close()
logging.debug('Deployment OK')
- HTML.CreateHtmlTestRow(self.Type, 'OK', CONST.ALL_PROCESSES_OK)
+ HTML.CreateHtmlTestRowQueue(self.Type, 'OK', 1, html_queue)
else:
mySSH.close()
logging.debug('Deployment went wrong')
- HTML.CreateHtmlTestRow(self.Type, 'KO', CONST.INVALID_PARAMETER)
+ HTML.CreateHtmlTestRowQueue(self.Type, 'KO', 1, html_queue)
def UndeployEpc(self, HTML):
logging.debug('Trying to undeploy')
@@ -552,24 +704,52 @@ class EPCManagement():
mySSH = SSH.SSHConnection()
mySSH.open(self.IPAddress, self.UserName, self.Password)
+ # Checking if it is a MAGMA deployment.
+ mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
+ mySSH.command('docker-compose ps -a', '\$', 5)
+ self.isMagmaUsed = False
+ result = re.search('magma', mySSH.getBefore())
+ if result is not None:
+ self.isMagmaUsed = True
+ logging.debug('MAGMA MME is used!')
# Recovering logs and pcap files
mySSH.command('cd ' + self.SourceCodePath + '/logs', '\$', 5)
mySSH.command('docker exec -it prod-oai-hss /bin/bash -c "killall --signal SIGINT oai_hss tshark"', '\$', 5)
- mySSH.command('docker exec -it prod-oai-mme /bin/bash -c "killall --signal SIGINT tshark"', '\$', 5)
+ if self.isMagmaUsed:
+ mySSH.command('docker exec -it prod-magma-mme /bin/bash -c "killall --signal SIGINT tshark"', '\$', 5)
+ else:
+ mySSH.command('docker exec -it prod-oai-mme /bin/bash -c "killall --signal SIGINT tshark"', '\$', 5)
mySSH.command('docker exec -it prod-oai-spgwc /bin/bash -c "killall --signal SIGINT oai_spgwc tshark"', '\$', 5)
mySSH.command('docker exec -it prod-oai-spgwu-tiny /bin/bash -c "killall --signal SIGINT tshark"', '\$', 5)
mySSH.command('docker logs prod-oai-hss > hss_' + self.testCase_id + '.log', '\$', 5)
- mySSH.command('docker logs prod-oai-mme > mme_' + self.testCase_id + '.log', '\$', 5)
+ if self.isMagmaUsed:
+ mySSH.command('docker cp --follow-link prod-magma-mme:/var/log/mme.log mme_' + self.testCase_id + '.log', '\$', 15)
+ else:
+ mySSH.command('docker logs prod-oai-mme > mme_' + self.testCase_id + '.log', '\$', 5)
mySSH.command('docker logs prod-oai-spgwc > spgwc_' + self.testCase_id + '.log', '\$', 5)
mySSH.command('docker logs prod-oai-spgwu-tiny > spgwu_' + self.testCase_id + '.log', '\$', 5)
mySSH.command('docker cp prod-oai-hss:/tmp/hss_check_run.pcap hss_' + self.testCase_id + '.pcap', '\$', 60)
- mySSH.command('docker cp prod-oai-mme:/tmp/mme_check_run.pcap mme_' + self.testCase_id + '.pcap', '\$', 60)
+ if self.isMagmaUsed:
+ mySSH.command('docker cp prod-magma-mme:/tmp/mme_check_run.pcap mme_' + self.testCase_id + '.pcap', '\$', 60)
+ else:
+ mySSH.command('docker cp prod-oai-mme:/tmp/mme_check_run.pcap mme_' + self.testCase_id + '.pcap', '\$', 60)
+ mySSH.command('tshark -r mme_' + self.testCase_id + '.pcap | egrep --colour=never "Tracking area update"', '\$', 60)
+ result = re.search('Tracking area update request', mySSH.getBefore())
+ if result is not None:
+ message = 'UE requested ' + str(mySSH.getBefore().count('Tracking area update request')) + 'Tracking area update request(s)'
+ else:
+ message = 'No Tracking area update request'
+ logging.debug(message)
mySSH.command('docker cp prod-oai-spgwc:/tmp/spgwc_check_run.pcap spgwc_' + self.testCase_id + '.pcap', '\$', 60)
mySSH.command('docker cp prod-oai-spgwu-tiny:/tmp/spgwu_check_run.pcap spgwu_' + self.testCase_id + '.pcap', '\$', 60)
# Remove all
mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
- listOfContainers = 'prod-cassandra prod-oai-hss prod-oai-mme prod-oai-spgwc prod-oai-spgwu-tiny'
- nbContainers = 5
+ if self.isMagmaUsed:
+ listOfContainers = 'prod-cassandra prod-oai-hss prod-magma-mme prod-oai-spgwc prod-oai-spgwu-tiny prod-redis'
+ nbContainers = 6
+ else:
+ listOfContainers = 'prod-cassandra prod-oai-hss prod-oai-mme prod-oai-spgwc prod-oai-spgwu-tiny'
+ nbContainers = 5
# Checking for additional services
mySSH.command('docker-compose config', '\$', 5)
configResponse = mySSH.getBefore()
@@ -581,17 +761,21 @@ class EPCManagement():
nbContainers += 1
mySSH.command('docker-compose down', '\$', 60)
+ mySSH.command('docker volume prune --force || true', '\$', 60)
mySSH.command('docker inspect --format=\'{{.State.Health.Status}}\' ' + listOfContainers, '\$', 10)
noMoreContainerNb = mySSH.getBefore().count('No such object')
mySSH.command('docker inspect --format=\'{{.Name}}\' prod-oai-public-net prod-oai-private-net', '\$', 10)
noMoreNetworkNb = mySSH.getBefore().count('No such object')
mySSH.close()
+ html_queue = SimpleQueue()
+ html_cell = '' + message + '
'
+ html_queue.put(html_cell)
if noMoreContainerNb == nbContainers and noMoreNetworkNb == 2:
logging.debug('Undeployment OK')
- HTML.CreateHtmlTestRow(self.Type, 'OK', CONST.ALL_PROCESSES_OK)
+ HTML.CreateHtmlTestRowQueue(self.Type, 'OK', 1, html_queue)
else:
logging.debug('Undeployment went wrong')
- HTML.CreateHtmlTestRow(self.Type, 'KO', CONST.INVALID_PARAMETER)
+ HTML.CreateHtmlTestRowQueue(self.Type, 'KO', 1, html_queue)
def LogCollectHSS(self):
mySSH = SSH.SSHConnection()
@@ -610,6 +794,8 @@ class EPCManagement():
mySSH.command('docker cp ' + self.containerPrefix + '-oai-hss:/openair-hss/hss_check_run.log .', '\$', 60)
mySSH.command('docker cp ' + self.containerPrefix + '-oai-hss:/tmp/hss_check_run.pcap .', '\$', 60)
mySSH.command('zip hss.log.zip hss_check_run.*', '\$', 60)
+ elif re.match('OAICN5G', self.Type, re.IGNORECASE):
+ logging.debug('LogCollect is bypassed for that variant')
elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
mySSH.command('zip hss.log.zip hss*.log', '\$', 60)
mySSH.command('echo ' + self.Password + ' | sudo -S rm hss*.log', '\$', 5)
@@ -640,6 +826,11 @@ class EPCManagement():
mySSH.command('docker cp ' + self.containerPrefix + '-oai-mme:/openair-mme/mme_check_run.log .', '\$', 60)
mySSH.command('docker cp ' + self.containerPrefix + '-oai-mme:/tmp/mme_check_run.pcap .', '\$', 60)
mySSH.command('zip mme.log.zip mme_check_run.*', '\$', 60)
+ elif re.match('OAICN5G', self.Type, re.IGNORECASE):
+ mySSH.command('cd ' + self.SourceCodePath + '/logs','\$', 5)
+ mySSH.command('cp -f /tmp/oai-cn5g.pcap .','\$', 30)
+ mySSH.command('zip mme.log.zip oai-amf.log oai-nrf.log oai-cn5g.pcap','\$', 30)
+ mySSH.command('mv mme.log.zip ' + self.SourceCodePath + '/scripts','\$', 30)
elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
mySSH.command('zip mme.log.zip mme*.log', '\$', 60)
mySSH.command('echo ' + self.Password + ' | sudo -S rm mme*.log', '\$', 5)
@@ -669,6 +860,10 @@ class EPCManagement():
mySSH.command('docker cp ' + self.containerPrefix + '-oai-spgwc:/tmp/spgwc_check_run.pcap .', '\$', 60)
mySSH.command('docker cp ' + self.containerPrefix + '-oai-spgwu-tiny:/tmp/spgwu_check_run.pcap .', '\$', 60)
mySSH.command('zip spgw.log.zip spgw*_check_run.*', '\$', 60)
+ elif re.match('OAICN5G', self.Type, re.IGNORECASE):
+ mySSH.command('cd ' + self.SourceCodePath + '/logs','\$', 5)
+ mySSH.command('zip spgw.log.zip oai-smf.log oai-spgwu.log','\$', 30)
+ mySSH.command('mv spgw.log.zip ' + self.SourceCodePath + '/scripts','\$', 30)
elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
mySSH.command('zip spgw.log.zip spgw*.log', '\$', 60)
mySSH.command('echo ' + self.Password + ' | sudo -S rm spgw*.log', '\$', 5)
@@ -678,4 +873,3 @@ class EPCManagement():
else:
logging.error('This option should not occur!')
mySSH.close()
-
diff --git a/ci-scripts/main.py b/ci-scripts/main.py
index d66c0c6305..d8cad1c962 100644
--- a/ci-scripts/main.py
+++ b/ci-scripts/main.py
@@ -154,6 +154,12 @@ def GetParametersFromXML(action):
elif action == 'Initialize_eNB':
RAN.eNB_Trace=test.findtext('eNB_Trace')
+ RAN.eNB_Stats=test.findtext('eNB_Stats')
+ datalog_rt_stats_file=test.findtext('rt_stats_cfg')
+ if datalog_rt_stats_file is None:
+ RAN.datalog_rt_stats_file='datalog_rt_stats.default.yaml'
+ else:
+ RAN.datalog_rt_stats_file=datalog_rt_stats_file
RAN.Initialize_eNB_args=test.findtext('Initialize_eNB_args')
eNB_instance=test.findtext('eNB_instance')
USRPIPAddress=test.findtext('USRP_IPAddress')
@@ -294,6 +300,11 @@ def GetParametersFromXML(action):
CiTestObj.ue_id = ""
else:
CiTestObj.ue_id = ue_id
+ ping_rttavg_threshold = test.findtext('ping_rttavg_threshold')
+ if (ping_rttavg_threshold is None):
+ CiTestObj.ping_rttavg_threshold = ""
+ else:
+ CiTestObj.ping_rttavg_threshold = ping_rttavg_threshold
elif action == 'Iperf':
CiTestObj.iperf_args = test.findtext('iperf_args')
@@ -304,6 +315,13 @@ def GetParametersFromXML(action):
CiTestObj.ue_id = ue_id
CiTestObj.iperf_direction = test.findtext('direction')#used for modules only
CiTestObj.iperf_packetloss_threshold = test.findtext('iperf_packetloss_threshold')
+ iperf_bitrate_threshold = test.findtext('iperf_bitrate_threshold')
+ if (iperf_bitrate_threshold is None):
+ CiTestObj.iperf_bitrate_threshold = "90" #if no threshold is specified, default will be 90%
+ else:
+ CiTestObj.iperf_bitrate_threshold = iperf_bitrate_threshold
+
+
CiTestObj.iperf_profile = test.findtext('iperf_profile')
if (CiTestObj.iperf_profile is None):
CiTestObj.iperf_profile = 'balanced'
@@ -409,6 +427,17 @@ def GetParametersFromXML(action):
if (string_field is not None):
CONTAINERS.cliOptions = string_field
+ elif action == 'Copy_Image_to_Test':
+ string_field = test.findtext('image_name')
+ if (string_field is not None):
+ CONTAINERS.imageToCopy = string_field
+ string_field = test.findtext('registry_svr_id')
+ if (string_field is not None):
+ CONTAINERS.registrySvrId = string_field
+ string_field = test.findtext('test_svr_id')
+ if (string_field is not None):
+ CONTAINERS.testSvrId = string_field
+
else: # ie action == 'Run_PhySim':
ldpc.runargs = test.findtext('physim_run_args')
@@ -532,7 +561,7 @@ elif re.match('^TerminateOAIUE$', mode, re.IGNORECASE):
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
signal.signal(signal.SIGUSR1, receive_signal)
- CiTestObj.TerminateOAIUE(HTML,RAN,COTS_UE,EPC,InfraUE)
+ CiTestObj.TerminateOAIUE(HTML,RAN,COTS_UE,EPC,InfraUE,CONTAINERS)
elif re.match('^TerminateHSS$', mode, re.IGNORECASE):
if EPC.IPAddress == '' or EPC.UserName == '' or EPC.Password == '' or EPC.Type == '' or EPC.SourceCodePath == '':
HELP.GenericHelp(CONST.Version)
@@ -733,6 +762,22 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
HTML.SethtmlUEConnected(len(CiTestObj.UEDevices) + len(CiTestObj.CatMDevices))
HTML.CreateHtmlTabHeader()
+ # On CI bench w/ containers, we need to validate if IP routes are set
+ if EPC.IPAddress == '192.168.18.210':
+ CONTAINERS.CheckAndAddRoute('porcepix', EPC.IPAddress, EPC.UserName, EPC.Password)
+ if CONTAINERS.eNBIPAddress == '192.168.18.194':
+ CONTAINERS.CheckAndAddRoute('asterix', CONTAINERS.eNBIPAddress, CONTAINERS.eNBUserName, CONTAINERS.eNBPassword)
+ if CONTAINERS.eNB1IPAddress == '192.168.18.194':
+ CONTAINERS.CheckAndAddRoute('asterix', CONTAINERS.eNB1IPAddress, CONTAINERS.eNB1UserName, CONTAINERS.eNB1Password)
+ if CONTAINERS.eNBIPAddress == '192.168.18.193':
+ CONTAINERS.CheckAndAddRoute('obelix', CONTAINERS.eNBIPAddress, CONTAINERS.eNBUserName, CONTAINERS.eNBPassword)
+ if CONTAINERS.eNB1IPAddress == '192.168.18.193':
+ CONTAINERS.CheckAndAddRoute('obelix', CONTAINERS.eNB1IPAddress, CONTAINERS.eNB1UserName, CONTAINERS.eNB1Password)
+ if CONTAINERS.eNBIPAddress == '192.168.18.209':
+ CONTAINERS.CheckAndAddRoute('nepes', CONTAINERS.eNBIPAddress, CONTAINERS.eNBUserName, CONTAINERS.eNBPassword)
+ if CONTAINERS.eNB1IPAddress == '192.168.18.209':
+ CONTAINERS.CheckAndAddRoute('nepes', CONTAINERS.eNB1IPAddress, CONTAINERS.eNB1UserName, CONTAINERS.eNB1Password)
+
CiTestObj.FailReportCnt = 0
RAN.prematureExit=True
HTML.startTime=int(round(time.time() * 1000))
@@ -778,42 +823,44 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
check_OAI_UE = False
RAN.pStatus=CiTestObj.CheckProcessExist(check_eNB, check_OAI_UE,RAN,EPC)
RAN.InitializeeNB(HTML, EPC)
+ if RAN.prematureExit:
+ CiTestObj.AutoTerminateeNB(HTML,RAN,EPC,CONTAINERS)
elif action == 'Terminate_eNB':
RAN.TerminateeNB(HTML, EPC)
elif action == 'Initialize_UE':
- CiTestObj.InitializeUE(HTML,RAN, EPC, COTS_UE, InfraUE, CiTestObj.ue_trace)
+ CiTestObj.InitializeUE(HTML,RAN, EPC, COTS_UE, InfraUE, CiTestObj.ue_trace, CONTAINERS)
elif action == 'Terminate_UE':
CiTestObj.TerminateUE(HTML,COTS_UE, InfraUE, CiTestObj.ue_trace)
elif action == 'Attach_UE':
- CiTestObj.AttachUE(HTML,RAN,EPC,COTS_UE,InfraUE)
+ CiTestObj.AttachUE(HTML,RAN,EPC,COTS_UE,InfraUE,CONTAINERS)
elif action == 'Detach_UE':
- CiTestObj.DetachUE(HTML,RAN,EPC,COTS_UE,InfraUE)
+ CiTestObj.DetachUE(HTML,RAN,EPC,COTS_UE,InfraUE,CONTAINERS)
elif action == 'DataDisable_UE':
CiTestObj.DataDisableUE(HTML)
elif action == 'DataEnable_UE':
CiTestObj.DataEnableUE(HTML)
elif action == 'CheckStatusUE':
- CiTestObj.CheckStatusUE(HTML,RAN,EPC,COTS_UE,InfraUE)
+ CiTestObj.CheckStatusUE(HTML,RAN,EPC,COTS_UE,InfraUE,CONTAINERS)
elif action == 'Build_OAI_UE':
CiTestObj.BuildOAIUE(HTML)
elif action == 'Initialize_OAI_UE':
- CiTestObj.InitializeOAIUE(HTML,RAN,EPC,COTS_UE,InfraUE)
+ CiTestObj.InitializeOAIUE(HTML,RAN,EPC,COTS_UE,InfraUE,CONTAINERS)
elif action == 'Terminate_OAI_UE':
- CiTestObj.TerminateOAIUE(HTML,RAN,COTS_UE,EPC,InfraUE)
+ CiTestObj.TerminateOAIUE(HTML,RAN,COTS_UE,EPC,InfraUE,CONTAINERS)
elif action == 'Initialize_CatM_module':
CiTestObj.InitializeCatM(HTML)
elif action == 'Terminate_CatM_module':
CiTestObj.TerminateCatM(HTML)
elif action == 'Attach_CatM_module':
- CiTestObj.AttachCatM(HTML,RAN,COTS_UE,EPC,InfraUE)
+ CiTestObj.AttachCatM(HTML,RAN,COTS_UE,EPC,InfraUE,CONTAINERS)
elif action == 'Detach_CatM_module':
CiTestObj.TerminateCatM(HTML)
elif action == 'Ping_CatM_module':
- CiTestObj.PingCatM(HTML,RAN,EPC,COTS_UE,EPC,InfraUE)
+ CiTestObj.PingCatM(HTML,RAN,EPC,COTS_UE,EPC,InfraUE,CONTAINERS)
elif action == 'Ping':
- CiTestObj.Ping(HTML,RAN,EPC,COTS_UE, InfraUE)
+ CiTestObj.Ping(HTML,RAN,EPC,COTS_UE, InfraUE, CONTAINERS)
elif action == 'Iperf':
- CiTestObj.Iperf(HTML,RAN,EPC,COTS_UE, InfraUE)
+ CiTestObj.Iperf(HTML,RAN,EPC,COTS_UE, InfraUE, CONTAINERS)
elif action == 'Reboot_UE':
CiTestObj.RebootUE(HTML,RAN,EPC)
elif action == 'Initialize_HSS':
@@ -852,6 +899,8 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
HTML=ldpc.Run_PhySim(HTML,CONST,id)
elif action == 'Build_Image':
CONTAINERS.BuildImage(HTML)
+ elif action == 'Copy_Image_to_Test':
+ CONTAINERS.Copy_Image_to_Test_Server(HTML)
elif action == 'Deploy_Object':
CONTAINERS.DeployObject(HTML, EPC)
elif action == 'Undeploy_Object':
@@ -865,7 +914,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
if CONTAINERS.exitStatus==1:
RAN.prematureExit = True
elif action == 'UndeployGenObject':
- CONTAINERS.UndeployGenObject(HTML)
+ CONTAINERS.UndeployGenObject(HTML, RAN)
if CONTAINERS.exitStatus==1:
RAN.prematureExit = True
elif action == 'PingFromContainer':
diff --git a/ci-scripts/mysql4testresults/docker-compose.yml b/ci-scripts/mysql4testresults/docker-compose.yml
new file mode 100644
index 0000000000..1c60057ab2
--- /dev/null
+++ b/ci-scripts/mysql4testresults/docker-compose.yml
@@ -0,0 +1,12 @@
+version: '2'
+services:
+ mysql:
+ container_name: oaicicd_mysql
+ restart: always
+ image: mysql:latest
+ environment:
+ MYSQL_ROOT_PASSWORD: 'ucZBc2XRYdvEm59F'
+ ports:
+ - "3307:3306"
+ volumes:
+ - /home/oaicicd/mysql/data:/var/lib/mysql
diff --git a/ci-scripts/mysql4testresults/sql_connect.py b/ci-scripts/mysql4testresults/sql_connect.py
new file mode 100644
index 0000000000..333e573e78
--- /dev/null
+++ b/ci-scripts/mysql4testresults/sql_connect.py
@@ -0,0 +1,33 @@
+import pymysql
+import sys
+from datetime import datetime
+
+#This is the script used to write the test results to the mysql DB
+#Called by Jenkins pipeline (jenkinsfile)
+#Must be located in /home/oaicicid/mysql on the database host
+#Usage from Jenkinsfile :
+#python3 /home/oaicicd/mysql/sql_connect.py ${JOB_NAME} ${params.eNB_MR} ${params.eNB_Branch} ${env.BUILD_ID} ${env.BUILD_URL} ${StatusForDb} ''
+
+class SQLConnect:
+ def __init__(self):
+ self.connection = pymysql.connect(
+ host='172.22.0.2',
+ user='root',
+ password = 'ucZBc2XRYdvEm59F',
+ db='oaicicd_tests',
+ port=3306
+ )
+
+ def put(self,TEST,MR,BRANCH,BUILD,BUILD_LINK,STATUS):
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ cur=self.connection.cursor()
+ cur.execute ('INSERT INTO test_results (TEST,MR,BRANCH,BUILD,BUILD_LINK,STATUS,DATE) VALUES (%s,%s,%s,%s,%s,%s,%s);' , (TEST, MR, BRANCH, BUILD, BUILD_LINK, STATUS, now))
+ self.connection.commit()
+ self.connection.close()
+
+
+if __name__ == "__main__":
+ mydb=SQLConnect()
+ mydb.put(sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4],sys.argv[5],sys.argv[6])
+
+
diff --git a/ci-scripts/ran.py b/ci-scripts/ran.py
index 5e3d8e388f..baf3a00725 100644
--- a/ci-scripts/ran.py
+++ b/ci-scripts/ran.py
@@ -39,7 +39,6 @@ import time
from multiprocessing import Process, Lock, SimpleQueue
import yaml
-
#-----------------------------------------------------------
# OAI Testing modules
#-----------------------------------------------------------
@@ -93,7 +92,9 @@ class RANManagement():
self.epcPcapFile = ''
self.runtime_stats= ''
self.datalog_rt_stats={}
+ self.datalog_rt_stats_file='datalog_rt_stats.default.yaml'
self.eNB_Trace = '' #if 'yes', Tshark will be launched at initialization
+ self.eNB_Stats = '' #if 'yes', Statistics Monitor will be launched at initialization
self.USRPIPAddress = ''
@@ -159,22 +160,23 @@ class RANManagement():
# Raphael: here add a check if git clone or git fetch went smoothly
mySSH.command('git config user.email "jenkins@openairinterface.org"', '\$', 5)
mySSH.command('git config user.name "OAI Jenkins"', '\$', 5)
+ mySSH.command('git advice.detachedHead false', '\$', 5)
# Checking the BUILD INFO file
if not self.backgroundBuild:
mySSH.command('ls *.txt', '\$', 5)
result = re.search('LAST_BUILD_INFO', mySSH.getBefore())
if result is not None:
mismatch = False
- mySSH.command('grep SRC_COMMIT LAST_BUILD_INFO.txt', '\$', 2)
+ mySSH.command('grep --colour=never SRC_COMMIT LAST_BUILD_INFO.txt', '\$', 2)
result = re.search(self.ranCommitID, mySSH.getBefore())
if result is None:
mismatch = True
- mySSH.command('grep MERGED_W_TGT_BRANCH LAST_BUILD_INFO.txt', '\$', 2)
+ mySSH.command('grep --colour=never MERGED_W_TGT_BRANCH LAST_BUILD_INFO.txt', '\$', 2)
if (self.ranAllowMerge):
result = re.search('YES', mySSH.getBefore())
if result is None:
mismatch = True
- mySSH.command('grep TGT_BRANCH LAST_BUILD_INFO.txt', '\$', 2)
+ mySSH.command('grep --colour=never TGT_BRANCH LAST_BUILD_INFO.txt', '\$', 2)
if self.ranTargetBranch == '':
result = re.search('develop', mySSH.getBefore())
else:
@@ -203,6 +205,7 @@ class RANManagement():
else:
logging.debug('Merging with the target branch: ' + self.ranTargetBranch)
mySSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5)
+ logging.debug(mySSH.getBefore()) # print what git said when merging/checking out
mySSH.command('source oaienv', '\$', 5)
mySSH.command('cd cmake_targets', '\$', 5)
mySSH.command('mkdir -p log', '\$', 5)
@@ -212,6 +215,7 @@ class RANManagement():
mySSH.command('echo "./build_oai ' + self.Build_eNB_args + '" > ./my-lte-softmodem-build.sh', '\$', 5)
mySSH.command('chmod 775 ./my-lte-softmodem-build.sh', '\$', 5)
mySSH.command('echo ' + lPassWord + ' | sudo -S ls', '\$', 5)
+ logging.debug(mySSH.getBefore()) # print current directory contents for verification
mySSH.command('echo $USER; nohup sudo -E ./my-lte-softmodem-build.sh' + ' > ' + lSourcePath + '/cmake_targets/compile_oai_enb.log ' + ' 2>&1 &', lUserName, 5)
mySSH.close()
HTML.CreateHtmlTestRow(self.Build_eNB_args, 'OK', CONST.ALL_PROCESSES_OK)
@@ -341,10 +345,12 @@ class RANManagement():
self.testCase_id = HTML.testCase_id
mySSH = SSH.SSHConnection()
+ cwd = os.getcwd()
+ mySSH.copyout(lIpAddr,lUserName,lPassWord, cwd + "/active_net_interfaces.awk", "/tmp")
#reboot USRP if requested in xml
if self.USRPIPAddress!='':
- logging.debug('USRP '+ self.USRPIPAddress +'reboot request')
+ logging.debug('USRP '+ self.USRPIPAddress +': reboot request')
mySSH.open(lIpAddr, lUserName, lPassWord)
cmd2usrp='ssh root@'+self.USRPIPAddress+' reboot'
mySSH.command2(cmd2usrp,1)
@@ -369,10 +375,11 @@ class RANManagement():
result = re.search('interfaceToUse=(?P[a-zA-Z0-9\-\_]+)done', mySSH.getBefore())
if result is not None:
eth_interface = result.group('eth_interface')
- logging.debug('\u001B[1m Launching tshark on interface ' + eth_interface + '\u001B[0m')
+ fltr = 'port 38412 or port 36412 or port 36422' # NGAP, S1AP, X2AP
+ logging.debug('\u001B[1m Launching tshark on interface ' + eth_interface + ' with filter "' + fltr + '"\u001B[0m')
pcapfile = pcapfile_prefix + self.testCase_id + '_log.pcap'
mySSH.command('echo ' + lPassWord + ' | sudo -S rm -f /tmp/' + pcapfile , '\$', 5)
- mySSH.command('echo $USER; nohup sudo -E tshark -i ' + eth_interface + ' -w /tmp/' + pcapfile + ' 2>&1 &','\$', 5)
+ mySSH.command('echo $USER; nohup sudo -E tshark -i ' + eth_interface + ' -f "' + fltr + '" -w /tmp/' + pcapfile + ' > /dev/null 2>&1 &','\$', 5)
mySSH.close()
@@ -388,10 +395,11 @@ class RANManagement():
result = re.search('interfaceToUse=(?P[a-zA-Z0-9\-\_]+)done', mySSH.getBefore())
if result is not None:
eth_interface = result.group('eth_interface')
- logging.debug('\u001B[1m Launching tshark on interface ' + eth_interface + '\u001B[0m')
+ fltr = 'port 38412 or port 36412 or port 36422' # NGAP, S1AP, X2AP
+ logging.debug('\u001B[1m Launching tshark on interface ' + eth_interface + ' with filter "' + fltr + '"\u001B[0m')
self.epcPcapFile = 'enb_' + self.testCase_id + '_s1log.pcap'
mySSH.command('echo ' + localEpcPassword + ' | sudo -S rm -f /tmp/' + self.epcPcapFile , '\$', 5)
- mySSH.command('echo $USER; nohup sudo tshark -f "host ' + lIpAddr +'" -i ' + eth_interface + ' -w /tmp/' + self.epcPcapFile + ' > /tmp/tshark.log 2>&1 &', localEpcUserName, 5)
+ mySSH.command('echo $USER; nohup sudo tshark -f "host ' + lIpAddr +'" -i ' + eth_interface + ' -f "' + fltr + '" -w /tmp/' + self.epcPcapFile + ' > /tmp/tshark.log 2>&1 &', localEpcUserName, 5)
mySSH.close()
mySSH.open(lIpAddr, lUserName, lPassWord)
mySSH.command('cd ' + lSourcePath, '\$', 5)
@@ -421,13 +429,13 @@ class RANManagement():
# do not reset board twice in IF4.5 case
result = re.search('^rru|^enb|^du.band', str(config_file))
if result is not None:
- mySSH.command('echo ' + lPassWord + ' | sudo -S uhd_find_devices', '\$', 90)
+ mySSH.command('echo ' + lPassWord + ' | sudo -S uhd_find_devices', '\$', 180)
result = re.search('type: b200', mySSH.getBefore())
if result is not None:
logging.debug('Found a B2xx device --> resetting it')
mySSH.command('echo ' + lPassWord + ' | sudo -S b2xx_fx3_utils --reset-device', '\$', 10)
# Reloading FGPA bin firmware
- mySSH.command('echo ' + lPassWord + ' | sudo -S uhd_find_devices', '\$', 90)
+ mySSH.command('echo ' + lPassWord + ' | sudo -S uhd_find_devices', '\$', 180)
# Make a copy and adapt to EPC / eNB IP addresses
mySSH.command('cp ' + full_config_file + ' ' + ci_full_config_file, '\$', 5)
localMmeIpAddr = EPC.MmeIPAddress
@@ -444,7 +452,7 @@ class RANManagement():
else:
mySSH.command('sed -i -e \'s/FLEXRAN_ENABLED.*;/FLEXRAN_ENABLED = "no";/\' ' + ci_full_config_file, '\$', 2);
self.eNBmbmsEnables[int(self.eNB_instance)] = False
- mySSH.command('grep enable_enb_m2 ' + ci_full_config_file, '\$', 2);
+ mySSH.command('grep --colour=never enable_enb_m2 ' + ci_full_config_file, '\$', 2);
result = re.search('yes', mySSH.getBefore())
if result is not None:
self.eNBmbmsEnables[int(self.eNB_instance)] = True
@@ -461,10 +469,36 @@ class RANManagement():
mySSH.command('if [ -e rbconfig.raw ]; then echo ' + lPassWord + ' | sudo -S rm rbconfig.raw; fi', '\$', 5)
mySSH.command('if [ -e reconfig.raw ]; then echo ' + lPassWord + ' | sudo -S rm reconfig.raw; fi', '\$', 5)
# NOTE: WE SHALL do a check if the executable is present (in case build went wrong)
- mySSH.command('echo "ulimit -c unlimited && ./ran_build/build/' + self.air_interface[self.eNB_instance] + ' -O ' + lSourcePath + '/' + ci_full_config_file + extra_options + '" > ./my-lte-softmodem-run' + str(self.eNB_instance) + '.sh', '\$', 5)
+
+ #hack UHD_RFNOC_DIR variable for gNB / N310 on RHEL8 server:
+ #if the USRP address is in the xml then we are using an eth USRP (N3xx)
+ if (self.air_interface[self.eNB_instance] == 'lte-softmodem') or (self.air_interface[self.eNB_instance] == 'ocp-enb'):
+ gNB = False
+ else:
+ gNB = True
+ if ((self.USRPIPAddress!='') and (gNB==True)):
+ mySSH.command('echo ' + lPassWord + ' | echo "ulimit -c unlimited && sudo UHD_RFNOC_DIR=/usr/local/share/uhd/rfnoc ./ran_build/build/' + self.air_interface[self.eNB_instance] + ' -O ' + lSourcePath + '/' + ci_full_config_file + extra_options + '" > ./my-lte-softmodem-run' + str(self.eNB_instance) + '.sh', '\$', 5)
+ #otherwise the regular command is ok
+ else:
+ mySSH.command('echo "ulimit -c unlimited && catchsegv ./ran_build/build/' + self.air_interface[self.eNB_instance] + ' -O ' + lSourcePath + '/' + ci_full_config_file + extra_options + '" > ./my-lte-softmodem-run' + str(self.eNB_instance) + '.sh', '\$', 5)
+
mySSH.command('chmod 775 ./my-lte-softmodem-run' + str(self.eNB_instance) + '.sh', '\$', 5)
mySSH.command('echo ' + lPassWord + ' | sudo -S rm -Rf enb_' + self.testCase_id + '.log', '\$', 5)
mySSH.command('echo $USER; nohup sudo -E ./my-lte-softmodem-run' + str(self.eNB_instance) + '.sh > ' + lSourcePath + '/cmake_targets/enb_' + self.testCase_id + '.log 2>&1 &', lUserName, 10)
+
+
+ #stats monitoring during runtime
+ time.sleep(20)
+ monitor_file='../ci-scripts/stats_monitor.py'
+ conf_file='../ci-scripts/stats_monitor_conf.yaml'
+ if self.eNB_Stats=='yes':
+ if (self.air_interface[self.eNB_instance] == 'lte-softmodem') or (self.air_interface[self.eNB_instance] == 'ocp-enb'):
+ mySSH.command('echo $USER; nohup python3 ' + monitor_file + ' ' + conf_file + ' enb 2>&1 > enb_stats_monitor_execution.log &', '\$', 5)
+ else:
+ mySSH.command('echo $USER; nohup python3 ' + monitor_file + ' ' + conf_file + ' gnb 2>&1 > gnb_stats_monitor_execution.log &', '\$', 5)
+
+
+
self.eNBLogFiles[int(self.eNB_instance)] = 'enb_' + self.testCase_id + '.log'
if extra_options != '':
self.eNBOptions[int(self.eNB_instance)] = extra_options
@@ -539,6 +573,8 @@ class RANManagement():
self.eNBstatuses[int(self.eNB_instance)] = int(self.eNB_serverId[self.eNB_instance])
mySSH.close()
+
+
HTML.CreateHtmlTestRow(self.air_interface[self.eNB_instance] + ' -O ' + config_file + extra_options, 'OK', CONST.ALL_PROCESSES_OK)
logging.debug('\u001B[1m Initialize eNB/gNB/ocp-eNB Completed\u001B[0m')
@@ -563,8 +599,12 @@ class RANManagement():
lPassWord = self.eNBPassword
mySSH = SSH.SSHConnection()
mySSH.open(lIpAddr, lUserName, lPassWord)
- mySSH.command('stdbuf -o0 ps -aux | grep --color=never ' + self.air_interface[self.eNB_instance] + ' | grep -v grep', '\$', 5)
- result = re.search(self.air_interface[self.eNB_instance], mySSH.getBefore())
+ if self.air_interface[self.eNB_instance] == '':
+ pattern = 'softmodem'
+ else:
+ pattern = self.air_interface[self.eNB_instance]
+ mySSH.command('stdbuf -o0 ps -aux | grep --color=never ' + pattern + ' | grep -v grep', '\$', 5)
+ result = re.search(pattern, mySSH.getBefore())
if result is None:
logging.debug('\u001B[1;37;41m eNB Process Not Found! \u001B[0m')
status_queue.put(CONST.ENB_PROCESS_FAILED)
@@ -657,6 +697,11 @@ class RANManagement():
fileToAnalyze = self.eNBLogFiles[int(self.eNB_instance)]
self.eNBLogFiles[int(self.eNB_instance)] = ''
if analyzeFile:
+ #*stats.log files + pickle + png
+ mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*stats.log', '.')
+ mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*.pickle', '.')
+ mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*.png', '.')
+ #
copyin_res = mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/' + fileToAnalyze, '.')
if (copyin_res == -1):
logging.debug('\u001B[1;37;41m Could not copy ' + nodeB_prefix + 'NB logfile to analyze it! \u001B[0m')
@@ -665,12 +710,23 @@ class RANManagement():
self.eNBmbmsEnables[int(self.eNB_instance)] = False
return
if self.eNB_serverId[self.eNB_instance] != '0':
+ #*stats.log files + pickle + png
+
+ #debug / tentative
+ mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './nrL1_stats.log', self.eNBSourceCodePath + '/cmake_targets/')
+ mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './nrMAC_stats.log', self.eNBSourceCodePath + '/cmake_targets/')
+ mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './gnb_stats_monitor.pickle', self.eNBSourceCodePath + '/cmake_targets/')
+ mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './gnb_stats_monitor.png', self.eNBSourceCodePath + '/cmake_targets/')
+ #
mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './' + fileToAnalyze, self.eNBSourceCodePath + '/cmake_targets/')
logging.debug('\u001B[1m Analyzing ' + nodeB_prefix + 'NB logfile \u001B[0m ' + fileToAnalyze)
logStatus = self.AnalyzeLogFile_eNB(fileToAnalyze, HTML)
if (logStatus < 0):
HTML.CreateHtmlTestRow('N/A', 'KO', logStatus)
- self.preamtureExit = True
+ #display rt stats for gNB only
+ if len(self.datalog_rt_stats)!=0 and nodeB_prefix == 'g':
+ HTML.CreateHtmlDataLogTable(self.datalog_rt_stats)
+ self.prematureExit = True
self.eNBmbmsEnables[int(self.eNB_instance)] = False
return
else:
@@ -691,8 +747,11 @@ class RANManagement():
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S mv /tmp/enb_*.pcap .','\$',20)
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S mv /tmp/gnb_*.pcap .','\$',20)
mySSH.command('echo ' + self.eNBPassword + ' | sudo -S rm -f enb.log.zip', '\$', 5)
- mySSH.command('echo ' + self.eNBPassword + ' | sudo -S zip enb.log.zip enb*.log core* enb_*record.raw enb_*.pcap gnb_*.pcap enb_*txt physim_*.log *stats.log', '\$', 60)
- mySSH.command('echo ' + self.eNBPassword + ' | sudo -S rm enb*.log core* enb_*record.raw enb_*.pcap gnb_*.pcap enb_*txt physim_*.log *stats.log', '\$', 5)
+ mySSH.command('echo ' + self.eNBPassword + ' | sudo -S zip enb.log.zip enb*.log enb_*record.raw enb_*.pcap gnb_*.pcap enb_*txt physim_*.log *stats.log *monitor.pickle *monitor*.png ping*.log.png log/*/*.log log/*/*.pcap', '\$', 60)
+ result = re.search('core.\d+', mySSH.getBefore())
+ if result is not None:
+ mySSH.command('echo ' + self.eNBPassword + ' | sudo -S zip enb.log.zip core* ran_build/build/{lte,nr}-softmodem', '\$', 60) # add core and executable to zip
+ mySSH.command('echo ' + self.eNBPassword + ' | sudo -S rm enb*.log core* enb_*record.raw enb_*.pcap gnb_*.pcap enb_*txt physim_*.log *stats.log *monitor.pickle *monitor*.png ping*.log.png log/*/*.log log/*/*.pcap', '\$', 15)
mySSH.close()
def AnalyzeLogFile_eNB(self, eNBlogFile, HTML):
@@ -705,6 +764,7 @@ class RANManagement():
msgLine = 0
foundSegFault = False
foundRealTimeIssue = False
+ foundRealTimeIssue_cnt = 0
rrcSetupComplete = 0
rrcReleaseRequest = 0
rrcReconfigRequest = 0
@@ -749,26 +809,26 @@ class RANManagement():
pb_receiving_samples_cnt = 0
#count "removing UE" msg
removing_ue = 0
+ #count"X2AP-PDU"
+ x2ap_pdu = 0
#NSA specific log markers
nsa_markers ={'SgNBReleaseRequestAcknowledge': [],'FAILURE': [], 'scgFailureInformationNR-r15': [], 'SgNBReleaseRequest': []}
+ nodeB_prefix_found = False
+ RealTimeProcessingIssue = False
- #the datalog config file has to be loaded
- datalog_rt_stats_file='datalog_rt_stats.yaml'
- if (os.path.isfile(datalog_rt_stats_file)):
- yaml_file=datalog_rt_stats_file
- elif (os.path.isfile('ci-scripts/'+datalog_rt_stats_file)):
- yaml_file='ci-scripts/'+datalog_rt_stats_file
- else:
- logging.error("Datalog RT stats yaml file cannot be found")
- sys.exit("Datalog RT stats yaml file cannot be found")
-
- with open(yaml_file,'r') as f:
- datalog_rt_stats = yaml.load(f,Loader=yaml.FullLoader)
- rt_keys = datalog_rt_stats['Ref'] #we use the keys from the Ref field
-
line_cnt=0 #log file line counter
for line in enb_log_file.readlines():
line_cnt+=1
+ # Detection of eNB/gNB from a container log
+ result = re.search('Starting eNB soft modem', str(line))
+ if result is not None:
+ nodeB_prefix_found = True
+ nodeB_prefix = 'e'
+ result = re.search('Starting gNB soft modem', str(line))
+ if result is not None:
+ nodeB_prefix_found = True
+ nodeB_prefix = 'g'
+ result = re.search('Run time:' ,str(line))
# Runtime statistics
result = re.search('Run time:' ,str(line))
if result is not None:
@@ -833,9 +893,6 @@ class RANManagement():
if result is not None and not exitSignalReceived:
foundSegFault = True
result = re.search('[Cc]ore [dD]ump', str(line))
- if result is not None and not exitSignalReceived:
- foundSegFault = True
- result = re.search('./ran_build/build/lte-softmodem', str(line))
if result is not None and not exitSignalReceived:
foundSegFault = True
result = re.search('[Aa]ssertion', str(line))
@@ -844,6 +901,7 @@ class RANManagement():
result = re.search('LLL', str(line))
if result is not None and not exitSignalReceived:
foundRealTimeIssue = True
+ foundRealTimeIssue_cnt += 1
if foundAssertion and (msgLine < 3):
msgLine += 1
msgAssertion += str(line)
@@ -919,7 +977,7 @@ class RANManagement():
if result is not None:
mbmsRequestMsg += 1
#FR1 NSA test : add new markers to make sure gNB is used
- result = re.search('\[gNB [0-9]+\]\[RAPROC\] PUSCH with TC_RNTI [0-9a-fA-F]+ received correctly, adding UE MAC Context UE_id [0-9]+\/RNTI [0-9a-fA-F]+', str(line))
+ result = re.search('\[gNB [0-9]+\]\[RAPROC\] PUSCH with TC_RNTI 0x[0-9a-fA-F]+ received correctly, adding UE MAC Context UE_id [0-9]+\/RNTI 0x[0-9a-fA-F]+', str(line))
if result is not None:
NSA_RAPROC_PUSCH_check = 1
#dlsch and ulsch statistics
@@ -932,15 +990,7 @@ class RANManagement():
if result is not None:
#remove 1- all useless char before relevant info (ulsch or dlsch) 2- trailing char
dlsch_ulsch_stats[k]=re.sub(r'^.*\]\s+', r'' , line.rstrip())
- #real time statistics for gNB
- for k in rt_keys:
- result = re.search(k, line)
- if result is not None:
- #remove 1- all useless char before relevant info 2- trailing char
- line=line.replace('[0m','')
- tmp=re.match(rf'^.*?(\b{k}\b.*)',line.rstrip()) #from python 3.6 we can use literal string interpolation for the variable k, using rf' in the regex
- if tmp!=None: #with ULULULUULULULLLL at the head of the line, we skip it
- real_time_stats[k]=tmp.group(1)
+
#count "problem receiving samples" msg
result = re.search('\[PHY\]\s+problem receiving samples', str(line))
@@ -950,7 +1000,10 @@ class RANManagement():
result = re.search('\[MAC\]\s+Removing UE', str(line))
if result is not None:
removing_ue += 1
-
+ #count "X2AP-PDU"
+ result = re.search('X2AP-PDU', str(line))
+ if result is not None:
+ x2ap_pdu += 1
#nsa markers logging
for k in nsa_markers:
result = re.search(k, line)
@@ -958,11 +1011,59 @@ class RANManagement():
nsa_markers[k].append(line_cnt)
enb_log_file.close()
- logging.debug(' File analysis completed')
- if (self.air_interface[self.eNB_instance] == 'lte-softmodem') or (self.air_interface[self.eNB_instance] == 'ocp-enb'):
- nodeB_prefix = 'e'
+
+
+ #the following part takes the *_stats.log files as source (not the stdout log file)
+
+ #the datalog config file has to be loaded
+ datalog_rt_stats_file=self.datalog_rt_stats_file
+ if (os.path.isfile(datalog_rt_stats_file)):
+ yaml_file=datalog_rt_stats_file
+ elif (os.path.isfile('ci-scripts/'+datalog_rt_stats_file)):
+ yaml_file='ci-scripts/'+datalog_rt_stats_file
else:
- nodeB_prefix = 'g'
+ logging.error("Datalog RT stats yaml file cannot be found")
+ sys.exit("Datalog RT stats yaml file cannot be found")
+
+ with open(yaml_file,'r') as f:
+ datalog_rt_stats = yaml.load(f,Loader=yaml.FullLoader)
+ rt_keys = datalog_rt_stats['Ref'] #we use the keys from the Ref field
+
+ if os.path.isfile('./nrL1_stats.log') and os.path.isfile('./nrMAC_stats.log'):
+ # don't use CI-nrL1_stats.log, as this will increase the processing time for
+ # no reason, we just need the last occurence
+ nrL1_stats = open('./nrL1_stats.log', 'r')
+ nrMAC_stats = open('./nrMAC_stats.log', 'r')
+ for line in nrL1_stats.readlines():
+ for k in rt_keys:
+ result = re.search(k, line)
+ if result is not None:
+ #remove 1- all useless char before relevant info 2- trailing char
+ tmp=re.match(rf'^.*?(\b{k}\b.*)',line.rstrip()) #from python 3.6 we can use literal string interpolation for the variable k, using rf' in the regex
+ if tmp!=None:
+ real_time_stats[k]=tmp.group(1)
+ for line in nrMAC_stats.readlines():
+ for k in rt_keys:
+ result = re.search(k, line)
+ if result is not None:
+ #remove 1- all useless char before relevant info 2- trailing char
+ tmp=re.match(rf'^.*?(\b{k}\b.*)',line.rstrip()) #from python 3.6 we can use literal string interpolation for the variable k, using rf' in the regex
+ if tmp!=None:
+ real_time_stats[k]=tmp.group(1)
+ nrL1_stats.close()
+ nrMAC_stats.close()
+ else:
+ logging.debug("NR Stats files for RT analysis not found")
+
+ #stdout log file and stat log files analysis completed
+ logging.debug(' File analysis (stdout, stats) completed')
+
+ #post processing depending on the node type
+ if not nodeB_prefix_found:
+ if (self.air_interface[self.eNB_instance] == 'lte-softmodem') or (self.air_interface[self.eNB_instance] == 'ocp-enb'):
+ nodeB_prefix = 'e'
+ else:
+ nodeB_prefix = 'g'
if nodeB_prefix == 'g':
if ulschReceiveOK > 0:
@@ -1031,8 +1132,7 @@ class RANManagement():
#check if there is a fail => will render the test as failed
for k in datalog_rt_stats['Data']:
if float(datalog_rt_stats['Data'][k][2])> datalog_rt_stats['Threshold'][k]: #condition for fail : avg/ref is greater than the fixed threshold
- #setting prematureExit is ok although not the best option
- self.prematureExit=True
+ RealTimeProcessingIssue = True
else:
statMsg = 'No real time stats found in the log file\n'
logging.debug('No real time stats found in the log file')
@@ -1044,6 +1144,11 @@ class RANManagement():
htmlMsg = statMsg+'\n'
logging.debug(statMsg)
htmleNBFailureMsg += htmlMsg
+ #X2AP-PDU log
+ statMsg = 'X2AP-PDU msg count = '+str(x2ap_pdu)
+ htmlMsg = statMsg+'\n'
+ logging.debug(statMsg)
+ htmleNBFailureMsg += htmlMsg
#nsa markers
statMsg = 'logfile line count = ' + str(line_cnt)
htmlMsg = statMsg+'\n'
@@ -1061,7 +1166,10 @@ class RANManagement():
logging.debug(statMsg)
htmleNBFailureMsg += htmlMsg
-
+ if RealTimeProcessingIssue:
+ logging.debug('\u001B[1;37;41m ' + nodeB_prefix + 'NB ended with real time processing issue! \u001B[0m')
+ htmleNBFailureMsg += 'Fail due to real time processing issue\n'
+ global_status = CONST.ENB_REAL_TIME_PROCESSING_ISSUE
if uciStatMsgCount > 0:
statMsg = nodeB_prefix + 'NB showed ' + str(uciStatMsgCount) + ' "uci->stat" message(s)'
logging.debug('\u001B[1;30;43m ' + statMsg + ' \u001B[0m')
@@ -1163,7 +1271,7 @@ class RANManagement():
global_status = CONST.ENB_PROCESS_ASSERTION
if foundRealTimeIssue:
logging.debug('\u001B[1;37;41m ' + nodeB_prefix + 'NB faced real time issues! \u001B[0m')
- htmleNBFailureMsg += nodeB_prefix + 'NB faced real time issues!\n'
+ htmleNBFailureMsg += nodeB_prefix + 'NB faced real time issues! COUNT = '+ str(foundRealTimeIssue_cnt) +' lines\n'
if rlcDiscardBuffer > 0:
rlcMsg = nodeB_prefix + 'NB RLC discarded ' + str(rlcDiscardBuffer) + ' buffer(s)'
logging.debug('\u001B[1;37;41m ' + rlcMsg + ' \u001B[0m')
diff --git a/ci-scripts/ran_dashboard/Hdashboard.py b/ci-scripts/ran_dashboard/Hdashboard.py
new file mode 100644
index 0000000000..864e31fc1f
--- /dev/null
+++ b/ci-scripts/ran_dashboard/Hdashboard.py
@@ -0,0 +1,600 @@
+#/*
+# * 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
+# */
+#---------------------------------------------------------------------
+# Merge Requests Dashboard for RAN on googleSheet
+#
+# Required Python Version
+# Python 3.x
+#
+#---------------------------------------------------------------------
+
+#-----------------------------------------------------------
+# Import
+#-----------------------------------------------------------
+
+
+#Author Remi
+import boto3
+import shlex
+import subprocess
+import json #json structures
+import datetime #now() and date formating
+from datetime import datetime
+import re
+import gitlab
+import yaml
+import os
+import time
+import sys
+
+from sqlconnect import SQLConnect
+
+#-----------------------------------------------------------
+# Class Declaration
+#-----------------------------------------------------------
+
+class Dashboard:
+ def __init__(self):
+
+
+ #init with data sources : git, yaml config file, test results databases
+ print("Collecting Data")
+ cmd="""curl --silent "https://gitlab.eurecom.fr/api/v4/projects/oai%2Fopenairinterface5g/merge_requests?state=opened&per_page=100" """
+ self.git = self.__getGitData(cmd) #git data from Gitlab
+ self.tests = self.__loadCfg('ran_dashboard_cfg.yaml') #tests table setup from yaml
+ self.db = self.__loadFromDB() #test results from database
+ self.mr_list=[] #mr list in string format
+ for x in range(len(self.git)):
+ self.mr_list.append(str(self.git[x]['iid']))
+
+ def __loadCfg(self,yaml_file):
+ with open(yaml_file,'r') as f:
+ tests = yaml.load(f)
+ return tests
+
+ def __getGitData(self,cmd):
+ #cmd="""curl --silent "https://gitlab.eurecom.fr/api/v4/projects/oai%2Fopenairinterface5g/merge_requests?state=opened&per_page=100" """
+ process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE)
+ output = process.stdout.readline()
+ tmp=output.decode("utf-8")
+ d = json.loads(tmp)
+ return d
+
+ def __loadFromDB(self):
+ mr_list=[]
+ for x in range(len(self.git)):
+ mr_list.append(str(self.git[x]['iid']))
+ mydb=SQLConnect()
+ for MR in mr_list:
+ mydb.get(MR)
+ mydb.close_connection()
+ return mydb.data
+
+ def singleMR_initHTML(self, date):
+ self.f_html.write('\n')
+ self.f_html.write('\n')
+ self.f_html.write('\n')
+ self.f_html.write('Test Dashboard\n')
+ self.f_html.write('\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+ self.f_html.write('\n')
+ self.f_html.write('\n')
+ self.f_html.write('| OAI RAN TEST Status Dashboard | \n')
+ self.f_html.write('\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+ self.f_html.write('\n')
+ self.f_html.write('| Update : '+date+' | \n')
+ self.f_html.write('\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+
+
+ def Test_initHTML(self, date):
+ self.f_html.write('\n')
+ self.f_html.write('\n')
+ self.f_html.write('\n')
+ self.f_html.write('Test Dashboard\n')
+ self.f_html.write('\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+ self.f_html.write('\n')
+ self.f_html.write('\n')
+ self.f_html.write('| OAI RAN TEST Status Dashboard | \n')
+ self.f_html.write('\n')
+ self.f_html.write('
\n')
+ self.f_html.write('| Merge Requests Dashboard | \n')
+ self.f_html.write('\n')
+ self.f_html.write('
\n')
+ self.f_html.write('\n')
+ self.f_html.write('| Update : '+date+' | \n')
+ self.f_html.write('\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+ self.f_html.write('
\n')
+
+ def Test_terminateHTML(self):
+ self.f_html.write('