From 9ea7df92107184e10a33ecd749dba77d46ce5d1f Mon Sep 17 00:00:00 2001
From: Remi Hardy <remi.hardy@openairinterface.org>
Date: Thu, 3 Dec 2020 22:12:54 +0100
Subject: [PATCH] func + log update of fr1 nsa test

---
 ci-scripts/build_fr1_from_yaml.py        |  83 ++++++++++
 ci-scripts/build_fr1_template.yaml       |  46 ++++++
 ci-scripts/ran.py                        | 183 ++++++++++++++++++++++-
 ci-scripts/xml_files/fr1_ran_ue_proc.xml |  39 ++++-
 4 files changed, 347 insertions(+), 4 deletions(-)
 create mode 100755 ci-scripts/build_fr1_from_yaml.py
 create mode 100755 ci-scripts/build_fr1_template.yaml

diff --git a/ci-scripts/build_fr1_from_yaml.py b/ci-scripts/build_fr1_from_yaml.py
new file mode 100755
index 00000000000..a5492df6f6b
--- /dev/null
+++ b/ci-scripts/build_fr1_from_yaml.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Tue Jul  7 23:04:51 2020
+
+@author: hardy
+"""
+
+
+
+import yaml
+import sys
+import subprocess
+
+      
+
+def main():
+  f_yaml=sys.argv[1]
+  f_sh=sys.argv[2]
+  #filename='py_params_template.yaml'
+  with open(f_yaml,'r') as file:
+    # The FullLoader parameter handles the conversion from YAML
+    # scalar values to Python the dictionary format
+    print('Loading '+f_yaml)
+    params = yaml.load(file,Loader=yaml.FullLoader)
+
+
+  with open(f_sh,'w') as f:
+    f.write('#!/bin/sh\n')
+    for i in range (0, len(params['steps'])):
+      step=params['steps'][i].split(',')
+      mode=step[0]
+      f_xml=step[1]
+      line='python3 main.py ' + \
+           '--mode='+ mode + ' ' + \
+           '--ranRepository=' + params['ranRepository'] + ' ' + \
+           '--ranBranch=' + params['ranBranch'] + ' ' + \
+           '--ranCommitID=' + params['ranCommitID'] + ' ' + \
+           '--ranAllowMerge=' + params['ranAllowMerge'] + ' ' + \
+           '--ranTargetBranch=' + params['ranTargetBranch'] + ' ' + \
+           \
+           '--ADBIPAddress=' + params['ADB']['ADBIPAddress'] + ' ' + \
+           '--ADBUserName=' + params['ADB']['ADBUserName'] + ' ' + \
+           '--ADBPassword=' + params['ADB']['ADBPassword'] + ' ' + \
+           \
+           '--UEIPAddress=' + params['UE']['UEIPAddress'] + ' ' + \
+           '--UEUserName=' + params['UE']['UEUserName'] + ' ' + \
+           '--UEPassword=' + params['UE']['UEPassword'] + ' ' + \
+           '--UESourceCodePath=' + params['UE']['UESourceCodePath'] + ' ' + \
+           \
+           '--EPCIPAddress=' + params['EPC']['EPCIPAddress'] + ' ' + \
+           '--EPCUserName=' + params['EPC']['EPCUserName'] + ' ' + \
+           '--EPCPassword=' + params['EPC']['EPCPassword'] + ' ' + \
+           '--EPCSourceCodePath=' + params['EPC']['EPCSourceCodePath'] + ' ' + \
+           '--EPCType=' + params['EPC']['EPCType'] + ' ' + \
+           \
+           '--eNBIPAddress=' + params['RAN'][0]['eNBIPAddress'] + ' ' + \
+           '--eNBUserName=' + params['RAN'][0]['eNBUserName'] + ' ' + \
+           '--eNBPassword=' + params['RAN'][0]['eNBPassword'] + ' ' + \
+           '--eNBSourceCodePath=' + params['RAN'][0]['eNBSourceCodePath'] + ' ' + \
+           \
+           '--eNB1IPAddress=' + params['RAN'][1]['eNB1IPAddress'] + ' ' + \
+           '--eNB1UserName=' + params['RAN'][1]['eNB1UserName'] + ' ' + \
+           '--eNB1Password=' + params['RAN'][1]['eNB1Password'] + ' ' + \
+           '--eNB1SourceCodePath=' + params['RAN'][1]['eNB1SourceCodePath'] + ' '
+      if mode!="InitiateHtml":
+         line+='--XMLTestFile=' + f_xml
+      #if mode is InitiateHTML we have a special processing to mention all xml files from the list
+      #loop starting at 1 to avoid the xml file mentioned with InitiateHtml in yaml file (file is none)
+      else:
+         for i in range (1, len(params['steps'])):
+            step=params['steps'][i].split(',')
+            f_xml=step[1]
+            line+='--XMLTestFile=' + f_xml+' '
+      line+='\n'
+      print(line)
+      f.write(line)
+  subprocess.call(['chmod','777',f_sh])
+
+if __name__ == "__main__":
+    main()
+
+
diff --git a/ci-scripts/build_fr1_template.yaml b/ci-scripts/build_fr1_template.yaml
new file mode 100755
index 00000000000..3aace262f5e
--- /dev/null
+++ b/ci-scripts/build_fr1_template.yaml
@@ -0,0 +1,46 @@
+
+ranRepository : https://gitlab.eurecom.fr/oai/openairinterface5g.git
+ranBranch : BRANCH_NAME 
+ranCommitID : COMMIT_ID 
+ranAllowMerge : 'true' 
+ranTargetBranch : develop
+
+steps:
+  - InitiateHtml,none
+  - TesteNB,xml_files/fr1_multi_node_build.xml
+  - TesteNB,xml_files/fr1_epc_start.xml
+  - TesteNB,xml_files/fr1_ran_ue_proc.xml #ue toggle, nodes initialize, ue toggle, ping, nodes terminate
+  - TesteNB,xml_files/fr1_epc_closure.xml
+
+
+ADB: #on Caracal
+  ADBIPAddress : 192.168.18.196
+  ADBUserName : oaici
+  ADBPassword : KkexF6CErOi1fNuebCPsuIVK
+
+RAN:
+    - eNBIPAddress : 192.168.18.199 #eNB on Minimassive
+      eNBUserName : oaicicd
+      eNBPassword : HzB*nkryaITdVd08TKlT#2Z5a!7M#~qn
+      eNBSourceCodePath : /tmp/CI-FR1-eNB
+    - eNB1IPAddress : 192.168.18.198 #gNB on Mozart
+      eNB1UserName : oaicicd
+      eNB1Password : 7zkDOFgh@w3HvRBMPTMh@BAx
+      eNB1SourceCodePath : /tmp/CI-FR1-gNB
+
+
+EPC: #on Nikaia 
+  EPCIPAddress : 192.168.18.99
+  EPCUserName : nikaia
+  EPCPassword : linux
+  EPCSourceCodePath : /tmp/CI-FR1-EPC
+  EPCType : ltebox
+
+
+UE:
+  UEIPAddress : none
+  UEUserName : none
+  UEPassword : none
+  UESourceCodePath : none
+
+
diff --git a/ci-scripts/ran.py b/ci-scripts/ran.py
index f78417e22cb..369a4bb95c5 100644
--- a/ci-scripts/ran.py
+++ b/ci-scripts/ran.py
@@ -77,6 +77,7 @@ class RANManagement():
 		self.backgroundBuildTestId = ['', '', '']
 		self.Build_eNB_forced_workspace_cleanup = False
 		self.Initialize_eNB_args = ''
+		self.imageKind = ''
 		self.air_interface = ['', '', ''] #changed from 'lte' to '' may lead to side effects in main
 		self.eNB_instance = 0
 		self.eNB_serverId = ['', '', '']
@@ -320,6 +321,174 @@ class RANManagement():
 				self.htmlObj.CreateHtmlTabFooter(False)
 			sys.exit(1)
 
+	def BuildImage(self):
+		if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
+			HELP.GenericHelp(CONST.Version)
+			sys.exit('Insufficient Parameter')
+		if self.eNB_serverId[self.eNB_instance] == '0':
+			lIpAddr = self.eNBIPAddress
+			lUserName = self.eNBUserName
+			lPassWord = self.eNBPassword
+			lSourcePath = self.eNBSourceCodePath
+		elif self.eNB_serverId[self.eNB_instance] == '1':
+			lIpAddr = self.eNB1IPAddress
+			lUserName = self.eNB1UserName
+			lPassWord = self.eNB1Password
+			lSourcePath = self.eNB1SourceCodePath
+		elif self.eNB_serverId[self.eNB_instance] == '2':
+			lIpAddr = self.eNB2IPAddress
+			lUserName = self.eNB2UserName
+			lPassWord = self.eNB2Password
+			lSourcePath = self.eNB2SourceCodePath
+		if lIpAddr == '' or lUserName == '' or lPassWord == '' or lSourcePath == '':
+			HELP.GenericHelp(CONST.Version)
+			sys.exit('Insufficient Parameter')
+		logging.debug('Building on server: ' + lIpAddr)
+		mySSH = SSH.SSHConnection()
+		mySSH.open(lIpAddr, lUserName, lPassWord)
+
+		imageNames = []
+		result = re.search('eNB', self.imageKind)
+		if result is not None:
+			imageNames.append(('oai-enb', 'eNB'))
+		else:
+			result = re.search('gNB', self.imageKind)
+			if result is not None:
+				imageNames.append(('oai-gnb', 'gNB'))
+			else:
+				result = re.search('all', self.imageKind)
+				if result is not None:
+					imageNames.append(('oai-enb', 'eNB'))
+					imageNames.append(('oai-gnb', 'gNB'))
+		if len(imageNames) == 0:
+			imageNames.append(('oai-enb', 'eNB'))
+		# Workaround for some servers, we need to erase completely the workspace
+		if self.Build_eNB_forced_workspace_cleanup:
+			mySSH.command('echo ' + lPassWord + ' | sudo -S rm -Rf ' + lSourcePath, '\$', 15)
+		if self.htmlObj is not None:
+			self.testCase_id = self.htmlObj.testCase_id
+		else:
+			self.testCase_id = '000000'
+		# on RedHat/CentOS .git extension is mandatory
+		result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
+		if result is not None:
+			full_ran_repo_name = self.ranRepository
+		else:
+			full_ran_repo_name = self.ranRepository + '.git'
+		mySSH.command('mkdir -p ' + 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)
+		# 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('echo ' + lPassWord + ' | sudo -S git clean -x -d -ff', '\$', 30)
+		mySSH.command('mkdir -p cmake_targets/log', '\$', 5)
+		# if the commit ID is provided use it to point to it
+		if self.ranCommitID != '':
+			mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 5)
+		# 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
+		imageTag = 'develop'
+		if (self.ranAllowMerge):
+			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)
+		# Let's remove any previous run artifacts if still there
+		mySSH.command('docker image prune --force', '\$', 5)
+		mySSH.command('docker image rm ran-build:' + imageTag, '\$', 5)
+		for image,pattern in imageNames:
+			mySSH.command('docker image rm ' + image + ':' + imageTag, '\$', 5)
+		# Build the shared image
+		mySSH.command('docker build --target ran-build --tag ran-build:' + imageTag + ' --file docker/Dockerfile.ran.ubuntu18 --build-arg NEEDED_GIT_PROXY="http://proxy.eurecom.fr:8080" . > cmake_targets/log/ran-build.log 2>&1', '\$', 800)
+		# Build the target image(s)
+		previousImage='ran-build:' + imageTag
+		danglingShaOnes=[]
+		for image,pattern in imageNames:
+			mySSH.command('docker build --target ' + image + ' --tag ' + image + ':' + imageTag + ' --file docker/Dockerfile.' + pattern + '.ubuntu18 . > cmake_targets/log/' + image + '.log 2>&1', '\$', 600)
+			# Retrieving the dangling image(s) for the log collection
+			mySSH.command('docker images --filter "dangling=true" --filter "since=' + previousImage + '" -q | sed -e "s#^#sha=#"', '\$', 5)
+			result = re.search('sha=(?P<imageShaOne>[a-zA-Z0-9\-\_]+)', mySSH.getBefore())
+			if result is not None:
+				danglingShaOnes.append((image, result.group('imageShaOne')))
+			previousImage=image + ':' + imageTag
+
+		imageTag = 'ci-temp'
+		# First verify if images were properly created.
+		status = True
+		mySSH.command('docker image inspect --format=\'Size = {{.Size}} bytes\' ran-build:' + imageTag, '\$', 5)
+		if mySSH.getBefore().count('No such object') != 0:
+			logging.error('Could not build properly ran-build')
+			status = False
+		else:
+			result = re.search('Size = (?P<size>[0-9\-]+) bytes', mySSH.getBefore())
+			if result is not None:
+				imageSize = float(result.group('size'))
+				imageSize = imageSize / 1000
+				if imageSize < 1000:
+					logging.debug('\u001B[1m   ran-build size is ' + ('%.0f' % imageSize) + ' kbytes\u001B[0m')
+				else:
+					imageSize = imageSize / 1000
+					if imageSize < 1000:
+						logging.debug('\u001B[1m   ran-build size is ' + ('%.0f' % imageSize) + ' Mbytes\u001B[0m')
+					else:
+						imageSize = imageSize / 1000
+						logging.debug('\u001B[1m   ran-build size is ' + ('%.3f' % imageSize) + ' Gbytes\u001B[0m')
+			else:
+				logging.debug('ran-build size is unknown')
+		for image,pattern in imageNames:
+			mySSH.command('docker image inspect --format=\'Size = {{.Size}} bytes\' ' + image + ':' + imageTag, '\$', 5)
+			if mySSH.getBefore().count('No such object') != 0:
+				logging.error('Could not build properly ' + image)
+				status = False
+			else:
+				result = re.search('Size = (?P<size>[0-9\-]+) bytes', mySSH.getBefore())
+				if result is not None:
+					imageSize = float(result.group('size'))
+					imageSize = imageSize / 1000
+					if imageSize < 1000:
+						logging.debug('\u001B[1m   ' + image + ' size is ' + ('%.0f' % imageSize) + ' kbytes\u001B[0m')
+					else:
+						imageSize = imageSize / 1000
+						if imageSize < 1000:
+							logging.debug('\u001B[1m   ' + image + ' size is ' + ('%.0f' % imageSize) + ' Mbytes\u001B[0m')
+						else:
+							imageSize = imageSize / 1000
+							logging.debug('\u001B[1m   ' + image + ' size is ' + ('%.3f' % imageSize) + ' Gbytes\u001B[0m')
+				else:
+					logging.debug('ran-build size is unknown')
+		if not status:
+			mySSH.close()
+			logging.error('\u001B[1m Building OAI Images Failed\u001B[0m')
+			if self.htmlObj is not None:
+				self.htmlObj.CreateHtmlTestRow(self.imageKind, 'KO', CONST.ALL_PROCESSES_OK)
+				self.htmlObj.CreateHtmlTabFooter(False)
+			sys.exit(1)
+
+		# Recover build logs, for the moment only possible when build is successful
+		mySSH.command('docker create --name test ran-build:' + imageTag, '\$', 5)
+		mySSH.command('mkdir -p cmake_targets/log/ran-build', '\$', 5)
+		mySSH.command('docker cp test:/oai-ran/cmake_targets/log/. cmake_targets/log/ran-build', '\$', 5)
+		mySSH.command('docker rm -f test', '\$', 5)
+		for image,shaone in danglingShaOnes:
+			mySSH.command('mkdir -p cmake_targets/log/' + image, '\$', 5)
+			mySSH.command('docker create --name test ' + shaone, '\$', 5)
+			mySSH.command('docker cp test:/oai-ran/cmake_targets/log/. cmake_targets/log/' + image, '\$', 5)
+			mySSH.command('docker rm -f test', '\$', 5)
+		mySSH.command('docker image prune --force', '\$', 5)
+		mySSH.command('cd cmake_targets', '\$', 5)
+		mySSH.command('mkdir -p build_log_' + self.testCase_id, '\$', 5)
+		mySSH.command('mv log/* ' + 'build_log_' + self.testCase_id, '\$', 5)
+		mySSH.close()
+
+		logging.info('\u001B[1m Building OAI Image(s) Pass\u001B[0m')
+		if self.htmlObj is not None:
+			self.htmlObj.CreateHtmlTestRow(self.imageKind, 'OK', CONST.ALL_PROCESSES_OK)		
+		
 	def InitializeeNB(self):
 		if self.eNB_serverId[self.eNB_instance] == '0':
 			lIpAddr = self.eNBIPAddress
@@ -716,7 +885,9 @@ class RANManagement():
 		NSA_RAPROC_PUSCH_check = 0
 		#dlsch and ulsch statistics (dictionary)
 		dlsch_ulsch_stats = {}
-		
+		#count L1 thread not ready msg 	
+		L1_thread_not_ready_cnt = 0
+	
 		for line in enb_log_file.readlines():
 			# Runtime statistics
 			result = re.search('Run time:' ,str(line))
@@ -881,6 +1052,10 @@ 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())
+			#count L1 thread not ready msg
+			result = re.search('\[PHY\]\s+L1_thread isn\'t ready', str(line))
+			if result is not None:
+				L1_thread_not_ready_cnt += 1			
 		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'):
@@ -910,6 +1085,12 @@ class RANManagement():
 				htmlMsg = statMsg+'\n'
 			logging.debug(statMsg)
 			htmleNBFailureMsg += htmlMsg
+			#L1 thread not ready log
+			if L1_thread_not_ready_cnt != 0:
+				statMsg = '[PHY] L1 thread is not ready msg count =  '+str(L1_thread_not_ready_cnt)
+				htmlMsg = statMsg+'\n'
+			logging.debug(statMsg)
+			htmleNBFailureMsg += htmlMsg
 			#ulsch and dlsch statistics
 			if len(dlsch_ulsch_stats)!=0: #check if dictionary is not empty
 				statMsg=''
diff --git a/ci-scripts/xml_files/fr1_ran_ue_proc.xml b/ci-scripts/xml_files/fr1_ran_ue_proc.xml
index 330fb27f21b..5730630ddd4 100644
--- a/ci-scripts/xml_files/fr1_ran_ue_proc.xml
+++ b/ci-scripts/xml_files/fr1_ran_ue_proc.xml
@@ -32,9 +32,16 @@
  000001
  050000
  050001
+ 050002
+ 050002
+ 000001
+ 060000
+ 060001
+ 000001
+ 010002
+ 000001
  070001
  070000
- 010002
  010003
 	</TestCaseRequestedList>
 	<TestCaseExclusionList></TestCaseExclusionList>
@@ -89,16 +96,42 @@
 		<class>Ping</class>
 		<desc>Ping: 20pings in 20sec</desc>
 		<ping_args>-c 20</ping_args>
-		<ping_packetloss_threshold>50</ping_packetloss_threshold>
+		<ping_packetloss_threshold>90</ping_packetloss_threshold>
 	</testCase>
 
 	<testCase id="050001">
 		<class>Ping</class>
 		<desc>Ping: 5pings in 1sec</desc>
 		<ping_args>-c 5 -i 0.2</ping_args>
-		<ping_packetloss_threshold>50</ping_packetloss_threshold>
+		<ping_packetloss_threshold>90</ping_packetloss_threshold>
+	</testCase>
+
+	<testCase id="050002">
+		<class>Ping</class>
+		<desc>Ping: 100pings in 20sec</desc>
+		<ping_args>-c 100 -i 0.2</ping_args>
+		<ping_packetloss_threshold>90</ping_packetloss_threshold>
 	</testCase>
 
+
+	<testCase id="060000">
+		<class>Iperf</class>
+		<desc>iperf (DL/10Kbps/UDP)(30 sec)(single-ue profile)</desc>
+		<iperf_args>-u -b 10K -t 30 -i 1</iperf_args>
+		<iperf_packetloss_threshold>90</iperf_packetloss_threshold>
+		<iperf_profile>single-ue</iperf_profile>
+	</testCase>
+
+	<testCase id="060001">
+		<class>Iperf</class>
+		<desc>iperf (UL/10Kbps/UDP)(30 sec)(single-ue profile)</desc>
+		<iperf_args>-u -b 10K -t 30 -i 1 -R</iperf_args>
+		<iperf_packetloss_threshold>90</iperf_packetloss_threshold>
+		<iperf_profile>single-ue</iperf_profile>
+	</testCase>
+
+
+
 	<testCase id="070000">
 		<class>Terminate_eNB</class>
 		<desc>Terminate eNB</desc>
-- 
GitLab