diff --git a/ci-scripts/Dockerfile.traffic.generator.rhel8 b/ci-scripts/Dockerfile.traffic.generator.rhel8 new file mode 100644 index 0000000..991fc06 --- /dev/null +++ b/ci-scripts/Dockerfile.traffic.generator.rhel8 @@ -0,0 +1,93 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- +# +# +#--------------------------------------------------------------------- + +#--------------------------------------------------------------------- +# BUILDER IMAGE +#--------------------------------------------------------------------- +FROM registry.access.redhat.com/ubi8/ubi:latest as trf-gen-builder + +COPY tmp/ca/redhat-uep.pem /etc/rhsm/ca +COPY tmp/entitlement/*.pem /etc/pki/entitlement + +RUN rm /etc/rhsm-host && \ + # Initialize /etc/yum.repos.d/redhat.repo + # See https://access.redhat.com/solutions/1443553 + yum repolist --disablerepo=* && \ + subscription-manager repos --enable codeready-builder-for-rhel-8-x86_64-rpms && \ + yum update -y && \ + yum -y install --enablerepo="codeready-builder-for-rhel-8-x86_64-rpms" \ + # diff, cmp and file are not in the ubi??? + gcc gcc-c++ \ + git \ + make \ + psmisc \ + traceroute \ + wget && \ + echo "/usr/local/lib" > /etc/ld.so.conf.d/local-lib.conf && \ + echo "/usr/local/lib64" >> /etc/ld.so.conf.d/local-lib.conf + +WORKDIR / +RUN wget --tries=3 https://iperf.fr/download/source/iperf-2.0.5-source.tar.gz && \ + gzip -cd iperf-2.0.5-source.tar.gz | tar -xvf - + +WORKDIR /iperf-2.0.5 +RUN sed -i -e "s@#include /etc/ld.so.conf.d/local-lib.conf && \ + yum clean all -y && \ + rm -rf /var/cache/yum + +WORKDIR /iperf-2.0.5/bin +COPY --from=trf-gen-builder /iperf-2.0.5/src/iperf . + +WORKDIR /iperf-2.0.10/bin +COPY --from=trf-gen-builder /iperf-2.0.10/src/iperf . + +CMD ["sleep", "infinity"] diff --git a/ci-scripts/Jenkinsfile-Magma-RHEL8-Sanity-Check b/ci-scripts/Jenkinsfile-Magma-RHEL8-Sanity-Check new file mode 100644 index 0000000..4b57a63 --- /dev/null +++ b/ci-scripts/Jenkinsfile-Magma-RHEL8-Sanity-Check @@ -0,0 +1,287 @@ +#!/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 + */ + +// Location of the CN executor node +def cn_ci_host = params.Host_CN_CI_Server + +// for lock +def cn_ci_resource = params.DockerContainers + +// Default tags / branches --> could be passed on by upstream job or by PR content +def hssTag = 'develop' +def hssBranch = 'develop' +def magmaMmeTag = 'master' +// No branch for MAGMA +def spgwcTag = 'develop' +def spgwcBranch = 'develop' +def spgwuTag = 'develop' +def spgwuBranch = 'develop' + +// RAN Tags to use +def oaiEnbTag = 'develop-2021-w20-gen' +def oaiLteUeTag = 'develop-2021-w20-gen' + +// Flags +def scmEvent = false +def upstreamEvent = false +def retrievedLogs = false + +//------------------------------------------------------------------------------- +// 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() + + // Clean workspace and prepare artifacts location + sh "git clean -x -d -f > /dev/null 2>&1" + sh "mkdir -p archives" + + // Find out the cause of the trigger + for (cause in currentBuild.getBuildCauses()) { + if (cause.toString() ==~ /.*GitHubPushCause.*/) { + scmEvent = true + } else if (cause.toString() ==~ /.*GhprbCause.*/) { + scmEvent = true + } else if (cause.toString() ==~ /.*UpstreamCause.*/) { + upstreamEvent = true + } + } + + if (upstreamEvent) { + if (params.HSS_TAG != null) { + hssTag = params.HSS_TAG + echo "Upstream Job passed HSS_TAG to use: ${hssTag}" + } + if (params.HSS_BRANCH != null) { + hssBranch = params.HSS_BRANCH + echo "Upstream Job passed HSS_BRANCH to use: ${hssBranch}" + } + if (params.MAGMA_MME_TAG != null) { + magmaMmeTag = params.MAGMA_MME_TAG + echo "Upstream Job passed MAGMA_MME_TAG to use: ${magmaMmeTag}" + } + if (params.SPGWC_TAG != null) { + spgwcTag = params.SPGWC_TAG + echo "Upstream Job passed SPGWC_TAG to use: ${spgwcTag}" + } + if (params.SPGWC_BRANCH != null) { + spgwcBranch = params.SPGWC_BRANCH + echo "Upstream Job passed SPGWC_BRANCH to use: ${spgwcBranch}" + } + if (params.SPGWU_TAG != null) { + spgwuTag = params.SPGWU_TAG + echo "Upstream Job passed SPGWU_TAG to use: ${spgwuTag}" + } + if (params.SPGWU_BRANCH != null) { + spgwuBranch = params.SPGWU_BRANCH + echo "Upstream Job passed SPGWU_BRANCH to use: ${spgwuBranch}" + } + } + // Here we verify if all images tags are available. + try { + sh 'echo "MAGMA_MME_TAG: magma-mme:' + magmaMmeTag +'" > archives/magma_mme_image_info.log' + sh 'sudo podman image inspect --format=\'Size = {{.Size}} bytes\' magma-mme:' + magmaMmeTag + ' >> archives/magma_mme_image_info.log' + sh 'sudo podman image inspect --format=\'Date = {{.Created}}\' magma-mme:' + magmaMmeTag + ' >> archives/magma_mme_image_info.log' + } catch (Exception e) { + error "Magma MME Image tag to test does not exist!" + } + try { + sh 'echo "OAI_HSS_TAG: oai-hss:' + hssTag +'" > archives/oai_hss_image_info.log' + sh 'sudo podman image inspect --format=\'Size = {{.Size}} bytes\' oai-hss:' + hssTag + ' >> archives/oai_hss_image_info.log' + sh 'sudo podman image inspect --format=\'Date = {{.Created}}\' oai-hss:' + hssTag + ' >> archives/oai_hss_image_info.log' + } catch (Exception e) { + error "OAI HSS Image tag to test does not exist!" + } + try { + sh 'echo "OAI_SPGWC_TAG: oai-spgwc:' + spgwcTag +'" > archives/oai_spgwc_image_info.log' + sh 'sudo podman image inspect --format=\'Size = {{.Size}} bytes\' oai-spgwc:' + spgwcTag + ' >> archives/oai_spgwc_image_info.log' + sh 'sudo podman image inspect --format=\'Date = {{.Created}}\' oai-spgwc:' + spgwcTag + ' >> archives/oai_spgwc_image_info.log' + } catch (Exception e) { + error "OAI SPGW-C Image tag to test does not exist!" + } + try { + sh 'echo "OAI_SPGWU_TAG: oai-spgwu-tiny:' + spgwuTag +'" > archives/oai_spgwu_image_info.log' + sh 'sudo podman image inspect --format=\'Size = {{.Size}} bytes\' oai-spgwu-tiny:' + spgwuTag + ' >> archives/oai_spgwu_image_info.log' + sh 'sudo podman image inspect --format=\'Date = {{.Created}}\' oai-spgwu-tiny:' + spgwuTag + ' >> archives/oai_spgwu_image_info.log' + } catch (Exception e) { + error "OAI SPGW-U-Tiny Image tag to test does not exist!" + } + + // Prepare workspace + sh './scripts/syncComponents.sh --hss-branch ' + hssBranch + ' --spgwc-branch ' + spgwcBranch + ' --spgwu-tiny-branch ' + spgwuBranch + } + } + } + stage ('Deploy EPC on RHEL8 host') { + steps { + script { + // Currently docker-compose does not work with podman3 the way we want + // Re-using a python based approach + // First making sure remanent from previous run + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RemoveAllContainers > /dev/null 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RemoveNetworks > /dev/null 2>&1' + // Create networks + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=CreateNetworks 2>&1' + // Deploy all CN containers + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeployCassandra 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeployHSS --tag=' + hssTag + ' 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeployRedis 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeployMME --tag=' + magmaMmeTag + ' 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeploySPGWC --tag=' + spgwcTag + ' 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeploySPGWU --tag=' + spgwuTag + ' 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeployTrfGen --tag=production 2>&1' + } + } + post { + always { + script { + // Check status on cassandra. + try { + sh 'sudo podman exec -i cicd-cassandra /bin/bash -c "nodetool status" > archives/cassandra_status.log 2>&1' + sh 'sudo podman inspect --format=\'STATUS: {{.State.Healthcheck.Status}}\' cicd-cassandra >> archives/cassandra_status.log' + } catch (Exception e) { + sh 'echo "STATUS: KO" >> archives/cassandra_status.log' + } + // Check status on redis. + try { + sh 'sudo podman inspect --format=\'STATUS: {{.State.Healthcheck.Status}}\' cicd-redis > archives/redis_status.log' + } catch (Exception e) { + sh 'echo "STATUS: KO" >> archives/redis_status.log' + } + try { + sh 'sudo podman inspect --format=\'STATUS: {{.State.Healthcheck.Status}}\' cicd-oai-hss > archives/hss_config.log' + } catch (Exception e) { + sh 'echo "STATUS: KO" >> archives/hss_config.log' + } + try { + sh 'sudo podman inspect --format=\'STATUS: {{.State.Healthcheck.Status}}\' cicd-oai-mme > archives/mme_config.log' + } catch (Exception e) { + sh 'echo "STATUS: KO" >> archives/mme_config.log' + } + try { + sh 'sudo podman inspect --format=\'STATUS: {{.State.Healthcheck.Status}}\' cicd-oai-spgwc > archives/spgwc_config.log' + } catch (Exception e) { + sh 'echo "STATUS: KO" >> archives/spgwc_config.log' + } + try { + sh 'sudo podman inspect --format=\'STATUS: {{.State.Healthcheck.Status}}\' cicd-oai-spgwu-tiny > archives/spgwu_config.log' + } catch (Exception e) { + sh 'echo "STATUS: KO" >> archives/spgwu_config.log' + } + } + } + success { + script { + sh 'echo "DEPLOYMENT: OK" > archives/deployment_status.log' + } + } + unsuccessful { + script { + sh 'echo "DEPLOYMENT: KO" > archives/deployment_status.log' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RetrieveLogs' + retrievedLogs = true + } + } + } + } + stage ('Deploy OAI eNB/UE RF Sim') { + steps { + script { + // Deploy all RAN/UE containers + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeployRfSimENB --tag=' + oaiEnbTag + ' 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=DeployRfSimUE --tag=' + oaiLteUeTag + ' 2>&1' + sh 'sleep 120' + // Retrieve all logs + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RetrieveLogs' + retrievedLogs = true + // Generate HTML report by parsing PCAP-captured packets is the final say on pass/fail criteria + // Something wrong occurs before this line, then it will be generate in the final post section + sh 'python3 ./ci-scripts/rhel8SanityCheckReport.py --job_name=' + JOB_NAME + ' --job_id=' + BUILD_ID + ' --job_url=' + BUILD_URL + } + } + post { + always { + script { + if (!retrievedLogs) { + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RetrieveLogs' + } + } + } + success { + script { + sh 'echo "OAI RAN STACK TEST: OK" > archives/oai_ran_stack_test.log' + } + } + unsuccessful { + script { + sh 'echo "OAI RAN STACK TEST: KO" > archives/oai_ran_stack_test.log' + } + } + } + } + stage ('Undeploy EPC') { + steps { + script { + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RemoveAllContainers 2>&1' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RemoveNetworks 2>&1' + } + } + } + } + post { + always { + script { + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RemoveAllContainers > /dev/null 2>&1 || true' + sh 'python3 ./ci-scripts/OAI-RAN-Sanity-Check-Deploy.py --action=RemoveNetworks > /dev/null 2>&1 || true' + // Zipping all archived log files + sh "zip -r -qq fed_podman_logs.zip archives" + if (fileExists('fed_podman_logs.zip')) { + archiveArtifacts artifacts: 'fed_podman_logs.zip' + } + // If report not present, certainly something wrong occured. + // Generate it anyway without caring out about result + if (!fileExists('test_results_oai_epc_rhel8.html')) { + sh 'python3 ./ci-scripts/rhel8SanityCheckReport.py --job_name=' + JOB_NAME + ' --job_id=' + BUILD_ID + ' --job_url=' + BUILD_URL + ' || true' + } + if (fileExists('test_results_oai_epc_rhel8.html')) { + sh 'mv test_results_oai_epc_rhel8.html test_results_magma_epc_rhel8.html' + archiveArtifacts artifacts: 'test_results_magma_epc_rhel8.html' + } + } + } + } +} diff --git a/ci-scripts/OAI-RAN-Sanity-Check-Deploy.py b/ci-scripts/OAI-RAN-Sanity-Check-Deploy.py new file mode 100644 index 0000000..91119af --- /dev/null +++ b/ci-scripts/OAI-RAN-Sanity-Check-Deploy.py @@ -0,0 +1,636 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- + +import os +import re +import sys +import subprocess +import time + + +CICD_PUBLIC_NETWORK_RANGE='192.168.61.192/26' + +CICD_CASS_IP_ADDR='192.168.61.194' +CICD_HSS_PUBLIC_ADDR='192.168.61.195' +CICD_MME_PUBLIC_ADDR='192.168.61.196' +CICD_REDIS_PUBLIC_ADDR='192.168.61.197' +CICD_SPGWC_PUBLIC_ADDR='192.168.61.198' +CICD_SPGWU_PUBLIC_ADDR='192.168.61.199' +CICD_TRFGEN_PUBLIC_ADDR='192.168.61.200' + +CICD_ENB_RF_SIM_PUBLIC_ADDR='192.168.61.210' +CICD_UE0_RF_SIM_PUBLIC_ADDR='192.168.61.220' + +class deployWithOAIran: + + def __init__(self): + + self.action = 'None' + self.tag = '' + self.cli = '' + res = subprocess.check_output('hostnamectl', shell=True, universal_newlines=True) + result = re.search('Ubuntu|Red Hat', res) + if result is not None: + if result.group(0) == 'Red Hat': + self.cli = 'sudo podman' + elif result.group(0) == 'Ubuntu': + self.cli = 'docker' + else: + sys.exit(-1) + + def createNetworks(self): + # first check if already up? + try: + res = subprocess.check_output(self.cli + ' network ls | egrep -c "cicd-oai-public-net"', shell=True, universal_newlines=True) + if int(str(res.strip())) > 0: + self.removeNetworks() + except: + pass + + subprocess_run_w_echo(self.cli + ' network create --subnet ' + CICD_PUBLIC_NETWORK_RANGE + ' --ip-range ' + CICD_PUBLIC_NETWORK_RANGE + ' cicd-oai-public-net') + + def removeNetworks(self): + try: + subprocess_run_w_echo(self.cli + ' network rm cicd-oai-public-net') + except: + pass + + def deployCassandra(self): + # first check if already up? If yes, remove everything. + try: + res = subprocess.check_output(self.cli + ' ps -a | grep -c "cicd-cassandra"', shell=True, universal_newlines=True) + if int(str(res.strip())) > 0: + self.removeAllContainers() + except: + pass + + subprocess_run_w_echo(self.cli + ' run --name cicd-cassandra --network cicd-oai-public-net --ip ' + CICD_CASS_IP_ADDR + ' -d -e CASSANDRA_CLUSTER_NAME="OAI HSS Cluster" -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch --health-cmd "nodetool status" cassandra:2.1') + # waiting for the service to be properly started + doLoop = True + while doLoop: + try: + res = subprocess.check_output(self.cli + ' exec cicd-cassandra /bin/bash -c "nodetool status"', shell=True, universal_newlines=True) + rackFound = re.search('UN ' + CICD_CASS_IP_ADDR, str(res)) + if rackFound is not None: + doLoop = False + except: + time.sleep(1) + pass + subprocess_run_w_echo(self.cli + ' exec cicd-cassandra /bin/bash -c "nodetool status" | tee archives/cassandra_status.log') + subprocess_run_w_echo(self.cli + ' cp component/oai-hss/src/hss_rel14/db/oai_db.cql cicd-cassandra:/home') + time.sleep(2) + doLoop = True + count = 0 + while doLoop and (count < 10): + try: + res = subprocess.check_output(self.cli + ' exec cicd-cassandra /bin/bash -c "cqlsh --file /home/oai_db.cql ' + CICD_CASS_IP_ADDR + '"', shell=True, universal_newlines=True) + print(self.cli + ' exec cicd-cassandra /bin/bash -c "cqlsh --file /home/oai_db.cql ' + CICD_CASS_IP_ADDR + '"') + doLoop = False + except: + time.sleep(2) + count += 1 + pass + if not doLoop: + print ('Deployment Cassandra --> OK') + else: + print ('Problem deploying Cassandra') + sys.exit(-1) + + def deployHSS(self): + res = '' + # first check if tag exists + try: + res = subprocess.check_output(self.cli + ' image inspect oai-hss:' + self.tag, shell=True, universal_newlines=True) + except: + sys.exit(-1) + + hssEnvFile = '' + cwd = os.getcwd() + if os.path.isfile(cwd + '/ci-scripts/oai-ran-sanity-check/hss.env'): + hssEnvFile = './ci-scripts/oai-ran-sanity-check/hss.env' + elif os.path.isfile(cwd + '/oai-ran-sanity-check/hss.env'): + hssEnvFile = './oai-ran-sanity-check/hss.env' + else: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-oai-hss --network cicd-oai-public-net --ip ' + CICD_HSS_PUBLIC_ADDR + ' --env-file ' + hssEnvFile + ' --health-cmd "pgrep --count oai_hss" -d oai-hss:' + self.tag) + + count = 0 + runCount = 0 + status = False + while (count < 10) and (runCount < 5): + res = subprocess.check_output(self.cli + ' top cicd-oai-hss', shell=True, universal_newlines=True) + if re.search('/openair-hss/bin/oai_hss -j /openair-hss/etc/hss_rel14.json --reloadkey true', str(res)): + runCount += 1 + if runCount == 5: + status = True + count += 1 + time.sleep(2) + if status: + print ('Deployment HSS --> OK') + else: + print ('Problem deploying HSS') + sys.exit(-1) + + def deployRedis(self): + redisConfFile = '' + cwd = os.getcwd() + if os.path.isfile(cwd + '/ci-scripts/oai-ran-sanity-check/redis_extern.conf'): + redisConfFile = './ci-scripts/oai-ran-sanity-check/redis_extern.conf' + elif os.path.isfile(cwd + '/oai-ran-sanity-check/redis_extern.conf'): + redisConfFile = './oai-ran-sanity-check/redis_extern.conf' + else: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-redis --network cicd-oai-public-net --ip ' + CICD_REDIS_PUBLIC_ADDR + ' --volume ' + redisConfFile + ':/usr/local/etc/redis/redis.conf --health-cmd "redis-cli -h ' + CICD_REDIS_PUBLIC_ADDR + ' -p 6380 ping" -d redis:6.0.5 "/usr/local/etc/redis/redis.conf"') + + count = 0 + runCount = 0 + status = False + while (count < 10) and (runCount < 5): + res = subprocess.check_output(self.cli + ' top cicd-redis', shell=True, universal_newlines=True) + if re.search('redis-server 192.168.61.197:6380', str(res)): + runCount += 1 + if runCount == 5: + status = True + count += 1 + time.sleep(2) + if status: + print ('Deployment REDIS --> OK') + else: + print ('Problem deploying REDIS') + sys.exit(-1) + + def deployMME(self): + res = '' + # first check if tag exists + try: + res = subprocess.check_output(self.cli + ' image inspect magma-mme:' + self.tag, shell=True, universal_newlines=True) + except: + sys.exit(-1) + + mmeEnvFile = '' + mmeEntrypoint = '' + cwd = os.getcwd() + if os.path.isfile(cwd + '/ci-scripts/oai-ran-sanity-check/mme.env'): + mmeEnvFile = './ci-scripts/oai-ran-sanity-check/mme.env' + mmeEntrypoint = './ci-scripts/oai-ran-sanity-check/mme-entrypoint.sh' + elif os.path.isfile(cwd + '/oai-ran-sanity-check/mme.env'): + mmeEnvFile = './oai-ran-sanity-check/mme.env' + mmeEntrypoint = './oai-ran-sanity-check/mme-entrypoint.sh' + else: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-oai-mme --hostname mme --network cicd-oai-public-net --ip ' + CICD_MME_PUBLIC_ADDR + ' --volume ' + mmeEntrypoint + ':/magma-mme/bin/entrypoint.sh --env-file ' + mmeEnvFile + ' --entrypoint "/magma-mme/bin/entrypoint.sh" --health-cmd "pgrep --count oai_mme" -d magma-mme:' + self.tag) + + count = 0 + runCount = 0 + status = False + while (count < 10) and (runCount < 5): + res = subprocess.check_output(self.cli + ' top cicd-oai-mme', shell=True, universal_newlines=True) + if re.search('/magma-mme/bin/oai_mme -c /magma-mme/etc/mme.conf', str(res)): + runCount += 1 + if runCount == 5: + status = True + count += 1 + time.sleep(2) + if status: + print ('Deployment MAGMA-MME --> OK') + else: + print ('Problem deploying MAGMA-MME') + sys.exit(-1) + + def deployOldMME(self): + res = '' + # first check if tag exists + try: + res = subprocess.check_output(self.cli + ' image inspect oai-mme:' + self.tag, shell=True, universal_newlines=True) + except: + sys.exit(-1) + + mmeEnvFile = '' + mmeEntrypoint = '' + cwd = os.getcwd() + if os.path.isfile(cwd + '/ci-scripts/oai-ran-sanity-check/mme-old.env'): + mmeEnvFile = './ci-scripts/oai-ran-sanity-check/mme-old.env' + elif os.path.isfile(cwd + '/oai-ran-sanity-check/mme-old.env'): + mmeEnvFile = './oai-ran-sanity-check/mme-old.env' + else: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-oai-mme --hostname mme --network cicd-oai-public-net --ip ' + CICD_MME_PUBLIC_ADDR + ' --env-file ' + mmeEnvFile + ' -d oai-mme:' + self.tag) + + time.sleep(1) + subprocess_run_w_echo(self.cli + ' exec -d cicd-oai-mme /bin/bash -c "nohup tcpdump -i any -w /openair-mme/mme.pcap > /dev/null 2>&1"') + + count = 0 + runCount = 0 + status = False + while (count < 10) and (runCount < 5): + res = subprocess.check_output(self.cli + ' top cicd-oai-mme', shell=True, universal_newlines=True) + if re.search('/openair-mme/bin/oai_mme -c /openair-mme/etc/mme.conf', str(res)): + runCount += 1 + if runCount == 5: + status = True + count += 1 + time.sleep(2) + if status: + print ('Deployment OAI-MME --> OK') + else: + print ('Problem deploying OAI-MME') + sys.exit(-1) + + def deploySPGWC(self): + res = '' + # first check if tag exists + try: + res = subprocess.check_output(self.cli + ' image inspect oai-spgwc:' + self.tag, shell=True, universal_newlines=True) + except: + sys.exit(-1) + + spgwcEnvFile = '' + cwd = os.getcwd() + if os.path.isfile(cwd + '/ci-scripts/oai-ran-sanity-check/spgwc.env'): + spgwcEnvFile = './ci-scripts/oai-ran-sanity-check/spgwc.env' + elif os.path.isfile(cwd + '/oai-ran-sanity-check/spgwc.env'): + spgwcEnvFile = './oai-ran-sanity-check/spgwc.env' + else: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-oai-spgwc --network cicd-oai-public-net --ip ' + CICD_SPGWC_PUBLIC_ADDR + ' --env-file ' + spgwcEnvFile + ' --health-cmd "pgrep --count oai_spgwc" -d oai-spgwc:' + self.tag) + time.sleep(2) + subprocess_run_w_echo(self.cli + ' exec -d cicd-oai-spgwc /bin/bash -c "nohup tcpdump -i any -w /openair-spgwc/spgwc.pcap > /dev/null 2>&1"') + + count = 0 + runCount = 0 + status = False + while (count < 10) and (runCount < 5): + res = subprocess.check_output(self.cli + ' top cicd-oai-spgwc', shell=True, universal_newlines=True) + if re.search('/openair-spgwc/bin/oai_spgwc -c /openair-spgwc/etc/spgw_c.json -o', str(res)): + runCount += 1 + if runCount == 5: + status = True + count += 1 + time.sleep(2) + if status: + print ('Deployment SPGWC --> OK') + else: + print ('Problem deploying SPGWC') + sys.exit(-1) + + def deploySPGWU(self): + res = '' + # first check if tag exists + try: + res = subprocess.check_output(self.cli + ' image inspect oai-spgwu-tiny:' + self.tag, shell=True, universal_newlines=True) + except: + sys.exit(-1) + + spgwuEnvFile = '' + cwd = os.getcwd() + if os.path.isfile(cwd + '/ci-scripts/oai-ran-sanity-check/spgwu.env'): + spgwuEnvFile = './ci-scripts/oai-ran-sanity-check/spgwu.env' + elif os.path.isfile(cwd + '/oai-ran-sanity-check/spgwu.env'): + spgwuEnvFile = './oai-ran-sanity-check/spgwu.env' + else: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-oai-spgwu-tiny --network cicd-oai-public-net --ip ' + CICD_SPGWU_PUBLIC_ADDR + ' --env-file ' + spgwuEnvFile + ' --health-cmd "pgrep --count oai_spgwu" -d oai-spgwu-tiny:' + self.tag) + + count = 0 + runCount = 0 + status = False + while (count < 10) and (runCount < 5): + res = subprocess.check_output(self.cli + ' top cicd-oai-spgwu-tiny', shell=True, universal_newlines=True) + if re.search('/openair-spgwu-tiny/bin/oai_spgwu -c /openair-spgwu-tiny/etc/spgw_u.conf -o', str(res)): + runCount += 1 + if runCount == 5: + status = True + count += 1 + time.sleep(2) + if status: + print ('Deployment SPGWU-TINY --> OK') + else: + print ('Problem deploying SPGWU-TINY') + sys.exit(-1) + + def deployTrfGen(self): + res = '' + # first check if tag exists + try: + res = subprocess.check_output(self.cli + ' image inspect trf-gen:' + self.tag, shell=True, universal_newlines=True) + except: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-trf-gen --network cicd-oai-public-net --ip ' + CICD_TRFGEN_PUBLIC_ADDR + ' -d trf-gen:' + self.tag) + time.sleep(2) + subprocess_run_w_echo(self.cli + ' exec cicd-trf-gen /bin/bash -c "ip route add 12.0.0.0/24 via ' + CICD_SPGWU_PUBLIC_ADDR + ' dev eth0"') + print ('Deployment TRF-GEN --> OK') + + def deployENB(self): + res = '' + # first check if tag exists + try: + res = subprocess.check_output(self.cli + ' image inspect oai-enb:' + self.tag, shell=True, universal_newlines=True) + except: + sys.exit(-1) + + enbEnvFile = '' + cwd = os.getcwd() + if os.path.isfile(cwd + '/ci-scripts/oai-ran-sanity-check/enb.env'): + enbEnvFile = './ci-scripts/oai-ran-sanity-check/enb.env' + elif os.path.isfile(cwd + '/oai-ran-sanity-check/enb.env'): + enbEnvFile = './oai-ran-sanity-check/enb.env' + else: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-oai-enb --network cicd-oai-public-net --ip ' + CICD_ENB_RF_SIM_PUBLIC_ADDR + ' --env-file ' + enbEnvFile + ' -d oai-enb:' + self.tag) + + count = 0 + runCount = 0 + status = False + while (count < 10) and (runCount < 5): + res = subprocess.check_output(self.cli + ' top cicd-oai-enb', shell=True, universal_newlines=True) + if re.search('/opt/oai-enb/bin/lte-softmodem.Rel15 -O /opt/oai-enb/etc/enb.conf', str(res)): + runCount += 1 + if runCount == 5: + status = True + count += 1 + time.sleep(2) + if status: + print ('Deployment RF-SIM-ENB --> OK') + else: + print ('Problem deploying RF-SIM-ENB') + sys.exit(-1) + + def deployUE(self): + res = '' + # first check if tag exists + try: + res = subprocess.check_output(self.cli + ' image inspect oai-lte-ue:' + self.tag, shell=True, universal_newlines=True) + except: + sys.exit(-1) + + ueEnvFile = '' + cwd = os.getcwd() + if os.path.isfile(cwd + '/ci-scripts/oai-ran-sanity-check/ue.env'): + ueEnvFile = './ci-scripts/oai-ran-sanity-check/ue.env' + elif os.path.isfile(cwd + '/oai-ran-sanity-check/ue.env'): + ueEnvFile = './oai-ran-sanity-check/ue.env' + else: + sys.exit(-1) + + subprocess_run_w_echo(self.cli + ' run --privileged --name cicd-oai-ue --network cicd-oai-public-net --ip ' + CICD_UE0_RF_SIM_PUBLIC_ADDR + ' --env-file ' + ueEnvFile + ' -d oai-lte-ue:' + self.tag) + + count = 0 + runCount = 0 + status = False + while (count < 10) and (runCount < 5): + res = subprocess.check_output(self.cli + ' top cicd-oai-ue', shell=True, universal_newlines=True) + if re.search('/opt/oai-lte-ue/bin/lte-uesoftmodem.Rel15', str(res)): + runCount += 1 + if runCount == 5: + status = True + count += 1 + time.sleep(2) + if status: + print ('Deployment RF-SIM-LTE-UE --> OK') + else: + print ('Problem deploying RF-SIM-LTE-UE') + sys.exit(-1) + + def removeAllContainers(self): + try: + subprocess_run_w_echo(self.cli + ' rm -f cicd-cassandra cicd-oai-hss cicd-redis cicd-oai-mme cicd-oai-spgwc cicd-oai-spgwu-tiny cicd-oai-enb cicd-oai-ue cicd-trf-gen') + except: + pass + + def retrieveContainerLogs(self): + subprocess_run_w_echo('mkdir -p archives') + magmaUsed = True + prefix = 'magma' + res = subprocess.check_output(self.cli + ' inspect --format="ImageName = {{.ImageName}}" cicd-oai-mme', shell=True, universal_newlines=True) + if re.search('oai-mme:', str(res)): + magmaUsed = False + prefix = 'openair' + + # First stop tcpdump capture on MME container + subprocess_run_w_echo(self.cli + ' exec cicd-oai-mme /bin/bash -c "killall tcpdump"') + subprocess_run_w_echo(self.cli + ' exec cicd-oai-spgwc /bin/bash -c "killall tcpdump"') + time.sleep(2) + try: + res = subprocess.check_output(self.cli + ' exec cicd-oai-mme /bin/bash -c "ls /' + prefix + '-mme/*.pcap"', shell=True, universal_newlines=True) + subprocess_run_w_echo(self.cli + ' cp cicd-oai-mme:' + res.strip() + ' archives/ || true') + except: + pass + if magmaUsed: + subprocess_run_w_echo(self.cli + ' cp cicd-oai-mme:/magma-mme/sctpd.log archives/ || true') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-mme:/magma-mme/mme-stdout.log archives/ || true') + subprocess_run_w_echo(self.cli + ' logs cicd-oai-mme > archives/mme.log 2>&1 || true') + subprocess_run_w_echo(self.cli + ' logs cicd-oai-hss > archives/hss.log 2>&1 || true') + subprocess_run_w_echo(self.cli + ' logs cicd-oai-spgwc > archives/spgwc.log 2>&1 || true') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-spgwc:/openair-spgwc/spgwc.pcap archives/ || true') + subprocess_run_w_echo(self.cli + ' logs cicd-oai-spgwu-tiny > archives/spgwu-tiny.log 2>&1 || true') + subprocess_run_w_echo(self.cli + ' logs cicd-oai-enb > archives/enb.log 2>&1 || true') + subprocess_run_w_echo(self.cli + ' logs cicd-oai-ue > archives/ue.log 2>&1 || true') + subprocess_run_w_echo('mkdir -p archives/conf/hss') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-hss:/openair-hss/etc/hss_rel14.json archives/conf/hss || true') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-hss:/openair-hss/etc/hss_rel14.conf archives/conf/hss || true') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-hss:/openair-hss/etc/hss_rel14_fd.conf archives/conf/hss || true') + subprocess_run_w_echo('mkdir -p archives/conf/mme') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-mme:/' + prefix + '-mme/etc/mme.conf archives/conf/mme || true') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-mme:/' + prefix + '-mme/etc/mme_fd.conf archives/conf/mme || true') + subprocess_run_w_echo('mkdir -p archives/conf/spgwc') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-spgwc:/openair-spgwc/etc/spgw_c.json archives/conf/spgwc || true') + subprocess_run_w_echo('mkdir -p archives/conf/spgwu-tiny') + subprocess_run_w_echo(self.cli + ' cp cicd-oai-spgwu-tiny:/openair-spgwu-tiny/etc/spgw_u.conf archives/conf/spgwu-tiny || true') + + def testTrafficWithPing(self): + subprocess_run_w_echo('mkdir -p archives') + # first retrieve IP address allocated to UE by EPC + res = subprocess.check_output(self.cli + ' exec cicd-oai-ue /bin/bash -c "ifconfig oaitun_ue1 | grep inet"', shell=True, universal_newlines=True) + res2 = re.search('inet (?P[0-9\.]+)', str(res.strip())) + if res2 is not None: + print('Found IP address: ' + res2.group('ip_addr')) + UE_EPC_IP_ADDRESS = res2.group('ip_addr') + else: + print('Could not foound out UE allocated IP address') + sys.exit(-1) + + # Then test ping from trf-gen container + subprocess_run_w_echo(self.cli + ' exec cicd-trf-gen /bin/bash -c "ping -c 10 ' + UE_EPC_IP_ADDRESS + '" > archives/ping_ue_from_trf_gen.log') + res = subprocess.check_output('cat archives/ping_ue_from_trf_gen.log', shell=True, universal_newlines=True) + res2 = re.search('10 packets transmitted, 10 received, 0% packet loss', str(res.strip())) + if res is not None: + print('TRF-GEN --> UE: OK') + else: + print('TRF-GEN --> UE: Missing packets') + sys.exit(-1) + + # Finally test ping from UE container + subprocess_run_w_echo(self.cli + ' exec cicd-oai-ue /bin/bash -c "ping -c 10 -I ' + UE_EPC_IP_ADDRESS + ' ' + CICD_TRFGEN_PUBLIC_ADDR + '" > archives/ping_trf_gen_from_ue.log') + res = subprocess.check_output('cat archives/ping_trf_gen_from_ue.log', shell=True, universal_newlines=True) + res2 = re.search('10 packets transmitted, 10 received, 0% packet loss', str(res.strip())) + if res is not None: + print('UE --> TRF-GEN: OK') + else: + print('UE --> TRF-GEN: Missing packets') + sys.exit(-1) + + +def subprocess_run_w_echo(cmd): + print(cmd) + subprocess.run(cmd, shell=True) + +def Usage(): + print('----------------------------------------------------------------------------------------------------------------------') + print('OAI-RAN-Sanity-Check-Deploy.py') + print(' Deploy for DsTester in the pipeline.') + print('----------------------------------------------------------------------------------------------------------------------') + print('Usage: python3 OAI-RAN-Sanity-Check-Deploy.py [options]') + print(' --help Show this help.') + print('---------------------------------------------------------------------------------------------- Mandatory Options -----') + print(' --action={CreateNetworks,RemoveNetworks,...}') + print('-------------------------------------------------------------------------------------------------------- Options -----') + print(' --tag=[Image Tag in registry]') + print('------------------------------------------------------------------------------------------------- Actions Syntax -----') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=CreateNetworks') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=RemoveNetworks') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeployCassandra') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeployHSS --tag=[tag]') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeployRedis') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeployMME --tag=[tag]') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeploySPGWC --tag=[tag]') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeploySPGWU --tag=[tag]') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeployTrfGen --tag=[tag]') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeployRfSimENB --tag=[tag]') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=DeployRfSimUE --tag=[tag]') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=TestTrafficPing') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=RetrieveLogs') + print('python3 OAI-RAN-Sanity-Check-Deploy.py --action=RemoveAllContainers') + +#-------------------------------------------------------------------------------------------------------- +# +# Start of main +# +#-------------------------------------------------------------------------------------------------------- + +SNRAN = deployWithOAIran() + +argvs = sys.argv +argc = len(argvs) + +while len(argvs) > 1: + myArgv = argvs.pop(1) + if re.match('^\-\-help$', myArgv, re.IGNORECASE): + Usage() + sys.exit(0) + elif re.match('^\-\-action=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-action=(.+)$', myArgv, re.IGNORECASE) + action = matchReg.group(1) + if action != 'CreateNetworks' and \ + action != 'RemoveNetworks' and \ + action != 'DeployCassandra' and \ + action != 'DeployHSS' and \ + action != 'DeployRedis' and \ + action != 'DeployMME' and \ + action != 'DeployOldMME' and \ + action != 'DeploySPGWC' and \ + action != 'DeploySPGWU' and \ + action != 'DeployTrfGen' and \ + action != 'DeployRfSimENB' and \ + action != 'DeployRfSimUE' and \ + action != 'RetrieveLogs' and \ + action != 'TestTrafficPing' and \ + action != 'RemoveAllContainers': + print('Unsupported Action => ' + action) + Usage() + sys.exit(-1) + SNRAN.action = action + elif re.match('^\-\-tag=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-tag=(.+)$', myArgv, re.IGNORECASE) + SNRAN.tag = matchReg.group(1) + +if SNRAN.action == 'CreateNetworks': + SNRAN.createNetworks() +elif SNRAN.action == 'RemoveNetworks': + SNRAN.removeNetworks() +elif SNRAN.action == 'DeployCassandra': + SNRAN.deployCassandra() +elif SNRAN.action == 'DeployHSS': + if SNRAN.tag == '': + print('Missing OAI-HSS image tag') + Usage() + sys.exit(-1) + SNRAN.deployHSS() +elif SNRAN.action == 'DeployRedis': + SNRAN.deployRedis() +elif SNRAN.action == 'DeployMME': + if SNRAN.tag == '': + print('Missing MAGMA-MME image tag') + Usage() + sys.exit(-1) + SNRAN.deployMME() +elif SNRAN.action == 'DeployOldMME': + if SNRAN.tag == '': + print('Missing OAI-MME image tag') + Usage() + sys.exit(-1) + SNRAN.deployOldMME() +elif SNRAN.action == 'DeploySPGWC': + if SNRAN.tag == '': + print('Missing OAI-SPGWC image tag') + Usage() + sys.exit(-1) + SNRAN.deploySPGWC() +elif SNRAN.action == 'DeploySPGWU': + if SNRAN.tag == '': + print('Missing OAI-SPGWU image tag') + Usage() + sys.exit(-1) + SNRAN.deploySPGWU() +elif SNRAN.action == 'DeployTrfGen': + if SNRAN.tag == '': + print('Missing TRF-GEN image tag') + Usage() + sys.exit(-1) + SNRAN.deployTrfGen() +elif SNRAN.action == 'DeployRfSimENB': + if SNRAN.tag == '': + print('Missing OAI-ENB image tag') + Usage() + sys.exit(-1) + SNRAN.deployENB() +elif SNRAN.action == 'DeployRfSimUE': + if SNRAN.tag == '': + print('Missing OAI-LTE-UE image tag') + Usage() + sys.exit(-1) + SNRAN.deployUE() +elif SNRAN.action == 'TestTrafficPing': + SNRAN.testTrafficWithPing() +elif SNRAN.action == 'RetrieveLogs': + SNRAN.retrieveContainerLogs() +elif SNRAN.action == 'RemoveAllContainers': + SNRAN.removeAllContainers() + +sys.exit(0) diff --git a/ci-scripts/oai-ran-sanity-check/enb.env b/ci-scripts/oai-ran-sanity-check/enb.env new file mode 100644 index 0000000..56c3c18 --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/enb.env @@ -0,0 +1,23 @@ +TZ=Europe/Paris +USE_FDD_MONO=yes +RFSIMULATOR=enb +ENB_NAME=eNB-rf-sim +MCC=208 +MNC=96 +MNC_LENGTH=2 +TAC=1 +UTRA_BAND_ID=7 +DL_FREQUENCY_IN_MHZ=2680 +UL_FREQUENCY_OFFSET_IN_MHZ=120 +NID_CELL=10 +NB_PRB=25 +MME_S1C_IP_ADDRESS=192.168.61.196 +ENB_S1C_IF_NAME=eth0 +ENB_S1C_IP_ADDRESS=192.168.61.210 +ENB_S1U_IF_NAME=eth0 +ENB_S1U_IP_ADDRESS=192.168.61.210 +ENB_X2_IP_ADDRESS=192.168.61.210 +FLEXRAN_ENABLED=no +FLEXRAN_INTERFACE_NAME=eth0 +FLEXRAN_IPV4_ADDRESS=192.168.61.196 +USE_ADDITIONAL_OPTIONS=--rfsim diff --git a/ci-scripts/oai-ran-sanity-check/hss.env b/ci-scripts/oai-ran-sanity-check/hss.env new file mode 100644 index 0000000..342e199 --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/hss.env @@ -0,0 +1,12 @@ +TZ=Europe/Paris +REALM=openairinterface.org +HSS_FQDN=hss.openairinterface.org +PREFIX=/openair-hss/etc +cassandra_Server_IP=192.168.61.194 +OP_KEY=1006020f0a478bf6b699f15c062e42b3 +LTE_K=fec86ba6eb707ed08905757b1bb44b8f +APN1=oai.ipv4 +APN2=internet +FIRST_IMSI=208960100000001 +NB_USERS=10 + diff --git a/ci-scripts/oai-ran-sanity-check/mme-entrypoint.sh b/ci-scripts/oai-ran-sanity-check/mme-entrypoint.sh new file mode 100755 index 0000000..c4d569d --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/mme-entrypoint.sh @@ -0,0 +1,235 @@ +#!/bin/bash + +function generateConfFile() +{ + set +x + rm -f mme.conf + echo 'MME :' >> mme.conf + echo '{' >> mme.conf + echo ' REALM = "'$REALM'"' >> mme.conf + echo ' PID_DIRECTORY = "/var/run";' >> mme.conf + echo ' MAXENB = 8; # power of 2' >> mme.conf + echo ' MAXUE = 16; # power of 2' >> mme.conf + echo ' RELATIVE_CAPACITY = 10;' >> mme.conf + echo '' >> mme.conf + echo ' EMERGENCY_ATTACH_SUPPORTED = "no";' >> mme.conf + echo ' UNAUTHENTICATED_IMSI_SUPPORTED = "no";' >> mme.conf + echo '' >> mme.conf + echo ' # EPS network feature support' >> mme.conf + echo ' EPS_NETWORK_FEATURE_SUPPORT_IMS_VOICE_OVER_PS_SESSION_IN_S1 = "no"; # DO NOT CHANGE' >> mme.conf + echo ' EPS_NETWORK_FEATURE_SUPPORT_EMERGENCY_BEARER_SERVICES_IN_S1_MODE = "no"; # DO NOT CHANGE' >> mme.conf + echo ' EPS_NETWORK_FEATURE_SUPPORT_LOCATION_SERVICES_VIA_EPC = "no"; # DO NOT CHANGE' >> mme.conf + echo ' EPS_NETWORK_FEATURE_SUPPORT_EXTENDED_SERVICE_REQUEST = "no"; # DO NOT CHANGE' >> mme.conf + echo '' >> mme.conf + echo ' # Display statistics about whole system (expressed in seconds)' >> mme.conf + echo ' MME_STATISTIC_TIMER = 10;' >> mme.conf + echo ' IP_CAPABILITY = "IPV4"; # UE PDN_TYPE' >> mme.conf + echo ' USE_STATELESS = "";' >> mme.conf + echo '' >> mme.conf + echo ' INTERTASK_INTERFACE :' >> mme.conf + echo ' {' >> mme.conf + echo ' # max queue size per task' >> mme.conf + echo ' ITTI_QUEUE_SIZE = 2000000;' >> mme.conf + echo ' };' >> mme.conf + echo '' >> mme.conf + echo ' S6A :' >> mme.conf + echo ' {' >> mme.conf + echo ' S6A_CONF = "'$PREFIX'/mme_fd.conf"; # YOUR MME freeDiameter config file path' >> mme.conf + echo ' HSS_HOSTNAME = "'$HSS_FQDN'";' >> mme.conf + echo ' HSS_REALM = "'$HSS_REALM'";' >> mme.conf + echo ' };' >> mme.conf + echo '' >> mme.conf + echo ' # ------- SCTP definitions' >> mme.conf + echo ' SCTP :' >> mme.conf + echo ' {' >> mme.conf + echo ' # Number of streams to use in input/output' >> mme.conf + echo ' SCTP_INSTREAMS = 8;' >> mme.conf + echo ' SCTP_OUTSTREAMS = 8;' >> mme.conf + echo ' };' >> mme.conf + echo '' >> mme.conf + echo ' # ------- S1AP definitions' >> mme.conf + echo ' S1AP :' >> mme.conf + echo ' {' >> mme.conf + echo ' # outcome drop timer value (seconds)' >> mme.conf + echo ' S1AP_OUTCOME_TIMER = 10;' >> mme.conf + echo ' };' >> mme.conf + echo ' # ------- MME served GUMMEIs' >> mme.conf + echo ' GUMMEI_LIST = (' >> mme.conf + echo ' { MCC="'$MCC'" ; MNC="'$MNC'"; MME_GID="455" ; MME_CODE="5"; }' >> mme.conf + echo ' );' >> mme.conf + echo '' >> mme.conf + echo ' # ------- MME served TAIs' >> mme.conf + echo ' TAI_LIST = (' >> mme.conf + echo ' {MCC="'$MCC'" ; MNC="'$MNC'"; TAC = "'$TAC0'"; },' >> mme.conf + echo ' {MCC="'$MCC'" ; MNC="'$MNC'"; TAC = "2"; },' >> mme.conf + echo ' {MCC="'$MCC'" ; MNC="'$MNC'"; TAC = "3"; }' >> mme.conf + echo ' );' >> mme.conf + echo '' >> mme.conf + echo ' TAC_LIST = (' >> mme.conf + echo ' {MCC="'$MCC'" ; MNC="'$MNC'"; TAC = "'$TAC0'"; }' >> mme.conf + echo ' );' >> mme.conf + echo '' >> mme.conf + echo ' CSFB :' >> mme.conf + echo ' {' >> mme.conf + echo ' NON_EPS_SERVICE_CONTROL = "OFF";' >> mme.conf + echo ' CSFB_MCC = "'$MCC'";' >> mme.conf + echo ' CSFB_MNC = "'$MNC'";' >> mme.conf + echo ' LAC = "1";' >> mme.conf + echo ' };' >> mme.conf + echo ' NAS :' >> mme.conf + echo ' {' >> mme.conf + echo ' ORDERED_SUPPORTED_INTEGRITY_ALGORITHM_LIST = [ "EIA2" , "EIA1" , "EIA0" ];' >> mme.conf + echo ' ORDERED_SUPPORTED_CIPHERING_ALGORITHM_LIST = [ "EEA0" , "EEA1" , "EEA2" ];' >> mme.conf + echo ' T3402 = 1 # in minutes (default is 12 minutes)' >> mme.conf + echo ' T3412 = 54 # in minutes (default is 54 minutes, network dependent)' >> mme.conf + echo ' T3422 = 6 # in seconds (default is 6s)' >> mme.conf + echo ' T3450 = 6 # in seconds (default is 6s)' >> mme.conf + echo ' T3460 = 6 # in seconds (default is 6s)' >> mme.conf + echo ' T3470 = 6 # in seconds (default is 6s)' >> mme.conf + echo ' T3485 = 8 # UNUSED in seconds (default is 8s)' >> mme.conf + echo ' T3486 = 8 # UNUSED in seconds (default is 8s)' >> mme.conf + echo ' T3489 = 4 # UNUSED in seconds (default is 4s)' >> mme.conf + echo ' T3495 = 8 # UNUSED in seconds (default is 8s)' >> mme.conf + echo ' };' >> mme.conf + echo '' >> mme.conf + echo ' SGS :' >> mme.conf + echo ' {' >> mme.conf + echo ' TS6_1 = 10 # in seconds (default is 10s)' >> mme.conf + echo ' TS8 = 4 # in seconds (default is 4s)' >> mme.conf + echo ' TS9 = 2 # in seconds (default is 4s)' >> mme.conf + echo ' TS10 = 4 # in seconds (default is 4s)' >> mme.conf + echo ' TS13 = 4 # in seconds (default is 4s)' >> mme.conf + echo ' };' >> mme.conf + echo '' >> mme.conf + echo ' NETWORK_INTERFACES :' >> mme.conf + echo ' {' >> mme.conf + echo ' MME_INTERFACE_NAME_FOR_S1_MME = "eth0";' >> mme.conf + echo ' MME_IPV4_ADDRESS_FOR_S1_MME = "'$MME_S1_IP_ADDR'/24";' >> mme.conf + echo ' MME_INTERFACE_NAME_FOR_S11_MME = "eth0";' >> mme.conf + echo ' MME_IPV4_ADDRESS_FOR_S11_MME = "'$MME_S1_IP_ADDR'/24";' >> mme.conf + echo ' MME_PORT_FOR_S11_MME = 2123;' >> mme.conf + echo ' };' >> mme.conf + echo '' >> mme.conf + echo ' LOGGING :' >> mme.conf + echo ' {' >> mme.conf + echo ' OUTPUT = "CONSOLE";' >> mme.conf + echo ' THREAD_SAFE = "no";' >> mme.conf + echo ' COLOR = "no";' >> mme.conf + echo '' >> mme.conf + echo ' SCTP_LOG_LEVEL = "ERROR";' >> mme.conf + echo ' GTPV1U_LOG_LEVEL = "INFO";' >> mme.conf + echo ' SPGW_APP_LOG_LEVEL = "INFO";' >> mme.conf + echo ' UDP_LOG_LEVEL = "INFO";' >> mme.conf + echo ' S1AP_LOG_LEVEL = "DEBUG";' >> mme.conf + echo ' NAS_LOG_LEVEL = "DEBUG";' >> mme.conf + echo ' MME_APP_LOG_LEVEL = "DEBUG";' >> mme.conf + echo ' GTPV2C_LOG_LEVEL = "DEBUG";' >> mme.conf + echo ' S11_LOG_LEVEL = "DEBUG";' >> mme.conf + echo ' S6A_LOG_LEVEL = "DEBUG";' >> mme.conf + echo ' UTIL_LOG_LEVEL = "INFO";' >> mme.conf + echo ' MSC_LOG_LEVEL = "ERROR";' >> mme.conf + echo ' ITTI_LOG_LEVEL = "ERROR";' >> mme.conf + echo ' MME_SCENARIO_PLAYER_LOG_LEVEL = "ERROR";' >> mme.conf + echo ' ASN1_VERBOSITY = "INFO";' >> mme.conf + echo ' };' >> mme.conf + echo '' >> mme.conf + echo ' S-GW :' >> mme.conf + echo ' {' >> mme.conf + echo ' SGW_IPV4_ADDRESS_FOR_S11 = "'$SPGWC0_IP_ADDR'";' >> mme.conf + echo ' };' >> mme.conf + echo '};' >> mme.conf + set -x +} + +function generateFdConfFile() +{ + set +x + rm -f mme_fd.conf + echo 'Identity = "'$MME_FQDN'";' >> mme_fd.conf + echo 'Realm = "'$REALM'";' >> mme_fd.conf + echo 'TLS_Cred = "'$PREFIX'/mme.cert.pem",' >> mme_fd.conf + echo ' "'$PREFIX'/mme.key.pem";' >> mme_fd.conf + echo 'TLS_CA = "'$PREFIX'/mme.cacert.pem";' >> mme_fd.conf + echo 'No_SCTP;' >> mme_fd.conf + echo 'Prefer_TCP;' >> mme_fd.conf + echo 'No_IPv6;' >> mme_fd.conf + echo 'SCTP_streams = 3;' >> mme_fd.conf + echo 'NoRelay;' >> mme_fd.conf + echo 'AppServThreads = 4;' >> mme_fd.conf + echo 'ListenOn = "'$MME_S1_IP_ADDR'";' >> mme_fd.conf + echo 'Port = 3870;' >> mme_fd.conf + echo 'SecPort = 5870;' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_3gpp2_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_draftload_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_etsi283034_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc4004_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc4006bis_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc4072_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc4590_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc5447_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc5580_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc5777_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc5778_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc6734_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc6942_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc7155_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc7683_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_rfc7944_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29061_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29128_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29154_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29173_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29212_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29214_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29215_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29217_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29229_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29272_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29273_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29329_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29336_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29337_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29338_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29343_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29344_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29345_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29368_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts29468_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_ts32299_avps.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_S6as6d.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_S6t.fdx";' >> mme_fd.conf + echo 'LoadExtension = "/usr/local/lib/freeDiameter/dict_S6c.fdx";' >> mme_fd.conf + echo '# -------- Peers ---------' >> mme_fd.conf + echo 'ConnectPeer= "'$HSS_FQDN'" { ConnectTo = "'$HSS_IP_ADDR'"; No_SCTP ; No_IPv6; Prefer_TCP; No_TLS; port = 3868;};' >> mme_fd.conf + set -x +} + +set -x +cd /magma-mme/scripts +mkdir -p $PREFIX + +pushd $PREFIX + +generateConfFile +generateFdConfFile + +# Configure REDIS container +sed -i -e "s@bind: 127.0.0.1@bind: $REDIS_IP_ADDR@" /etc/magma/redis.yml + +# Generate freeDiameter certificate +popd +./check_mme_s6a_certificate $PREFIX mme.$REALM +set +x + +echo "Starting tcpdump capture" +nohup tcpdump -i any -w /magma-mme/mme-`date -u +"%Y-%m-%dT%H-%M-%S"`.pcap > /dev/null 2>&1 & +sleep 5 + +echo "Starting sctpd" +nohup /magma-mme/bin/sctpd > /magma-mme/sctpd.log 2>&1 & +sleep 2 +echo "Starting mme executable" +nohup /magma-mme/bin/oai_mme -c $PREFIX/mme.conf > /magma-mme/mme-stdout.log 2>&1 & +sleep 2 +tail -f /var/log/mme.log + diff --git a/ci-scripts/oai-ran-sanity-check/mme-old.env b/ci-scripts/oai-ran-sanity-check/mme-old.env new file mode 100644 index 0000000..cb079a5 --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/mme-old.env @@ -0,0 +1,45 @@ +TZ=Europe/Paris +REALM=openairinterface.org +PREFIX=/openair-mme/etc +HSS_HOSTNAME=hss +HSS_FQDN=hss.openairinterface.org +HSS_REALM=openairinterface.org +MME_FQDN=mme.openairinterface.org +MCC=208 +MNC=96 +HSS_IP_ADDR=192.168.61.195 +MME_S1_IP_ADDR=192.168.61.196 +SPGWC0_IP_ADDR=192.168.61.198 +INSTANCE=1 +PID_DIRECTORY=/var/run +MME_GID=32768 +MME_CODE=3 +TAC_0=1 +TAC_1=2 +TAC_2=3 +MME_S6A_IP_ADDR=192.168.61.196 +MME_INTERFACE_NAME_FOR_S1_MME=eth0 +MME_IPV4_ADDRESS_FOR_S1_MME=192.168.61.196 +MME_INTERFACE_NAME_FOR_S11=eth0 +MME_IPV4_ADDRESS_FOR_S11=192.168.61.196 +MME_INTERFACE_NAME_FOR_S10=lo +MME_IPV4_ADDRESS_FOR_S10=127.0.0.10 +OUTPUT=CONSOLE +SGW_IPV4_ADDRESS_FOR_S11_0=192.168.61.198 +PEER_MME_IPV4_ADDRESS_FOR_S10_0=0.0.0.0 +PEER_MME_IPV4_ADDRESS_FOR_S10_1=0.0.0.0 +MCC_SGW_0=208 +MNC3_SGW_0=096 +TAC_LB_SGW_0=01 +TAC_HB_SGW_0=00 +MCC_MME_0=208 +MNC3_MME_0=096 +TAC_LB_MME_0=02 +TAC_HB_MME_0=00 +MCC_MME_1=208 +MNC3_MME_1=096 +TAC_LB_MME_1=03 +TAC_HB_MME_1=00 +TAC_LB_SGW_TEST_0=03 +TAC_HB_SGW_TEST_0=00 +SGW_IPV4_ADDRESS_FOR_S11_TEST_0=0.0.0.0 diff --git a/ci-scripts/oai-ran-sanity-check/mme.env b/ci-scripts/oai-ran-sanity-check/mme.env new file mode 100644 index 0000000..17ceb66 --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/mme.env @@ -0,0 +1,15 @@ +TZ=Europe/Paris +REALM=openairinterface.org +PREFIX=/magma-mme/etc +HSS_HOSTNAME=hss +HSS_FQDN=hss.openairinterface.org +HSS_REALM=openairinterface.org +MME_FQDN=mme.openairinterface.org +FEATURES=mme_oai +MCC=208 +MNC=96 +TAC0=1 +HSS_IP_ADDR=192.168.61.195 +REDIS_IP_ADDR=192.168.61.197 +MME_S1_IP_ADDR=192.168.61.196 +SPGWC0_IP_ADDR=192.168.61.198 diff --git a/ci-scripts/oai-ran-sanity-check/redis_extern.conf b/ci-scripts/oai-ran-sanity-check/redis_extern.conf new file mode 100644 index 0000000..2c001bb --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/redis_extern.conf @@ -0,0 +1,33 @@ +# +# Copyright (c) 2016-present, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. + +# Jinja template for Redis configuration +# See the default config file for options and explanations: +# https://github.com/antirez/redis/blob/unstable/redis.conf + +# TODO: make sensible, production-aware config decisions + +bind 192.168.61.197 +port 6380 + +daemonize no +loglevel notice + +timeout 0 +databases 1 + +dbfilename redis_dump.rdb +dir /data + +# Save the DB on disk + +save 900 1 + +save 300 10 + +save 60 1000 diff --git a/ci-scripts/oai-ran-sanity-check/spgwc.env b/ci-scripts/oai-ran-sanity-check/spgwc.env new file mode 100644 index 0000000..8d3f02a --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/spgwc.env @@ -0,0 +1,17 @@ +TZ=Europe/Paris +SGW_INTERFACE_NAME_FOR_S11=eth0 +PGW_INTERFACE_NAME_FOR_SX=eth0 +DEFAULT_DNS_IPV4_ADDRESS=192.168.18.129 +DEFAULT_DNS_SEC_IPV4_ADDRESS=8.8.4.4 +PUSH_PROTOCOL_OPTION=true +APN_NI_1=oai.ipv4 +APN_NI_2=oai.ipv4_2 +DEFAULT_APN_NI_1=oai.ipv4 +UE_IP_ADDRESS_POOL_1=12.0.0.2 - 12.0.0.254 +UE_IP_ADDRESS_POOL_2=12.1.1.2 - 12.1.1.254 +MCC=208 +MNC=96 +MNC03=096 +TAC=1 +GW_ID=1 +REALM=openairinterface.org diff --git a/ci-scripts/oai-ran-sanity-check/spgwu.env b/ci-scripts/oai-ran-sanity-check/spgwu.env new file mode 100644 index 0000000..7922683 --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/spgwu.env @@ -0,0 +1,15 @@ +TZ=Europe/Paris +PID_DIRECTORY=/var/run +INSTANCE=1 +SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP=eth0 +PGW_INTERFACE_NAME_FOR_SGI=eth0 +SGW_INTERFACE_NAME_FOR_SX=eth0 +SPGWC0_IP_ADDRESS=192.168.61.198 +NETWORK_UE_IP=12.0.0.0/24 +NETWORK_UE_NAT_OPTION=yes +MCC=208 +MNC=96 +MNC03=096 +TAC=1 +GW_ID=1 +REALM=openairinterface.org diff --git a/ci-scripts/oai-ran-sanity-check/ue.env b/ci-scripts/oai-ran-sanity-check/ue.env new file mode 100644 index 0000000..48f326f --- /dev/null +++ b/ci-scripts/oai-ran-sanity-check/ue.env @@ -0,0 +1,10 @@ +TZ=Europe/Paris +RFSIMULATOR=192.168.61.210 +MCC=208 +MNC=96 +SHORT_IMSI=0100000001 +LTE_KEY=fec86ba6eb707ed08905757b1bb44b8f +OPC=c42449363bbad02b66d16bc975d77cc1 +MSISDN=001011234561010 +HPLMN=20896 +USE_ADDITIONAL_OPTIONS=--rfsim -C 2680000000 -r 25 --ue-rxgain 140 --ue-txgain 120 --nokrnmod 1 diff --git a/ci-scripts/pcap_check.py b/ci-scripts/pcap_check.py new file mode 100644 index 0000000..e398b9d --- /dev/null +++ b/ci-scripts/pcap_check.py @@ -0,0 +1,334 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- + +import os +import re +import socket +import struct +import sys +import subprocess +import pyshark + +# IP addresses +HSS_IP_ADDRESS = '192.168.61.195' +MME_IP_ADDRESS = '192.168.61.196' +SPGWC_IP_ADDRESS = '192.168.61.198' +SPGWU_IP_ADDRESS = '192.168.61.199' +ENB_IP_ADDRESS = '192.168.61.210' + +# Diameter constants +FLAGS__REQUEST = '0x00000080' +FLAGS__ANSWER = '0x00000000' + +CMD_CODE__ABORT_SESSION = '274' +CMD_CODE__ACCOUNTING = '271' +CMD_CODE__CAPABILITIES_EXCHANGE = '257' +CMD_CODE__DEVICE_WATCHDOG = '280' +CMD_CODE__DISCONNECT_PEER = '282' +CMD_CODE__RE_AUTH = '258' +CMD_CODE__SESSION_TERMINATE = '275' + +RES_CODE__DIAMETER_SUCCESS = '2001' +RES_CODE__DIAMETER_LIMITED_SUCCESS = '2002' +RES_CODE__COMMAND_UNSUPPORTED = '3001' +RES_CODE__UNABLE_TO_DELIVER = '3002' +RES_CODE__REALM_NOT_SERVED = '3003' +RES_CODE__TOO_BUSY = '3003' + +# SCTP Constants +CHUNK_TYPE__DATA = '0' +CHUNK_TYPE__INIT = '1' +CHUNK_TYPE__INIT_ACK = '2' +CHUNK_TYPE__SACK = '3' +CHUNK_TYPE__ABORT = '6' +CHUNK_TYPE__SHUTDOWN = '7' +CHUNK_TYPE__SHUTDOWN_ACK = '8' +CHUNK_TYPE__ERROR = '9' +CHUNK_TYPE__AUTH = '15' + +# S1AP Constants +S1AP_PDU_INITIATINGMESSAGE = '0' +S1AP_PDU_SUCCESSFULOUTCOME = '1' + +PROC_CODE__ID_S1SETUP = '17' +PROC_CODE__ID_INITIALUEMESSAGE = '12' +PROC_CODE__ID_DOWNLINKNASTRANSPORT = '11' +PROC_CODE__ID_UPLINKNASTRANSPORT = '13' +PROC_CODE__ID_INITIALCONTEXTSETUP = '9' + +NAS__AUTHENTICATION_REQ = '82' +NAS__AUTHENTICATION_RES = '83' +NAS__SECURITY_MODE_CMD = '93' +NAS__SECURITY_MODE_COMPLETE = '94' +NAS__ATTACH_ACCEPT = '66' + +# GTPV2 Constants +GTPV2_MSG_TYPE__CREATE_SESSION_REQUEST = '32' +GTPV2_MSG_TYPE__CREATE_SESSION_RESPONSE = '33' +GTPV2_MSG_TYPE__MODIFY_BEARER_REQUEST = '34' +GTPV2_MSG_TYPE__MODIFY_BEARER_RESPONSE = '35' +GTPV2_MSG_TYPE__DELETE_SESSION_REQUEST = '36' +GTPV2_MSG_TYPE__DELETE_SESSION_RESPONSE = '37' + +# PGCP Constants +PFCP_MSG_TYPE__SX_HEARTBEAT_REQUEST = '1' +PFCP_MSG_TYPE__SX_HEARTBEAT_RESPONSE = '2' +PFCP_MSG_TYPE__SX_ASSOCIATION_SETUP_REQUEST = '5' +PFCP_MSG_TYPE__SX_ASSOCIATION_SETUP_RESPONSE = '6' +PFCP_NODE_ID_TYPE__FQDN = '2' + +def check_if_mme_connects_to_hss(pcap_file): + """ Normally the 2 first DIAMETER packets are the MME <-> HSS peerage """ + res = {} + res['mme_request'] = False + res['hss_answer'] = False + res['origin_host'] = '' + res['origin_realm'] = '' + try: + cap = {} + cap = pyshark.FileCapture(pcap_file, keep_packets=True, display_filter="tcp.port == 3868") + cnt = 0 + for pkt in cap: + if pkt is not None: + if 'DIAMETER' in pkt: + if cnt == 0: + if pkt.diameter.flags == FLAGS__REQUEST and pkt.diameter.cmd_code == CMD_CODE__CAPABILITIES_EXCHANGE: + res['mme_request'] = True + if cnt == 1: + if pkt.diameter.flags == FLAGS__ANSWER and pkt.diameter.cmd_code == CMD_CODE__CAPABILITIES_EXCHANGE: + if pkt.diameter.result_code == RES_CODE__DIAMETER_SUCCESS: + res['hss_answer'] = True + res['origin_host']=pkt.diameter.origin_host + res['origin_realm']=pkt.diameter.origin_realm + cnt += 1 + cap.close() + except Exception as e: + print('Could not open MME PCAP file') + return res + +def check_if_enb_connects_to_mme(pcap_file): + """ + Sequence is: + - INIT + - INIT_ACK + - S1SetupRequest + - S1SetupResponse + """ + res = {} + res['enb_init_req'] = False + res['mme_init_answer'] = False + res['enb_s1_setup_req'] = False + res['mme_s1_setup_res'] = False + res['enb_mcc'] = '' + res['enb_mnc'] = '' + res['enb_name'] = '' + try: + cap = {} + cap = pyshark.FileCapture(pcap_file, keep_packets=True, display_filter="sctp.port == 36412") + cnt = 0 + for pkt in cap: + if pkt is not None: + if 'SCTP' in pkt: + if pkt.sctp.chunk_type == CHUNK_TYPE__INIT: + if pkt.ip.src == ENB_IP_ADDRESS and pkt.ip.dst == MME_IP_ADDRESS: + res['enb_init_req'] = True + if pkt.sctp.chunk_type == CHUNK_TYPE__INIT_ACK: + if pkt.ip.src == MME_IP_ADDRESS and pkt.ip.dst == ENB_IP_ADDRESS: + res['mme_init_answer'] = True + if pkt.sctp.chunk_type == CHUNK_TYPE__DATA: + if pkt.ip.src == ENB_IP_ADDRESS and pkt.ip.dst == MME_IP_ADDRESS and 'S1AP' in pkt: + if pkt.s1ap.procedurecode == PROC_CODE__ID_S1SETUP and pkt.s1ap.s1ap_pdu == S1AP_PDU_INITIATINGMESSAGE: + res['enb_s1_setup_req'] = True + res['enb_mcc'] = pkt.s1ap.e212_mcc + res['enb_mnc'] = pkt.s1ap.e212_mnc + res['enb_name'] = pkt.s1ap.enbname + if pkt.ip.src == MME_IP_ADDRESS and pkt.ip.dst == ENB_IP_ADDRESS and 'S1AP' in pkt: + if pkt.s1ap.procedurecode == PROC_CODE__ID_S1SETUP and pkt.s1ap.s1ap_pdu == S1AP_PDU_SUCCESSFULOUTCOME: + res['mme_s1_setup_res'] = True + elif pkt.s1ap.procedurecode == PROC_CODE__ID_S1SETUP: + res['mme_s1_setup_res'] = False + cnt += 1 + cap.close() + except Exception as e: + print('Could not open MME PCAP file') + return res + +def check_if_ue_attachs(pcap_file): + res = {} + res['ue_init_msg'] = False + res['nas_auth_req'] = False + res['nas_auth_res'] = False + res['nas_security_cmd'] = False + res['nas_security_cmplt'] = False + res['initial_ue_context_req'] = False + res['initial_ue_context_res'] = False + res['s11_create_session_req'] = False + res['s11_create_session_res'] = False + res['s11_modify_bearer_req'] = False + res['s11_modify_bearer_res'] = False + res['ue_imsi'] = '' + res['apn'] = '' + res['transportlayeraddress'] = '' + res['enb_transportlayeraddress'] = '' + res['s11_cr_sess_imsi'] = '' + res['s11_cr_sess_mcc'] = '' + res['s11_cr_sess_mnc'] = '' + res['s11_cr_sess_apn'] = '' + res['s11_cr_sess_pdn_addr'] = '' + res['s11_mod_bear_fteid'] = '' + res['s11_mod_bear_ufteid'] = '' + try: + cap = {} + cap = pyshark.FileCapture(pcap_file, keep_packets=True, display_filter="sctp.port == 36412 || udp.port == 2123") + cnt = 0 + for pkt in cap: + if pkt is not None: + if 'SCTP' in pkt: + if pkt.sctp.chunk_type == CHUNK_TYPE__DATA: + if pkt.ip.src == ENB_IP_ADDRESS and pkt.ip.dst == MME_IP_ADDRESS and 'S1AP' in pkt: + if pkt.s1ap.procedurecode == PROC_CODE__ID_INITIALUEMESSAGE and pkt.s1ap.s1ap_pdu == S1AP_PDU_INITIATINGMESSAGE: + res['ue_init_msg'] = True + res['ue_imsi'] = pkt.s1ap.e212_imsi + print('UE --> send NAS INITIAL UE MESSAGE (pkt #' + str(cnt) + ')') + # The UE Initial Context Response has 2 S1AP layers! + myLayers = pkt.get_multiple_layers('S1AP') + if len(myLayers) == 2: + if myLayers[0].procedurecode == PROC_CODE__ID_INITIALCONTEXTSETUP and myLayers[0].s1ap_pdu == S1AP_PDU_SUCCESSFULOUTCOME and \ + myLayers[1].procedurecode == PROC_CODE__ID_UPLINKNASTRANSPORT and myLayers[1].s1ap_pdu == S1AP_PDU_INITIATINGMESSAGE: + res['initial_ue_context_res'] = True + res['enb_transportlayeraddress'] = myLayers[0].transportlayeraddressipv4 + #print (myLayers[1].nas_eps_nas_msg_emm_type) + print('UE --> send UE INITIAL UE CONTEXT RESPONSE (pkt #' + str(cnt) + ')') + + if pkt.sctp.chunk_type == CHUNK_TYPE__SACK: + if pkt.ip.src == MME_IP_ADDRESS and pkt.ip.dst == ENB_IP_ADDRESS and 'S1AP' in pkt: + if pkt.s1ap.procedurecode == PROC_CODE__ID_DOWNLINKNASTRANSPORT and pkt.s1ap.s1ap_pdu == S1AP_PDU_INITIATINGMESSAGE: + if pkt.s1ap.nas_eps_nas_msg_emm_type == NAS__AUTHENTICATION_REQ: + res['nas_auth_req'] = True + print('MME --> send NAS AUTHENTICATION_REQ (pkt #' + str(cnt) + ')') + if pkt.s1ap.nas_eps_nas_msg_emm_type == NAS__SECURITY_MODE_CMD: + res['nas_security_cmd'] = True + print('MME --> send NAS SECURITY_MODE_CMD (pkt #' + str(cnt) + ')') + if pkt.s1ap.procedurecode == PROC_CODE__ID_INITIALCONTEXTSETUP and pkt.s1ap.s1ap_pdu == S1AP_PDU_INITIATINGMESSAGE: + if pkt.s1ap.nas_eps_nas_msg_emm_type == NAS__ATTACH_ACCEPT: + res['initial_ue_context_req'] = True + res['apn'] = pkt.s1ap.gsm_a_gm_sm_apn + res['transportlayeraddress'] = pkt.s1ap.transportlayeraddressipv4 + print('MME --> send NAS INITIAL UE CONTEXT (pkt #' + str(cnt) + ')') + + if pkt.ip.src == ENB_IP_ADDRESS and pkt.ip.dst == MME_IP_ADDRESS and 'S1AP' in pkt: + if pkt.s1ap.procedurecode == PROC_CODE__ID_UPLINKNASTRANSPORT and pkt.s1ap.s1ap_pdu == S1AP_PDU_INITIATINGMESSAGE: + if pkt.s1ap.nas_eps_nas_msg_emm_type == NAS__AUTHENTICATION_RES: + res['nas_auth_res'] = True + print('eNB --> send NAS AUTHENTICATION_RES (pkt #' + str(cnt) + ')') + if pkt.s1ap.nas_eps_nas_msg_emm_type == NAS__SECURITY_MODE_COMPLETE: + res['nas_security_cmplt'] = True + print('eNB --> send NAS SECURITY_MODE_COMPLETE (pkt #' + str(cnt) + ')') + cnt += 1 + if 'GTPV2' in pkt: + if pkt.ip.src == MME_IP_ADDRESS and pkt.ip.dst == SPGWC_IP_ADDRESS: + if pkt.gtpv2.message_type == GTPV2_MSG_TYPE__CREATE_SESSION_REQUEST: + res['s11_create_session_req'] = True + res['s11_cr_sess_imsi'] = pkt.gtpv2.e212_imsi + res['s11_cr_sess_mcc'] = pkt.gtpv2.e212_tai_mcc + res['s11_cr_sess_mnc'] = pkt.gtpv2.e212_tai_mnc + res['s11_cr_sess_apn'] = pkt.gtpv2.apn + print('S11 CREATE SESSION REQ (pkt #' + str(cnt) + ')') + if pkt.gtpv2.message_type == GTPV2_MSG_TYPE__MODIFY_BEARER_REQUEST: + res['s11_modify_bearer_req'] = True + res['s11_mod_bear_fteid'] = pkt.gtpv2.f_teid_ipv4 + print('S11 MODIFY BEARER REQ (pkt #' + str(cnt) + ')') + if pkt.ip.src == SPGWC_IP_ADDRESS and pkt.ip.dst == MME_IP_ADDRESS: + if pkt.gtpv2.message_type == GTPV2_MSG_TYPE__CREATE_SESSION_RESPONSE: + res['s11_create_session_res'] = True + res['s11_cr_sess_pdn_addr'] = pkt.gtpv2.pdn_addr_and_prefix_ipv4 + print('S11 CREATE SESSION RES (pkt #' + str(cnt) + ')') + if pkt.gtpv2.message_type == GTPV2_MSG_TYPE__MODIFY_BEARER_RESPONSE: + res['s11_modify_bearer_res'] = True + res['s11_mod_bear_ufteid'] = pkt.gtpv2.f_teid_ipv4 + print('S11 MODIFY BEARER RES (pkt #' + str(cnt) + ')') + cnt += 1 + cap.close() + except Exception as e: + print('Could not open MME PCAP file') + return res + +def check_if_spgwu_connects_to_spgwc(pcap_file): + """ Normally the 2 first PFCP packets are the SPGWC <-> SPGWU association """ + res = {} + res['spgwu_request'] = False + res['spgwc_answer'] = False + res['spgwu_fdqn'] = '' + res['spgwc_ipv4'] = '' + try: + cap = {} + cap = pyshark.FileCapture(pcap_file, keep_packets=True, display_filter="udp.port == 8805") + cnt = 0 + for pkt in cap: + if pkt is not None: + if 'PFCP' in pkt: + if cnt == 0: + if pkt.pfcp.msg_type == PFCP_MSG_TYPE__SX_ASSOCIATION_SETUP_REQUEST and pkt.pfcp.node_id_type == PFCP_NODE_ID_TYPE__FQDN: + res['spgwu_request'] = True + res['spgwu_fdqn'] = pkt.pfcp.node_id_fqdn + if cnt == 1: + if pkt.pfcp.msg_type == PFCP_MSG_TYPE__SX_ASSOCIATION_SETUP_RESPONSE: + if pkt.pfcp.cause == '1': + res['spgwc_answer'] = True + res['spgwc_ipv4']=pkt.pfcp.node_id_ipv4 + cnt += 1 + cap.close() + except Exception as e: + print('Could not open SPGWC PCAP file') + return res + +def check_cups_heartbeat(pcap_file): + """ Normally the heartbeat request/response pace is one every second """ + res = {} + res['spgwc_hrt_request'] = False + res['spgwu_hrt_answer'] = False + res['spgwc_hrt_nb_req'] = '0' + res['spgwu_hrt_nb_res'] = '0' + nb_requests = 0 + nb_responses = 0 + try: + cap = {} + cap = pyshark.FileCapture(pcap_file, keep_packets=True, display_filter="udp.port == 8805") + cnt = 0 + for pkt in cap: + if pkt is not None: + if 'PFCP' in pkt: + if pkt.pfcp.msg_type == PFCP_MSG_TYPE__SX_HEARTBEAT_REQUEST: + nb_requests += 1 + if pkt.pfcp.msg_type == PFCP_MSG_TYPE__SX_HEARTBEAT_RESPONSE: + nb_responses += 1 + cnt += 1 + cap.close() + except Exception as e: + print('Could not open SPGWC PCAP file') + if nb_requests > 0: + res['spgwc_hrt_request'] = True + res['spgwc_hrt_nb_req'] = str(nb_requests) + if nb_responses > 0: + res['spgwu_hrt_answer'] = True + res['spgwu_hrt_nb_res'] = str(nb_responses) + return res diff --git a/ci-scripts/rhel8SanityCheckReport.py b/ci-scripts/rhel8SanityCheckReport.py new file mode 100644 index 0000000..9dc4f9d --- /dev/null +++ b/ci-scripts/rhel8SanityCheckReport.py @@ -0,0 +1,425 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- + +import os +import re +import sys +import subprocess +from pcap_check import * + +class HtmlReport(): + def __init__(self): + self.job_name = '' + self.job_id = '' + self.job_url = '' + self.job_start_time = 'TEMPLATE_TIME' + + def generate(self): + cwd = os.getcwd() + self.file = open(cwd + '/test_results_oai_epc_rhel8.html', 'w') + self.generateHeader() + + self.deploymentSummaryHeader() + + finalStatus = True + [finalStatus, details] = self.testSummaryDetails() + self.testSummaryHeader(finalStatus) + self.file.write(details) + self.testSummaryFooter() + + self.generateFooter() + self.file.close() + + if finalStatus: + sys.exit(0) + else: + sys.exit(-1) + + def generateHeader(self): + # HTML Header + self.file.write('\n') + self.file.write('\n') + self.file.write('\n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' OAI Core Network Test Results for ' + self.job_name + ' job build #' + self.job_id + '\n') + self.file.write('\n') + self.file.write('
\n') + self.file.write('
\n') + self.file.write(' \n') + self.file.write(' \n') + # SVG has a invisible background color -- adding it. + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write('
\n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' Job Summary -- Job: ' + self.job_name + ' -- Build-ID: ' + self.job_id + '\n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write('
\n') + self.file.write('
\n') + + def generateFooter(self): + self.file.write('
End of Test Report -- Copyright 2020 OpenAirInterface. All Rights Reserved.
\n') + self.file.write('
\n') + self.file.write('\n') + + def deploymentSummaryHeader(self): + self.file.write('

Deployment Summary

\n') + cwd = os.getcwd() + if os.path.isfile(cwd + '/archives/deployment_status.log'): + cmd = 'egrep -c "DEPLOYMENT: OK" archives/deployment_status.log || true' + status = False + ret = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') + if ret.stdout is not None: + if ret.stdout.strip() == '1': + status = True + if status: + self.file.write('
\n') + self.file.write(' Successful Deployment! \n') + self.file.write('
\n') + else: + self.file.write('
\n') + self.file.write(' Failed Deployment! \n') + self.file.write('
\n') + else: + self.file.write('
\n') + self.file.write(' LogFile not available! \n') + self.file.write('
\n') + self.file.write('
\n') + self.file.write(' \n') + self.file.write('
\n') + self.file.write('
\n') + self.file.write('
\n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.file.write(' \n') + self.addImageRow('cassandra') + self.addImageRow('redis') + self.addImageRow('magma_mme') + self.addImageRow('oai_hss') + self.addImageRow('oai_spgwc') + self.addImageRow('oai_spgwu') + self.file.write('
Container NameUsed Image TagImage Creation DateUsed Image SizeConfiguration Status
\n') + self.file.write('
\n') + + def addImageRow(self, imageInfoPrefix): + cwd = os.getcwd() + if imageInfoPrefix == 'magma_mme': + containerName = 'magma-mme' + tagPattern = 'MAGMA_MME_TAG' + statusPrefix = 'mme' + if imageInfoPrefix == 'oai_hss': + containerName = 'oai-hss' + tagPattern = 'OAI_HSS_TAG' + statusPrefix = 'hss' + if imageInfoPrefix == 'oai_spgwc': + containerName = 'oai-spgwc' + tagPattern = 'OAI_SPGWC_TAG' + statusPrefix = 'spgwc' + if imageInfoPrefix == 'oai_spgwu': + containerName = 'oai-spgwu-tiny' + tagPattern = 'OAI_SPGWU_TAG' + statusPrefix = 'spgwu' + if imageInfoPrefix == 'cassandra': + containerName = imageInfoPrefix + tagPattern = 'N/A' + if imageInfoPrefix == 'redis': + containerName = imageInfoPrefix + tagPattern = 'N/A' + if os.path.isfile(cwd + '/archives/' + imageInfoPrefix + '_image_info.log'): + usedTag = '' + createDate = '' + size = '' + with open(cwd + '/archives/' + imageInfoPrefix + '_image_info.log') as imageLog: + for line in imageLog: + line = line.strip() + result = re.search(tagPattern + ': (?P[a-zA-Z0-9\-\_:]+)', line) + if result is not None: + usedTag = result.group('tag') + result = re.search('Date = (?P[a-zA-Z0-9 \-\_:]+)', line) + if result is not None: + createDate = result.group('date') + result = re.search('Size = (?P[0-9]+) bytes', line) + if result is not None: + sizeInt = int(result.group('size')) + if sizeInt < 1000000: + sizeInt = int(sizeInt / 1000) + size = str(sizeInt) + ' kB' + else: + sizeInt = int(sizeInt / 1000000) + size = str(sizeInt) + ' MB' + imageLog.close() + configState = 'KO' + cmd = 'egrep -c "STATUS: healthy" archives/' + statusPrefix + '_config.log || true' + ret = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') + if ret.stdout is not None: + if ret.stdout.strip() == '1': + configState = 'OK' + self.file.write(' \n') + self.file.write(' ' + containerName + '\n') + self.file.write(' ' + usedTag + '\n') + self.file.write(' ' + createDate + '\n') + self.file.write(' ' + size + '\n') + if configState == 'OK': + self.file.write(' ' + configState + '\n') + else: + self.file.write(' ' + configState + '\n') + self.file.write(' \n') + else: + if imageInfoPrefix == 'cassandra': + self.file.write(' \n') + self.file.write(' ' + containerName + '\n') + self.file.write(' cassandra:2.1\n') + self.file.write(' N/A\n') + self.file.write(' 367 MB\n') + configState = 'KO' + cmd = 'egrep -c "STATUS: healthy" archives/cassandra_status.log || true' + ret = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') + if ret.stdout is not None: + if ret.stdout.strip() == '1': + configState = 'OK' + if configState == 'OK': + self.file.write(' OK\n') + else: + self.file.write(' KO\n') + self.file.write(' \n') + elif imageInfoPrefix == 'redis': + self.file.write(' \n') + self.file.write(' ' + containerName + '\n') + self.file.write(' redis:6.0.5\n') + self.file.write(' N/A\n') + self.file.write(' 108 MB\n') + configState = 'KO' + cmd = 'egrep -c "STATUS: healthy" archives/redis_status.log || true' + ret = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') + if ret.stdout is not None: + if ret.stdout.strip() == '1': + configState = 'OK' + if configState == 'OK': + self.file.write(' OK\n') + else: + self.file.write(' KO\n') + self.file.write(' \n') + else: + self.file.write(' \n') + self.file.write(' ' + containerName + '\n') + self.file.write(' UNKNOWN\n') + self.file.write(' N/A\n') + self.file.write(' N/A\n') + self.file.write(' UNKNOW\n') + self.file.write(' \n') + + def testSummaryHeader(self, status): + self.file.write('

Test with full OAI RAN Stack Summary

\n') + finalStatusOK = False + + if status: + self.file.write('
\n') + self.file.write(' Successful Test with OAI RAN stack! \n') + self.file.write('
\n') + else: + self.file.write('
\n') + self.file.write(' Failed Test with OAI RAN stack! \n') + self.file.write('
\n') + + def testSummaryDetails(self): + status = False + details = '' + details += '
\n' + details += ' \n' + details += '
\n' + details += '
\n' + details += '
\n' + details += ' \n' + details += ' \n' + details += ' \n' + details += ' \n' + details += ' \n' + details += ' \n' + # Checking the MME packets + pcap_list = [f for f in os.listdir('archives') if os.path.isfile(os.path.join('archives', f)) and f.startswith('mme') and f.endswith('.pcap')] + for pcap_fil in pcap_list: + # There should be only one (1) MME PCAP file and if present, then we can start resuming test OK + status = True + # MME to HSS Connection + res = check_if_mme_connects_to_hss('archives/' + pcap_fil) + details += self.addSectionRow('MME HSS S6A Connection') + details += self.addDetailsRow('MME S6A Request', res['mme_request'], 'Realm = ' + res['origin_realm']) + details += self.addDetailsRow('HSS S6A Response', res['hss_answer'], 'Host = ' + res['origin_host']) + if not res['mme_request'] or not res['hss_answer']: + status = False + # eNB to MME S1 Connection + res = check_if_enb_connects_to_mme('archives/' + pcap_fil) + details += self.addSectionRow('eNB S1 Setup to MME') + details += self.addDetailsRow('eNB SCTP INIT', res['enb_init_req'], 'n/a') + details += self.addDetailsRow('MME SCTP INIT_ACK', res['mme_init_answer'], 'n/a') + if not res['enb_init_req'] or not res['mme_init_answer']: + status = False + rowDetails = 'MCC = ' + res['enb_mcc'] + '\n' + rowDetails += 'MNC = ' + res['enb_mnc'] + '\n' + rowDetails += 'eNB Name = ' + res['enb_name'] + details += self.addDetailsRow('eNB S1 Setup Request', res['enb_s1_setup_req'], rowDetails) + details += self.addDetailsRow('MME S1 Setup Response', res['mme_s1_setup_res'], 'n/a') + if not res['enb_s1_setup_req'] or not res['mme_s1_setup_res']: + status = False + # UE attachment + res = check_if_ue_attachs('archives/' + pcap_fil) + details += self.addSectionRow('UE Attachment') + details += self.addDetailsRow('UE Attach Request', res['ue_init_msg'], 'IMSI = ' + res['ue_imsi']) + details += self.addDetailsRow('MME NAS Auth Request', res['nas_auth_req'], 'n/a') + details += self.addDetailsRow('UE NAS Auth Res', res['nas_auth_res'], 'n/a') + if not res['ue_init_msg'] or not res['nas_auth_req'] or not res['nas_auth_res']: + status = False + details += self.addDetailsRow('MME NAS Security Mode Command', res['nas_security_cmd'], 'n/a') + details += self.addDetailsRow('UE NSA Security Mode Complete', res['nas_security_cmplt'], 'n/a') + if not res['nas_security_cmd'] or not res['nas_security_cmplt']: + status = False + rowDetails = 'IMSI = ' + res['s11_cr_sess_imsi'] + '\n' + rowDetails += 'MCC = ' + res['s11_cr_sess_mcc'] + '\n' + rowDetails += 'MNC = ' + res['s11_cr_sess_mnc'] + '\n' + rowDetails += 'APN = ' + res['s11_cr_sess_apn'] + details += self.addDetailsRow('S11 Create Session Request', res['s11_create_session_req'], rowDetails) + rowDetails = 'PDN IP ADDR = ' + res['s11_cr_sess_pdn_addr'] + details += self.addDetailsRow('S11 Create Session Response', res['s11_create_session_res'], rowDetails) + rowDetails = 'APN = ' + res['apn'] + '\n' + if not res['s11_create_session_req'] or not res['s11_create_session_res']: + status = False + rowDetails += 'Transport Layer IP (ie SPGW-U) = ' + res['transportlayeraddress'] + details += self.addDetailsRow('MME NAS UE Initial Context', res['initial_ue_context_req'], rowDetails) + rowDetails = 'Transport Layer IP (ie eNB) = ' + res['enb_transportlayeraddress'] + details += self.addDetailsRow('UE NAS UE Initial Context Response', res['initial_ue_context_res'], rowDetails) + if not res['initial_ue_context_req'] or not res['initial_ue_context_res']: + status = False + rowDetails = 'eNB F-TEID = ' + res['s11_mod_bear_fteid'] + details += self.addDetailsRow('S11 Modify Bearer Request', res['s11_modify_bearer_req'], rowDetails) + rowDetails = 'SPGW-U F-TEID = ' + res['s11_mod_bear_ufteid'] + details += self.addDetailsRow('S11 Modify Bearer Response', res['s11_modify_bearer_res'], rowDetails) + if not res['s11_modify_bearer_req'] or not res['s11_modify_bearer_res']: + status = False + # Checking the CUPS packets + pcap_list = [f for f in os.listdir('archives') if os.path.isfile(os.path.join('archives', f)) and f.startswith('spgwc') and f.endswith('.pcap')] + for pcap_fil in pcap_list: + # SPGWU to SPGWC Connection + res = check_if_spgwu_connects_to_spgwc('archives/' + pcap_fil) + details += self.addSectionRow('CUPS Association') + details += self.addDetailsRow('SPGWU SX Assoc Request', res['spgwu_request'], 'FDQN = ' + res['spgwu_fdqn']) + details += self.addDetailsRow('SPGWC SX Assoc Response', res['spgwc_answer'], 'IP = ' + res['spgwc_ipv4']) + if not res['spgwu_request'] or not res['spgwc_answer']: + status = False + # SPGWC to SPGWU HeartBeat + res = check_cups_heartbeat('archives/' + pcap_fil) + details += self.addDetailsRow('SPGWC SX HRT Request', res['spgwc_hrt_request'], 'NB REQ = ' + res['spgwc_hrt_nb_req']) + details += self.addDetailsRow('SPGWU SX HRT Response', res['spgwu_hrt_answer'], 'NB RES = ' + res['spgwu_hrt_nb_res']) + if not res['spgwc_hrt_request'] or not res['spgwu_hrt_answer']: + status = False + details += '
Test NameTest StatusTest Details
\n' + details += '
\n' + return [status, details] + + def addSectionRow(self, sectionName): + sectionRow = '' + sectionRow += ' \n' + sectionRow += ' ' + sectionName + '\n' + sectionRow += ' \n' + return sectionRow + + def addDetailsRow(self, testName, status, details): + detailsRow = '' + detailsRow += ' \n' + detailsRow += ' ' + testName + '\n' + if status: + detailsRow += ' OK\n' + else: + detailsRow += ' KO\n' + detailsRow += '
'+ details + '\n'
+		detailsRow += '     \n'
+		return detailsRow
+
+	def testSummaryFooter(self):
+		self.file.write('  
\n') + +def Usage(): + print('----------------------------------------------------------------------------------------------------------------------') + print('dsTestGenerateHTMLReport.py') + print(' Generate an HTML report for the Jenkins pipeline on openair-epc-fed.') + print('----------------------------------------------------------------------------------------------------------------------') + print('Usage: python3 generateHtmlReport.py [options]') + print(' --help Show this help.') + print('---------------------------------------------------------------------------------------------- Mandatory Options -----') + print(' --job_name=[Jenkins Job name]') + print(' --job_id=[Jenkins Job Build ID]') + print(' --job_url=[Jenkins Job Build URL]') + +#-------------------------------------------------------------------------------------------------------- +# +# Start of main +# +#-------------------------------------------------------------------------------------------------------- + +argvs = sys.argv +argc = len(argvs) + +HTML = HtmlReport() + +while len(argvs) > 1: + myArgv = argvs.pop(1) + if re.match('^\-\-help$', myArgv, re.IGNORECASE): + Usage() + sys.exit(0) + elif re.match('^\-\-job_name=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-job_name=(.+)$', myArgv, re.IGNORECASE) + HTML.job_name = matchReg.group(1) + elif re.match('^\-\-job_id=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-job_id=(.+)$', myArgv, re.IGNORECASE) + HTML.job_id = matchReg.group(1) + elif re.match('^\-\-job_url=(.+)$', myArgv, re.IGNORECASE): + matchReg = re.match('^\-\-job_url=(.+)$', myArgv, re.IGNORECASE) + job_url = matchReg.group(1) + result = re.search('oai-public.apps.5glab.nsa.eurecom.fr', job_url) + if result is not None: + job_url = job_url.replace('oai-public.apps.5glab.nsa.eurecom.fr','oai.eurecom.fr') + HTML.job_url = job_url + else: + sys.exit('Invalid Parameter: ' + myArgv) + +if HTML.job_name == '' or HTML.job_id == '' or HTML.job_url == '': + sys.exit('Missing Parameter in job description') + +HTML.generate() +