Commit 78502b9e authored by Raphael Defosseux's avatar Raphael Defosseux

feat(ci): adding the PCAP analysis --> NRF registration and PFCP association

Signed-off-by: default avatarRaphael Defosseux <raphael.defosseux@eurecom.fr>
parent a6ac298f
...@@ -334,7 +334,7 @@ pipeline { ...@@ -334,7 +334,7 @@ pipeline {
if ("MERGE".equals(env.gitlabActionType)) { if ("MERGE".equals(env.gitlabActionType)) {
sh "python3 ci-scripts/generateHtmlReport.py --job_name=${JOB_NAME} --job_id=${BUILD_ID} --job_url=${BUILD_URL} --git_url=${GIT_URL} --git_src_branch=${env.gitlabSourceBranch} --git_src_commit=${env.gitlabMergeRequestLastCommit} --git_pull_request=True --git_target_branch=${env.gitlabTargetBranch} --git_target_commit=${GIT_COMMIT}" sh "python3 ci-scripts/generateHtmlReport.py --job_name=${JOB_NAME} --job_id=${BUILD_ID} --job_url=${BUILD_URL} --git_url=${GIT_URL} --git_src_branch=${env.gitlabSourceBranch} --git_src_commit=${env.gitlabMergeRequestLastCommit} --git_pull_request=True --git_target_branch=${env.gitlabTargetBranch} --git_target_commit=${GIT_COMMIT}"
if (fileExists('test_results_oai_upf.html')) { if (fileExists('test_results_oai_upf.html')) {
// sh "sed -i -e 's#TEMPLATE_MERGE_REQUEST_LINK#${gitlabMergeRequestLink}#g' test_results_oai_upf.html" sh "sed -i -e 's#TEMPLATE_MERGE_REQUEST_LINK#${gitlabMergeRequestLink}#g' test_results_oai_upf.html"
sh "sed -i -e 's#TEMPLATE_MERGE_REQUEST_TEMPLATE#${env.gitlabMergeRequestTitle}#' test_results_oai_upf.html" sh "sed -i -e 's#TEMPLATE_MERGE_REQUEST_TEMPLATE#${env.gitlabMergeRequestTitle}#' test_results_oai_upf.html"
} }
} else { } else {
......
...@@ -25,6 +25,7 @@ import os ...@@ -25,6 +25,7 @@ import os
import re import re
import sys import sys
import subprocess import subprocess
from pcap_check import *
class HtmlReport(): class HtmlReport():
def __init__(self): def __init__(self):
...@@ -693,6 +694,28 @@ class HtmlReport(): ...@@ -693,6 +694,28 @@ class HtmlReport():
self.addImageRow('oai_smf') self.addImageRow('oai_smf')
self.addImageRow('oai_upf_vpp') self.addImageRow('oai_upf_vpp')
self.file.write(' </table>\n') self.file.write(' </table>\n')
if os.path.isfile(cwd + '/archives/ci-upf-vpp-sanity.pcap'):
res1 = check_if_upf_registers_to_nrf('./archives/ci-upf-vpp-sanity.pcap')
res2 = check_pcfp_association('./archives/ci-upf-vpp-sanity.pcap')
self.file.write(' <br>\n')
self.file.write(' <table class="table-bordered" width = "90%" align = "center" border = 1>\n')
self.file.write(' <tr bgcolor = "#33CCFF" >\n')
self.file.write(' <th>Test Name</th>\n')
self.file.write(' <th>Test Status</th>\n')
self.file.write(' <th>Test Details</th>\n')
self.file.write(' </tr>\n')
self.file.write(self.addSectionRow('UPF registration to NRF'))
self.file.write(self.addDetailsRow('UPF Request', res1['upf_nrf_reg_req'], 'n/a'))
self.file.write(self.addDetailsRow('NRF Answer', res1['upf_nrf_reg_res'], 'UPF_FQDN = ' + res1['upf_nrf_fqdn']))
self.file.write(self.addSectionRow('PFCP Association'))
self.file.write(self.addDetailsRow('SMF Request', res2['pfcp_ass_req'], 'PFCP IPv4 = ' + res2['pfcp_ass_ipv4']))
details = 'FQDN = ' + res2['pfcp_ass_upf_fqdn'] + '\n'
details += 'Entreprise = ' + res2['pfcp_ass_entreprise'] + '\n'
details += 'Build = ' + res2['pfcp_ass_build_str']
self.file.write(self.addDetailsRow('UPF Answer', res2['pfcp_ass_res'], details))
self.file.write(' </table>\n')
self.file.write(' </div>\n') self.file.write(' </div>\n')
def addImageRow(self, imageInfoPrefix): def addImageRow(self, imageInfoPrefix):
...@@ -742,6 +765,25 @@ class HtmlReport(): ...@@ -742,6 +765,25 @@ class HtmlReport():
self.file.write(' <td>' + size + '</td>\n') self.file.write(' <td>' + size + '</td>\n')
self.file.write(' </tr>\n') self.file.write(' </tr>\n')
def addSectionRow(self, sectionName):
sectionRow = ''
sectionRow += ' <tr bgcolor = "LightGray">\n'
sectionRow += ' <td align = "center" colspan = "3">' + sectionName + '</td>\n'
sectionRow += ' </tr>\n'
return sectionRow
def addDetailsRow(self, testName, status, details):
detailsRow = ''
detailsRow += ' <tr>\n'
detailsRow += ' <td>' + testName + '</td>\n'
if status:
detailsRow += ' <td bgcolor = "Green"><font color="white"><b>OK</b></font></td>\n'
else:
detailsRow += ' <td bgcolor = "Red"><font color="white"><b>KO</b></font></td>\n'
detailsRow += ' <td><pre>'+ details + '</td>\n'
detailsRow += ' </tr>\n'
return detailsRow
def testSummaryFooter(self): def testSummaryFooter(self):
self.file.write(' <br>\n') self.file.write(' <br>\n')
......
#/*
# * 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
NRF_IP_ADDRESS = '192.168.71.130'
SMF_IP_ADDRESS = '192.168.71.133'
UPF_IP_ADDRESS = '192.168.71.134'
UPF_N4_IP_ADDRESS = '192.168.71.202'
# 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__IPV4 = '0'
PFCP_NODE_ID_TYPE__FQDN = '2'
PFCP_CAUSE__SUCCESS = '1'
def check_if_upf_registers_to_nrf(pcap_file):
res = {}
res['upf_nrf_reg_req'] = False
res['upf_nrf_reg_res'] = False
res['upf_nrf_fqdn'] = ''
try:
cap = {}
#cap = pyshark.FileCapture(pcap_file, keep_packets=True, display_filter="http2", use_json=True)
cap = pyshark.FileCapture(pcap_file, keep_packets=True, display_filter="http2")
cnt = 0
for pkt in cap:
if pkt is not None:
cnt += 1
if pkt.ip.src == UPF_IP_ADDRESS and pkt.ip.dst == NRF_IP_ADDRESS:
result = re.search('headers_method', str(pkt.http2.field_names))
if result is not None:
if pkt.http2.headers_method == 'PUT':
res['upf_nrf_reg_req'] = True
if pkt.ip.src == NRF_IP_ADDRESS and pkt.ip.dst == UPF_IP_ADDRESS:
status201 = False
upf_fqdn = False
result = re.search('header', str(pkt.http2.field_names))
if result is not None:
if pkt.http2.header == 'Header: :status: 201 Created':
status201 = True
result = re.search('json_value_string', str(pkt.http2.field_names))
if result is not None:
if pkt.http2.json_value_string == 'gw1.vppupf.node.5gcn.mnc95.mcc208.3gppnetwork.org':
upf_fqdn = True
res['upf_nrf_fqdn'] = pkt.http2.json_value_string
if status201 and upf_fqdn:
res['upf_nrf_reg_res'] = True
cap.close()
except Exception as e:
print(e.message, e.args)
print('Could not open PCAP file')
return res
def check_pcfp_association(pcap_file):
res = {}
res['pfcp_ass_req'] = False
res['pfcp_ass_res'] = False
res['pfcp_ass_ipv4'] = ''
res['pfcp_ass_upf_fqdn'] = ''
res['pfcp_ass_entreprise'] = ''
res['pfcp_ass_build_str'] = ''
try:
cap = {}
cap = pyshark.FileCapture(pcap_file, keep_packets=True, display_filter="pfcp")
cnt = 0
for pkt in cap:
if pkt is not None:
cnt += 1
if pkt.ip.src == SMF_IP_ADDRESS and pkt.ip.dst == UPF_N4_IP_ADDRESS:
if pkt.pfcp.msg_type == PFCP_MSG_TYPE__SX_ASSOCIATION_SETUP_REQUEST and pkt.pfcp.node_id_type == PFCP_NODE_ID_TYPE__IPV4:
if pkt.pfcp.node_id_ipv4 == SMF_IP_ADDRESS:
res['pfcp_ass_req'] = True
res['pfcp_ass_ipv4'] = pkt.pfcp.node_id_ipv4
else:
res['pfcp_ass_ipv4'] = pkt.pfcp.node_id_ipv4
if pkt.ip.src == UPF_N4_IP_ADDRESS and pkt.ip.dst == SMF_IP_ADDRESS:
if pkt.pfcp.msg_type == PFCP_MSG_TYPE__SX_ASSOCIATION_SETUP_RESPONSE and pkt.pfcp.node_id_type == PFCP_NODE_ID_TYPE__FQDN:
if pkt.pfcp.cause == PFCP_CAUSE__SUCCESS:
res['pfcp_ass_res'] = True
res['pfcp_ass_upf_fqdn'] = pkt.pfcp.node_id_fqdn
if pkt.pfcp.enterprise_id == '18681':
res['pfcp_ass_entreprise'] = 'Travelping GmbH'
res['pfcp_ass_build_str'] = pkt.pfcp.travelping_build_id_str
cap.close()
except Exception as e:
print(e.message, e.args)
print('Could not open PCAP file')
return res
...@@ -27,6 +27,7 @@ import shutil ...@@ -27,6 +27,7 @@ import shutil
import subprocess import subprocess
import sys import sys
import time import time
from pcap_check import *
logging.basicConfig( logging.basicConfig(
level=logging.DEBUG, level=logging.DEBUG,
...@@ -181,18 +182,39 @@ def checkDeployment(service, compose_file): ...@@ -181,18 +182,39 @@ def checkDeployment(service, compose_file):
subprocess_run_w_echo('sudo chmod 666 /tmp/ci-upf-vpp-sanity.pcap') subprocess_run_w_echo('sudo chmod 666 /tmp/ci-upf-vpp-sanity.pcap')
subprocess_run_w_echo('cp /tmp/ci-upf-vpp-sanity.pcap archives') subprocess_run_w_echo('cp /tmp/ci-upf-vpp-sanity.pcap archives')
upf_register = False
pfcp_association = False
if os.path.exists('./archives/ci-upf-vpp-sanity.pcap'):
res1 = check_if_upf_registers_to_nrf('./archives/ci-upf-vpp-sanity.pcap')
if res1['upf_nrf_reg_req'] and res1['upf_nrf_reg_res']:
logging.debug('UPF did register at NRF')
upf_register = True
else:
logging.error('UPF did NOT register at NRF')
res2 = check_pcfp_association('./archives/ci-upf-vpp-sanity.pcap')
if res2['pfcp_ass_req'] and res2['pfcp_ass_res']:
logging.debug('PCFP association b/w SMF and UPF was successful')
pfcp_association = True
else:
logging.error('PCFP association b/w SMF and UPF failed')
os.chdir('docker-compose') os.chdir('docker-compose')
if not os.path.exists('./ci-' + compose_file): if not os.path.exists('./ci-' + compose_file):
return return
cmd = 'docker-compose -p vpp-sanity -f ./ci-' + compose_file + ' ps -a' cmd = 'docker-compose -p vpp-sanity -f ./ci-' + compose_file + ' ps -a'
res = subprocess.check_output(cmd, shell=True, universal_newlines=True) res = subprocess.check_output(cmd, shell=True, universal_newlines=True)
healthy_cnt = 0
for line in res.split('\n'): for line in res.split('\n'):
result = re.search('Up.*healthy', line) result = re.search('Up.*healthy', line)
if result is not None: if result is not None:
subprocess_run_w_echo('docker logs ' + line.split(' ')[0] + ' > ../archives/' + line.split(' ')[0] + '.log 2>&1') subprocess_run_w_echo('docker logs ' + line.split(' ')[0] + ' > ../archives/' + line.split(' ')[0] + '.log 2>&1')
healthy_cnt += 1
subprocess_run_w_echo('echo "SANITY-CHECK-DEPLOYMENT: OK" > ../archives/deployment_status.log') if upf_register and (healthy_cnt == 5):
subprocess_run_w_echo('echo "SANITY-CHECK-DEPLOYMENT: OK" > ../archives/deployment_status.log')
else:
subprocess_run_w_echo('echo "SANITY-CHECK-DEPLOYMENT: KO" > ../archives/deployment_status.log')
def subprocess_run_w_echo(cmd): def subprocess_run_w_echo(cmd):
logging.debug(cmd) logging.debug(cmd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment