From ec6362733401bbdb971275b3039c74628db23d87 Mon Sep 17 00:00:00 2001 From: ismail <mohammed.ismail@openairinterface.org> Date: Wed, 12 May 2021 13:59:42 +0200 Subject: [PATCH] CI: initial ci draft Signed-off-by: ismail <mohammed.ismail@openairinterface.org> --- ci-scripts/Jenkinsfile-GitLab-Helm | 275 +++++++++++++++++++++ ci-scripts/helmDeploy.py | 279 ++++++++++++++++++++++ ci-scripts/sshconnection.py | 259 ++++++++++++++++++++ openshift/oai-amf-image-stream.yml | 30 +++ openshift/oai-nrf-image-stream.yml | 30 +++ openshift/oai-smf-image-stream.yml | 30 +++ openshift/oai-spgwu-tiny-image-stream.yml | 30 +++ 7 files changed, 933 insertions(+) create mode 100644 ci-scripts/Jenkinsfile-GitLab-Helm create mode 100644 ci-scripts/helmDeploy.py create mode 100644 ci-scripts/sshconnection.py create mode 100644 openshift/oai-amf-image-stream.yml create mode 100644 openshift/oai-nrf-image-stream.yml create mode 100644 openshift/oai-smf-image-stream.yml create mode 100644 openshift/oai-spgwu-tiny-image-stream.yml diff --git a/ci-scripts/Jenkinsfile-GitLab-Helm b/ci-scripts/Jenkinsfile-GitLab-Helm new file mode 100644 index 00000000..74ad3f2a --- /dev/null +++ b/ci-scripts/Jenkinsfile-GitLab-Helm @@ -0,0 +1,275 @@ +#!/bin/groovy +/* + * 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 + */ + +//------------------------------------------------------------------------------- +// Abstraction function to send social media messages: +// like on Slack or Mattermost +def sendSocialMediaMessage(pipeChannel, pipeColor, pipeMessage) { + if (params.pipelineUsesSlack != null) { + if (params.pipelineUsesSlack) { + slackSend channel: pipeChannel, color: pipeColor, message: pipeMessage + } + } +} + +// Location of the CN executor node +def cn_ci_host = params.Host_CN_CI_Server + +// for lock +def ds_tester_ci_resource = params.DsTester + +// Location of the DsTester workspace +def dsTestFrameworkLocation = params.dsTestFrameworkLocation + +// When triggered by upstream, specify which tag to use +def upstreamTagToUse = params.upstreamTagToUse + +// Location of the CN tester +def dsT_host_flag = false +def dsT_host = "" +def dsT_host_user = "" +def dsT_host_ip_addr = "" + +// Flags +def scmEvent = false +def upstreamEvent = false + +// Default tags --> could be passed on by upstream job or by PR content +def nrfTag = params.nrfTag +def amfTag = params.amfTag +def smfTag = params.smfTag +def spgwuTag = params.spgwuTag + +//------------------------------------------------------------------------------- +// Pipeline start +pipeline { + agent { + label cn_ci_host + } + options { + disableConcurrentBuilds() + timestamps() + ansiColor('xterm') + lock(cn_ci_resource) + } + stages { + stage ('Verify Parameters') { + steps { + script { + echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' + + JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"' + JOB_TIMESTAMP = JOB_TIMESTAMP.trim() + + def allParametersPresent = true + if (params.eNB_IPAddress == null) { + allParametersPresent = false + if (params.eNB_Credentials == null) { + allParametersPresent = false + if (params.OC_Credentials == null) { + allParametersPresent = false + } + if (params.OC_ProjectName == null) { + allParametersPresent = false + } + if (allParametersPresent) { + echo "Cluster Access parameters are present" + } else { + echo "Some Cluster Access parameters are missing" + sh "./ci-scripts/fail.sh" + } + if (params.DS_Tester_Server_Flag != null) { + dsT_host_flag = params.DS_Tester_Server_Flag + if (dsT_host_flag) { + def allParametersPresent = true + if (params.DS_Tester_Server_Name == null) { + allParametersPresent = false + } else { + dsT_host = params.DS_Tester_Server_Name + } + if (params.DS_Tester_Server_Login == null) { + allParametersPresent = false + } else { + dsT_host_user = params.DS_Tester_Server_Login + } + if (params.DS_Tester_Server_IP_Addr == null) { + allParametersPresent = false + } else { + dsT_host_ip_addr = params.DS_Tester_Server_IP_Addr + } + if (allParametersPresent) { + echo "DS Tester is on ${dsT_host}" + } else { + echo "Some DS Tester parameters are missing!" + sh "./ci-scripts/fail.sh" + } + } + } + + // Find out the cause of the trigger + for (cause in currentBuild.getBuildCauses()) { + if (cause.toString() ==~ /.*UpstreamCause.*/) { + upstreamEvent = true + //} else { + // scmEvent = true + } + } + withCredentials([ + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'], + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password'] + ]) { + if (upstreamEvent) { + if (params.NRF_TAG != null) { + nrfTag = params.NRF_TAG + echo "Upstream Job passed NRF_TAG to use: ${nrfTag}" + } + if (params.AMF_TAG != null) { + amfTag = params.AMF_TAG + echo "Upstream Job passed AMF_TAG to use: ${amfTag}" + } + if (params.SMF_TAG != null) { + smfTag = params.SMF_TAG + echo "Upstream Job passed SMF_TAG to use: ${smfTag}" + } + sh "git clean -x -d -f > /dev/null 2>&1" + sh "git fetch --prune > /dev/null 2>&1" + sh 'git checkout -f ' + upstreamTagToUse + sh "zip -r -qq oai-cn5g-fed.zip .git" + sh "mkdir -p archives DS-TEST-RESULTS" + // Prepare the workspace in the remote server + copyTo2ndServer('oai-cn5g-fed.zip', true, ${eNB_Username}, ${params.eNB_IPAddress}) + myShCmd('git clean -x -d -f > /dev/null 2>&1', true, ${eNB_Username}, ${params.eNB_IPAddress}) + myShCmd('mkdir -p archives DS-TEST-RESULTS', true, ${eNB_Username}, ${params.eNB_IPAddress}) + } + if (scmEvent) { + sh "git clean -x -d -f > /dev/null 2>&1" + if ("MERGE".equals(env.gitlabActionType)) { + sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}" + } + sh "zip -r -qq oai-cn5g-fed.zip .git" + sh "mkdir -p archives DS-TEST-RESULTS" + // Prepare the workspace in remote server + copyTo2ndServer('oai-cn5g-fed.zip', true, ${eNB_Username}, ${params.eNB_IPAddress}) + myShCmd('git clean -x -d -f > /dev/null 2>&1', true, ${eNB_Username}, ${params.eNB_IPAddress}) + if ("MERGE".equals(env.gitlabActionType)) { + myShCmd("./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}", new_host_flag, new_host_user, new_host) + } + myShCmd('mkdir -p archives DS-TEST-RESULTS', true, ${eNB_Username}, ${params.eNB_IPAddress}) + } + if ((!upstreamEvent) && (!scmEvent)) { + sh "git clean -x -d -f > /dev/null 2>&1" + sh "zip -r -qq oai-cn5g-fed.zip .git" + sh "mkdir -p archives DS-TEST-RESULTS" + // Prepare the workspace in the remote server + copyTo2ndServer('oai-cn5g-fed.zip', true, ${eNB_Username}, ${params.eNB_IPAddress}) + myShCmd('git clean -x -d -f > /dev/null 2>&1', true, ${eNB_Username}, ${params.eNB_IPAddress}) + myShCmd('mkdir -p archives DS-TEST-RESULTS', true, ${eNB_Username}, ${params.eNB_IPAddress}) + } + imageTags = "oai-nrf:${nrfTag},oai-amf:${amfTag},oai-smf:${smfTag},oai-spgwu-tiny:${spgwuTag}" + } + } + } + } + stage ('Deploy Whole 5G Core Network') { + steps { + script { + echo '\u2705 \u001B[32mDeploy CN5G in idle mode\u001B[0m' + withCredentials([ + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'], + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password'] + ]) { + try { + sh "python3 helmDeploy.py --mode=Deploy --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName} --imageTags=${imageTags}" + } catch (Exception e) { + currentBuild.result = 'FAILURE' + } + } + } + } + } + stage ('Undeploy 5G-CN') { + steps { + script { + withCredentials([ + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'], + [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password'] + ]) { + try { + sh "python3 helmDeploy.py --mode=UnDeploy --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName} --imageTags=${imageTags}" + } catch (Exception e) { + currentBuild.result = 'FAILURE' + } + } + } + } + } + } +} + +// Functions + +def copyTo2ndServer(filename, flag, user, host) { + if (flag) { + if ("oai-cn5g-fed.zip".equals(filename)) { + sh "ssh ${user}@${host} 'rm -rf /tmp/CI-CN5G-FED-RHEL8'" + sh "ssh ${user}@${host} 'mkdir -p /tmp/CI-CN5G-FED-RHEL8'" + } + sh "scp ${filename} ${user}@${host}:/tmp/CI-CN5G-FED-RHEL8" + if ("oai-cn5g-fed.zip".equals(filename)) { + sh "ssh ${user}@${host} 'cd /tmp/CI-CN5G-FED-RHEL8 && unzip -qq oai-cn5g-fed.zip && rm oai-cn5g-fed.zip'" + sh "ssh ${user}@${host} 'cd /tmp/CI-CN5G-FED-RHEL8 && git checkout -f ${GIT_COMMIT}'" + sh "ssh ${user}@${host} 'cd /tmp/CI-CN5G-FED-RHEL8 && git log -n1'" + } + } +} + +def copyFrom2ndServer(filename, target, flag, user, host) { + if (flag) { + sh "scp ${user}@${host}:/tmp/CI-CN5G-FED-RHEL8/${filename} ${target}" + } +} + +def myShCmd(cmd, flag, user, host) { + if (flag) { + sh "ssh -t -t ${user}@${host} 'cd /tmp/CI-CN5G-FED-RHEL8 && ${cmd}'" + } else { + sh "${cmd}" + } +} + +def myShCmdWithLog(cmd, logFile, flag, user, host) { + if (flag) { + sh "ssh -t -t ${user}@${host} 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:.:/usr/local/devsol/bin && ${cmd}' > ${logFile} 2>&1" + } else { + sh "${cmd} > ${logFile} 2>&1" + } +} + +def myShRetCmd(cmd, flag, user, host) { + if (flag) { + ret = sh returnStdout: true, script: "ssh -t -t ${user}@${host} 'cd /tmp/CI-CN5G-FED-RHEL8 && ${cmd}'" + } else { + ret = sh returnStdout: true, script: "${cmd}" + } + ret = ret.trim() + return ret +} diff --git a/ci-scripts/helmDeploy.py b/ci-scripts/helmDeploy.py new file mode 100644 index 00000000..431cadca --- /dev/null +++ b/ci-scripts/helmDeploy.py @@ -0,0 +1,279 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- +# +# Required Python Version +# Python 3.x +# +# Required Python Package +# pexpect +#--------------------------------------------------------------------- + +#----------------------------------------------------------- +# Import +#----------------------------------------------------------- +import logging +import sshconnection as SSH +import html +import os +import re +import time +import sys + +class ClusterDeploy: + def __init__(self): + self.eNBIPAddress = "" + self.eNBUserName = "" + self.eNBPassword = "" + self.OCUserName = "" + self.OCPassword = "" + self.OCProjectName = "" + self.sourceCodePath = "/tmp/CI-CN5G-FED-RHEL8" + self.imageTags = "" + self.mode = "" + +#-----------------$ +#PUBLIC Methods$ +#-----------------$ + + def Deploy_5gcn(self): + lIpAddr = self.eNBIPAddress + lUserName = self.eNBUserName + lPassWord = self.eNBPassword + lSourcePath = self.sourceCodePath + ocUserName = self.OCUserName + ocPassword = self.OCPassword + ocProjectName = self.OCProjectName + limageTags = self.imageTags + if lIpAddr == '' or lUserName == '' or lPassWord == '' or lSourcePath == '' or ocUserName == '' or ocPassword == '' or ocProjectName == '' or limageTags == '': + sys.exit('Insufficient Parameter') + logging.debug('Running on server: ' + lIpAddr) + mySSH = SSH.SSHConnection() + mySSH.open(lIpAddr, lUserName, lPassWord) + mySSH.command('cd ' + lSourcePath, '\$', 5) + + images = limageTags.split(',') + for image in images: + eachImage = image.split(':') + imageName = eachImage(0) + imageTag = eachImage(1) + # Check if image is exist on the Red Hat server, before pushing it to OC cluster + mySSH.command("sudo podman image inspect --format='Size = {{.Size}} bytes' " + imageName + ":" + imageTag, '\$', 60) + if mySSH.getBefore().count('no such image') != 0: + logging.error(f'\u001B[1m No such image {imageName}]\u001B[0m') + mySSH.close() + sys.exit(-1) + 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(f'\u001B[1m {imageName} size is ' + ('%.0f' % imageSize) + ' kbytes\u001B[0m') + else: + imageSize = imageSize / 1000 + if imageSize < 1000: + logging.debug(f'\u001B[1m {imageName} size is ' + ('%.0f' % imageSize) + ' Mbytes\u001B[0m') + else: + imageSize = imageSize / 1000 + logging.debug(f'\u001B[1m {imageName} is ' + ('%.3f' % imageSize) + ' Gbytes\u001B[0m') + else: + logging.debug(f'{imageName} size is unknown') + + # logging to OC Cluster and then switch to corresponding project + mySSH.command(f'oc login -u {ocUserName} -p {ocPassword}', '\$', 6) + if mySSH.getBefore().count('Login successful.') == 0: + logging.error('\u001B[1m OC Cluster Login Failed\u001B[0m') + mySSH.close() + sys.exit(-1) + else: + logging.debug('\u001B[1m Login to OC Cluster Successfully\u001B[0m') + 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: + logging.error(f'\u001B[1m Unable to access OC project {ocProjectName}\u001B[0m') + mySSH.close() + sys.exit(-1) + else: + logging.debug(f'\u001B[1m Now using project {ocProjectName}\u001B[0m') + + # Tag the image and push to the OC cluster + mySSH.command('oc whoami -t | sudo podman login -u ' + ocUserName + ' --password-stdin https://default-route-openshift-image-registry.apps.5glab.nsa.eurecom.fr/ --tls-verify=false', '\$', 6) + if mySSH.getBefore().count('Login Succeeded!') == 0: + logging.error('\u001B[1m Podman Login to OC Cluster Registry Failed\u001B[0m') + mySSH.close() + sys.exit(-1) + else: + logging.debug('\u001B[1m Podman Login to OC Cluster Registry Successfully\u001B[0m') + for image in images: + imageName = image(0) + imageTag = image(1) + mySSH.command(f'oc create -f openshift/{imageName}-image-stream.yml', '\$', 6) + if mySSH.getBefore().count('(AlreadyExists):') == 0 and mySSH.getBefore().count('created') == 0: + logging.error(f'\u001B[1m Image Stream "{imageName}" Creation Failed on OC Cluster {ocProjectName}\u001B[0m') + mySSH.close() + sys.exit(-1) + else: + logging.debug(f'\u001B[1m Image Stream "{imageName}" created on OC project {ocProjectName}\u001B[0m') + mySSH.command(f'sudo podman tag {imageName}:{imageTag} default-route-openshift-image-registry.apps.5glab.nsa.eurecom.fr/{self.OCProjectName}/{imageName}:{imageTag}', '\$', 6) + mySSH.command(f'sudo podman push default-route-openshift-image-registry.apps.5glab.nsa.eurecom.fr/{self.OCProjectName}/{imageName}:{imageTag} --tls-verify=false', '\$', 60) + if mySSH.getBefore().count('Storing signatures') == 0: + logging.error(f'\u001B[1m Image "{imageName}" push to OC Cluster Registry Failed\u001B[0m') + mySSH.close() + sys.exit(-1) + else: + logging.debug(f'\u001B[1m Image "{imageName}" push to OC Cluster Registry Successfully\u001B[0m') + + passPods = 0 + # Using helm charts deployment + time.sleep(5) + for image in images: + eachImage = image.split(':') + imageName = eachImage(0) + imageTag = eachImage(1) + mySSH.command(f'sed -i -e "s#TAG#{imageTag}#g" ./charts/oai-5gcn/charts/{imageName}/values.yaml', '\$', 6) + if imageName == 'oai-nrf': + nameSufix = 'nrf' + elif imageName == 'oai-amf': + nameSufix = 'amf' + elif imageName == 'oai-smf': + nameSufix = 'smf' + elif imageName == 'oai-spgwu-tiny': + nameSufix = 'spgwu' + mySSH.command(f'helm install {imageName} ./charts/{imageName}/ | tee -a archives/5gcn_helm_summary.txt 2>&1', '\$', 6) + if mySSH.getBefore().count('STATUS: deployed') == 0: + logging.error(f'\u001B[1m Deploying "{imageName}" Failed using helm chart on OC Cluster\u001B[0m') + else: + logging.debug(f'\u001B[1m Deployed "{imageName}" Successfully using helm chart\u001B[0m') + time.sleep(60) + mySSH.command(f'oc get pods -o wide -l app.kubernetes.io/name={imageName} | tee -a archives/5gcn_pods_summary.txt', '\$', 6, resync=True) + podName = re.findall(f'{imageName}[\S\d\w]+', mySSH.getBefore()) + isRunning = False + count = 0 + while count < 2 and isRunning == False: + time.sleep(60) + mySSH.command(f'oc exec {podName} -c {nameSufix} -it -- ps aux', '\$', 6, resync=True) + if mySSH.getBefore().count(f'oai_{nameSufix}') != 0: + logging.debug(f'\u001B[1m POD "{imageName}" Service Running Sucessfully\u001B[0m') + isRunning = True + passPods += 1 + count +=1 + if isRunning == False: + logging.error(f'\u001B[1m POD "{imageName}" Service Running FAILED \u001B[0m') + + if passPods == 4: + logging.debug(f'\u001B[1m Deployment: OK \u001B[0m') + else: + logging.error(f'\u001B[1m Deployment: KO \u001B[0m') + self.UnDeploy_5gcn() + self.AnalyzeLogFile_5gcn() + sys.exit(-1) + self.AnalyzeLogFile_5gcn() + + def UnDeploy_5gcn(self): + mySSH = SSH.SSHConnection() + mySSH.open(lIpAddr, lUserName, lPassWord) + mySSH.command('cd ' + lSourcePath, '\$', 5) + logging.debug('\u001B[1m UnDeploying the 5gcn\u001B[0m') + # logging to OC Cluster and then switch to corresponding project + mySSH.command(f'oc login -u {ocUserName} -p {ocPassword}', '\$', 6) + if mySSH.getBefore().count('Login successful.') == 0: + logging.error('\u001B[1m OC Cluster Login Failed\u001B[0m') + mySSH.close() + sys.exit(-1) + else: + logging.debug('\u001B[1m Login to OC Cluster Successfully\u001B[0m') + 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: + logging.error(f'\u001B[1m Unable to access OC project {ocProjectName}\u001B[0m') + mySSH.close() + sys.exit(-1) + else: + logging.debug(f'\u001B[1m Now using project {ocProjectName}\u001B[0m') + + # UnDeploy the 5gcn pods + images = self.imageTags.split(',') + for image in images: + eachImage = image.split(':') + imageName = eachImage(0) + imageTag = eachImage(1) + mySSH.command(f'helm uninstall {imageName} | tee -a archives/5gcn_helm_summary.txt 2>&1', '\$', 6) + if mySSH.getBefore().count(f'release "{imageName}" uninstalled') == 0 and mySSH.getBefore().count('release: not found') == 0: + logging.error(f'\u001B[1m UnDeploying "{imageName}" Failed using helm chart on OC Cluster\u001B[0m') + else: + logging.debug(f'\u001B[1m UnDeployed "{imageName}" Successfully on OC Cluster\u001B[0m') + # Delete images and imagestream + mySSH.command(f'sudo podman rmi default-route-openshift-image-registry.apps.5glab.nsa.eurecom.fr/{self.OCProjectName}/{imageName}:{imageTag}', '\$', 6) + mySSH.command(f'oc delete is {imageName}', '\$', 6) + logging.debug(f'\u001B[1m Deleted the "{imageName}" Image and ImageStream\u001B[0m') + mySSH.command('oc logout', '\$', 6) + mySSH.close() + self.AnalyzeLogFile_5gcn() + + + def AnalyzeLogFile_5gcn(self): + pass + + + +#-------------------------------------------------------------------------------------------------------- +# +# Start of main +# +#-------------------------------------------------------------------------------------------------------- + +CN = ClusterDeploy() + +argvs = sys.argv +argc = len(argvs) + +while len(argvs) > 1: + myArgv = argvs.pop(1) + if re.match('^\-\-mode=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-mode=(.+)$', myArgv, re.IGNORECASE) + CN.mode = matchReg.group(1) + elif re.match('^\-\-eNBIPAddress=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-eNBIPAddress=(.+)$', myArgv, re.IGNORECASE) + CN.eNBIPAddress = matchReg.group(1) + elif re.match('^\-\-eNBUserName=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-eNBUserName=(.+)$', myArgv, re.IGNORECASE) + CN.eNBUserName = matchReg.group(1) + elif re.match('^\-\-eNBPassword=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-eNBPassword=(.+)$', myArgv, re.IGNORECASE) + CN.eNBPassword = matchReg.group(1) + elif re.match('^\-\-OCUserName=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-OCUserName=(.+)$', myArgv, re.IGNORECASE) + CN.OCUserName = matchReg.group(1) + elif re.match('^\-\-OCPassword=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-OCPassword=(.+)$', myArgv, re.IGNORECASE) + CN.OCPassword = matchReg.group(1) + elif re.match('^\-\-OCProjectName=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-OCProjectName=(.+)$', myArgv, re.IGNORECASE) + CN.OCProjectName = matchReg.group(1) + elif re.match('^\-\-imageTags=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-imageTags=(.+)$', myArgv, re.IGNORECASE) + CN.imageTags = matchReg.group(1) + else: + sys.exit('Invalid Parameter: ' + myArgv) + +if CN.mode == 'Deploy': + CN.Deploy_5gcn() +elif CN.mode == 'UnDeploy': + CN.UnDeploy_5gcn() \ No newline at end of file diff --git a/ci-scripts/sshconnection.py b/ci-scripts/sshconnection.py new file mode 100644 index 00000000..b4087c96 --- /dev/null +++ b/ci-scripts/sshconnection.py @@ -0,0 +1,259 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# Python for CI of OAI-eNB + COTS-UE +# +# Required Python Version +# Python 3.x +# +# Required Python Package +# pexpect +#--------------------------------------------------------------------- + +#----------------------------------------------------------- +# Import +#----------------------------------------------------------- +import pexpect # pexpect +import logging +import time # sleep +import re +import subprocess +import sys + +#----------------------------------------------------------- +# Class Declaration +#----------------------------------------------------------- +class SSHConnection(): + def __init__(self): + self.ssh = '' + self.picocom_closure = False + self.ipaddress = '' + self.username = '' + self.cmd2Results = '' + + def disablePicocomClosure(self): + self.picocom_closure = False + + def enablePicocomClosure(self): + self.picocom_closure = True + + def open(self, ipaddress, username, password): + count = 0 + connect_status = False + while count < 4: + self.ssh = pexpect.spawn('ssh -o PubkeyAuthentication=no {}@{}'.format(username,ipaddress)) + self.ssh.timeout = 5 + self.sshresponse = self.ssh.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', 'Last login', pexpect.EOF, pexpect.TIMEOUT]) + if self.sshresponse == 0: + self.ssh.sendline('yes') + self.sshresponse = self.ssh.expect(['password:', username + '@']) + if self.sshresponse == 0: + self.ssh.sendline(password) + self.sshresponse = self.ssh.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT]) + if self.sshresponse == 0: + count = 10 + connect_status = True + else: + logging.debug('self.sshresponse = ' + str(self.sshresponse)) + elif self.sshresponse == 1: + self.ssh.sendline(password) + self.sshresponse = self.ssh.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT]) + if self.sshresponse == 0: + count = 10 + connect_status = True + else: + logging.debug('self.sshresponse = ' + str(self.sshresponse)) + elif self.sshresponse == 2: + # Checking if we are really on the remote client defined by its IP address + self.command('stdbuf -o0 ifconfig | egrep --color=never "inet addr:|inet "', '\$', 5) + result = re.search(str(ipaddress), str(self.ssh.before)) + if result is None: + self.close() + else: + count = 10 + connect_status = True + else: + # debug output + logging.debug(str(self.ssh.before)) + logging.debug('self.sshresponse = ' + str(self.sshresponse)) + # adding a tempo when failure + if not connect_status: + time.sleep(1) + count += 1 + if connect_status: + pass + else: + sys.exit('SSH Connection Failed') + self.ipaddress = ipaddress + self.username = username + + + + + def cde_check_value(self, commandline, expected, timeout): + logging.debug(commandline) + self.ssh.timeout = timeout + self.ssh.sendline(commandline) + expected.append(pexpect.EOF) + expected.append(pexpect.TIMEOUT) + self.sshresponse = self.ssh.expect(expected) + return self.sshresponse + + def command(self, commandline, expectedline, timeout, silent=False, resync=False): + if not silent: + logging.debug(commandline) + self.ssh.timeout = timeout + # Nasty patch when pexpect output is out of sync. + # Much pronounced when running back-to-back-back oc commands + if resync: + self.ssh.send(commandline) + self.ssh.expect([commandline, pexpect.TIMEOUT]) + self.ssh.send('\r\n') + self.sshresponse = self.ssh.expect([expectedline, pexpect.EOF, pexpect.TIMEOUT]) + else: + self.ssh.sendline(commandline) + self.sshresponse = self.ssh.expect([expectedline, pexpect.EOF, pexpect.TIMEOUT]) + if self.sshresponse == 0: + return 0 + elif self.sshresponse == 1: + logging.debug('\u001B[1;37;41m Unexpected EOF \u001B[0m') + logging.debug('Expected Line : ' + expectedline) + logging.debug(str(self.ssh.before)) + sys.exit(self.sshresponse) + elif self.sshresponse == 2: + logging.debug('\u001B[1;37;41m Unexpected TIMEOUT \u001B[0m') + logging.debug('Expected Line : ' + expectedline) + result = re.search('ping |iperf |picocom', str(commandline)) + if result is None: + logging.debug(str(self.ssh.before)) + sys.exit(self.sshresponse) + else: + return -1 + else: + logging.debug('\u001B[1;37;41m Unexpected Others \u001B[0m') + logging.debug('Expected Line : ' + expectedline) + sys.exit(self.sshresponse) + + def command2(self, commandline, timeout, silent=False): + if not silent: + logging.debug(commandline) + self.cmd2Results = '' + myHost = self.username + '@' + self.ipaddress + # CAUTION: THIS METHOD IMPLIES THAT THERE ARE VALID SSH KEYS + # BETWEEN THE PYTHON EXECUTOR NODE AND THE REMOTE HOST + # OTHERWISE IT WON'T WORK + lSsh = subprocess.Popen(["ssh", "%s" % myHost, commandline],shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + self.cmd2Results = str(lSsh.stdout.readlines()) + + def close(self): + self.ssh.timeout = 5 + self.ssh.sendline('exit') + self.sshresponse = self.ssh.expect([pexpect.EOF, pexpect.TIMEOUT]) + self.ipaddress = '' + self.username = '' + if self.sshresponse == 0: + pass + elif self.sshresponse == 1: + if not self.picocom_closure: + logging.debug('\u001B[1;37;41m Unexpected TIMEOUT during closing\u001B[0m') + else: + logging.debug('\u001B[1;37;41m Unexpected Others during closing\u001B[0m') + + def copyin(self, ipaddress, username, password, source, destination): + count = 0 + copy_status = False + logging.debug('scp '+ username + '@' + ipaddress + ':' + source + ' ' + destination) + while count < 10: + scp_spawn = pexpect.spawn('scp '+ username + '@' + ipaddress + ':' + source + ' ' + destination, timeout = 100) + scp_response = scp_spawn.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', pexpect.EOF, pexpect.TIMEOUT]) + if scp_response == 0: + scp_spawn.sendline('yes') + scp_spawn.expect('password:') + scp_spawn.sendline(password) + scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT]) + if scp_response == 0: + count = 10 + copy_status = True + else: + logging.debug('1 - scp_response = ' + str(scp_response)) + elif scp_response == 1: + scp_spawn.sendline(password) + scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT]) + if scp_response == 0 or scp_response == 3: + count = 10 + copy_status = True + else: + logging.debug('2 - scp_response = ' + str(scp_response)) + elif scp_response == 2: + count = 10 + copy_status = True + else: + logging.debug('3 - scp_response = ' + str(scp_response)) + # adding a tempo when failure + if not copy_status: + time.sleep(1) + count += 1 + if copy_status: + return 0 + else: + return -1 + + def copyout(self, ipaddress, username, password, source, destination): + count = 0 + copy_status = False + logging.debug('scp ' + source + ' ' + username + '@' + ipaddress + ':' + destination) + while count < 4: + scp_spawn = pexpect.spawn('scp ' + source + ' ' + username + '@' + ipaddress + ':' + destination, timeout = 100) + scp_response = scp_spawn.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', pexpect.EOF, pexpect.TIMEOUT]) + if scp_response == 0: + scp_spawn.sendline('yes') + scp_spawn.expect('password:') + scp_spawn.sendline(password) + scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT]) + if scp_response == 0: + count = 10 + copy_status = True + else: + logging.debug('1 - scp_response = ' + str(scp_response)) + elif scp_response == 1: + scp_spawn.sendline(password) + scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT]) + if scp_response == 0 or scp_response == 3: + count = 10 + copy_status = True + else: + logging.debug('2 - scp_response = ' + str(scp_response)) + elif scp_response == 2: + count = 10 + copy_status = True + else: + logging.debug('3 - scp_response = ' + str(scp_response)) + # adding a tempo when failure + if not copy_status: + time.sleep(1) + count += 1 + if copy_status: + pass + else: + sys.exit('SCP failed') + + def getBefore(self): + return str(self.ssh.before) diff --git a/openshift/oai-amf-image-stream.yml b/openshift/oai-amf-image-stream.yml new file mode 100644 index 00000000..894517e1 --- /dev/null +++ b/openshift/oai-amf-image-stream.yml @@ -0,0 +1,30 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- +# +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: oai-amf +spec: + lookupPolicy: + local: true + diff --git a/openshift/oai-nrf-image-stream.yml b/openshift/oai-nrf-image-stream.yml new file mode 100644 index 00000000..6587261a --- /dev/null +++ b/openshift/oai-nrf-image-stream.yml @@ -0,0 +1,30 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- +# +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: oai-nrf +spec: + lookupPolicy: + local: true + diff --git a/openshift/oai-smf-image-stream.yml b/openshift/oai-smf-image-stream.yml new file mode 100644 index 00000000..cd807617 --- /dev/null +++ b/openshift/oai-smf-image-stream.yml @@ -0,0 +1,30 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- +# +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: oai-smf +spec: + lookupPolicy: + local: true + diff --git a/openshift/oai-spgwu-tiny-image-stream.yml b/openshift/oai-spgwu-tiny-image-stream.yml new file mode 100644 index 00000000..37e53693 --- /dev/null +++ b/openshift/oai-spgwu-tiny-image-stream.yml @@ -0,0 +1,30 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- +# +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: oai-spgwu-tiny +spec: + lookupPolicy: + local: true + -- GitLab