diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c56fad9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +# misc cruft +*.log +log.txt +rmr/* +docs_and_diagrams/ + +# documentation +.tox +docs/_build/ + +# standard python ignore template +.pytest_cache/ +xunit-results.xml +.DS_Store +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +venv-tox/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject + +# Test report +xunit-reports +coverage-reports + +# Eclipse +.project +.pydevproject +.settings + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 28be4cb..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,9 +0,0 @@ - - -We expect all ONF employees, member companies, and participants to abide by our [Code of Conduct](https://www.opennetworking.org/wp-content/themes/onf/img/onf-code-of-conduct.pdf). - -If you are being harassed, notice that someone else is being harassed, or have any other concerns involving someone’s welfare, please notify a member of the ONF team or email [conduct@opennetworking.org](conduct@opennetworking.org). diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8e57479 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +# ================================================================================== +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# ================================================================================== +FROM python:3.8-alpine + +# copy rmr libraries from builder image in lieu of an Alpine package +COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/lib64/librmr* /usr/local/lib64/ +# RMR setup +RUN mkdir -p /opt/route/ +COPY init/test_route.rt /opt/route/test_route.rt +ENV LD_LIBRARY_PATH /usr/local/lib/:/usr/local/lib64 +ENV RMR_SEED_RT /opt/route/test_route.rt +# install a legacy ver of betterproto TODO: upgrade later +RUN pip install betterproto==2.0.0b4 + +# sdl needs gcc +RUN apk update && apk add gcc musl-dev bash + +# Install +COPY setup.py /tmp +COPY README.md /tmp +COPY LICENSE.txt /tmp/ +COPY src/ /tmp/src +COPY init/ /tmp/init +RUN pip install /tmp + +# Env - TODO- Configmap +ENV PYTHONUNBUFFERED 1 +ENV CONFIG_FILE=/tmp/init/config-file.json + +# For Default DB connection, modify for resp kubernetes env +ENV DBAAS_SERVICE_PORT=6379 +ENV DBAAS_SERVICE_HOST=service-ricplt-dbaas-tcp.ricplt.svc.cluster.local + +#Run +CMD run-xapp.py + diff --git a/INFO.yaml b/INFO.yaml new file mode 100644 index 0000000..cac973f --- /dev/null +++ b/INFO.yaml @@ -0,0 +1,35 @@ +--- +project: 'mobiflow_auditor' +project_creation_date: '2024-3-2' +project_category: '' +lifecycle_state: 'Incubation' +project_lead: &oran_ric_app_se_ran + name: 'SE-RAN' + email: 'team@5gsec.com' + id: 'SE-RAN' + company: 'Security-Enhanced Radio Access Network' + timezone: 'America/New_York' +primary_contact: *oran_ric_app_se_ran +issue_tracking: + type: 'Github' + url: 'https://github.com/5GSEC/MobiFlow-Auditor/issues' + key: 'MobiFlow-Auditor' +mailing_list: + type: 'groups.io' + url: 'https://lists.o-ran-sc.org/g/main' + tag: '[]' +repositories: + - 5GSEC/MobiFlow-Auditor +committers: + - <<: *oran_ric_app_se_ran + - name: 'Haohuang Wen' + email: 'wen.423@osu.edu' + company: 'The Ohio State University' + id: 'onehouwong' + timezone: 'America/New_York' + # - name: 'Naman Gupta' + # email: 'naman.gupta@samsung.com' + # company: 'Samsung' + # id: 'naman.gupta' + # timezone: 'Asia/Kolkata' + diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 57bc88a..0000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 - - 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. - diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..69a2cef --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,29 @@ + + Unless otherwise specified, all software contained herein is licensed + under the Apache License, Version 2.0 (the "Software License"); + you may not use this software except in compliance with the Software + License. You may obtain a copy of the Software License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the Software License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the Software License for the specific language governing permissions + and limitations under the Software License. + + + + Unless otherwise specified, all documentation contained herein is licensed + under the Creative Commons License, Attribution 4.0 Intl. (the + "Documentation License"); you may not use this documentation except in + compliance with the Documentation License. You may obtain a copy of the + Documentation License at + + https://creativecommons.org/licenses/by/4.0/ + + Unless required by applicable law or agreed to in writing, documentation + distributed under the Documentation License is distributed on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the Documentation License for the specific language governing + permissions and limitations under the Documentation License. diff --git a/Makefile b/Makefile deleted file mode 100644 index f244c9b..0000000 --- a/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -# SPDX-FileCopyrightText: Copyright 2004-present Facebook. All Rights Reserved. -# SPDX-FileCopyrightText: 2019-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -.PHONY: help -help: ## display help text - @grep -E '^[-a-zA-Z_/\%]+:.*?## .*$$' $(MAKEFILE_LIST) \ - | sort \ - | awk 'BEGIN {FS = ":.*?## "}; {printf "%-30s %s\n", $$1, $$2}' - -###################### -# detect podman installed -# simple test: if podman is installed, use podman to build images - -_IMAGECMD=podman -ifeq (, $(shell /usr/bin/which podman)) - _IMAGECMD=docker -endif - -###################### -# docker operations - -IMAGES=mobiflow-auditor - -define IMAGE_HELP - -Build xApp images. - -Usage: make image/ - Apps: ${IMAGES} - - or: image/all to make all images - -endef -export IMAGE_HELP - -SM_TMP ?= ${TMPDIR}/mobi-expert-xapp-sync-sm - -.PHONY: get-bindings -get-bindings: ## clone onos-e2-sm repo and prepare python bindings for use - if [ ! -d "onos_e2_sm" ]; then \ - rm -rf ${SM_TMP}; \ - git clone --depth 1 --branch servicemodels/e2sm_rsm/v0.8.17 https://github.com/onosproject/onos-e2-sm.git ${SM_TMP}; \ - cp -v -r ${SM_TMP}/python onos_e2_sm; \ - fi - -.PHONY: image -image: ## Build xApp docker image - @echo "$$IMAGE_HELP" - -image/%: get-bindings - if [ "all" = "$*" ]; then exit 0; fi; \ - $(_IMAGECMD) build --tag localhost:5000/$*:latest -f $*/Dockerfile .; - -_IMAGES=$(patsubst %,image/%,$(IMAGES)) -image/all: $(_IMAGES) -images: $(_IMAGES) - -build-tools: # @HELP install the ONOS build tools if needed - @if [ ! -d "../build-tools" ]; then cd .. && git clone https://github.com/onosproject/build-tools.git; fi - -publish: # @HELP publish version on github and dockerhub - if ! grep dev VERSION.fb-ah-xapp; then ./../build-tools/publish-version fb-ah-xapp/${VERSION} onosproject/fb-ah-xapp; fi - if ! grep dev VERSION.ah-eson-test-server; then ./../build-tools/publish-version ah-eson-test-server/${VERSION} onosproject/ah-eson-test-server; fi - if ! grep dev VERSION.fb-kpimon-xapp; then ./../build-tools/publish-version fb-kpimon-xapp/${VERSION} onosproject/fb-kpimon-xapp; fi - -jenkins-publish: build-tools # @HELP Jenkins calls this to publish artifacts - ./build/bin/push-images - VERSIONFILE=VERSION.fb-ah-xapp ../build-tools/release-merge-commit - VERSIONFILE=VERSION.ah-eson-test-server ../build-tools/release-merge-commit - VERSIONFILE=VERSION.fb-kpimon-xapp ../build-tools/release-merge-commit - -jenkins-test: # @HELP run the unit tests and source code validation producing a junit style report for Jenkins -jenkins-test: build-tools license - -reuse-tool: # @HELP install reuse if not present - command -v reuse || python3 -m pip install --user reuse - -license: reuse-tool # @HELP run license checks - reuse lint - -test: build-tools license diff --git a/README.md b/README.md index b6c4424..a0a3f09 100644 --- a/README.md +++ b/README.md @@ -141,3 +141,4 @@ Undeploy the MobiFlow-Auditor xApp from Kubernetes } ``` + diff --git a/build.sh b/build.sh index 0cc1fef..74c8f35 100755 --- a/build.sh +++ b/build.sh @@ -1,24 +1,2 @@ -#!/usr/bin/env bash -sudo apt-get install -y gcc-multilib flex csh - -_OS_SYSN=$(uname -s | tr "-" "_") -_OS_VERS=$(uname -r | cut -d. -f1-2 | cut -d- -f1) -_OS_VERS_STR=$(uname -r | cut -d. -f1-2 | cut -d- -f1 | tr '.' '_') -_OS_ARCH=$(uname -m | sed 's/ /_/g') -_OS_NAME="${_OS_SYSN}-${_OS_VERS}" -_OS_NAME_ARCH="${_OS_SYSN}-${_OS_ARCH}" - -#if [ ! -d "./mobiflow-auditor/pbest/pbcc/$_OS_NAME_ARCH" ]; then -# echo "Compiling pbcc" -# pushd ./mobiflow-auditor/pbest/pbcc && make && popd -#fi -# -#if [ ! -d "./mobiflow-auditor/pbest/libpb/$_OS_NAME_ARCH" ]; then -# echo "Compiling libpb" -# pushd ./mobiflow-auditor/pbest/libpb && make && popd -#fi -# -#(pushd ./mobiflow-auditor/pbest/expert && make clean && make && popd) - -sudo make image/mobiflow-auditor -sudo docker push localhost:5000/mobiflow-auditor +#!/bin/bash +sudo docker build -t 5gsec.se-ran.org:10004/xapp/mobiflow-auditor:0.0.1 . diff --git a/charts/Chart.yaml b/charts/Chart.yaml new file mode 100644 index 0000000..db2277e --- /dev/null +++ b/charts/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: Standard xApp Helm Chart +name: mobiflow-auditor +version: 0.0.1 diff --git a/charts/config/config-file.json b/charts/config/config-file.json new file mode 100644 index 0000000..21b4248 --- /dev/null +++ b/charts/config/config-file.json @@ -0,0 +1 @@ +{"name": "mobiflow-auditor", "version": "0.0.1", "containers": [{"name": "mobiflow-auditor", "image": {"registry": "5gsec.se-ran.org:10004", "name": "xapp/mobiflow-auditor", "tag": "0.0.1"}}], "messaging": {"ports": [{"name": "http", "container": "mobiflow-auditor", "port": 8080, "description": "http service"}, {"name": "rmrroute", "container": "mobiflow-auditor", "port": 4561, "description": "rmr route port for mobiflow-auditor xapp"}, {"name": "rmrdata", "container": "mobiflow-auditor", "port": 4560, "rxMessages": ["RIC_SUB_RESP", "RIC_SUB_FAILURE", "RIC_SUB_DEL_RESP", "RIC_INDICATION"], "txMessages": ["RIC_SUB_REQ", "RIC_SUB_DEL_REQ", "RIC_SGNB_ADDITION_REQ", "RIC_SGNB_ADDITION_ACK"], "mtypes": [{"name": "TESTNAME1", "id": 55555}, {"name": "TESTNAME2", "id": 55556}], "policies": [1], "description": "rmr data port for mobiflow-auditor"}]}, "rmr": {"protPort": "tcp:4560", "maxSize": 2072, "numWorkers": 1, "txMessages": ["RIC_SUB_REQ", "A1_POLICY_RESP", "A1_POLICY_QUERY", "RIC_HEALTH_CHECK_RESP"], "rxMessages": ["RIC_SUB_RESP", "A1_POLICY_REQ", "RIC_HEALTH_CHECK_REQ"], "policies": [1]}, "controls": {"fileStrorage": false}, "db": {"waitForSdl": false}} \ No newline at end of file diff --git a/charts/descriptors/schema.json b/charts/descriptors/schema.json new file mode 100644 index 0000000..84d8bfe --- /dev/null +++ b/charts/descriptors/schema.json @@ -0,0 +1 @@ +{"$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://o-ran-sc.org/xapp_root.json", "type": "object", "title": "The xApp Root Schema", "properties": {"name": {"$id": "#/properties/name", "type": "string", "title": "The xApp Name", "default": "xapp", "examples": ["example_xapp"]}, "version": {"$id": "#/properties/version", "type": "string", "title": "The xApp version", "default": "1.0.0", "examples": ["1.0.0"], "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"}, "annotations": {"$id": "#/properties/annotation", "type": "object", "title": "The k8s pod annotation", "additionalProperties": {"anyOf": [{"type": "string"}, {"type": "array", "items": {"type": "object"}}]}}, "containers": {"$id": "#/properties/containers", "type": "array", "title": "The Container Schema", "items": {"$id": "#/properties/containers/items", "type": "object", "title": "The Container Items Schema", "required": ["name", "image"], "properties": {"name": {"$id": "#/properties/containers/items/properties/name", "type": "string", "title": "The xApp Container Name", "default": "xapp", "examples": ["xapp"]}, "image": {"$id": "#/properties/containers/items/properties/image", "type": "object", "title": "The Container Image", "required": ["registry", "name", "tag"], "properties": {"registry": {"$id": "#/properties/containers/items/properties/image/properties/registry", "type": "string", "title": "The xApp Image Registry", "default": "nexus3.o-ran-sc.org:10002", "examples": ["nexus3.o-ran-sc.org:10002"], "pattern": "^([A-Za-z0-9\\.-]{1,}\\.[A-Za-z]{1,}|((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))(?:\\:\\d+)?$"}, "name": {"$id": "#/properties/containers/items/properties/image/properties/name", "type": "string", "title": "The xApp Image Name", "default": "xapp", "examples": ["xapp"]}, "tag": {"$id": "#/properties/containers/items/properties/image/properties/tag", "type": "string", "title": "The xApp Image Tag", "default": "latest", "examples": ["latest"]}}}, "command": {"$id": "#/properties/containers/items/properties/command", "type": "array", "items": [{"$id": "#/properties/containers/items/properties/command/item", "type": "string", "title": "The Command Item", "default": "/bin/sh", "examples": ["/bin/sh"]}]}, "args": {"$id": "#/properties/containers/items/properties/args", "type": "array", "items": [{"$id": "#/properties/containers/items/properties/args/item", "type": "string", "title": "The Command Arguement Item", "default": "-c", "examples": ["-c"]}]}}}}, "livenessProbe": {"$id": "#/properties/livenessprobe", "type": "object", "title": "The Liveness Probe Definition", "properties": {"exec": {"$id": "#/properties/livenessprobe/exec", "type": "object", "title": "Script of Liveness Probe", "properties": {"command": {"$id": "#/properties/livenessprobe/exec/command", "type": "array", "items": [{"$id": "#/properties/livenessprobe/exec/command/item", "type": "string", "title": "The Command Item", "default": "/bin/sh", "examples": ["/bin/sh"]}]}}, "required": ["command"]}, "httpGet": {"$id": "#/properties/livenessprobe/httpget", "type": "object", "title": "Http of Liveness Probe", "properties": {"path": {"$id": "#/properties/livenessprobe/httpget/path", "type": "string", "title": "The Path of Http Liveness Probe", "default": "/health", "examples": ["/health"]}, "port": {"$id": "#/properties/livenessprobe/httpget/port", "type": "integer", "title": "The Port of Http Liveness Probe", "default": 80, "examples": [80]}}, "required": ["path", "port"]}, "initialDelaySeconds": {"$id": "#/properties/livenessprobe/initialdelayseconds", "type": "integer", "title": "Initial Delay of Liveness Probe", "default": 5, "examples": [5]}, "periodSeconds": {"$id": "#/properties/livenessprobe/periodseconds", "type": "integer", "title": "Period of Liveness Probe", "default": 15, "examples": [15]}}, "oneOf": [{"$id": "#/properties/livenessprobe/oneof/exec", "required": ["exec", "initialDelaySeconds", "periodSeconds"]}, {"$id": "#/properties/livenessprobe/oneof/httpget", "required": ["httpGet", "initialDelaySeconds", "periodSeconds"]}]}, "readinessProbe": {"$id": "#/properties/readinessprobe", "type": "object", "title": "The Readiness Probe Definition", "properties": {"exec": {"$id": "#/properties/readinessprobe/exec", "type": "object", "title": "Script of Readiness Probe", "properties": {"command": {"$id": "#/properties/readinessprobe/exec/command", "type": "array", "items": [{"type": "string"}]}}, "required": ["command"]}, "httpGet": {"$id": "#/properties/readinessprobe/httpget", "type": "object", "title": "Http of Readiness Probe", "properties": {"path": {"$id": "#/properties/readinessprobe/httpget/path", "type": "string", "title": "The Path of Http Readiness Probe", "default": "/health", "examples": ["/health"]}, "port": {"$id": "#/properties/readinessprobe/httpget/port", "type": "integer", "title": "The Port of Http Readiness Probe", "default": 80, "examples": [80]}}, "required": ["path", "port"]}, "initialDelaySeconds": {"$id": "#/properties/readinessprobe/initialdelayseconds", "type": "integer", "title": "Initial Delay of Readiness Probe", "default": 5, "examples": [5]}, "periodSeconds": {"$id": "#/properties/readinessprobe/periodseconds", "type": "integer", "title": "Period of Readiness Probe", "default": 15, "examples": [15]}}, "oneOf": [{"$id": "#/properties/readinessprobe/oneof/exec", "required": ["exec", "initialDelaySeconds", "periodSeconds"]}, {"$id": "#/properties/readinessprobe/oneof/httpget", "required": ["httpGet", "initialDelaySeconds", "periodSeconds"]}]}, "messaging": {"type": "object", "$id": "#/properties/messaging", "title": "The Messaging Schema", "properties": {"ports": {"$id": "#/properties/messaging/ports", "type": "array", "title": "The Ports for Messaging", "items": {"$id": "#/properties/messaging/ports/items", "type": "object", "title": "The Item of Port", "required": ["name", "container", "port"], "dependencies": {"txMessages": ["rxMessages", "policies"], "rxMessages": ["txMessages", "policies"], "policies": ["rxMessages", "txMessages"]}, "properties": {"name": {"$id": "#/properties/messaging/ports/items/name", "type": "string", "title": "The Name of the Port", "default": "App", "examples": ["App"]}, "container": {"$id": "#/properties/messaging/ports/items/container", "type": "string", "title": "The Container of the Port", "default": "xapp", "examples": ["xapp"]}, "port": {"$id": "#/properties/messaging/ports/items/port", "type": "integer", "title": "The Port Number", "default": 8080, "examples": [8080]}, "description": {"$id": "#/properties/messaging/ports/items/description", "type": "string", "title": "The description for the port", "default": "port description", "examples": ["port description"]}, "txMessages": {"$id": "#/properties/messaging/ports/items/txmessages", "type": "array", "title": "The txMessage Types", "items": {"$id": "#/properties/messaging/ports/items//txmessages/item", "type": "string", "title": "The txMessage Types Item", "default": "RIC_SUB", "examples": ["RIC_SUB"]}}, "rxMessages": {"$id": "#/properties/messaging/ports/items/rxmessages", "type": "array", "title": "The rxMessage Types", "items": {"$id": "#/properties/messaging/ports/items/rxmessages/item", "type": "string", "title": "The rxMessage Types Item", "default": "RIC_SUB", "examples": ["RIC_SUB"]}}, "policies": {"$id": "#/properties/messaging/ports/items/policies", "type": "array", "title": "The Policies Types", "items": {"$id": "#/properties/messaging/ports/items/policies/item", "type": "integer", "title": "The Policy Types Item", "default": 1, "examples": [1]}}}}}}, "required": ["ports"]}, "metrics": {"type": "object", "$id": "#/properties/metrics", "title": "The Metrics Schema", "items": {"$id": "#/properties/metrics/items", "type": "object", "title": "The Metrics Items Schema", "required": ["objectName", "objectInstance", "name", "type", "description"], "properties": {"objectName": {"$id": "#/properties/metrics/items/objectname", "type": "string", "title": "The Object Name"}, "objectInstance": {"$id": "#/properties/metrics/items/objectinstance", "type": "string", "title": "The Object Instance"}, "name": {"$id": "#/properties/metrics/items/name", "type": "string", "title": "The Object Name"}, "type": {"$id": "#/properties/metrics/items/type", "type": "string", "title": "The Object Type"}, "description": {"$id": "#/properties/metrics/items/description", "type": "string", "title": "The Object Description"}}}}, "controls": {"$schema": "http://json-schema.org/draft-07/schema#", "$id": "#/controls", "type": "object", "title": "Controls Section Schema", "required": [], "properties": {}}}} \ No newline at end of file diff --git a/charts/templates/_helpers.tpl b/charts/templates/_helpers.tpl new file mode 100644 index 0000000..d492ff8 --- /dev/null +++ b/charts/templates/_helpers.tpl @@ -0,0 +1,82 @@ +################################################################################ +# Copyright (c) 2019 AT&T Intellectual Property. # +# # +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # +# # +# 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. # +################################################################################ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "ricxapp.name" -}} + {{- default .Chart.Name .Values.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "ricxapp.fullname" -}} + {{- $name := ( include "ricxapp.name" . ) -}} + {{- $fullname := ( printf "%s-%s" .Release.Namespace $name ) -}} + {{- default $fullname .Values.fullname | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "ricxapp.chart" -}} + {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "ricxapp.namespace" -}} + {{- default .Release.Namespace .Values.nsPrefix -}} +{{- end -}} + +{{- define "ricxapp.servicename.rmr" -}} + {{- $name := ( include "ricxapp.fullname" . ) -}} + {{- printf "service-%s-rmr" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "ricxapp.servicename.http" -}} + {{- $name := ( include "ricxapp.fullname" . ) -}} + {{- printf "service-%s-http" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "ricxapp.configmapname" -}} + {{- $name := ( include "ricxapp.fullname" . ) -}} + {{- printf "configmap-%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "ricxapp.servicename" -}} + {{- $name := ( include "ricxapp.fullname" . ) -}} + {{- printf "service-%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "ricxapp.deploymentname" -}} + {{- $name := ( include "ricxapp.fullname" . ) -}} + {{- printf "deployment-%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + +{{- define "ricxapp.containername" -}} + {{- $name := ( include "ricxapp.fullname" . ) -}} + {{- printf "container-%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + +{{- define "ricxapp.imagepullsecret" -}} + {{- $reponame := .repo -}} + {{- $postfix := $reponame | replace "." "-" | replace ":" "-" | replace "/" "-" | trunc 63 | trimSuffix "-" -}} + {{- printf "secret-%s" $postfix -}} +{{- end -}} diff --git a/charts/templates/appconfig.yaml b/charts/templates/appconfig.yaml new file mode 100644 index 0000000..4ef35b4 --- /dev/null +++ b/charts/templates/appconfig.yaml @@ -0,0 +1,544 @@ +################################################################################ +# Copyright (c) 2019 AT&T Intellectual Property. # +# # +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # +# # +# 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. # +################################################################################' + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "ricxapp.configmapname" . }}-appconfig +data: + config-file.json: | + { + "name": {{ index .Values "name" | toJson }}, + "version": {{ index .Values "version" | toJson }}, + "containers": {{ index .Values "containers" | toJson }}, + "messaging": { + "ports": {{ index .Values "messaging" "ports" | toJson }} + }, + "rmr": { + "protPort": {{ index .Values "rmr" "protPort" | toJson }}, + "maxSize": {{ index .Values "rmr" "maxSize" | toJson }}, + "numWorkers": {{ index .Values "rmr" "numWorkers" | toJson }}, + "txMessages": {{ index .Values "rmr" "txMessages" | toJson }}, + "rxMessages": {{ index .Values "rmr" "rxMessages" | toJson }}, + "policies": {{ index .Values "rmr" "policies" | toJson }} + }, + "controls": { + "fileStrorage": {{ index .Values "controls" "fileStrorage" | toJson }} + }, + "db": { + "waitForSdl": {{ index .Values "db" "waitForSdl" | toJson }} + } + } + schema.json: | + { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://o-ran-sc.org/xapp_root.json", + "type": "object", + "title": "The xApp Root Schema", + "properties": { + "name": { + "$id": "#/properties/name", + "type": "string", + "title": "The xApp Name", + "default": "xapp", + "examples": [ + "example_xapp" + ] + }, + "version": { + "$id": "#/properties/version", + "type": "string", + "title": "The xApp version", + "default": "1.0.0", + "examples": [ + "1.0.0" + ], + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + }, + "annotations": { + "$id": "#/properties/annotation", + "type": "object", + "title": "The k8s pod annotation", + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "object" + } + } + ] + } + }, + "containers": { + "$id": "#/properties/containers", + "type": "array", + "title": "The Container Schema", + "items": { + "$id": "#/properties/containers/items", + "type": "object", + "title": "The Container Items Schema", + "required": [ + "name", + "image" + ], + "properties": { + "name": { + "$id": "#/properties/containers/items/properties/name", + "type": "string", + "title": "The xApp Container Name", + "default": "xapp", + "examples": [ + "xapp" + ] + }, + "image": { + "$id": "#/properties/containers/items/properties/image", + "type": "object", + "title": "The Container Image", + "required": [ + "registry", + "name", + "tag" + ], + "properties": { + "registry": { + "$id": "#/properties/containers/items/properties/image/properties/registry", + "type": "string", + "title": "The xApp Image Registry", + "default": "nexus3.o-ran-sc.org:10002", + "examples": [ + "nexus3.o-ran-sc.org:10002" + ], + "pattern": "^([A-Za-z0-9\\.-]{1,}\\.[A-Za-z]{1,}|((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))(?:\\:\\d+)?$" + }, + "name": { + "$id": "#/properties/containers/items/properties/image/properties/name", + "type": "string", + "title": "The xApp Image Name", + "default": "xapp", + "examples": [ + "xapp" + ] + }, + "tag": { + "$id": "#/properties/containers/items/properties/image/properties/tag", + "type": "string", + "title": "The xApp Image Tag", + "default": "latest", + "examples": [ + "latest" + ] + } + } + }, + "command": { + "$id": "#/properties/containers/items/properties/command", + "type": "array", + "items": [ + { + "$id": "#/properties/containers/items/properties/command/item", + "type": "string", + "title": "The Command Item", + "default": "/bin/sh", + "examples": [ + "/bin/sh" + ] + } + ] + }, + "args": { + "$id": "#/properties/containers/items/properties/args", + "type": "array", + "items": [ + { + "$id": "#/properties/containers/items/properties/args/item", + "type": "string", + "title": "The Command Arguement Item", + "default": "-c", + "examples": [ + "-c" + ] + } + ] + } + } + } + }, + "livenessProbe": { + "$id": "#/properties/livenessprobe", + "type": "object", + "title": "The Liveness Probe Definition", + "properties": { + "exec": { + "$id": "#/properties/livenessprobe/exec", + "type": "object", + "title": "Script of Liveness Probe", + "properties": { + "command": { + "$id": "#/properties/livenessprobe/exec/command", + "type": "array", + "items": [ + { + "$id": "#/properties/livenessprobe/exec/command/item", + "type": "string", + "title": "The Command Item", + "default": "/bin/sh", + "examples": [ + "/bin/sh" + ] + } + ] + } + }, + "required": [ + "command" + ] + }, + "httpGet": { + "$id": "#/properties/livenessprobe/httpget", + "type": "object", + "title": "Http of Liveness Probe", + "properties": { + "path": { + "$id": "#/properties/livenessprobe/httpget/path", + "type": "string", + "title": "The Path of Http Liveness Probe", + "default": "/health", + "examples": [ + "/health" + ] + }, + "port": { + "$id": "#/properties/livenessprobe/httpget/port", + "type": "integer", + "title": "The Port of Http Liveness Probe", + "default": 80, + "examples": [ + 80 + ] + } + }, + "required": [ + "path", + "port" + ] + }, + "initialDelaySeconds": { + "$id": "#/properties/livenessprobe/initialdelayseconds", + "type": "integer", + "title": "Initial Delay of Liveness Probe", + "default": 5, + "examples": [ + 5 + ] + }, + "periodSeconds": { + "$id": "#/properties/livenessprobe/periodseconds", + "type": "integer", + "title": "Period of Liveness Probe", + "default": 15, + "examples": [ + 15 + ] + } + }, + "oneOf": [ + { + "$id": "#/properties/livenessprobe/oneof/exec", + "required": [ + "exec", + "initialDelaySeconds", + "periodSeconds" + ] + }, + { + "$id": "#/properties/livenessprobe/oneof/httpget", + "required": [ + "httpGet", + "initialDelaySeconds", + "periodSeconds" + ] + } + ] + }, + "readinessProbe": { + "$id": "#/properties/readinessprobe", + "type": "object", + "title": "The Readiness Probe Definition", + "properties": { + "exec": { + "$id": "#/properties/readinessprobe/exec", + "type": "object", + "title": "Script of Readiness Probe", + "properties": { + "command": { + "$id": "#/properties/readinessprobe/exec/command", + "type": "array", + "items": [ + { + "type": "string" + } + ] + } + }, + "required": [ + "command" + ] + }, + "httpGet": { + "$id": "#/properties/readinessprobe/httpget", + "type": "object", + "title": "Http of Readiness Probe", + "properties": { + "path": { + "$id": "#/properties/readinessprobe/httpget/path", + "type": "string", + "title": "The Path of Http Readiness Probe", + "default": "/health", + "examples": [ + "/health" + ] + }, + "port": { + "$id": "#/properties/readinessprobe/httpget/port", + "type": "integer", + "title": "The Port of Http Readiness Probe", + "default": 80, + "examples": [ + 80 + ] + } + }, + "required": [ + "path", + "port" + ] + }, + "initialDelaySeconds": { + "$id": "#/properties/readinessprobe/initialdelayseconds", + "type": "integer", + "title": "Initial Delay of Readiness Probe", + "default": 5, + "examples": [ + 5 + ] + }, + "periodSeconds": { + "$id": "#/properties/readinessprobe/periodseconds", + "type": "integer", + "title": "Period of Readiness Probe", + "default": 15, + "examples": [ + 15 + ] + } + }, + "oneOf": [ + { + "$id": "#/properties/readinessprobe/oneof/exec", + "required": [ + "exec", + "initialDelaySeconds", + "periodSeconds" + ] + }, + { + "$id": "#/properties/readinessprobe/oneof/httpget", + "required": [ + "httpGet", + "initialDelaySeconds", + "periodSeconds" + ] + } + ] + }, + "messaging": { + "type": "object", + "$id": "#/properties/messaging", + "title": "The Messaging Schema", + "properties": { + "ports": { + "$id": "#/properties/messaging/ports", + "type": "array", + "title": "The Ports for Messaging", + "items": { + "$id": "#/properties/messaging/ports/items", + "type": "object", + "title": "The Item of Port", + "required": [ + "name", + "container", + "port" + ], + "dependencies": { + "txMessages": [ + "rxMessages", + "policies" + ], + "rxMessages": [ + "txMessages", + "policies" + ], + "policies": [ + "rxMessages", + "txMessages" + ] + }, + "properties": { + "name": { + "$id": "#/properties/messaging/ports/items/name", + "type": "string", + "title": "The Name of the Port", + "default": "App", + "examples": [ + "App" + ] + }, + "container": { + "$id": "#/properties/messaging/ports/items/container", + "type": "string", + "title": "The Container of the Port", + "default": "xapp", + "examples": [ + "xapp" + ] + }, + "port": { + "$id": "#/properties/messaging/ports/items/port", + "type": "integer", + "title": "The Port Number", + "default": 8080, + "examples": [ + 8080 + ] + }, + "description": { + "$id": "#/properties/messaging/ports/items/description", + "type": "string", + "title": "The description for the port", + "default": "port description", + "examples": [ + "port description" + ] + }, + "txMessages": { + "$id": "#/properties/messaging/ports/items/txmessages", + "type": "array", + "title": "The txMessage Types", + "items": { + "$id": "#/properties/messaging/ports/items//txmessages/item", + "type": "string", + "title": "The txMessage Types Item", + "default": "RIC_SUB", + "examples": [ + "RIC_SUB" + ] + } + }, + "rxMessages": { + "$id": "#/properties/messaging/ports/items/rxmessages", + "type": "array", + "title": "The rxMessage Types", + "items": { + "$id": "#/properties/messaging/ports/items/rxmessages/item", + "type": "string", + "title": "The rxMessage Types Item", + "default": "RIC_SUB", + "examples": [ + "RIC_SUB" + ] + } + }, + "policies": { + "$id": "#/properties/messaging/ports/items/policies", + "type": "array", + "title": "The Policies Types", + "items": { + "$id": "#/properties/messaging/ports/items/policies/item", + "type": "integer", + "title": "The Policy Types Item", + "default": 1, + "examples": [ + 1 + ] + } + } + } + } + } + }, + "required": [ + "ports" + ] + }, + "metrics": { + "type": "object", + "$id": "#/properties/metrics", + "title": "The Metrics Schema", + "items": { + "$id": "#/properties/metrics/items", + "type": "object", + "title": "The Metrics Items Schema", + "required": [ + "objectName", + "objectInstance", + "name", + "type", + "description" + ], + "properties": { + "objectName": { + "$id": "#/properties/metrics/items/objectname", + "type": "string", + "title": "The Object Name" + }, + "objectInstance": { + "$id": "#/properties/metrics/items/objectinstance", + "type": "string", + "title": "The Object Instance" + }, + "name": { + "$id": "#/properties/metrics/items/name", + "type": "string", + "title": "The Object Name" + }, + "type": { + "$id": "#/properties/metrics/items/type", + "type": "string", + "title": "The Object Type" + }, + "description": { + "$id": "#/properties/metrics/items/description", + "type": "string", + "title": "The Object Description" + } + } + } + }, + "controls": { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "#/controls", + "type": "object", + "title": "Controls Section Schema", + "required": [], + "properties": {} + } + } + } \ No newline at end of file diff --git a/charts/templates/appenv.yaml b/charts/templates/appenv.yaml new file mode 100644 index 0000000..3ab0003 --- /dev/null +++ b/charts/templates/appenv.yaml @@ -0,0 +1,32 @@ +################################################################################ +# Copyright (c) 2019 AT&T Intellectual Property. # +# # +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # +# # +# 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. # +################################################################################ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "ricxapp.configmapname" . }}-appenv +data: + {{- if .Values.appenv }} + {{- toYaml .Values.appenv | nindent 2 }} + {{- end }} + RMR_RTG_SVC: "4561" + RMR_SRC_ID: {{ include "ricxapp.servicename.rmr" . }}.{{ include "ricxapp.namespace" . }} + XAPP_DESCRIPTOR_PATH: {{ .Values.appconfig.path }} + DBAAS_SERVICE_HOST: "{{ .Values.ricplt.dbaasService }}" + DBAAS_SERVICE_PORT: "6379" + DBAAS_PORT_6379_TCP_ADDR: "{{ .Values.ricplt.dbaasService }}" + DBAAS_PORT_6379_TCP_PORT: "6379" + SERVICE_METRICSDB_HOST: "{{ .Values.ricplt.metricsdbService }}" + SERVICE_METRICSDB_PORT: "8086" diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml new file mode 100644 index 0000000..0a622f3 --- /dev/null +++ b/charts/templates/deployment.yaml @@ -0,0 +1,121 @@ +################################################################################ +# Copyright (c) 2019 AT&T Intellectual Property. # +# # +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # +# # +# 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. # +################################################################################' +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "ricxapp.fullname" . }} + labels: + app: {{ include "ricxapp.namespace" . }}-{{ include "ricxapp.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ include "ricxapp.namespace" . }}-{{ include "ricxapp.name" . }} + release: {{ .Release.Name }} + template: + metadata: + {{- if .Values.annotations }} + annotations: + {{- range $key, $value := .Values.annotations }} + {{- if kindIs "string" $value }} + {{ $key }}: {{ $value | toPrettyJson }} + {{- else }} + {{ $key }}: | +{{- $value | toPrettyJson | nindent 10 }} + {{- end }} + {{- end -}} + {{ end }} + labels: + app: {{ include "ricxapp.namespace" . }}-{{ include "ricxapp.name" . }} + kubernetes_name: {{ include "ricxapp.namespace" . }}_{{ include "ricxapp.name" . }} + release: {{ .Release.Name }} + spec: + hostname: {{ include "ricxapp.name" . }} + imagePullSecrets: +{{- range .Values.containers }} + - name: {{ .image.registry | replace "." "-" | replace ":" "-" | replace "/" "-" | trunc 63 | trimSuffix "-" }} +{{- end }} + volumes: + - name: config-volume + configMap: + name: {{ include "ricxapp.configmapname" . }}-appconfig + {{- if .Values.controls }} + {{- if .Values.controls.fileStorage }} + - name: diskwriter + hostPath: + path: {{ .Values.storage.mountPath }} + type: DirectoryOrCreate + {{- end }} + {{- end }} + containers: +{{- $containers := .Values.containers }} +{{- $ports := .Values.messaging.ports }} +{{- range $container := $containers }} + {{- $portlist := list }} + {{- range $port := $ports }} + {{- if eq $port.container $container.name }} + {{- $portlist = append $portlist $port }} + {{- end }} + {{- end }} + - name: {{ $container.name }} + image: "{{ $container.image.registry }}/{{ $container.image.name }}:{{ $container.image.tag }}" + {{- if $container.command }} + command: [ + {{- range $command := $container.command -}} + {{- $command | quote -}} + {{- if ne $command (last $container.command) }} + {{- print ", " -}} + {{- end -}} + {{- end -}} + {{- print "]" -}} + {{- end}} + {{- if $container.args }} + args: [ + {{- range $arg := $container.args -}} + {{- $arg | quote -}} + {{- if ne $arg (last $container.args) }} + {{- print ", " -}} + {{- end -}} + {{- end -}} + {{- print "]" -}} + {{- end}} + imagePullPolicy: {{ $.Values.image_pull_policy }} + {{- if $portlist }} + ports: + {{- range $port := $portlist }} + - name: {{ $port.name }} + containerPort: {{ $port.port }} + protocol: TCP + {{- end }} + {{- end }} +{{- end }} + volumeMounts: + - name: config-volume + mountPath: {{ .Values.appconfig.path }} + {{- if .Values.controls }} + {{- if .Values.controls.fileStorage }} + - name: diskwriter + mountPath: {{ .Values.storage.mountPath }} + {{- end }} + {{- end }} + envFrom: + - configMapRef: + name: {{ include "ricxapp.configmapname" . }}-appenv + - configMapRef: + name: dbaas-appconfig diff --git a/charts/templates/service-http.yaml b/charts/templates/service-http.yaml new file mode 100644 index 0000000..32b7e58 --- /dev/null +++ b/charts/templates/service-http.yaml @@ -0,0 +1,43 @@ +################################################################################ +# Copyright (c) 2019 AT&T Intellectual Property. # +# Copyright (c) 2019 Nokia. # +# # +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # +# # +# 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. # +################################################################################ +{{- $topCtx := . }} +{{- range .Values.messaging.ports }} +{{- if contains "rmr" .name }} +{{ else }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ricxapp.servicename" $topCtx }}-{{ .name }} + namespace: {{ include "ricxapp.namespace" $topCtx }} + labels: + app: {{ include "ricxapp.namespace" $topCtx }}-{{ include "ricxapp.name" $topCtx }} + chart: {{ $topCtx.Chart.Name }}-{{ $topCtx.Chart.Version | replace "+" "_" }} + release: {{ $topCtx.Release.Name }} + heritage: {{ $topCtx.Release.Service }} +spec: + type: ClusterIP + ports: + - port: {{ .port }} + targetPort: {{ .name }} + protocol: TCP + name: {{ .name }} + selector: + app: {{ include "ricxapp.namespace" $topCtx }}-{{ include "ricxapp.name" $topCtx }} + release: {{ $topCtx.Release.Name }} +{{- end }} +{{- end }} diff --git a/charts/templates/service-rmr.yaml b/charts/templates/service-rmr.yaml new file mode 100644 index 0000000..8c7b656 --- /dev/null +++ b/charts/templates/service-rmr.yaml @@ -0,0 +1,50 @@ +################################################################################ +# Copyright (c) 2019 AT&T Intellectual Property. # +# Copyright (c) 2019 Nokia. # +# # +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # +# # +# 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. # +################################################################################ + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ricxapp.servicename.rmr" . }} + namespace: {{ include "ricxapp.namespace" . }} + labels: + app: {{ include "ricxapp.namespace" . }}-{{ include "ricxapp.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: ClusterIP + ports: + {{- range .Values.messaging.ports }} + {{- if eq .name "rmrdata" }} + - port: {{ .port }} + targetPort: {{ .name }} + protocol: TCP + name: {{ .name }} + {{- end }} + {{- end }} + {{- range .Values.messaging.ports }} + {{- if contains "rmr" .name }} {{- if ne .name "rmrdata" }} + - port: {{ .port }} + targetPort: {{ .name }} + protocol: TCP + name: {{ .name }} + {{- end }} + {{- end }} + {{- end }} + selector: + app: {{ include "ricxapp.namespace" . }}-{{ include "ricxapp.name" . }} + release: {{ .Release.Name }} diff --git a/charts/values.yaml b/charts/values.yaml new file mode 100644 index 0000000..4095981 --- /dev/null +++ b/charts/values.yaml @@ -0,0 +1,118 @@ +################################################################################ +# Copyright (c) 2019 AT&T Intellectual Property. # +# # +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # +# # +# 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. # +################################################################################' +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +ricplt: +# This section is reserved for values imported from RIC Platform charts + dbaasService: "service-ricplt-dbaas-tcp.ricplt.svc.cluster.local" + metricsdbService: "service-metricsdb.ricplt.svc.cluster.local" + pltIngressUrl: "ricplt-entry" + xappIngressUrl: "ricxapp-entry" + + appmgrRMRService: "service-ricplt-appmgr-rmr.ricplt.svc.cluster.local" + e2mgrRMRService: "service-ricplt-e2mgr-rmr.ricplt.svc.cluster.local" + e2termRMRService: "service-ricplt-e2term-rmr.ricplt.svc.cluster.local" + rtmgrRMRService: "service-ricplt-rtmgr-rmr.ricplt.svc.cluster.local" + a1mediatorRMRService: "service-ricplt-a1mediator-rmr.ricplt.svc.cluster.local" + +# Location of the xApp config files injected from the xApp descriptor +appconfig: + path: /opt/ric/config + +# Location of volume based on HostPath of type Directory +storage: + mountPath: /home/kpm/storage + +# Number of replica +replicaCount: 1 + +# Image pulling policy +image_pull_policy: IfNotPresent + +# Environment variables that will be injected +appenv: {} + +# Liveness probe definition. If empty, liveness probe will be disabled +livenessProbe: {} + +# Readiness probe definition. If empty, readiness probe will be disabled +readinessProbe: {} + +# Instance name. If empty, chart name will be used +name: {} + +# Full instance name. If empty, full name will be constructed from name +fullname: {} + +############### The following are from the xApp descriptor ############### +containers: +- image: + name: xapp/mobiflow-auditor + registry: 5gsec.se-ran.org:10004 + tag: 0.0.1 + name: mobiflow-auditor +controls: + fileStrorage: false +db: + waitForSdl: false +messaging: + ports: + - container: mobiflow-auditor + description: http service + name: http + port: 8080 + - container: mobiflow-auditor + description: rmr route port for mobiflow-auditor xapp + name: rmrroute + port: 4561 + - container: mobiflow-auditor + description: rmr data port for mobiflow-auditor + mtypes: + - id: 55555 + name: TESTNAME1 + - id: 55556 + name: TESTNAME2 + name: rmrdata + policies: + - 1 + port: 4560 + rxMessages: + - RIC_SUB_RESP + - RIC_SUB_FAILURE + - RIC_SUB_DEL_RESP + - RIC_INDICATION + txMessages: + - RIC_SUB_REQ + - RIC_SUB_DEL_REQ + - RIC_SGNB_ADDITION_REQ + - RIC_SGNB_ADDITION_ACK +name: mobiflow-auditor +rmr: + maxSize: 2072 + numWorkers: 1 + policies: + - 1 + protPort: tcp:4560 + rxMessages: + - RIC_SUB_RESP + - A1_POLICY_REQ + - RIC_HEALTH_CHECK_REQ + txMessages: + - RIC_SUB_REQ + - A1_POLICY_RESP + - A1_POLICY_QUERY + - RIC_HEALTH_CHECK_RESP +version: 0.0.1 diff --git a/clear_docker.sh b/clear_docker.sh deleted file mode 100755 index 1a3ea86..0000000 --- a/clear_docker.sh +++ /dev/null @@ -1,3 +0,0 @@ -sudo docker container prune -#docker rmi $(docker images -q -f "dangling=true" -f "label=autodelete=true") -sudo docker rmi $(docker images -f "dangling=true" -q) diff --git a/container-tag.yaml b/container-tag.yaml new file mode 100644 index 0000000..5721e22 --- /dev/null +++ b/container-tag.yaml @@ -0,0 +1,5 @@ +# The Jenkins job requires a tag to build the Docker image. +# Global-JJB script assumes this file is in the repo root. +--- +tag: 0.0.1 + diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..c9a79b8 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,2 @@ +#!/bin/bash +sudo -E dms_cli install mobiflow-auditor 0.0.1 ricxapp diff --git a/docs/_static/logo.png b/docs/_static/logo.png new file mode 100644 index 0000000..c3b6ce5 Binary files /dev/null and b/docs/_static/logo.png differ diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..07032ac --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,7 @@ +from docs_conf.conf import * + +linkcheck_ignore = [ + 'http://localhost.*', + 'http://127.0.0.1.*', + 'https://gerrit.o-ran-sc.org.*' +] diff --git a/docs/conf.yaml b/docs/conf.yaml new file mode 100644 index 0000000..067f59b --- /dev/null +++ b/docs/conf.yaml @@ -0,0 +1,3 @@ +--- +project_cfg: oran +project: ric-app-hw-py diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000..00b0fd0 Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..47d7f62 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,20 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. SPDX-License-Identifier: CC-BY-4.0 +.. Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved.Copyright (C) 2020 + + +Welcome to O-RAN SC HelloWorld xAPP Documentation +======================================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + overview.rst + release-notes.rst + installation-guide.rst + user-guide.rst + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/installation-guide.rst b/docs/installation-guide.rst new file mode 100644 index 0000000..e71bea8 --- /dev/null +++ b/docs/installation-guide.rst @@ -0,0 +1,69 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. SPDX-License-Identifier: CC-BY-4.0 +.. Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. + + +Installation Guide +================== + +.. contents:: + :depth: 3 + :local: + +Abstract +-------- + +This document describes how to install the HelloWorld (HW) Python xAPP. + +Version history + ++--------------------+--------------------+--------------------+--------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| - |- |Rahul Banerji | - | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ + + +Introduction +------------ + +This document provides guidelines on how to install and configure the HW Python xAPP in various environments/operating modes. +The audience of this document is assumed to have good knowledge in RIC Platform. + + +Preface +------- +This xAPP can be run directly as a Linux binary, as a docker image, or in a pod in a Kubernetes environment. The first +two can be used for dev testing. The last option is how an xAPP is deployed in the RAN Intelligent Controller environment. +This document covers all three methods. + + + + +Software Installation and Deployment +------------------------------------ +The build process assumes a Linux environment with python >= 3.8 and has been tested on Ubuntu. For building docker images, +the Docker environment must be present in the system. + + +Build Process +~~~~~~~~~~~~~ +The HW xAPP can be either tested as a Linux binary or as a docker image. + 1. **Linux binary**: + TBD + + 2. **Docker Image**: From the root of the repository, run *docker --no-cache build -t ./* . + + +Deployment +~~~~~~~~~~ +End to end deployment of `hw-python` can be referred at : + + :ref: `Deployment Guide`. + +Testing +-------- + +Unit tests TBD diff --git a/docs/onboard-and-deploy.rst b/docs/onboard-and-deploy.rst new file mode 100644 index 0000000..0308a48 --- /dev/null +++ b/docs/onboard-and-deploy.rst @@ -0,0 +1,222 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. SPDX-License-Identifier: CC-BY-4.0 +.. Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved.Copyright (C) 2021 + +.. _Deployment Guide: + +### Onboarding of hw-python using dms_cli tool + +`dms_cli` offers rich set of command line utility to onboard `hw-python` xapp +to `chartmuseme`. + +First checkout the [hw-python](https://gerrit.o-ran-sc.org/r/ric-app/hw-python) repository from gerrit. + +.. code-block:: bash + git clone "https://gerrit.o-ran-sc.org/r/ric-app/hw-python" + +`hw-python` has following folder structure +.. code-block:: bash + +---docs + | + +---hw_python.egg-info + | + +---init + | config-file.json # descriptor for xapp deployment. + | init_script.py + | test_route.rt + | schema.json #schema for validating the config-file.json + | + +---releases + | + +---resources + | + +---src + +For onboarding `hw-python` make sure that `dms_cli` and helm3 is installed. One can follow [documentation](https://docs.o-ran-sc.org/projects/o-ran-sc-it-dep/en/latest/installation-guides.html#ric-applications) to +configure `dms_cli`. + +Once `dms_cli` is availabe we can proceed to onboarding proceure. + +configure the `export CHART_REPO_URL` to point `chartmuseme`. +.. code-block:: bash + $export CHART_REPO_URL=http://:8080 + + +check if `dms_cli` working fine. +.. code-block:: bash + $ dms_cli health + True + + +Now move to `init` folder to initiate onboarding. + +.. code-block:: bash + $ cd init + + $ dms_cli onboard --config_file_path=config-file.json --shcema_file_path=schema.json + httpGet: + path: '{{ index .Values "readinessProbe" "httpGet" "path" | toJson }}' + port: '{{ index .Values "readinessProbe" "httpGet" "port" | toJson }}' + initialDelaySeconds: '{{ index .Values "readinessProbe" "initialDelaySeconds" | toJson }}' + periodSeconds: '{{ index .Values "readinessProbe" "periodSeconds" | toJson }}' + + httpGet: + path: '{{ index .Values "livenessProbe" "httpGet" "path" | toJson }}' + port: '{{ index .Values "livenessProbe" "httpGet" "port" | toJson }}' + initialDelaySeconds: '{{ index .Values "livenessProbe" "initialDelaySeconds" | toJson }}' + periodSeconds: '{{ index .Values "livenessProbe" "periodSeconds" | toJson }}' + + { + "status": "Created" + } + + +Check if `hw-python` is onborded +.. code-block:: bash + $ curl --location --request GET "http://:32080/onboard/api/v1/charts" --header 'Content-Type: application/json' + { + "hw-python": [ + { + "name": "hw-python", + "version": "1.0.0", + "description": "Standard xApp Helm Chart", + "apiVersion": "v1", + "appVersion": "1.0", + "urls": [ + "charts/hw-python-1.0.0.tgz" + ], + "created": "2021-07-05T15:07:34.518377486Z", + "digest": "e9db874d35154643a2c6f26dd52929c9dcf143f165683c03d07518bb0c2d768d" + } + ], + "hw-python": [ + { + "name": "hw-python", + "version": "1.0.0", + "description": "Standard xApp Helm Chart", + "apiVersion": "v1", + "appVersion": "1.0", + "urls": [ + "charts/hw-python-1.0.0.tgz" + ], + "created": "2021-07-05T15:20:13.965653743Z", + "digest": "975b1da1f8669e8ed1b1e5be809e7cf4841ef33abcb88207bc3a735e9b543a9a" + } + ] + } + + +If we would wish to download the charts then we can perform following curl operation : + +.. code-block:: bash +curl --location --request GET "http://:32080/onboard/api/v1/charts/xapp/hw-python/ver/1.0.0" --header 'Content-Type: application/json' --output hw-python.tgz + +The downloaded folder has the deployment files for hw-python +.. code-block:: bash + tar -xvzf hw-python.tgz + hw-python/Chart.yaml + hw-python/values.yaml + hw-python/templates/_helpers.tpl + hw-python/templates/appconfig.yaml + hw-python/templates/appenv.yaml + hw-python/templates/deployment.yaml + hw-python/templates/service-http.yaml + hw-python/templates/service-rmr.yaml + hw-python/config/config-file.json + hw-python/descriptors/schema.json + +``` + +Now the onboarding is done. + +### Deployment of hw-python + +Once charts are available we can deploy the the `hw-python` using following curl command : + +.. code-block:: bash + $ curl --location --request POST "http://:32080/appmgr/ric/v1/xapps" --header 'Content-Type: application/json' --data-raw '{"xappName": "hw-python", "helmVersion": "1.0.0"}' + {"instances":null,"name":"hw-python","status":"deployed","version":"1.0"} + + +Deployment will be done in `ricxapp` ns : + +``` +# kubectl get pods -n ricxapp +NAME READY STATUS RESTARTS AGE +ricxapp-hw-python-64b5447dcc-mbt5w 1/1 Running 0 5m45s + + CLUSTER-IP EXTERNAL-IP PORT(S) AGE +aux-entry ClusterIP 10.111.35.76 80/TCP,443/TCP 9d +service-ricxapp-hw-python-http ClusterIP 10.104.223.245 8080/TCP 6m23s +service-ricxapp-hw-python-rmr ClusterIP 10.103.243.21 4560/TCP,4561/TCP 6m23s + +``` + +Now we can query to appmgr to get list of all the deployed xapps : + +``` +# curl http://service-ricplt-appmgr-http.ricplt:8080/ric/v1/xapps | jq . + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 347 100 347 0 0 578 0 --:--:-- --:--:-- --:--:-- 579 +[ + { + "instances": [ + { + "ip": "service-ricxapp-hw-python-rmr.ricxapp", + "name": "hw-python-55ff7549df-kpj6k", + "policies": [ + 1 + ], + "port": 4560, + "rxMessages": [ + "RIC_SUB_RESP", + "A1_POLICY_REQ", + "RIC_HEALTH_CHECK_REQ" + ], + "status": "running", + "txMessages": [ + "RIC_SUB_REQ", + "A1_POLICY_RESP", + "A1_POLICY_QUERY", + "RIC_HEALTH_CHECK_RESP" + ] + } + ], + "name": "hw-python", + "status": "deployed", + "version": "1.0" + } +] + +``` +Logs from `hw-python` : + +``` +# kubectl logs ricxapp-hw-python-55ff7549df-kpj6k -n ricxapp +{"ts":1624562552123,"crit":"INFO","id":"hw-app","mdc":{"time":"2021-06-24T19:22:32"},"msg":"Using config file: config/config-file.json"} +{"ts":1624562552124,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"Serving metrics on: url=/ric/v1/metrics namespace=ricxapp"} +{"ts":1624562552133,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"Register new counter with opts: {ricxapp SDL Stored The total number of stored SDL transactions map[]}"} +{"ts":1624562552133,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"Register new counter with opts: {ricxapp SDL StoreError The total number of SDL store errors map[]}"} +1624562552 6/RMR [INFO] ric message routing library on SI95 p=0 mv=3 flg=00 (fd4477a 4.5.2 built: Jan 21 2021) +{"ts":1624562552140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"new rmrClient with parameters: ProtPort=0 MaxSize=0 ThreadType=0 StatDesc=RMR LowLatency=false FastAck=false Policies=[]"} +{"ts":1624562552140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"Register new counter with opts: {ricxapp RMR Transmitted The total number of transmited RMR messages map[]}"} +{"ts":1624562552140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"Register new counter with opts: {ricxapp RMR Received The total number of received RMR messages map[]}"} +{"ts":1624562552140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"Register new counter with opts: {ricxapp RMR TransmitError The total number of RMR transmission errors map[]}"} +{"ts":1624562552140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"Register new counter with opts: {ricxapp RMR ReceiveError The total number of RMR receive errors map[]}"} +{"ts":1624562552140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"Xapp started, listening on: :8080"} +{"ts":1624562552140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:32"},"msg":"rmrClient: Waiting for RMR to be ready ..."} +{"ts":1624562553140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:33"},"msg":"rmrClient: RMR is ready after 1 seconds waiting..."} +{"ts":1624562553141,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:33"},"msg":"xApp ready call back received"} +1624562553 6/RMR [INFO] sends: ts=1624562553 src=service-ricxapp-hw-python-rmr.ricxapp:0 target=localhost:4591 open=0 succ=0 fail=0 (hard=0 soft=0) +1624562553 6/RMR [INFO] sends: ts=1624562553 src=service-ricxapp-hw-python-rmr.ricxapp:0 target=localhost:4560 open=0 succ=0 fail=0 (hard=0 soft=0) +1624562553 6/RMR [INFO] sends: ts=1624562553 src=service-ricxapp-hw-python-rmr.ricxapp:0 target=service-ricplt-a1mediator-rmr.ricplt:4562 open=0 succ=0 fail=0 (hard=0 soft=0) +RMR is ready now ... +{"ts":1624562557140,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:37"},"msg":"Application='hw-python' is not ready yet, waiting ..."} +{"ts":1624562562141,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:42"},"msg":"Application='hw-python' is not ready yet, waiting ..."} +{"ts":1624562567141,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:47"},"msg":"Application='hw-python' is not ready yet, waiting ..."} +{"ts":1624562567370,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:47"},"msg":"restapi: method=GET url=/ric/v1/health/ready"} +{"ts":1624562569766,"crit":"INFO","id":"hw-app","mdc":{"CONTAINER_NAME":"","HOST_NAME":"","HWApp":"0.0.1","PID":"6","POD_NAME":"","SERVICE_NAME":"","SYSTEM_NAME":"","time":"2021-06-24T19:22:49"},"msg":"restapi: method=GET url=/ric/v1/health/alive"} +``` + +Here we are done with the onboaring and deployment of `hw-python`. \ No newline at end of file diff --git a/docs/overview.rst b/docs/overview.rst new file mode 100644 index 0000000..be50c76 --- /dev/null +++ b/docs/overview.rst @@ -0,0 +1,27 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. SPDX-License-Identifier: CC-BY-4.0 +.. Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved.Copyright (C) 2020 + + + + +HelloWorld Python xAPP Overview +================================ + +This repository contains open-source code for a prototype python xAPP for near real-time +RAN Intelligent Controller which makes use of python Xapp Framework. +This xAPP aims to provide basic implementation of : + +-E2, A1 interfaces interactions. + +-Read-write operations into a persistent storage. + +-xAPP Configuration management + +-RMR Health Check + +-xAPP Health Check + +-Raising alarms + +-Generating metrics diff --git a/docs/release-notes.rst b/docs/release-notes.rst new file mode 100644 index 0000000..1c2085b --- /dev/null +++ b/docs/release-notes.rst @@ -0,0 +1,79 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. SPDX-License-Identifier: CC-BY-4.0 +.. Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved.Copyright (C) 2020 + + +Release Notes +============= + + +This document provides the release notes for the Bronze Release of the HelloWorld (HW) xAPP. + +.. contents:: + :depth: 3 + :local: + + +Version history +--------------- + ++--------------------+--------------------+--------------------+--------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| 2021-06-23 | 1.0.0 | Naman Gupta | Dawn Release | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ + + + +Summary +------- + +The Dawn release of the go HW xAPP demonstrates the following: + +- A1 interfaces interactions. + +- -xAPP Health Check + +- SDL Handler + +- Alarm Management + + +Release Data +------------ + ++--------------------------------------+--------------------------------------+ +| **Project** | RAN Intelligent Controller | +| | | ++--------------------------------------+--------------------------------------+ +| **Repo/commit-ID** | TBD | +| | | ++--------------------------------------+--------------------------------------+ +| **Release designation** | TBD | +| | | ++--------------------------------------+--------------------------------------+ +| **Release date** | TBD | +| | | ++--------------------------------------+--------------------------------------+ +| **Purpose of the delivery** | open-source Python Hello World xAPP | +| | | +| | | ++--------------------------------------+--------------------------------------+ + +Components +---------- + +TBD + + + + +Limitations +----------- +- The HW xAPP doesn't have any usecase in particular to display SDL capabilities. + +- The subscription process assumes, on sending subscription request results in valid subscription response. + +- The HW xAPP doesn't address any RIC usecase in particular. diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt new file mode 100644 index 0000000..09a0c1c --- /dev/null +++ b/docs/requirements-docs.txt @@ -0,0 +1,5 @@ +sphinx +sphinx-rtd-theme +sphinxcontrib-httpdomain +recommonmark +lfdocs-conf diff --git a/docs/user-guide.rst b/docs/user-guide.rst new file mode 100644 index 0000000..19a751d --- /dev/null +++ b/docs/user-guide.rst @@ -0,0 +1,28 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. SPDX-License-Identifier: CC-BY-4.0 +.. Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved.Copyright (C) 2020 + +============================================================================================ +HelloWorld xAPP (Python) +============================================================================================ +-------------------------------------------------------------------------------------------- +User's Guide +-------------------------------------------------------------------------------------------- + +Introduction +============================================================================================ + +The RIC platform provides set of functions as part of xAPP Python Framework that the xAPPs can use to accomplish their tasks. +This xAPP is envisioned to provide python xAPP developers, examples of implementing these sets of functions. +Note, HW xAPP does not address/implement any RIC Usecases. + +HelloWorld xAPP Features +============================================================================================ + +RIC Platform provides many Frameworks, APIs and libraries to aid the development of xAPPs. All xAPPs will have some custom +processing functional logic core to the xApp and some additional non-functional platform related processing using +these APIs and libraries. This xAPP attempts to show the usage of such additional platform processing using xapp RIC framework APIs and libraries. + + +The Hello World xApp demonstrates how a python based xApp uses the A1, and E2 interfaces and persistent database read-write operations. + diff --git a/helm-charts/mobiflow-auditor/Chart.yaml b/helm-charts/mobiflow-auditor/Chart.yaml deleted file mode 100644 index 3a298b3..0000000 --- a/helm-charts/mobiflow-auditor/Chart.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2020-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -apiVersion: v2 -name: mobiflow-auditor -description: SecSM python xApp -kubeVersion: ">=1.17.0" -type: application -version: 1.0.0 -appVersion: v1.0.0 -keywords: - - onos - - sdn - - ric - - mobiflow - - SE-RAN -home: https://5gsec.com -maintainers: - - name: SE-RAN Team - email: team@5gsec.com - diff --git a/helm-charts/mobiflow-auditor/files/certs/tls.cacrt b/helm-charts/mobiflow-auditor/files/certs/tls.cacrt deleted file mode 100644 index 879bc06..0000000 --- a/helm-charts/mobiflow-auditor/files/certs/tls.cacrt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDYDCCAkgCCQDe99fSN9qxSTANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJV -UzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCU1lbmxvUGFyazEMMAoGA1UECgwDT05G -MRQwEgYDVQQLDAtFbmdpbmVlcmluZzEeMBwGA1UEAwwVY2Eub3Blbm5ldHdvcmtp -bmcub3JnMB4XDTE5MDQxMTA5MDYxM1oXDTI5MDQwODA5MDYxM1owcjELMAkGA1UE -BhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlNZW5sb1BhcmsxDDAKBgNVBAoM -A09ORjEUMBIGA1UECwwLRW5naW5lZXJpbmcxHjAcBgNVBAMMFWNhLm9wZW5uZXR3 -b3JraW5nLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEg7CZR -X8Y+syKHaQCh6mNIL1D065trwX8RnuKM2kBwSu034zefQAPloWugSoJgJnf5fe0j -nUD8gN3Sm8XRhCkvf67pzfabgw4n8eJmHScyL/ugyExB6Kahwzn37bt3oT3gSqhr -6PUznWJ8fvfVuCHZZkv/HPRp4eyAcGzbJ4TuB0go4s6VE0WU5OCxCSlAiK3lvpVr -3DOLdYLVoCa5q8Ctl3wXDrfTLw5/Bpfrg9fF9ED2/YKIdV8KZ2ki/gwEOQqWcKp8 -0LkTlfOWsdGjp4opPuPT7njMBGXMJzJ8/J1e1aJvIsoB7n8XrfvkNiWL5U3fM4N7 -UZN9jfcl7ULmm7cCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIh6FjkQuTfXddmZY -FYpoTen/VD5iu2Xxc1TexwmKeH+YtaKp1Zk8PTgbCtMEwEiyslfeHTMtODfnpUIk -DwvtB4W0PAnreRsqh9MBzdU6YZmzGyZ92vSUB3yukkHaYzyjeKM0AwgVl9yRNEZw -Y/OM070hJXXzJh3eJpLl9dlUbMKzaoAh2bZx6y3ZJIZFs/zrpGfg4lvBAvfO/59i -mxJ9bQBSN3U2Hwp6ioOQzP0LpllfXtx9N5LanWpB0cu/HN9vAgtp3kRTBZD0M1XI -Ctit8bXV7Mz+1iGqoyUhfCYcCSjuWTgAxzir+hrdn7uO67Hv4ndCoSj4SQaGka3W -eEfVeA== ------END CERTIFICATE----- diff --git a/helm-charts/mobiflow-auditor/files/certs/tls.crt b/helm-charts/mobiflow-auditor/files/certs/tls.crt deleted file mode 100644 index fa38fa1..0000000 --- a/helm-charts/mobiflow-auditor/files/certs/tls.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDdDCCAlwCFBveh0tyYQtXayQPaXgW+5dLuK8DMA0GCSqGSIb3DQEBCwUAMHIx -CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJTWVubG9QYXJrMQww -CgYDVQQKDANPTkYxFDASBgNVBAsMC0VuZ2luZWVyaW5nMR4wHAYDVQQDDBVjYS5v -cGVubmV0d29ya2luZy5vcmcwHhcNMTkwNzE2MTkwNjU2WhcNMjkwNzEzMTkwNjU2 -WjB7MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCU1lbmxvUGFy -azEMMAoGA1UECgwDT05GMRQwEgYDVQQLDAtFbmdpbmVlcmluZzEnMCUGA1UEAwwe -b25vcy1jb25maWcub3Blbm5ldHdvcmtpbmcub3JnMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAs1ER/FG9COwYnyDOxxPIGJb8p9l59GLqYdnb3PH5EI6y -AfhpJDhp+Z/RN8v+/NhH6JPYquPxRKWmHhRXmwEHgYREfkqK+HikTv86H5Tq/Xzz -BqadHkopMz+Q66Wo4LoD3M+Vl/11gUU9s7SIWBmcJly9ETQN5nddeGa+v+Mg9QoS -W/8CK7Y1iCguu3h2FYju+OQ7HJ9hO5abs3XiuJJJN9hPJRr1O1UtgUzlVbj2QLcT -BDntIxPKUc3/M5IpzgLt2vgHW9VMO5vFOXsr+UyQd/lByVXTxQKS5aw0kwMUJSdN -vasV3FAz4usP9IBx5Pq3MwMQF1uO5zJ8Xo2ytlsI0wIDAQABMA0GCSqGSIb3DQEB -CwUAA4IBAQB9W8/ygG87QLR37GZfutYLi9hBJQHSppCxZB2SBP1yl/Js1hHM0paP -ywlbRbmuCv7LM3CiaXdJo9w7onYha/BtJIYG0YHZqnXLjk0uufP07CBISGKXnfp0 -y35YqruHjXtkoKdcqOGZsuz88iQ579PrvJpWCOon936FM7vBzKeBoNbBso52vt7b -FtKISoGAr8XwCrFYxgHOjYUP8A9d9ylaa3Hgfd7Xul2BKO3tPx/7oFxiaz40ATuQ -wN9OEsvNmP2TcrLEFKoUJBtyYV9gFCqQPuf7obwbTV5m1JW+1+NEsKNcWemdKU6x -N0u+jCC7D7so8pjqfZRouCW2dKVlIRjj ------END CERTIFICATE----- - diff --git a/helm-charts/mobiflow-auditor/files/certs/tls.key b/helm-charts/mobiflow-auditor/files/certs/tls.key deleted file mode 100644 index 77cac55..0000000 --- a/helm-charts/mobiflow-auditor/files/certs/tls.key +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzURH8Ub0I7Bif -IM7HE8gYlvyn2Xn0Yuph2dvc8fkQjrIB+GkkOGn5n9E3y/782Efok9iq4/FEpaYe -FFebAQeBhER+Sor4eKRO/zoflOr9fPMGpp0eSikzP5DrpajgugPcz5WX/XWBRT2z -tIhYGZwmXL0RNA3md114Zr6/4yD1ChJb/wIrtjWIKC67eHYViO745Dscn2E7lpuz -deK4kkk32E8lGvU7VS2BTOVVuPZAtxMEOe0jE8pRzf8zkinOAu3a+Adb1Uw7m8U5 -eyv5TJB3+UHJVdPFApLlrDSTAxQlJ029qxXcUDPi6w/0gHHk+rczAxAXW47nMnxe -jbK2WwjTAgMBAAECggEBALE57iHIk6Hu3wLfhR9DUN/k0r/+dMdywB6IGQKNcVHJ -BuxlnV08GJmsODcCevlhteiLv3hfI4Vqhv1xs4gygz2iAaQkfqfHbPXa0ZKHN53j -0bqAEAfw2jmZmN0edQPvNYQW8s4upuVmDu6+/oa8gRV1UTRgk2B2Fz65pcu3a0pi -yVeZzeAO/vD7WNP3uM7Yuv0AQTKKLvOqZmKLeDwPeC+2sbi03opYbIaEl+OJ94DX -9hCm1/vqojlzw+BHrxGS8bqXMnJnqxLep/gmbvL0Iy92SJgEBk6l8t7xzpBqSjDW -+DjDLqpda6r/Dy9b5hBZ0a/4V+fRJPz4S/k+qjp9DyECgYEA7KMQur7QZcSAWPPM -ZV5TM6+SDYn6TP9csbyB4IbOfeLdIkVKY+svVz2m7FynfQb4whtxERbMy0QjYEgc -us3p/QVjHBs/JZIdtIg5ff6qgOFAkuaCXEJKyH4wcEA091cr6HWslWCHzop2SD+T -dpvSgXJXpx1RYP/6X3sUf1mKAOMCgYEAwf1K4fOYZwyT44VPurxhmzgDWW2PKm6I -gCOZN9EN2Mvt0J5zKKoHw8ORHGN9zxLMyB11803Uwm7nceApM3FUacmLHP05Q1Vo -ukHVLoFQYwXLQzSWiZbtxcbMK3P1EU2Sg5+yfoRf6usmRIWDlr4+TIzT3a20nhV0 -olOj3xFtC1ECgYBHyeiHXuAGH6j4U24MyqLfKUJbzSIcPdQ3L4MPRJZcZnjDrtW0 -nmLMSq3bQvik23qYGI8iqhITEGbTDM16doGn+vxoSHPNyBgu7qzSZnH/i1Z1umyN -5KafUHkNdM0cxFtTuG9VGeXZaPQdUvw/nWItVyz0S9amYMHqOYKsZ9OQFQKBgQC6 -tfYq//CBzqNQ63DPxJ0VbpYars6LApQy8RB5nqb7MVyV3MvuKakLjOHQNxpSAkBD -o8dxBEpxUZlGC4DcWInsv+U7Y6aH3l7MVqy+9AvRIzR+XNE/YJs+Lpc4g4UuwEDy -mvSLvREs5GOStAbxQe5oc2tAA/7B4Ni/nE0vWHBh4QKBgDUukLjK4i+AqCm6Z4iu -/FcKtNC2+BLxZ2sjaRVn78aQ2DXWaKWVaKwSBJOtLghOyjAzvH8VDOtvp92rQgDk -ltnoTp09B9B80dNOVqIi113Inr89N/QDNVYctqPbzizbWHlf3ENwVklwAXhmOIbN -1l0Cge8ERKGcAIl9SsTl+5Se ------END PRIVATE KEY----- - diff --git a/helm-charts/mobiflow-auditor/files/configs/config.json b/helm-charts/mobiflow-auditor/files/configs/config.json deleted file mode 100644 index a097740..0000000 --- a/helm-charts/mobiflow-auditor/files/configs/config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "report_period": { - "interval": 1000, - "granularity": 1000 - } -} - diff --git a/helm-charts/mobiflow-auditor/files/configs/mobiflow.json b/helm-charts/mobiflow-auditor/files/configs/mobiflow.json deleted file mode 100644 index 2a18c23..0000000 --- a/helm-charts/mobiflow-auditor/files/configs/mobiflow.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "mobiflow": { - "sqlite3_db_path": "/etc/mobiflow/mobiflow.db", - "rpc_port": 50051, - "mongo_db_name": "mobiflow", - "mongo_db_port": 27017 - }, - - "pbest": { - "pbest_csv_file": "", - "pbest_exec_name": "./main", - "pbest_log_path": "/usr/local/pbest.out", - "maintenance_time_threshold": 5000 - } -} - diff --git a/helm-charts/mobiflow-auditor/files/configs/onos.json b/helm-charts/mobiflow-auditor/files/configs/onos.json deleted file mode 100644 index 9a4d456..0000000 --- a/helm-charts/mobiflow-auditor/files/configs/onos.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "e2_client": { - "app_id": "fb-kpimon-v2", - "e2t_endpoint": "onos-e2t:5150", - "ca_path": "/etc/onos/certs/tls.cacrt", - "cert_path": "/etc/onos/certs/tls.crt", - "key_path": "/etc/onos/certs/tls.key" - }, - "sdl_client": { - "topo_endpoint": "onos-topo:5150", - "ca_path": "/etc/onos/certs/tls.cacrt", - "cert_path": "/etc/onos/certs/tls.crt", - "key_path": "/etc/onos/certs/tls.key" - } -} - diff --git a/helm-charts/mobiflow-auditor/templates/_helpers.tpl b/helm-charts/mobiflow-auditor/templates/_helpers.tpl deleted file mode 100644 index 3c7be65..0000000 --- a/helm-charts/mobiflow-auditor/templates/_helpers.tpl +++ /dev/null @@ -1,57 +0,0 @@ -# SPDX-FileCopyrightText: 2022 2020-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "mobiflow-auditor.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "mobiflow-auditor.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "mobiflow-auditor.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "mobiflow-auditor.labels" -}} -helm.sh/chart: {{ include "mobiflow-auditor.chart" . }} -{{ include "mobiflow-auditor.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define "mobiflow-auditor.selectorLabels" -}} -app.kubernetes.io/name: {{ include "mobiflow-auditor.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - diff --git a/helm-charts/mobiflow-auditor/templates/configmap.yaml b/helm-charts/mobiflow-auditor/templates/configmap.yaml deleted file mode 100644 index f525ca2..0000000 --- a/helm-charts/mobiflow-auditor/templates/configmap.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: 2020-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "mobiflow-auditor.fullname" . }}-config - labels: - app: {{ template "mobiflow-auditor.fullname" . }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -data: - config.json: |- -{{ .Files.Get "files/configs/config.json" | indent 4}} - onos.json: |- -{{ .Files.Get "files/configs/onos.json" | indent 4}} - mobiflow.json: |- -{{ .Files.Get "files/configs/mobiflow.json" | indent 4}} - diff --git a/helm-charts/mobiflow-auditor/templates/deployment.yaml b/helm-charts/mobiflow-auditor/templates/deployment.yaml deleted file mode 100644 index c571236..0000000 --- a/helm-charts/mobiflow-auditor/templates/deployment.yaml +++ /dev/null @@ -1,155 +0,0 @@ -# SPDX-FileCopyrightText: 2020-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "mobiflow-auditor.fullname" . }} - labels: - {{- include "mobiflow-auditor.labels" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - name: {{ template "mobiflow-auditor.fullname" . }} - app: mobiflow-auditor - resource: {{ template "mobiflow-auditor.fullname" . }} - {{- include "mobiflow-auditor.selectorLabels" . | nindent 6 }} - template: - metadata: - annotations: - proxy.onosproject.org/inject: "true" - labels: - name: {{ template "mobiflow-auditor.fullname" . }} - app: mobiflow-auditor - resource: {{ template "mobiflow-auditor.fullname" . }} - {{- include "mobiflow-auditor.selectorLabels" . | nindent 8 }} - spec: - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - args: - - "secsm/main.py" - - "--path={{ .Values.config.servicePath }}/config.json" - - "--ric-config={{ .Values.config.configPath }}/onos.json" - - "--mobiflow-config={{ .Values.config.servicePath }}/mobiflow.json" - ports: - - name: http - containerPort: 8080 - startupProbe: - httpGet: - path: /status - port: 8080 - periodSeconds: 5 - failureThreshold: 60 - readinessProbe: - httpGet: - path: /status - port: 8080 - initialDelaySeconds: 10 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /status - port: 8080 - initialDelaySeconds: 10 - periodSeconds: 10 - volumeMounts: - - name: mobiflow - mountPath: {{ .Values.config.mobiflow.sqlite3_db_path }} - readOnly: false - - name: secret - mountPath: /etc/onos/certs - readOnly: true - - name: config - mountPath: {{ .Values.config.servicePath }} - readOnly: false - - name: onos - mountPath: {{ .Values.config.configPath }} - readOnly: false - - name: staticfiles - mountPath: /tmp - readOnly: false - resources: - {{- toYaml .Values.resources | nindent 12 }} - - name: "rpc-server" - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - args: - - "secsm/main_server.py" - - "--mobiflow-config={{ .Values.config.servicePath }}/mobiflow.json" - ports: - - name: http - containerPort: 8080 - - name: rpc - containerPort: {{ .Values.config.mobiflow.rpc_port }} - startupProbe: - httpGet: - path: /status - port: 8080 - periodSeconds: 5 - failureThreshold: 60 - readinessProbe: - httpGet: - path: /status - port: 8080 - initialDelaySeconds: 10 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /status - port: 8080 - initialDelaySeconds: 10 - periodSeconds: 10 - volumeMounts: - - name: mobiflow - mountPath: {{ .Values.config.mobiflow.sqlite3_db_path }} - readOnly: false - - name: secret - mountPath: /etc/onos/certs - readOnly: true - - name: config - mountPath: {{ .Values.config.servicePath }} - readOnly: false - - name: onos - mountPath: {{ .Values.config.configPath }} - readOnly: false - - name: staticfiles - mountPath: /tmp - readOnly: false - volumes: - - name: mobiflow - - name: secret - secret: - secretName: {{ template "mobiflow-auditor.fullname" . }}-secret - - name: config - configMap: - name: {{ template "mobiflow-auditor.fullname" . }}-config - - name: onos - configMap: - name: {{ template "mobiflow-auditor.fullname" . }}-config - - name: staticfiles - hostPath: - path: /tmp - type: Directory - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - diff --git a/helm-charts/mobiflow-auditor/templates/secret.yaml b/helm-charts/mobiflow-auditor/templates/secret.yaml deleted file mode 100644 index 4e9c739..0000000 --- a/helm-charts/mobiflow-auditor/templates/secret.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-FileCopyrightText: 2020-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "mobiflow-auditor.fullname" . }}-secret - labels: - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -data: - {{ $root := . }} - {{ range $path, $bytes := .Files.Glob "files/certs/tls.*" }} - {{ base $path }}: '{{ $root.Files.Get $path | b64enc }}' - {{ end }} -type: Opaque - diff --git a/helm-charts/mobiflow-auditor/templates/service.yaml b/helm-charts/mobiflow-auditor/templates/service.yaml deleted file mode 100644 index 041cb23..0000000 --- a/helm-charts/mobiflow-auditor/templates/service.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-FileCopyrightText: 2020-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ template "mobiflow-auditor.fullname" . }} - labels: - app: {{ template "mobiflow-auditor.fullname" . }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" - {{- include "mobiflow-auditor.labels" . | nindent 4 }} -spec: - type: ClusterIP - selector: - name: mobiflow-auditor - app: mobiflow-auditor - resource: {{ template "mobiflow-auditor.fullname" . }} - {{- include "mobiflow-auditor.selectorLabels" . | nindent 4 }} - ports: - - name: http - port: 8080 - - name: rpc - protocol: TCP - port: {{ .Values.config.mobiflow.rpc_port }} - targetPort: {{ .Values.config.mobiflow.rpc_port }} diff --git a/helm-charts/mobiflow-auditor/templates/servicemonitor.yaml b/helm-charts/mobiflow-auditor/templates/servicemonitor.yaml deleted file mode 100644 index b4b17ca..0000000 --- a/helm-charts/mobiflow-auditor/templates/servicemonitor.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-FileCopyrightText: 2021-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -{{- if index .Values "enable-prometheus" }} - -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: {{ template "mobiflow-auditor.fullname" . }} - labels: - app: {{ template "mobiflow-auditor.fullname" . }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" - {{- include "mobiflow-auditor.labels" . | nindent 4 }} -spec: - selector: - matchLabels: - {{- include "mobiflow-auditor.selectorLabels" . | nindent 6 }} - endpoints: - - port: http - path: /metrics - scheme: HTTP - -{{- end }} - diff --git a/helm-charts/mobiflow-auditor/values.yaml b/helm-charts/mobiflow-auditor/values.yaml deleted file mode 100644 index 954987a..0000000 --- a/helm-charts/mobiflow-auditor/values.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: localhost:5000/mobiflow-auditor - tag: latest - pullPolicy: Always - pullSecrets: [] - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "mobiflow-auditor" - -enable-prometheus: false - -config: - servicePath: "/etc/onos/config" - configPath: "/etc/fb/config" - mobiflowPath: "/etc/mobiflow/config" - ric: - e2tEndpoint: "onos-e2t:5150" - topoEndpoint: "onos-topo:5150" - mobiflow: - sqlite3_db_path: "/etc/mobiflow/" - mongo_db_port: 27017 - rpc_port: 50051 - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - - -tolerations: [] - -affinity: {} diff --git a/init/config-file.json b/init/config-file.json new file mode 100644 index 0000000..ad832de --- /dev/null +++ b/init/config-file.json @@ -0,0 +1,63 @@ +{ + "name": "mobiflow-auditor", + "version": "0.0.1", + "containers": [ + { + "name": "mobiflow-auditor", + "image": { + "registry": "5gsec.se-ran.org:10004", + "name": "xapp/mobiflow-auditor", + "tag": "0.0.1" + } + } + ], + "messaging": { + "ports": [ + { + "name": "http", + "container": "mobiflow-auditor", + "port": 8080, + "description": "http service" + }, + { + "name": "rmrroute", + "container": "mobiflow-auditor", + "port": 4561, + "description": "rmr route port for mobiflow-auditor xapp" + }, + { + "name": "rmrdata", + "container": "mobiflow-auditor", + "port": 4560, + "rxMessages": ["RIC_SUB_RESP", "RIC_SUB_FAILURE", "RIC_SUB_DEL_RESP", "RIC_INDICATION"], + "txMessages": ["RIC_SUB_REQ", "RIC_SUB_DEL_REQ", "RIC_SGNB_ADDITION_REQ", "RIC_SGNB_ADDITION_ACK"], + "mtypes" : [ + {"name":"TESTNAME1","id":55555}, + {"name":"TESTNAME2","id":55556} + ], + "policies": [1], + "description": "rmr data port for mobiflow-auditor" + } + ] + }, + "rmr": { + "protPort": "tcp:4560", + "maxSize": 2072, + "numWorkers": 1, + "txMessages": [ + "RIC_SUB_REQ", "A1_POLICY_RESP", "A1_POLICY_QUERY", "RIC_HEALTH_CHECK_RESP" + ], + "rxMessages": [ + "RIC_SUB_RESP", + "A1_POLICY_REQ", "RIC_HEALTH_CHECK_REQ" + ], + "policies": [1] + }, + "controls": { + "fileStrorage": false + }, + "db" : { + "waitForSdl": false + } +} + diff --git a/init/init_script.py b/init/init_script.py new file mode 100644 index 0000000..b1e0270 --- /dev/null +++ b/init/init_script.py @@ -0,0 +1,126 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + + +# This initialization script reads in a json from the specified config map path +# to set up the initializations (route config map, variables etc) for the main +# xapp process + +import json +import sys +import os +import signal +import time + +default_routing_file = "/opt/route/test_route.rt" +lport = 0 + + +def signal_handler(signum, frame): + print("Received signal {0}\n".format(signum)) + if xapp_subprocess is None or xapp_pid is None: + print("No xapp running. Quiting without sending signal to xapp\n", flush=True) + else: + print("Sending signal {0} to xapp ...".format(signum), flush=True) + xapp_subprocess.send_signal(signum) + + +def parseConfigJson(config): + for k1 in config.keys(): + if k1 in ParseSection: + result = ParseSection[k1](config) + if not result: + return False + + +def getMessagingInfo(config): + global lport + if 'messaging' in config.keys() and 'ports' in config['messaging'].keys(): + port_list = config['messaging']['ports'] + for portdesc in port_list: + if 'port' in portdesc.keys() and 'name' in portdesc.keys() and portdesc['name'] == 'rmr-data': + lport = portdesc['port'] + # Set the environment variable + os.environ["HW_PORT"] = str(lport) + return True + if lport == 0: + print("Error! No valid listening port", flush=True) + return False + + +def getXappName(config): + myKey = "xapp_name" + if myKey not in config.keys(): + print(("Error ! No information found for {0} in config\n".format(myKey)), flush=True) + return False + + xapp_name = config[myKey] + print("Xapp Name is: " + xapp_name) + os.environ["XAPP_NAME"] = xapp_name + + +ParseSection = dict() +ParseSection["xapp_name"] = getXappName +ParseSection["messaging"] = getMessagingInfo + +# ================================================================ +if __name__ == "__main__": + + import subprocess + + cmd = ["/usr/local/bin/run-xapp.py"] + config_file = os.getenv("CONFIG_FILE", None) + + if config_file is None: + print("Error! No configuration file specified\n", flush=True) + sys.exit(1) + + with open(config_file, 'r') as f: + try: + config = json.load(f) + except Exception as e: + print(("Error loading json file from {0}. Reason = {1}\n".format(config_file, e)), flush=True) + sys.exit(1) + + result = parseConfigJson(config) + if not result: + print("Error parsing config json. Not executing xAPP", flush=True) + sys.exit(1) + + else: + + print("Config read successfully", flush=True) + + # Register signal handlers + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + # Start the xAPP + print("Executing xAPP ....", flush=True) + xapp_subprocess = subprocess.Popen(cmd, shell=False, stdin=None, stdout=None, stderr=None) + xapp_pid = xapp_subprocess.pid + + # Periodically poll the process every 5 seconds to check if still alive + while 1: + xapp_status = xapp_subprocess.poll() + if xapp_status is None: + time.sleep(5) + else: + print("XaPP terminated via signal {0}\n".format(-1 * xapp_status), flush=True) + break + diff --git a/init/schema.json b/init/schema.json new file mode 100644 index 0000000..01c8445 --- /dev/null +++ b/init/schema.json @@ -0,0 +1,11 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema#", +"$id": "#/controls", +"type": "object", +"title": "Controls Section Schema", +"required": [ +], +"properties": { +} +} + diff --git a/init/test_route.rt b/init/test_route.rt new file mode 100644 index 0000000..8e82103 --- /dev/null +++ b/init/test_route.rt @@ -0,0 +1,5 @@ +newrt|start +rte|13111|127.0.0.1:4560 +rte|20011|service-ricplt-a1mediator-rmr.ricplt:4562 +rte|20012|service-ricplt-a1mediator-rmr.ricplt:4562 +newrt|end diff --git a/install_xapp.sh b/install_xapp.sh deleted file mode 100755 index 1737c76..0000000 --- a/install_xapp.sh +++ /dev/null @@ -1,7 +0,0 @@ -helm upgrade --install \ - --namespace riab \ - --values ./helm-charts/mobiflow-auditor/values.yaml \ - mobiflow-auditor \ - ./helm-charts/mobiflow-auditor/ && \ - sleep 20 && \ - kubectl wait pod -n riab --for=condition=Ready -l app=mobiflow-auditor --timeout=600s diff --git a/log.sh b/log.sh new file mode 100755 index 0000000..cdf1633 --- /dev/null +++ b/log.sh @@ -0,0 +1,2 @@ +#!/bin/bash +sudo kubectl logs $(sudo kubectl get pods -o name -n ricxapp | grep "mobiflow-auditor") -n ricxapp -f diff --git a/mobiflow-auditor/Dockerfile b/mobiflow-auditor/Dockerfile deleted file mode 100644 index dbe3a33..0000000 --- a/mobiflow-auditor/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -# SPDX-FileCopyrightText: Copyright 2004-present Facebook. All Rights Reserved. -# SPDX-FileCopyrightText: 2019-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -FROM python:3.8-slim - -# install all deps -WORKDIR /usr/local - -RUN pip install grpcio grpcio-tools - -COPY onos_e2_sm ./onos_e2_sm -RUN pip install --upgrade pip ./onos_e2_sm --no-cache-dir - -# speed up subsequent image builds by pre-dl and pre-installing pre-reqs -COPY mobiflow-auditor/setup.py ./secsm/setup.py -RUN pip install ./secsm --no-cache-dir - -# install actual app code -COPY mobiflow-auditor ./secsm -RUN pip install ./secsm --no-cache-dir - -ENTRYPOINT [ "python" ] diff --git a/mobiflow-auditor/README.md b/mobiflow-auditor/README.md deleted file mode 100644 index 7bf934e..0000000 --- a/mobiflow-auditor/README.md +++ /dev/null @@ -1,28 +0,0 @@ - -# MobiFlow-Auditor -MobiFlow Auditor xApp - -This app subscribes to the kpm (key performance metrics) service model and exposes -the metrics via a prometheus gauge endpoint. - -## Deploy container - -You can deploy the `mobiflow-auditor` image using helm: -``` -./install_secsm_xapp.sh -``` - -uninstall: -``` -./uninstall_secsm_xapp.sh -``` - -view logs: -``` -kubectl logs --namespace=riab --tail=100 -lname=mobiflow-auditor -f -``` diff --git a/mobiflow-auditor/config.json b/mobiflow-auditor/config.json deleted file mode 100644 index 18ba0e3..0000000 --- a/mobiflow-auditor/config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ric_config": { - "ric_address": "127.0.0.1", - "ric_port": 36422 - }, - - "report_config": { - "interval": 1000 - } -} diff --git a/mobiflow-auditor/main.py b/mobiflow-auditor/main.py deleted file mode 100644 index 28213a1..0000000 --- a/mobiflow-auditor/main.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2004-present Facebook. All Rights Reserved. -# SPDX-FileCopyrightText: 2019-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -import argparse -import asyncio -import pathlib -import json -from typing import Any, Dict - -from secsm.onos_ric_secsm import * -import onos_ric_sdk_py as sdk - -async def async_main( - app_config: Dict[str, Any], - e2_client: sdk.E2Client, - sdl_client: sdk.SDLClient, -) -> None: - - asyncio.create_task(update_mobiflow()) - - async with e2_client, sdl_client: - async for e2_node_id, e2_node in sdl_client.watch_e2_connections(): - try: - service_model = next( - sm - for oid, sm in e2_node.service_models.items() - if oid == KPM_SERVICE_MODEL_OID_V2 - ) - except StopIteration: - continue - - asyncio.create_task( - run( - app_config, - e2_client, - sdl_client, - e2_node_id, - e2_node, - service_model, - ) - ) - - -def main(args: argparse.Namespace) -> None: - config = json.loads(pathlib.Path(args.ric_config).read_text()) - e2_client = sdk.E2Client(**config["e2_client"]) - sdl_client = sdk.SDLClient(**config["sdl_client"]) - - with open(args.path) as f: - app_config = json.load(f) - with open(args.mobiflow_config) as f: - mobiflow_config = json.load(f) - - init_global(mobiflow_config) - sdk.run(async_main(app_config, e2_client, sdl_client), args.path) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="MobiFlow Auditor xApp.") - parser.add_argument("--path", type=str, help="path to the service's JSON configuration file") - parser.add_argument("--ric-config", type=str, help="ric module config", default="/etc/fb/config/onos.json") - parser.add_argument("--mobiflow-config", type=str, help="mobiflow config") - args = parser.parse_args() - main(args) - - diff --git a/mobiflow-auditor/main_flexric.py b/mobiflow-auditor/main_flexric.py deleted file mode 100644 index 11d24fb..0000000 --- a/mobiflow-auditor/main_flexric.py +++ /dev/null @@ -1,75 +0,0 @@ -import time -import logging -import pathlib -import argparse -import json -from secsm.PBest import PBest -import sys -sys.path.append('../../') -import xapp_sdk as ric - -# Create a callback for NEW which derived it from C++ class new_cb -# class SecSmCallback(ric.new_cb): -# def __init__(self): -# # Inherit C++ new_cb class -# ric.new_cb.__init__(self) -# -# # Create an override C++ method -# def handle(self, ind): -# if len(ind.rb_stats) > 0: -# t_now = time.time_ns() / 1000.0 -# t_new = ind.tstamp / 1.0 -# t_diff = t_now - t_new -# print('new Indication tstamp = ' + str(ind.tstamp) + ' diff = ' + str(t_diff)) - - -def main(args: argparse.Namespace) -> None: - config = json.loads(pathlib.Path(args.ric_config).read_text()) - report_interval = config["report_config"]["interval"] - - logging.info(f'Report Interval: {report_interval}') - - # init ric component - ric_address = config["ric_config"]["ric_address"] - ric_port = config["ric_config"]["ric_port"] - ric.init() - - # listen to RIC subscriptions - conn = ric.conn_e2_nodes() - assert(len(conn) > 0) - for i in range(0, len(conn)): - # conn[i] is e2_node_connected_t - # pull basic node info - type = conn[i].id.type - cell_id = conn[i].id.nb_id - print(f"Type {type}") - print(f"ID {cell_id}") - print("Global E2 Node [" + str(i) + "]: PLMN MCC = " + str(conn[i].id.plmn.mcc)) - print("Global E2 Node [" + str(i) + "]: PLMN MNC = " + str(conn[i].id.plmn.mnc)) - - # query supported service model - ran_func = conn[i].ran_func - ran_func_len = len(ran_func) - print(ran_func) - print(ran_func_len) - - # for i in range(0, len(conn)): - # secsm_cb = SecSmCallback() - # ric.report_sm_xapp_api(conn[i].id, ric.SM_KPM_ID, secsm_cb) - # time.sleep(report_interval/1000) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description="secsm xApp.") - parser.add_argument( - "--path", type=str, help="path to the service's JSON configuration file" - ) - parser.add_argument( - "--ric-config", - type=str, - help="xApp module config", - default="config.json", - ) - args = parser.parse_args() - main(args) - diff --git a/mobiflow-auditor/main_server.py b/mobiflow-auditor/main_server.py deleted file mode 100644 index 7da5c8e..0000000 --- a/mobiflow-auditor/main_server.py +++ /dev/null @@ -1,59 +0,0 @@ -import argparse -import json -import time -import grpc -import logging -import asyncio -from concurrent import futures -from typing import Any, Dict -from secsm.rpc.server import MobiFlowService -from secsm.rpc.protos.mobiflow_service_pb2_grpc import add_MobiFlowQueryServicer_to_server - -rpc_server = None - -async def start_rpc_server(db_path, rpc_port): - global rpc_server - logging.info(f"[RPC Server] Server starting, listening on {rpc_port}") - rpc_server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) - add_MobiFlowQueryServicer_to_server(MobiFlowService(db_path), rpc_server) - rpc_server.add_insecure_port(f"0.0.0.0:{rpc_port}") - rpc_server.start() - # rpc_server.wait_for_termination() - - try: - # Keep the server running until terminated - await asyncio.Future() # This future never resolves, effectively keeping the event loop running - except asyncio.CancelledError: - rpc_server.stop(0) - logging.info("[RPC Server] Server stopped") - -async def init_global(mobiflow_config: Dict[str, Any]): - # load configs - db_path = mobiflow_config["mobiflow"]["sqlite3_db_path"] - rpc_port = int(mobiflow_config["mobiflow"]["rpc_port"]) - csv_file = mobiflow_config["pbest"]["pbest_csv_file"] - # Start rpc server - await start_rpc_server(db_path, rpc_port) - -async def async_main(args): - with open(args.mobiflow_config) as f: - mobiflow_config = json.load(f) - rpc_task = asyncio.create_task(init_global(mobiflow_config)) - await rpc_task - - while True: - time.sleep(500) # Loop - -if __name__ == "__main__": - logging.basicConfig( - format="%(levelname)s %(asctime)s %(filename)s:%(lineno)d] %(message)s", - level=logging.INFO, - datefmt="%Y-%m-%d %H:%M:%S", - ) - - parser = argparse.ArgumentParser(description="MobiFlow Auditor xApp.") - parser.add_argument("--mobiflow-config", type=str, help="mobiflow config") - args = parser.parse_args() - - asyncio.run(async_main(args)) - diff --git a/mobiflow-auditor/secsm/metrics.py b/mobiflow-auditor/secsm/metrics.py deleted file mode 100644 index d69019c..0000000 --- a/mobiflow-auditor/secsm/metrics.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright 2004-present Facebook. All Rights Reserved. -# SPDX-FileCopyrightText: 2019-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -from typing import Iterator, List - -from prometheus_client.core import GaugeMetricFamily, Metric, REGISTRY - - -class CustomCollector: - def __init__(self) -> None: - self.metrics = {} - - def register(self, measurement_id: int, measurement_name: str,) -> None: - if measurement_id in self.metrics or measurement_name in self.metrics: - return - - metric_name = measurement_name.lower().replace(".", "_") - metric = GaugeMetricFamily( - metric_name, - measurement_name, # description - labels=["nodeid", "cellid"], - ) - - self.metrics[measurement_id] = metric - self.metrics[measurement_name] = metric - - def collect(self) -> Iterator[Metric]: - for metric_family in self.metrics.values(): - yield metric_family - metric_family.samples.clear() - - -CUSTOM_COLLECTOR = CustomCollector() -REGISTRY.register(CUSTOM_COLLECTOR) diff --git a/mobiflow-auditor/secsm/mobiflow/encoding.py b/mobiflow-auditor/secsm/mobiflow/encoding.py deleted file mode 100644 index c7fffa4..0000000 --- a/mobiflow-auditor/secsm/mobiflow/encoding.py +++ /dev/null @@ -1,226 +0,0 @@ - -# NAS_LTE -nas_emm_code = { -65: "ATTACH_REQUEST", -66: "ATTACH_ACCEPT", -67: "ATTACH_COMPLETE", -68: "ATTACH_REJECT", -69: "DETACH_REQUEST", -70: "DETACH_ACCEPT", -72: "TRACKING_AREA_UPDATE_REQUEST", -73: "TRACKING_AREA_UPDATE_ACCEPT", -74: "TRACKING_AREA_UPDATE_COMPLETE", -75: "TRACKING_AREA_UPDATE_REJECT", -76: "EXTENDED_SERVICE_REQUEST", -78: "SERVICE_REJECT", -80: "GUTI_REALLOCATION_COMMAND", -81: "GUTI_REALLOCATION_COMPLETE", -82: "AUTHENTICATION_REQUEST", -83: "AUTHENTICATION_RESPONSE", -84: "AUTHENTICATION_REJECT", -92: "AUTHENTICATION_FAILURE", -85: "IDENTITY_REQUEST", -86: "IDENTITY_RESPONSE", -93: "SECURITY_MODE_COMMAND", -94: "SECURITY_MODE_COMPLETE", -95: "SECURITY_MODE_REJECT", -96: "EMM_STATUS", -97: "EMM_INFORMATION", -98: "DOWNLINK_NAS_TRANSPORT", -99: "UPLINK_NAS_TRANSPORT", -100: "CS_SERVICE_NOTIFICATION", -77: "SERVICE_REQUEST"} - - -# RRC_LTE -rrc_ul_dcch_code = { -1: "CSFBParametersRequestCDMA2000", -2: "MeasurementReport", -3: "RRCConnectionReconfigurationComplete", -4: "RRCConnectionReestablishmentComplete", -5: "RRCConnectionSetupComplete", -6: "SecurityModeComplete", -7: "SecurityModeFailure", -8: "UECapabilityInformation", -9: "ULHandoverPreparationTransfer", -10: "ULInformationTransfer", -11: "CounterCheckResponse", -12: "UEInformationResponse-r9", -13: "ProximityIndication-r9", -14: "RNReconfigurationComplete-r10", -15: "MBMSCountingResponse-r10", -16: "InterFreqRSTDMeasurementIndication-r10"} - -rrc_ul_ccch_code = {1: "RRCConnectionReestablishmentRequest", -2: "RRCConnectionRequest"} - -rrc_dl_dcch_code = {1: "CSFBParametersResponseCDMA2000", -2: "DLInformationTransfer", -3: "HandoverFromEUTRAPreparationRequest", -4: "MobilityFromEUTRACommand", -5: "RRCConnectionReconfiguration", -6: "RRCConnectionRelease", -7: "SecurityModeCommand", -8: "UECapabilityEnquiry", -9: "CounterCheck", -10: "UEInformationRequest-r9", -11: "LoggedMeasurementConfiguration-r10", -12: "RNReconfiguration-r10", -13: "RRCConnectionResume-r13",} - - -rrc_dl_ccch_code = {1: "RRCConnectionReestablishment", -2: "RRCConnectionReestablishmentReject", -3: "RRCConnectionReject", -4: "RRCConnectionSetup"} - - -# NAS_NR: openair3/NAS/COMMON/NR_NAS_defs.h -nas_emm_code_NR = { -0x41: 'Registrationrequest', -0x42: 'Registrationaccept', -0x43: 'Registrationcomplete', -0x44: 'Registrationreject', -0x45: 'DeregistrationrequestUEoriginating', -0x46: 'DeregistrationacceptUEoriginating', -0x47: 'DeregistrationrequestUEterminated', -0x48: 'DeregistrationacceptUEterminated', -0x4c: 'Servicerequest', -0x4d: 'Servicereject', -0x4e: 'Serviceaccept', -0x4f: 'Controlplaneservicerequest', -0x50: 'Networkslicespecificauthenticationcommand', -0x51: 'Networkslicespecificauthenticationcomplete', -0x52: 'Networkslicespecificauthenticationresult', -0x54: 'Configurationupdatecommand', -0x55: 'Configurationupdatecomplete', -0x56: 'Authenticationrequest', -0x57: 'Authenticationresponse', -0x58: 'Authenticationreject', -0x59: 'Authenticationfailure', -0x5a: 'Authenticationresult', -0x5b: 'Identityrequest', -0x5c: 'Identityresponse', -0x5d: 'Securitymodecommand', -0x5e: 'Securitymodecomplete', -0x5f: 'Securitymodereject', -0x64: 'SGMMstatus', -0x65: 'Notification', -0x66: 'Notificationresponse', -0x67: 'ULNAStransport', -0x68: 'DLNAStransport', -0xc1: 'PDUsessionestablishmentrequest', -0xc2: 'PDUsessionestablishmentaccept', -0xc3: 'PDUsessionestablishmentreject', -0xc5: 'PDUsessionauthenticationcommand', -0xc6: 'PDUsessionauthenticationcomplete', -0xc7: 'PDUsessionauthenticationresult', -0xc9: 'PDUsessionmodificationrequest', -0xca: 'PDUsessionmodificationreject', -0xcb: 'PDUsessionmodificationcommand', -0xcc: 'PDUsessionmodificationcomplete', -0xcd: 'PDUsessionmodificationcommandreject', -0xd1: 'PDUsessionreleaserequest', -0xd2: 'PDUsessionreleasereject', -0xd3: 'PDUsessionreleasecommand', -0xd4: 'PDUsessionreleasecomplete', -0xd6: 'SGSMstatus' -} - - -# RRC_NR: openair2/RRC/NR/MESSAGES/ASN.1/nr-rrc-17.3.0.asn1 -rrc_dl_ccch_code_NR = { -1: "RRCReject", -2: "RRCSetup" -} - -rrc_dl_dcch_code_NR = { -1: "RRCReconfiguration", -2: "RRCResume", -3: "RRCRelease", -4: "RRCReestablishment", -5: "SecurityModeCommand", -6: "DLInformationTransfer", -7: "UECapabilityEnquiry", -8: "CounterCheck", -9: "MobilityFromNRCommand", -10: "DLDedicatedMessageSegment-r16", -11: "UEInformationRequest-r16", -12: "DLInformationTransferMRDC-r16", -13: "LoggedMeasurementConfiguration-r16" -} - -rrc_ul_ccch_code_NR = { -1: "RRCSetupRequest", -2: "RRCResumeRequest", -3: "RRCReestablishmentRequest", -4: "RRCSystemInfoRequest", -} - -rrc_ul_dcch_code_NR = { -1: "MeasurementReport", -2: "RRCReconfigurationComplete", -3: "RRCSetupComplete", -4: "RRCReestablishmentComplete", -5: "RRCResumeComplete", -6: "SecurityModeComplete", -7: "SecurityModeFailure", -8: "ULInformationTransfer", -9: "LocationMeasurementIndication", -10: "UECapabilityInformation", -11: "CounterCheckResponse", -12: "UEAssistanceInformation", -13: "FailureInformation", -14: "ULInformationTransferMRDC", -15: "SCGFailureInformation", -16: "SCGFailureInformationEUTRA", -} - - -def decode_rrc_msg(dcch: int, downlink: int, msg_id: int, rat: int) -> str: - if dcch == 1 and downlink == 1: - if rat == 0: - return rrc_dl_dcch_code.get(msg_id) - elif rat == 1: - return rrc_dl_dcch_code_NR.get(msg_id) - else: - return "" - elif dcch == 1 and downlink == 0: - if rat == 0: - return rrc_ul_dcch_code.get(msg_id) - elif rat == 1: - return rrc_ul_dcch_code_NR.get(msg_id) - else: - return "" - elif dcch == 0 and downlink == 1: - if rat == 0: - return rrc_dl_ccch_code.get(msg_id) - elif rat == 1: - return rrc_dl_ccch_code_NR.get(msg_id) - else: - return "" - else: - if rat == 0: - return rrc_ul_ccch_code.get(msg_id) - elif rat == 1: - return rrc_ul_ccch_code_NR.get(msg_id) - else: - return "" - - -def decode_nas_msg(dis: int, msg_id: int, rat: int) -> str: - if rat == 0: # LTE - if dis == 1: - return nas_emm_code.get(msg_id) - else: - # TODO implement ESM - return "" - elif rat == 1: # NR - if dis == 0: # EMM = 0x7e & 1 = 0 - return nas_emm_code_NR.get(msg_id) - else: - # TODO implement ESM - return "" - - return "" - diff --git a/mobiflow-auditor/secsm/mobiflow/factbase.py b/mobiflow-auditor/secsm/mobiflow/factbase.py deleted file mode 100644 index eb89d75..0000000 --- a/mobiflow-auditor/secsm/mobiflow/factbase.py +++ /dev/null @@ -1,78 +0,0 @@ -from .mobiflow import * -import time - -class FactBase: - def __init__(self): - self.facts = {} - self.bs_id_counter = 0 - self.bs_name_map = {} - - def add_bs(self, bs: BS): - if not self.facts.keys().__contains__(bs.bs_id): - bs.ts = time.time() * 1000 - bs.bs_id = self.bs_id_counter - self.bs_name_map[bs.name] = bs.bs_id - self.bs_id_counter += 1 - self.facts[bs.bs_id] = bs - else: - self.facts[bs.bs_id].ts = time.time() - - def add_ue(self, bsId, ue: UE): - if not self.facts.keys().__contains__(bsId): - print("[Error] BS id not exist when trying to add UE: %s" % bsId) - return - else: - ue.ts = time.time() * 1000 - ue.bs_id = bsId - if self.facts[bsId] is not None: - self.facts[bsId].add_ue(ue) - - def get_bs_index_by_name(self, bs_name: str): - if self.bs_name_map.keys().__contains__(bs_name): - return self.bs_name_map[bs_name] - else: - return -1 - - # BS related operations - def get_all_bs(self): - bss = [] - for bsId in self.facts.keys(): - bss.append(self.facts[bsId]) - return bss - - def get_bs(self, bsId): - return self.facts.get(bsId) - - def remove_bs(self, bsId): - if bsId in self.facts.keys(): - del self.facts[bsId] - return True - else: - return False - - # UE related operations - def get_all_ue(self): - ues = [] - for bsId in self.facts.keys(): - ues += self.facts[bsId].ue - return ues - - def get_ue(self, rnti: int): - ues = self.get_all_ue() - for ue in ues: - if ue.rnti == rnti: - return ue - return None - - def remove_ue(self, rnti: int): - ue_to_remove = None - for bsId in self.facts.keys(): - for ue in self.facts[bsId].ue: - if ue.rnti == rnti: - ue_to_remove = ue - break - if ue_to_remove is not None: - self.facts[bsId].ue.remove(ue_to_remove) - return True - return False - diff --git a/mobiflow-auditor/secsm/mobiflow/lockutil.py b/mobiflow-auditor/secsm/mobiflow/lockutil.py deleted file mode 100644 index c792de7..0000000 --- a/mobiflow-auditor/secsm/mobiflow/lockutil.py +++ /dev/null @@ -1,26 +0,0 @@ -import fcntl - -def acquire_lock(f): - if f is None: - return - - while True: - try: - fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) - return - except BlockingIOError: - # Wait until lock can be acquired - continue - - -def release_lock(f): - if f is None: - return - - while True: - try: - fcntl.flock(f, fcntl.LOCK_UN) - return - except BlockingIOError: - # Wait until lock can be acquired - continue diff --git a/mobiflow-auditor/secsm/mobiflow/mobiflow.py b/mobiflow-auditor/secsm/mobiflow/mobiflow.py deleted file mode 100644 index 547e27a..0000000 --- a/mobiflow-auditor/secsm/mobiflow/mobiflow.py +++ /dev/null @@ -1,368 +0,0 @@ -import time -from enum import Enum - -def get_time_ms(): - return time.time() * 1000 - -###################### Auxiliary classes ###################### - -class State(Enum): - def __str__(self): - return str(self.value) - - def __lt__(self, other): - if self.__class__ is other.__class__: - return self.value < other.value - return NotImplemented - - def __gt__(self, other): - if self.__class__ is other.__class__: - return self.value > other.value - return NotImplemented - - def __ge__(self, other): - if self.__class__ is other.__class__: - return self.value >= other.value - return NotImplemented - - def __le__(self, other): - if self.__class__ is other.__class__: - return self.value <= other.value - return NotImplemented - -class RRCState(State): - INACTIVE = 0 - RRC_IDLE = 1 - RRC_CONNECTED = 2 - RRC_RECONGIFURED = 3 - - -class EMMState(State): - EMM_DEREGISTERED = 0 - EMM_REGISTER_INIT = 1 - EMM_REGISTERED = 2 - - -class SecState(State): - SEC_CONTEXT_NOT_EXIST = 0 - SEC_CONTEXT_EXIST = 1 - -###################### Constants ###################### - -MOBIFLOW_VERSION = "v2.0" -GENERATOR_NAME = "SECSM" -UE_MOBIFLOW_ID_COUNTER = 0 -BS_MOBIFLOW_ID_COUNTER = 0 -MOBIFLOW_DELIMITER = ";" - -###################### MobiFlow Structures ###################### - -class UEMobiFlow: - def __init__(self): - self.msg_type = "UE" # Msg hdr - mobiflow type [UE, BS] - self.msg_id = 0 # Msg hdr - unique mobiflow event ID - self.timestamp = get_time_ms() # Msg hdr - timestamp (ms) - self.mobiflow_ver = MOBIFLOW_VERSION # Msg hdr - version of Mobiflow - self.generator_name = GENERATOR_NAME # Msg hdr - generator name (e.g., SECSM) - ##################################################################### - self.bs_id = 0 # UE meta - basestation id - self.rnti = 0 # UE meta - ue rnti - self.tmsi = 0 # UE meta - ue mtmsi - self.imsi = "" # UE meta - ue imsi - self.imei = "" # UE meta - ue imei - ##################################################################### - self.cipher_alg = 0 # UE stats - cipher algorithm - self.integrity_alg = 0 # UE stats - integrity algorithm - self.establish_cause = 0 # UE stats - establishment cause - self.msg = "" # UE stats - RRC/NAS message - self.rrc_state = 0 # UE stats - RRC state [INACTIVE, RRC_IDLE, RRC_CONNECTED, RRC_RECONFIGURED] - self.nas_state = 0 # UE stats - NAS state (EMM) [EMM_DEREGISTERED, EMM_REGISTER_INIT, EMM_REGISTERED] - self.sec_state = 0 # UE stats - security state [SEC_CONTEXT_NOT_EXIST, SEC_CONTEXT_EXIST] - self.emm_cause = 0 # UE stats - EMM cause code - ##################################################################### - self.rrc_initial_timer = 0 # UE timer - - self.rrc_inactive_timer = 0 # UE timer - - self.nas_initial_timer = 0 # UE timer - - self.nas_inactive_timer = 0 # UE timer - - - def __str__(self): - attrs = [] - for a in self.__dict__.values(): - if str(a) == "": - attrs.append(" ") # avoid parse error in C - else: - attrs.append(str(a)) - return MOBIFLOW_DELIMITER.join(attrs) - -class BSMobiFlow: - def __init__(self): - self.msg_type = "BS" # Msg hdr - mobiflow type [UE, BS] - self.msg_id = 0 # Msg hdr - unique mobiflow event ID - self.timestamp = get_time_ms() # Msg hdr - timestamp (ms) - self.mobiflow_ver = MOBIFLOW_VERSION # Msg hdr - version of Mobiflow - self.generator_name = GENERATOR_NAME # Msg hdr - generator name (e.g., SECSM) - ##################################################################### - self.bs_id = 0 # BS meta - basestation id - self.mcc = "" # BS meta - mobile country code - self.mnc = "" # BS meta - mobile network code - self.tac = "" # BS meta - tracking area code - self.cell_id = "" # BS meta - cell Id - self.report_period = 0 # BS meta - report period (ms) - ##################################################################### - self.connected_ue_cnt = 0 # BS stats - - self.idle_ue_cnt = 0 # BS stats - - self.max_ue_cnt = 0 # BS stats - - ##################################################################### - self.initial_timer = 0 # BS timer - - self.inactive_timer = 0 # BS timer - - - def __str__(self): - attrs = [] - for a in self.__dict__.values(): - if str(a) == "": - attrs.append(" ") # avoid parse error in C - else: - attrs.append(str(a)) - return MOBIFLOW_DELIMITER.join(attrs) - - -###################### SECSM Structures ###################### - -class UE: - def __init__(self) -> None: - #### UE Meta #### - self.bs_id = 0 - self.rnti = 0 - self.tmsi = 0 - self.imsi = "" - self.imei = 0 - #### UE Attributes #### - self.rat = -1 - self.cipher_alg = 0 - self.integrity_alg = 0 - self.establish_cause = 0 - self.rrc_state = RRCState.INACTIVE - self.nas_state = EMMState.EMM_DEREGISTERED - self.sec_state = SecState.SEC_CONTEXT_NOT_EXIST - self.emm_cause = 0 - #### UE Timer #### - self.rrc_initial_timer = 0 - self.rrc_inactive_timer = 0 - self.nas_initial_timer = 0 - self.nas_inactive_timer = 0 - #### History record #### - self.msg_trace = [] - self.current_msg_index = 0 # indicate how many messages are reported to PBEST - # UE mobiflow report criteria: report new msg - self.should_report = True # indicate whether the UE state has changed and should be reported - self.last_ts = 0 # indicate timestamp last time the UE is updated - - def update(self, ur) -> bool: - if isinstance(ur, UE): - if self.msg_trace.__len__() != ur.msg_trace.__len__(): - self.msg_trace = ur.msg_trace - self.should_report = True - if self.tmsi != ur.tmsi: - self.tmsi = ur.tmsi - self.should_report = True - if self.establish_cause != ur.establish_cause: - self.establish_cause = ur.establish_cause - self.should_report = True - if self.cipher_alg != ur.cipher_alg: - self.cipher_alg = ur.cipher_alg - self.should_report = True - if self.integrity_alg != ur.integrity_alg: - self.integrity_alg = ur.integrity_alg - self.should_report = True - return True - else: - return False - - def __str__(self) -> str: - s = "UE: {rnti: %s, tmsi: %s, imsi: %s, imei: %s}\n" % (hex(self.rnti), hex(self.tmsi), self.imsi, self.imei) - m = "MsgTrace: \n" - for msg in self.msg_trace: - m = m + msg + "\n" - return s + m - - def __eq__(self, other) -> bool: - if not isinstance(other, UE): - return False - return self.bs_id == other.bs_id and self.rnti == other.rnti - - def propagate(self, msg: str): - # implement state transition and counter update - prev_rrc_state = self.rrc_state - prev_nas_state = self.nas_state - prev_sec_state = self.sec_state - if msg == "RRCConnectionResume-r13" or msg == "RRCConnectionSetup" or msg == "RRCConnectionReestablishment"\ - or msg == "RRCResume" or msg == "RRCSetup" or msg == "RRCReestablishment": - if self.rrc_state == RRCState.INACTIVE or self.rrc_state == RRCState.RRC_IDLE: - self.rrc_state = RRCState.RRC_CONNECTED - - elif msg == "RRCConnectionRelease" \ - or msg == "RRCRelease": - self.rrc_state = RRCState.RRC_IDLE - self.nas_state = EMMState.EMM_DEREGISTERED - self.sec_state = SecState.SEC_CONTEXT_NOT_EXIST - - elif msg == "RRCConnectionReject" or msg == "RRCConnectionReestablishmentReject"\ - or msg == "RRCReject": - self.rrc_state = RRCState.RRC_IDLE - - elif msg == "ATTACH_REQUEST" or msg == "RRCConnectionReestablishmentComplete"\ - or msg == "Registrationrequest" or msg == "Servicerequest": - self.nas_state = EMMState.EMM_REGISTER_INIT - - elif msg == "SERVICE_REQUEST": # LTE: The UE shall treat that the service request procedure as successful, when the lower layers indicate that the user plane radio bearer is successfully set up - self.nas_state = EMMState.EMM_REGISTERED - - elif msg == "SecurityModeComplete" \ - or msg == "Securitymodecomplete": - self.sec_state = SecState.SEC_CONTEXT_EXIST - - elif msg == "ATTACH_COMPLETE"\ - or msg == "RRCReconfigurationComplete" or msg == "Registrationcomplete" or msg == "Serviceaccept": - self.nas_state = EMMState.EMM_REGISTERED - - elif msg == "ATTACH_REJECT" or msg == "SERVICE_REJECT" or msg == "DETACH_ACCEPT" or msg == "TRACKING_AREA_UPDATE_REJECT" \ - or msg == "Registrationreject" or msg == "Servicereject" or msg == "DeregistrationacceptUEoriginating": - self.nas_state = EMMState.EMM_DEREGISTERED - self.sec_state = SecState.SEC_CONTEXT_NOT_EXIST - - # update timer - if prev_rrc_state != self.rrc_state: - if prev_rrc_state < RRCState.RRC_CONNECTED <= self.rrc_state: - self.rrc_initial_timer = self.last_ts - if self.rrc_state < RRCState.RRC_CONNECTED <= prev_rrc_state: - self.rrc_inactive_timer = self.last_ts - if prev_nas_state != self.nas_state: - if prev_nas_state <= EMMState.EMM_DEREGISTERED < self.nas_state: - self.nas_initial_timer = self.last_ts - if self.nas_state <= EMMState.EMM_DEREGISTERED < prev_nas_state or prev_nas_state < EMMState.EMM_REGISTERED <= self.nas_state: - self.nas_inactive_timer = self.last_ts - if prev_sec_state != self.sec_state: - pass - - return prev_rrc_state, prev_nas_state, prev_sec_state, self.rrc_state, self.nas_state, self.sec_state - - def generate_mobiflow(self): - umf = UEMobiFlow() - # propagate constant - global UE_MOBIFLOW_ID_COUNTER - umf.msg_id = UE_MOBIFLOW_ID_COUNTER - UE_MOBIFLOW_ID_COUNTER += 1 - umf.timestamp = get_time_ms() - umf.bs_id = self.bs_id - umf.rnti = self.rnti - umf.tmsi = self.tmsi - umf.imsi = self.imsi - umf.imei = self.imei - umf.cipher_alg = self.cipher_alg - umf.integrity_alg = self.integrity_alg - umf.establish_cause = self.establish_cause - umf.emm_cause = self.emm_cause - prev_rrc, prev_nas, prev_sec, rrc, nas, sec = 0, 0, 0, 0, 0, 0 - if self.current_msg_index < self.msg_trace.__len__(): - umf.msg = self.msg_trace[self.current_msg_index] - self.current_msg_index += 1 - prev_rrc, prev_nas, prev_sec, rrc, nas, sec = self.propagate(umf.msg) # update UE state - umf.rrc_state = self.rrc_state - umf.nas_state = self.nas_state - umf.sec_state = self.sec_state - umf.rrc_initial_timer = self.rrc_initial_timer - umf.rrc_inactive_timer = self.rrc_inactive_timer - umf.nas_initial_timer = self.nas_initial_timer - umf.nas_inactive_timer = self.nas_inactive_timer - if self.current_msg_index >= self.msg_trace.__len__(): - self.should_report = False - return umf, prev_rrc, prev_nas, prev_sec, rrc, nas, sec - - -class BS: - def __init__(self) -> None: - #### BS Meta #### - self.bs_id = 0 - self.mcc = 0 - self.mnc = 0 - self.tac = 0 - self.cell_id = 0 - self.report_period = 0 - #### BS Stats #### - self.connected_ue_cnt = 0 - self.idle_ue_cnt = 0 - self.max_ue_cnt = 0 - #### BS Timer #### - self.initial_timer = get_time_ms() - self.inactive_timer = 0 - #### History record #### - self.ue = [] - # BS mobiflow report criteria: change of BS stats / timer - self.should_report = True - self.name = "" - - def add_ue(self, ur: UE) -> None: - # update UE if found - for u in self.ue: - if u.__eq__(ur): - u.last_ts = get_time_ms() - u.update(ur) - return - - # add new UE - ur.last_ts = get_time_ms() - self.ue.append(ur) - - def __str__(self) -> str: - uestr = "" - for u in self.ue: - uestr = uestr + hex(u.rnti) + " " - s = "BsRecord: {%d}" % self.bs_id - return s - - def __eq__(self, other) -> bool: - if not isinstance(other, BS): - return False - return self.bs_id == other.bs_id - - def update_counters(self, prev_rrc, prev_nas, prev_sec, rrc, nas, sec): - if prev_rrc != rrc: - if rrc == RRCState.RRC_CONNECTED: - self.connected_ue_cnt += 1 - self.should_report = True - if rrc == RRCState.RRC_IDLE: - self.idle_ue_cnt += 1 - self.should_report = True - if prev_rrc == RRCState.RRC_CONNECTED and rrc < RRCState.RRC_CONNECTED: - self.connected_ue_cnt -= 1 - if prev_rrc == RRCState.RRC_IDLE and rrc > RRCState.RRC_IDLE: - self.idle_ue_cnt -= 1 - if prev_nas != nas: - self.should_report = True - if prev_sec != sec: - self.should_report = True - if prev_rrc == rrc and prev_nas == nas and prev_sec == sec: - self.should_report = False - - def generate_mobiflow(self) -> BSMobiFlow: - bmf = BSMobiFlow() - # propagate constant - global BS_MOBIFLOW_ID_COUNTER - bmf.msg_id = BS_MOBIFLOW_ID_COUNTER - BS_MOBIFLOW_ID_COUNTER += 1 - bmf.timestamp = get_time_ms() - bmf.bs_id = self.bs_id - bmf.mcc = self.mcc - bmf.mnc = self.mnc - bmf.tac = self.tac - bmf.cell_id = self.cell_id - bmf.report_period = self.report_period - bmf.connected_ue_cnt = self.connected_ue_cnt - bmf.idle_ue_cnt = self.idle_ue_cnt - bmf.max_ue_cnt = self.max_ue_cnt - bmf.initial_timer = self.initial_timer - bmf.inactive_timer = self.inactive_timer - self.should_report = False - return bmf - - diff --git a/mobiflow-auditor/secsm/mobiflow/mobiflow_writer.py b/mobiflow-auditor/secsm/mobiflow/mobiflow_writer.py deleted file mode 100644 index 0a50633..0000000 --- a/mobiflow-auditor/secsm/mobiflow/mobiflow_writer.py +++ /dev/null @@ -1,196 +0,0 @@ -import datetime -import sqlite3 -import logging -import asyncio -import time -from typing import Union -from .lockutil import * -from .mobiflow import * -from .factbase import FactBase - -class MobiFlowWriter: - def __init__(self, csv_file, db_path): - self.csv_file = csv_file - self.db_path = db_path - # init csv file if necessary - if self.csv_file != "": - self.clear_file() - # init db if necessary - self.client = None - self.db = None - self.ue_mobiflow_table_name = "ue_mobiflow" - self.bs_mobiflow_table_name = "bs_mobiflow" - if self.db_path != "": - self.init_db() - - def init_db(self): - self.db = sqlite3.connect(self.db_path, check_same_thread=False) - cursor = self.db.cursor() - # Create tables if not exist - cursor.execute(self.generate_create_table_statement(UEMobiFlow(), self.ue_mobiflow_table_name)) - cursor.execute(self.generate_create_table_statement(BSMobiFlow(), self.bs_mobiflow_table_name)) - self.db.commit() - - @staticmethod - def generate_create_table_statement(class_instance, table_name): - attributes = [] - for a in class_instance.__dict__.keys(): - if str(a) == "": - attributes.append(" ") # avoid parse error in C - else: - attributes.append(str(a)) - - columns = [] - for attr in attributes: - value = getattr(class_instance, attr) - data_type = "VARCHAR(255)" if isinstance(value, str) else "INT" if isinstance(value, int) else "TEXT" - columns.append(f"{attr} {data_type}") - - create_table_statement = f"CREATE TABLE IF NOT EXISTS {table_name} (\n {', '.join(columns)}\n);" - return create_table_statement - - @staticmethod - def generate_insert_statement(list_of_mf: list, table_name: str) -> str: - - def mobiflow_2_sql_val_set(mf: Union[BSMobiFlow, UEMobiFlow]) -> str: - attrs = [] - for a in mf.__dict__.values(): - if str(a) == "": - attrs.append(" ") # avoid parse error in C - else: - if isinstance(a, str): - attrs.append(f"'{a}'") - else: - attrs.append(str(a)) - return ", ".join(attrs) - - if len(list_of_mf) <= 0: - return "" - class_instance = list_of_mf[0] - attributes = [] - for a in class_instance.__dict__.keys(): - if str(a) == "": - attributes.append(" ") # avoid parse error in C - else: - attributes.append(str(a)) - - columns = [] - for attr in attributes: - columns.append(attr) - - values = [] - for mf in list_of_mf: - mf_str = mobiflow_2_sql_val_set(mf) - values.append(f"({mf_str})") - - value_str = ",\n\t".join(values) - insert_statement = f"INSERT INTO {table_name}\n\t({', '.join(columns)})\nVALUES\n\t{value_str};" - return insert_statement - - def clear_db(self): - if self.db is None: - return - cursor = self.db.cursor() - cursor.execute(f"DELETE FROM {self.ue_mobiflow_table_name};") - cursor.execute(f"DELETE FROM {self.ue_mobiflow_table_name};") - self.db.commit() - - def close_db(self): - if self.db is not None: - self.db.close() - - async def write_mobiflow(self, fb: FactBase) -> None: - loop = asyncio.get_event_loop() - if self.csv_file != "": - await loop.run_in_executor(None, self.write_mobiflow_csv, fb) - elif self.db_path != "": - await loop.run_in_executor(None, self.write_mobiflow_db, fb) - - # write mobiflow to a CSV file - def write_mobiflow_csv(self, fb: FactBase) -> None: - f = open(self.csv_file, "a") - while True: - write_should_end = True - for ue in fb.get_all_ue(): - if ue.should_report: - write_should_end = False - # generate UE mobiflow record - umf, prev_rrc, prev_nas, prev_sec, rrc, nas, sec = ue.generate_mobiflow() - logging.info("[MobiFlow] Writing UE Mobiflow to CSV: " + umf.__str__()) - # Assign lock - acquire_lock(f) - f.write(umf.__str__() + "\n") - f.flush() - release_lock(f) - # update BS - bs = fb.get_bs(umf.bs_id) - if bs is not None: - bs.update_counters(prev_rrc, prev_nas, prev_sec, rrc, nas, sec) - for bs in fb.get_all_bs(): - if bs.should_report: - write_should_end = False - # generate BS mobiflow record - bmf = bs.generate_mobiflow() - logging.info("[MobiFlow] Writing BS Mobiflow to CSV: " + bmf.__str__()) - # Assign lock - acquire_lock(f) - f.write(bmf.__str__() + "\n") - f.flush() - release_lock(f) - if write_should_end: # end writing if no mobiflow record to update - break - f.close() - - # write mobiflow to a database - def write_mobiflow_db(self, fb: FactBase) -> None: - ue_mf_list = [] - bs_mf_list = [] - while True: - write_should_end = True - for ue in fb.get_all_ue(): - if ue.should_report: - write_should_end = False - # generate UE mobiflow record - umf, prev_rrc, prev_nas, prev_sec, rrc, nas, sec = ue.generate_mobiflow() - ue_mf_list.append(umf) - # update BS - bs = fb.get_bs(umf.bs_id) - if bs is not None: - bs.update_counters(prev_rrc, prev_nas, prev_sec, rrc, nas, sec) - for bs in fb.get_all_bs(): - if bs.should_report: - write_should_end = False - # generate BS mobiflow record - bmf = bs.generate_mobiflow() - bs_mf_list.append(bmf) - - if write_should_end: # end writing if no mobiflow record to update - break - - # Write to database - if len(ue_mf_list) > 0: - # sqlite3 will handle concurrent write - insert_stmt = self.generate_insert_statement(ue_mf_list, self.ue_mobiflow_table_name) - logging.info("[MobiFlow] Writing UE Mobiflow to DB: \n" + insert_stmt) - self.db.cursor().execute(insert_stmt) - self.db.commit() - - if len(bs_mf_list) > 0: - # sqlite3 will handle concurrent write - insert_stmt = self.generate_insert_statement(bs_mf_list, self.bs_mobiflow_table_name) - logging.info("[MobiFlow] Writing BS Mobiflow to DB: \n" + insert_stmt) - self.db.cursor().execute(insert_stmt) - self.db.commit() - - def clear_file(self) -> None: - f = open(self.csv_file, "w") - acquire_lock(f) - f.write("") - release_lock(f) - f.close() - - @staticmethod - def timestamp2str(ts): - return datetime.datetime.fromtimestamp(ts/1000).__str__() # convert ms into s - - diff --git a/mobiflow-auditor/secsm/onos_ric_secsm.py b/mobiflow-auditor/secsm/onos_ric_secsm.py deleted file mode 100644 index 2b1b17b..0000000 --- a/mobiflow-auditor/secsm/onos_ric_secsm.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright 2004-present Facebook. All Rights Reserved. -# SPDX-FileCopyrightText: 2019-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -import threading -from typing import Any, Dict - -import betterproto -import onos_ric_sdk_py as sdk -from onos_api.e2t.e2.v1beta1 import ( - Action, - ActionType, - SubsequentAction, - SubsequentActionType, - TimeToWait, -) -from onos_api.topo import ( - E2Node, - KpmReportStyle, - ServiceModelInfo, -) -from onos_e2_sm.e2smkpmv2.v2 import ( - CellObjectId, - E2SmKpmActionDefinition, - E2SmKpmActionDefinitionFormat1, - E2SmKpmEventTriggerDefinition, - E2SmKpmEventTriggerDefinitionFormat1, - E2SmKpmIndicationHeader, - E2SmKpmIndicationMessage, - GranularityPeriod, - MeasurementInfoItem, - MeasurementInfoList, - MeasurementType, - MeasurementTypeName, - RicStyleType, - SubscriptionId, -) - -from .metrics import CUSTOM_COLLECTOR -from .mobiflow.encoding import * -from .mobiflow.mobiflow_writer import * - -KPM_SERVICE_MODEL_OID_V2 = "1.3.6.1.4.1.53148.1.2.2.2" - -# Global vars -fb = FactBase() -lock = threading.Lock() -mf_writer = None -rpc_server = None -async_queue = asyncio.Queue() - -def init_global(mobiflow_config: Dict[str, Any]): - # load configs - db_path = mobiflow_config["mobiflow"]["sqlite3_db_path"] - rpc_port = int(mobiflow_config["mobiflow"]["rpc_port"]) - csv_file = mobiflow_config["pbest"]["pbest_csv_file"] - pbest_exec_name = mobiflow_config["pbest"]["pbest_exec_name"] - pbest_log_path = mobiflow_config["pbest"]["pbest_log_path"] - maintenance_time_threshold = int(mobiflow_config["pbest"]["maintenance_time_threshold"]) - # Init mobiflow writer configs - global mf_writer - mf_writer = MobiFlowWriter(csv_file, db_path) - -async def update_mobiflow(): - logging.info("MobiFlow update async coroutine started") - if mf_writer is not None: - while True: - await async_queue.get() - await mf_writer.write_mobiflow(fb) - -async def subscribe( - app_config: Dict[str, Any], - e2_client: sdk.E2Client, - sdl_client: sdk.SDLClient, - e2_node_id: str, - e2_node: E2Node, - report_style: KpmReportStyle, -) -> None: - # Save subscription ID -> cell global ID for Prometheus metric labeling - sub_map = {} - - # Sort cell IDs to create identical, deterministic subscriptions for demo - actions = [] - for idx, cell in enumerate( - sorted(await sdl_client.get_cells(e2_node_id), key=lambda c: c.cell_object_id) - ): - meas_info_list = MeasurementInfoList() - for measurement in report_style.measurements: - meas_id = measurement.id - if type(meas_id) is str: - meas_id = int(measurement.id.replace("value:", "")) - CUSTOM_COLLECTOR.register(meas_id, measurement.name) - meas_info_list.value.append( - MeasurementInfoItem( - meas_type=MeasurementType( - meas_name=MeasurementTypeName(value=measurement.name) - ) - ) - ) - # parse new BS information - cell_global_id = cell.cell_global_id.value - bs_name = e2_node_id + "_" + cell_global_id - sub_map[idx + 1] = cell_global_id - # construct mcc and mnc from plmnid - mcc = 0 - mnc = 0 - # add new BS record - logging.info("Adding new BS: %s" % bs_name) - report_period = app_config["report_period"]["granularity"] - bs = BS() - bs.bs_id = -1 # indicate a new BS - bs.cell_id = cell_global_id - bs.name = bs_name - bs.mcc = mcc - bs.mnc = mnc - bs.report_period = report_period - # only allow one thread to update fact base - lock.acquire() - fb.add_bs(bs) - lock.release() - - await async_queue.put(0) # notify mobiflow writer thread - - action_def = E2SmKpmActionDefinition( - ric_style_type=RicStyleType(value=report_style.type), - ) - action_def.action_definition_formats.action_definition_format1=E2SmKpmActionDefinitionFormat1( - cell_obj_id=CellObjectId(value=cell.cell_object_id), - meas_info_list=meas_info_list, - granul_period=GranularityPeriod( - value=app_config["report_period"]["granularity"] - ), - subscript_id=SubscriptionId(value=idx + 1), - ) - action = Action( - id=idx, - type=ActionType.ACTION_TYPE_REPORT, - subsequent_action=SubsequentAction( - type=SubsequentActionType.SUBSEQUENT_ACTION_TYPE_CONTINUE, - time_to_wait=TimeToWait.TIME_TO_WAIT_ZERO, - ), - payload=bytes( - action_def - ), - ) - actions.append( - action - ) - - if not actions: - logging.warning(f"No cells found for E2 node with ID: '{e2_node_id}'") - return - - trigger_def = E2SmKpmEventTriggerDefinition() - trigger_def.event_definition_formats.event_definition_format1=E2SmKpmEventTriggerDefinitionFormat1( - reporting_period=app_config["report_period"]["interval"] - ) - - async for (header, message) in e2_client.subscribe( - e2_node_id=e2_node_id, - service_model_name="oran-e2sm-kpm", - service_model_version="v2", - subscription_id=f"oran-e2sm-secsm_sub_{e2_node_id}", - trigger=bytes(trigger_def), - actions=actions, - ): - # use lock to make sure the update order - lock.acquire() - logging.debug(f"raw header: {header}") - logging.debug(f"raw message: {message}") - - ind_header = E2SmKpmIndicationHeader() - ind_header.parse(header) - ts = int.from_bytes( - ind_header.indication_header_formats.indication_header_format1.collet_start_time.value, "big" - ) - - ind_message = E2SmKpmIndicationMessage() - ind_message.parse(message) - subscript_id = ind_message.indication_message_formats.indication_message_format1.subscript_id.value - - cellid = sub_map[subscript_id] - bs_name = e2_node_id + "_" + cellid - meas_info_list = ind_message.indication_message_formats.indication_message_format1.meas_info_list.value - for meas_data_item in ind_message.indication_message_formats.indication_message_format1.meas_data.value: - ue = UE() - for idx, meas_record_item in enumerate(meas_data_item.meas_record.value): - _, metric_value = betterproto.which_one_of( - meas_record_item, "measurement_record_item" - ) - if metric_value is None: - logging.warning("Got a measurement record item with unset value") - continue - - _, type_value = betterproto.which_one_of( - meas_info_list[idx].meas_type, "measurement_type" - ) - if type_value is None: - logging.warning("Got a measurement with unset type") - continue - - metric_family = CUSTOM_COLLECTOR.metrics.get(type_value.value) - if metric_family is None: - logging.warning(f"No metric family found for '{type_value.value}'") - continue - - #logging.info(f"{metric_family.name}{{nodeid={e2_node_id}, cellid={cellid}}} {metric_value} {ts}") - - # SECSM: process received data - if "rnti" in metric_family.name: - if metric_value == 0: - break # don't display log for empty record - - if not "msg" in metric_family.name: - if "rnti" in metric_family.name: - ue.rnti = metric_value - elif "imsi1" in metric_family.name: - ue.imsi = metric_value - elif "imsi2" in metric_family.name: - ue.imsi = ue.imsi + (metric_value << 32) - elif "tmsi" in metric_family.name: - ue.tmsi = metric_value - elif "cipher_alg" in metric_family.name: - ue.cipher_alg = metric_value - elif "integrity_alg" in metric_family.name: - ue.integrity_alg = metric_value - elif "establish_cause" in metric_family.name: - ue.establish_cause = metric_value - elif "emm_cause" in metric_family.name: - ue.emm_cause = metric_value - elif "rat" in metric_family.name: - ue.rat = metric_value - #logging.info(f"Metadata: {metric_family.name}: {{nodeid={e2_node_id}, cellid={cellid}}} {metric_value} {ts}") - elif metric_value & 1 == 1: - # RRC - dcch = (metric_value >> 1) & 1 - downlink = (metric_value >> 2) & 1 - msg_id = (metric_value >> 3) - msg_name = decode_rrc_msg(dcch, downlink, msg_id, ue.rat) - if msg_name != "" and msg_name is not None: - ue.msg_trace.append(msg_name) - elif msg_id != 0: - logging.error(f"Invalid RRC Msg {metric_family.name}: dcch={dcch}, downlink={downlink} {msg_id} {ts}") - #logging.info(f"RRC: {metric_family.name}: {{nodeid={e2_node_id}, cellid={cellid}}} {msg_name} {ts}") - - else: - # NAS - dis = (metric_value >> 1) & 1 - msg_id = (metric_value >> 2) - msg_name = decode_nas_msg(dis, msg_id, ue.rat) - if msg_name != "" and msg_name is not None: - ue.msg_trace.append(msg_name) - elif msg_id != 0: - logging.error(f"Invalid NAS Msg: {metric_family.name}: discriminator={dis} {msg_id} {ts}") - #logging.info(f"NAS: {metric_family.name}: {{nodeid={e2_node_id}, cellid={cellid}}} {msg_name} {ts}") - - metric_family.add_metric( - labels=[e2_node_id, cellid], - value=metric_value, - timestamp=ts, - ) - # update ue record in BS - if ue.rnti == 0: - continue # ignore empty ue record - - fb.add_ue(fb.get_bs_index_by_name(bs_name), ue) - #logging.info("======================New SecSM Event========================") - #logging.info(fb.get_bs(bs_id)) - #logging.info(ue) - #logging.info("======================End SecSM Event========================") - - await async_queue.put(0) # notify mobiflow writer thread - # thread finish processing all records, relaese lock - lock.release() - -async def run( - app_config: Dict[str, Any], - e2_client: sdk.E2Client, - sdl_client: sdk.SDLClient, - e2_node_id: str, - e2_node: E2Node, - service_model: ServiceModelInfo, -) -> None: - subscriptions = [] - for report_style in service_model.ran_functions[0].report_styles: - subscriptions.append( - subscribe( - app_config, e2_client, sdl_client, e2_node_id, e2_node, report_style - ) - ) - - await asyncio.gather(*subscriptions) - diff --git a/mobiflow-auditor/secsm/rpc/protos/client_test.py b/mobiflow-auditor/secsm/rpc/protos/client_test.py deleted file mode 100644 index 3379135..0000000 --- a/mobiflow-auditor/secsm/rpc/protos/client_test.py +++ /dev/null @@ -1,17 +0,0 @@ -import grpc -import mobiflow_service_pb2_grpc -import mobiflow_service_pb2 -import time - -def run(): - with grpc.insecure_channel('localhost:50051') as channel: - stub = mobiflow_service_pb2_grpc.MobiFlowQueryStub(channel) - - while True: - response = stub.MobiFlowQuery(mobiflow_service_pb2.MobiFlowQueryRequest(name="mobi-expert", table="bs_mobiflow")) - print(f"Read data from Program 1's database: Value='{response.message}'") - time.sleep(5) # Adjust the interval as needed - - -if __name__ == '__main__': - run() diff --git a/mobiflow-auditor/secsm/rpc/protos/mobiflow_service.proto b/mobiflow-auditor/secsm/rpc/protos/mobiflow_service.proto deleted file mode 100644 index eb52b0b..0000000 --- a/mobiflow-auditor/secsm/rpc/protos/mobiflow_service.proto +++ /dev/null @@ -1,24 +0,0 @@ -// Protocol buffer definition of MobiFlow RPC services -// Author: Team SE-RAN (team@5gsec.com) -// Version v0.0.2 -// Install instructions below -// pip3 install grpcio grpcio-tools -// cd MobiFlow-Auditor/mobiflow-auditor (make sure it is at the MobiFlow-Auditor/mobiflow-auditor with main.py) -// python3 -m grpc_tools.protoc --proto_path=. --python_out=. --pyi_out=. --grpc_python_out=. secsm/rpc/protos/mobiflow_service.proto - -// Syntax definition -syntax = "proto3"; - -// The service definition. -service MobiFlowQuery { - rpc MobiFlowStream (MobiFlowStreamRequest) returns (stream MobiFlowStreamResponse); -} - -message MobiFlowStreamRequest { - string name = 1; - string table = 2; -} - -message MobiFlowStreamResponse { - string message = 1; -} diff --git a/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2.py b/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2.py deleted file mode 100644 index 1feddbc..0000000 --- a/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: secsm/rpc/protos/mobiflow_service.proto -# Protobuf Python Version: 4.25.0 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'secsm/rpc/protos/mobiflow_service.proto\"4\n\x15MobiFlowStreamRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05table\x18\x02 \x01(\t\")\n\x16MobiFlowStreamResponse\x12\x0f\n\x07message\x18\x01 \x01(\t2T\n\rMobiFlowQuery\x12\x43\n\x0eMobiFlowStream\x12\x16.MobiFlowStreamRequest\x1a\x17.MobiFlowStreamResponse0\x01\x62\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'secsm.rpc.protos.mobiflow_service_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_MOBIFLOWSTREAMREQUEST']._serialized_start=43 - _globals['_MOBIFLOWSTREAMREQUEST']._serialized_end=95 - _globals['_MOBIFLOWSTREAMRESPONSE']._serialized_start=97 - _globals['_MOBIFLOWSTREAMRESPONSE']._serialized_end=138 - _globals['_MOBIFLOWQUERY']._serialized_start=140 - _globals['_MOBIFLOWQUERY']._serialized_end=224 -# @@protoc_insertion_point(module_scope) diff --git a/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2.pyi b/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2.pyi deleted file mode 100644 index a8d235e..0000000 --- a/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Optional as _Optional - -DESCRIPTOR: _descriptor.FileDescriptor - -class MobiFlowStreamRequest(_message.Message): - __slots__ = ("name", "table") - NAME_FIELD_NUMBER: _ClassVar[int] - TABLE_FIELD_NUMBER: _ClassVar[int] - name: str - table: str - def __init__(self, name: _Optional[str] = ..., table: _Optional[str] = ...) -> None: ... - -class MobiFlowStreamResponse(_message.Message): - __slots__ = ("message",) - MESSAGE_FIELD_NUMBER: _ClassVar[int] - message: str - def __init__(self, message: _Optional[str] = ...) -> None: ... diff --git a/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2_grpc.py b/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2_grpc.py deleted file mode 100644 index f28958a..0000000 --- a/mobiflow-auditor/secsm/rpc/protos/mobiflow_service_pb2_grpc.py +++ /dev/null @@ -1,69 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc - -from secsm.rpc.protos import mobiflow_service_pb2 as secsm_dot_rpc_dot_protos_dot_mobiflow__service__pb2 - - -class MobiFlowQueryStub(object): - """The service definition. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.MobiFlowStream = channel.unary_stream( - '/MobiFlowQuery/MobiFlowStream', - request_serializer=secsm_dot_rpc_dot_protos_dot_mobiflow__service__pb2.MobiFlowStreamRequest.SerializeToString, - response_deserializer=secsm_dot_rpc_dot_protos_dot_mobiflow__service__pb2.MobiFlowStreamResponse.FromString, - ) - - -class MobiFlowQueryServicer(object): - """The service definition. - """ - - def MobiFlowStream(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_MobiFlowQueryServicer_to_server(servicer, server): - rpc_method_handlers = { - 'MobiFlowStream': grpc.unary_stream_rpc_method_handler( - servicer.MobiFlowStream, - request_deserializer=secsm_dot_rpc_dot_protos_dot_mobiflow__service__pb2.MobiFlowStreamRequest.FromString, - response_serializer=secsm_dot_rpc_dot_protos_dot_mobiflow__service__pb2.MobiFlowStreamResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'MobiFlowQuery', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) - - - # This class is part of an EXPERIMENTAL API. -class MobiFlowQuery(object): - """The service definition. - """ - - @staticmethod - def MobiFlowStream(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_stream(request, target, '/MobiFlowQuery/MobiFlowStream', - secsm_dot_rpc_dot_protos_dot_mobiflow__service__pb2.MobiFlowStreamRequest.SerializeToString, - secsm_dot_rpc_dot_protos_dot_mobiflow__service__pb2.MobiFlowStreamResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/mobiflow-auditor/secsm/rpc/server.py b/mobiflow-auditor/secsm/rpc/server.py deleted file mode 100644 index 2b1862b..0000000 --- a/mobiflow-auditor/secsm/rpc/server.py +++ /dev/null @@ -1,54 +0,0 @@ -import sqlite3 -import logging -import asyncio -from .protos.mobiflow_service_pb2_grpc import MobiFlowQueryServicer -from .protos.mobiflow_service_pb2 import MobiFlowStreamResponse - -class MobiFlowService(MobiFlowQueryServicer): - def __init__(self, db_path, rpc_port=50051): - self.db_path = db_path - self.rpc_port = rpc_port - self.MOBIFLOW_DELIMITER = ";" - self.client_last_record = {} # a dict to track the last MobiFlow record each client has read - - def open_db_conn(self): - # Connect to SQLite database (local.db) - if self.db_path is not None and self.db_path != "": - return sqlite3.connect(self.db_path, check_same_thread=False) - else: - logging.error(f"[Server] Invalid DB Path {self.db_path}") - return None - - async def fetchall_async(self, conn, query): - loop = asyncio.get_event_loop() - return await loop.run_in_executor( - None, lambda: conn.cursor().execute(query).fetchall()) - - def MobiFlowStream(self, request, context): - request_initiator = request.name - request_table = request.table - - last_id = -1 - try: - last_id = self.client_last_record[request_initiator][request_table] - except KeyError: - if request_initiator not in self.client_last_record.keys(): - self.client_last_record[request_initiator] = {} - self.client_last_record[request_initiator][request_table] = -1 # init if not found - - logging.info(f"[Server] MobiFlow Streaming Request from {request_initiator} for {request_table} last index {last_id}") - - # Read data from the database - db = self.open_db_conn() - if db is None: - logging.error(f"[Server] DB instance is NULL, exiting...") - return - - query_res = db.cursor().execute(f'SELECT * FROM {request_table} WHERE msg_id > ?', (last_id,)).fetchall() - # query_res = await self.fetchall_async(db, query_stmt) - for row in query_res: - str_list = [str(a) for a in row] - self.client_last_record[request_initiator][request_table] = int(str_list[1]) # update with latest msg_id - msg = str(self.MOBIFLOW_DELIMITER.join(str_list)) - yield MobiFlowStreamResponse(message=msg) - diff --git a/mobiflow-auditor/setup.py b/mobiflow-auditor/setup.py deleted file mode 100644 index 4c7176f..0000000 --- a/mobiflow-auditor/setup.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2004-present Facebook. All Rights Reserved. -# SPDX-FileCopyrightText: 2019-present Open Networking Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -from setuptools import find_packages, setup - - -setup( - name="secsm", - packages=find_packages(), - python_requires=">=3.7", - install_requires=["onos-ric-sdk-python>=0.2.5"], -) diff --git a/onos_e2_sm/onos_e2_sm/e2sm_kpm_v2/__init__.py b/onos_e2_sm/onos_e2_sm/e2sm_kpm_v2/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/onos_e2_sm/onos_e2_sm/e2sm_mho/__init__.py b/onos_e2_sm/onos_e2_sm/e2sm_mho/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/onos_e2_sm/onos_e2_sm/e2sm_ni/__init__.py b/onos_e2_sm/onos_e2_sm/e2sm_ni/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/onos_e2_sm/onos_e2_sm/e2sm_rc_pre/__init__.py b/onos_e2_sm/onos_e2_sm/e2sm_rc_pre/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/onos_e2_sm/onos_e2_sm/e2smkpmv2/__init__.py b/onos_e2_sm/onos_e2_sm/e2smkpmv2/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/onos_e2_sm/onos_e2_sm/validate/__init__.py b/onos_e2_sm/onos_e2_sm/validate/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/onos_e2_sm/setup.py b/onos_e2_sm/setup.py deleted file mode 100644 index 9f5eb2c..0000000 --- a/onos_e2_sm/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 - -from setuptools import find_packages, setup - -setup( - name="onos_e2_sm", - packages=find_packages(), - python_requires=">=3.7", - install_requires=["betterproto>=2.0.0b4,<3.0"] -) diff --git a/releases/container-release-ric-app-hw-python.yaml b/releases/container-release-ric-app-hw-python.yaml new file mode 100644 index 0000000..0db942a --- /dev/null +++ b/releases/container-release-ric-app-hw-python.yaml @@ -0,0 +1,10 @@ +--- +distribution_type: container +container_release_tag: 1.1.1 +container_pull_registry: nexus3.o-ran-sc.org:10004 +container_push_registry: nexus3.o-ran-sc.org:10002 +project: ric-app/hw-python +ref: 8cf17dc3de639da53d034608c8a7efb3295d0ea0 +containers: + - name: ric-app-hw-python + version: 1.1.1 diff --git a/resources/pod.yaml b/resources/pod.yaml new file mode 100644 index 0000000..443771d --- /dev/null +++ b/resources/pod.yaml @@ -0,0 +1,33 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +apiVersion: v1 +kind: Pod +metadata: + name: mobiflow-auditor + labels: + role: xapp +spec: + containers: + - name: mobiflow-auditor + image: mobiflow-auditor:1.0 + ports: + - name: rmr + containerPort: 4560 + protocol: TCP + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..5d8b635 --- /dev/null +++ b/setup.py @@ -0,0 +1,42 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# ================================================================================== + +from setuptools import setup, find_packages +import os + + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + + +setup( + name='mobiflow_auditor', + version='0.0.1', + packages=find_packages(), + url='https://github.com/5GSEC/MobiFlow-Auditor', + license='Apache 2.0', + description="O-RAN compliant xApp supporting fine-grained and security-aware statistics monitoring over the RAN data plane", + long_description=read('README.md'), + author='SE-RAN', + author_email='team@5gsec.com', + python_requires='>=3.8', + install_requires=["ricxappframe>=1.1.1,<2.3.0"], + entry_points={"console_scripts": ["run-xapp.py=src.main:launchXapp"]}, # adds a magical entrypoint for Docker + data_files=[("", ["LICENSE.txt"])], +) + + diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..f035acd --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,17 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== diff --git a/src/handler/A1PolicyHandler.py b/src/handler/A1PolicyHandler.py new file mode 100644 index 0000000..b21a0ac --- /dev/null +++ b/src/handler/A1PolicyHandler.py @@ -0,0 +1,60 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +import json +from ricxappframe.xapp_frame import RMRXapp, rmr +from ..utils.constants import Constants +from ._BaseHandler import _BaseHandler + + +class A1PolicyHandler(_BaseHandler): + + def __init__(self, rmr_xapp: RMRXapp, msgtype): + super().__init__(rmr_xapp, msgtype) + + def request_handler(self, rmr_xapp, summary, sbuf): + self._rmr_xapp.rmr_free(sbuf) + try: + req = json.loads(summary[rmr.RMR_MS_PAYLOAD]) # input should be a json encoded as bytes + self.logger.debug("A1PolicyHandler.resp_handler:: Handler processing request") + except (json.decoder.JSONDecodeError, KeyError): + self.logger.error("A1PolicyManager.resp_handler:: Handler failed to parse request") + return + + if self.verifyPolicy(req): + self.logger.info("A1PolicyHandler.resp_handler:: Handler processed request: {}".format(req)) + else: + self.logger.error("A1PolicyHandler.resp_handler:: Request verification failed: {}".format(req)) + return + self.logger.debug("A1PolicyHandler.resp_handler:: Request verification success: {}".format(req)) + + resp = self.buildPolicyResp(req) + self._rmr_xapp.rmr_send(json.dumps(resp).encode(), Constants.A1_POLICY_RESP) + self.logger.info("A1PolicyHandler.resp_handler:: Response sent: {}".format(resp)) + + def verifyPolicy(self, req: dict): + for i in ["policy_type_id", "operation", "policy_instance_id"]: + if i not in req: + return False + return True + + def buildPolicyResp(self, req: dict): + req["handler_id"] = self._rmr_xapp.config["xapp_name"] + del req["operation"] + req["status"] = "OK" + return req diff --git a/src/handler/HealthCheckHandler.py b/src/handler/HealthCheckHandler.py new file mode 100644 index 0000000..25f7dc9 --- /dev/null +++ b/src/handler/HealthCheckHandler.py @@ -0,0 +1,38 @@ +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +import json +from ricxappframe.xapp_frame import RMRXapp +from ..utils.constants import Constants +from ._BaseHandler import _BaseHandler +# from ..manager import SdlAlarmManager + + +class HealthCheckHandler(_BaseHandler): + + def __init__(self, rmr_xapp: RMRXapp, msgtype): + super().__init__(rmr_xapp, msgtype) + # self.sdl_alarm_mgr = SdlAlarmManager() + + def request_handler(self, rmr_xapp, summary, sbuf): + ok = self._rmr_xapp.healthcheck() + # self.sdl_alarm_mgr.checkSdl() + if ok: + payload = b"OK\n" + else: + payload = b"ERROR [RMR or SDL is unhealthy]\n" + self._rmr_xapp.rmr_rts(sbuf, new_payload=payload, new_mtype=Constants.RIC_HEALTH_CHECK_RESP) + self._rmr_xapp.rmr_free(sbuf) diff --git a/src/handler/KpmIndicationHandler.py b/src/handler/KpmIndicationHandler.py new file mode 100644 index 0000000..7799b76 --- /dev/null +++ b/src/handler/KpmIndicationHandler.py @@ -0,0 +1,46 @@ +import json +from ricxappframe.xapp_frame import RMRXapp, rmr +from mdclogpy import Level +from ._BaseHandler import _BaseHandler + +class KpmIndicationHandler(_BaseHandler): + + def __init__(self, rmr_xapp: RMRXapp, msgtype): + super().__init__(rmr_xapp, msgtype) + self.logger.set_level(Level.INFO) + + def request_handler(self, rmr_xapp, summary, sbuf): + """ + Handles Indication messages. + + Parameters + ---------- + rmr_xapp: rmr Instance Context + + summary: dict (required) + buffer content + + sbuf: str (required) + length of the message + """ + print(f"KpmIndicationHandler.request_handler:: Handler processing request {summary[rmr.RMR_MS_PAYLOAD]} {sbuf}") + try: + req = json.loads(summary[rmr.RMR_MS_PAYLOAD]) # input should be a json encoded as bytes + self.logger.info("KpmIndicationHandler.request_handler:: Handler processing request") + except (json.decoder.JSONDecodeError, KeyError): + self.logger.error("KpmIndicationHandler.request_handler:: Handler failed to parse request") + return + + if self.verify_indication(req): + self.logger.info("KpmIndicationHandler.request_handler:: Handler processed request: {}".format(req)) + else: + self.logger.error("KpmIndicationHandler.request_handler:: Request verification failed: {}".format(req)) + return + self.logger.debug("KpmIndicationHandler.request_handler:: Request verification success: {}".format(req)) + + self._rmr_xapp.rmr_free(sbuf) + + def verify_indication(self, req: dict): + # TODO + return True + diff --git a/src/handler/SubscriptionHandler.py b/src/handler/SubscriptionHandler.py new file mode 100644 index 0000000..465c367 --- /dev/null +++ b/src/handler/SubscriptionHandler.py @@ -0,0 +1,82 @@ +# ================================================================================== +# +# Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== +""" +Handles subscription messages from enbs and gnbs through rmr. +""" + +import json +from ricxappframe.xapp_frame import RMRXapp, rmr +from ..utils.constants import Constants +from ._BaseHandler import _BaseHandler +from ..manager.SdlManager import SdlManager +from mdclogpy import Level + + +class SubscriptionHandler(_BaseHandler): + + def __init__(self, rmr_xapp: RMRXapp, msgtype): + super().__init__(rmr_xapp, msgtype) + self.logger.set_level(Level.INFO) + + + def request_handler(self, rmr_xapp, summary, sbuf): + """ + Handles subscription messages. + + Parameters + ---------- + rmr_xapp: rmr Instance Context + + summary: dict (required) + buffer content + + sbuf: str (required) + length of the message + """ + self._rmr_xapp.rmr_free(sbuf) + try: + req = json.loads(summary[rmr.RMR_MS_PAYLOAD]) # input should be a json encoded as bytes + self.logger.debug("SubscriptionHandler.resp_handler:: Handler processing request") + except (json.decoder.JSONDecodeError, KeyError): + self.logger.error("Subscription.resp_handler:: Handler failed to parse request") + return + + if self.verifySubscription(req): + self.logger.info("SubscriptionHandler.resp_handler:: Handler processed request: {}".format(req)) + else: + self.logger.error("SubscriptionHandler.resp_handler:: Request verification failed: {}".format(req)) + return + self.logger.debug("SubscriptionHandler.resp_handler:: Request verification success: {}".format(req)) + + + def verifySubscription(self, req: dict): + for i in ["subscription_id", "message"]: + if i not in req: + return False + return True + + + + + + + + + + + diff --git a/src/handler/_BaseHandler.py b/src/handler/_BaseHandler.py new file mode 100644 index 0000000..b99ed81 --- /dev/null +++ b/src/handler/_BaseHandler.py @@ -0,0 +1,41 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +from ricxappframe.xapp_frame import RMRXapp +from abc import ABC, abstractmethod + + +class _BaseHandler(ABC): + """ + Represents base Abstract Handler class + Here initialize variables which will be common to all xapp + + Parameters: + rmr_xapp: Reference to original RMRxappframe object + msgtype: Integer specifying messagetype + """ + + def __init__(self, rmr_xapp: RMRXapp, msgtype): + self._rmr_xapp = rmr_xapp + self.logger = self._rmr_xapp.logger + self.msgtype = msgtype + self._rmr_xapp.register_callback(self.request_handler, msgtype) + + @abstractmethod + def request_handler(self, rmr_xapp, summary, sbuf): + pass diff --git a/src/handler/__init__.py b/src/handler/__init__.py new file mode 100644 index 0000000..a746b6c --- /dev/null +++ b/src/handler/__init__.py @@ -0,0 +1,23 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +from .A1PolicyHandler import A1PolicyHandler +from .HealthCheckHandler import HealthCheckHandler +from .SubscriptionHandler import SubscriptionHandler +from .KpmIndicationHandler import KpmIndicationHandler + diff --git a/src/hwxapp.py b/src/hwxapp.py new file mode 100644 index 0000000..88bf162 --- /dev/null +++ b/src/hwxapp.py @@ -0,0 +1,175 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== +import requests +from os import getenv +from ricxappframe.xapp_frame import RMRXapp, rmr +from .utils.constants import Constants +from .manager import * +from .handler import * +from mdclogpy import Level + +class HWXapp: + + __XAPP_CONFIG_PATH = "/tmp/init/config-file.json" + __XAPP_NAME = "mobiflow-auditor" + __XAPP_VERSION = "0.0.1" + __XAPP_NAME_SPACE = "ricxapp" + __PLT_NAME_SPACE = "ricplt" + __HTTP_PORT = 8080 + __RMR_PORT = 4560 + __XAPP_HTTP_END_POINT = "service-%s-%s-http.%s:%d" % (__XAPP_NAME_SPACE, __XAPP_NAME, __XAPP_NAME_SPACE, __HTTP_PORT) + __XAPP_RMR_END_POINT = "service-%s-%s-rmr.%s:%d" % (__XAPP_NAME_SPACE, __XAPP_NAME, __XAPP_NAME_SPACE, __RMR_PORT) + __CONFIG_PATH = "/ric/v1/config" + + def __init__(self): + fake_sdl = getenv("USE_FAKE_SDL", False) + self._rmr_xapp = RMRXapp(self._default_handler, + config_handler=self._handle_config_change, + rmr_port=self.__RMR_PORT, + post_init=self._post_init, + use_fake_sdl=bool(fake_sdl)) + + def _post_init(self, rmr_xapp): + """ + Function that runs when xapp initialization is complete + """ + rmr_xapp.logger.set_level(Level.DEBUG) + rmr_xapp.logger.info("HWXapp.post_init :: post_init called") + sdl_mgr = SdlManager(rmr_xapp) + sub_mgr = SubscriptionManager(rmr_xapp) + e2_mgr = E2Manager(rmr_xapp) + # self.sdl_alarm_mgr = SdlAlarmManager() + # a1_mgr = A1PolicyManager(rmr_xapp) + # a1_mgr.startup() + # metric_mgr = MetricManager(rmr_xapp) + # metric_mgr.send_metric() + + self._register(rmr_xapp) + + TARGET_OID_LIST = ["1.3.6.1.4.1.53148.1.2.2.2"] + + # obtain nodeb list + enb_list = sdl_mgr.get_enb_list() + subscribe_nb_list = [] + for enb_nb_identity in enb_list: + inventory_name = enb_nb_identity.inventory_name + nodeb_info_json = sdl_mgr.get_nodeb_info_by_inventory_name(inventory_name) + + gnb_list = sdl_mgr.get_gnb_list() + for gnb_nb_identity in gnb_list: + inventory_name = gnb_nb_identity.inventory_name + connection_status = gnb_nb_identity.connection_status + nodeb_info_json = sdl_mgr.get_nodeb_info_by_inventory_name(inventory_name) + for ran_func in nodeb_info_json["gnb"]["ranFunctions"]: + rf_id = ran_func["ranFunctionId"] + rf_def = ran_func["ranFunctionDefinition"] + rf_oid = ran_func["ranFunctionOid"] + if rf_oid in TARGET_OID_LIST: + print(f"Found target ran function for gNB {inventory_name}: {ran_func}") + + # decoded_rf_def = e2_mgr.decode_ran_function_description_hex_str(rf_def) + + # subscribe_nb_list.append(gnb_nb_identity) + # break + + # Subscribe to NodeB + print(f"connection status {connection_status}") + if connection_status == 1: + sub_mgr.send_subscription_request(gnb_nb_identity, rf_id) + + def _register(self, rmr_xapp): + # Register xApp to the App mgr + url = "http://service-%s-appmgr-http.%s:8080/ric/v1/register" % (self.__PLT_NAME_SPACE, self.__PLT_NAME_SPACE) + with open(self.__XAPP_CONFIG_PATH, 'r') as config_file: + config_json_str = config_file.read() + body = { + "appName": self.__XAPP_NAME, + "httpEndpoint": self.__XAPP_HTTP_END_POINT, + "rmrEndpoint": self.__XAPP_RMR_END_POINT, + "appInstanceName": self.__XAPP_NAME, + "appVersion": self.__XAPP_VERSION, + "configPath": self.__CONFIG_PATH, + "config": config_json_str + } + try: + rmr_xapp.logger.info(f"Sending registration request to {url} {body}") + response = requests.post(url, json=body) + rmr_xapp.logger.info(f"Registration response {response.status_code} {response.text}") + if response.status_code == 201: # registration request success + rmr_xapp.logger.info(f"Registration success") + + except IOError as err_h: + rmr_xapp.logger.error("An IO Error occurred:" + repr(err_h)) + + def _deregister(self, rmr_xapp): + # Deregister xApp to the App mgr + url = "http://service-%s-appmgr-http.%s:8080/ric/v1/deregister" % (self.__PLT_NAME_SPACE, self.__PLT_NAME_SPACE) + body = { + "appName": self.__XAPP_NAME, + "appInstanceName": f"{self.__XAPP_NAME}_{self.__XAPP_VERSION}", + } + try: + rmr_xapp.logger.info(f"Sending deregistration request to {url}") + response = requests.post(url, json=body) + rmr_xapp.logger.info(f"Deregistration response {response.status_code} {response.text}") + if response.status_code == 201: # registration request success + rmr_xapp.logger.info(f"Deregistration success") + + except IOError as err_h: + rmr_xapp.logger.error("An IO Error occurred:" + repr(err_h)) + + def _handle_config_change(self, rmr_xapp, config): + """ + Function that runs at start and on every configuration file change. + """ + rmr_xapp.logger.info("HWXapp.handle_config_change:: config: {}".format(config)) + rmr_xapp.config = config # No mutex required due to GIL + + def _default_handler(self, rmr_xapp, summary, sbuf): + """ + Function that processes messages for which no handler is defined + """ + rmr_xapp.logger.info("HWXapp.default_handler called for msg type = " + + str(summary[rmr.RMR_MS_MSG_TYPE])) + rmr_xapp.rmr_free(sbuf) + + def createHandlers(self): + """ + Function that creates all the handlers for RMR Messages + """ + HealthCheckHandler(self._rmr_xapp, Constants.RIC_HEALTH_CHECK_REQ) + A1PolicyHandler(self._rmr_xapp, Constants.A1_POLICY_REQ) + SubscriptionHandler(self._rmr_xapp, Constants.SUBSCRIPTION_REQ) + KpmIndicationHandler(self._rmr_xapp, Constants.INDICATION_REQ) + + def start(self, thread=False): + """ + This is a convenience function that allows this xapp to run in Docker + for "real" (no thread, real SDL), but also easily modified for unit testing + (e.g., use_fake_sdl). The defaults for this function are for the Dockerized xapp. + """ + self.createHandlers() + self._rmr_xapp.run(thread) + + def stop(self): + """ + can only be called if thread=True when started + TODO: could we register a signal handler for Docker SIGTERM that calls this? + """ + self._rmr_xapp.stop() + diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..34875b3 --- /dev/null +++ b/src/main.py @@ -0,0 +1,28 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +from .hwxapp import HWXapp + + +def launchXapp(): + hwxapp = HWXapp() + hwxapp.start() + + +if __name__ == "__main__": + launchXapp() diff --git a/src/manager/A1PolicyManager.py b/src/manager/A1PolicyManager.py new file mode 100644 index 0000000..57c6760 --- /dev/null +++ b/src/manager/A1PolicyManager.py @@ -0,0 +1,34 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +import json +from ricxappframe.xapp_frame import RMRXapp, rmr +from ..utils.constants import Constants +from ._BaseManager import _BaseManager + + +class A1PolicyManager(_BaseManager): + + def __init__(self, rmr_xapp: RMRXapp): + super().__init__(rmr_xapp) + + def startup(self): + policy_query = '{"policy_type_id":"' + str(Constants.HELLOWORLD_POLICY_ID) + '"}' + self._rmr_xapp.rmr_send(policy_query.encode(), Constants.A1_POLICY_QUERY) + self.logger.info("A1PolicyManager.startup:: Sent A1 policy query = " + policy_query) + diff --git a/src/manager/E2Manager.py b/src/manager/E2Manager.py new file mode 100644 index 0000000..1a2682c --- /dev/null +++ b/src/manager/E2Manager.py @@ -0,0 +1,31 @@ +from ricxappframe.xapp_frame import RMRXapp +from mdclogpy import Level +from ._BaseManager import _BaseManager +from .onos_e2_sm.e2smkpmv2.v2 import E2SmKpmRanfunctionDescription + +import binascii + +class E2Manager(_BaseManager): + + def __init__(self, rmr_xapp: RMRXapp) -> None: + super().__init__(rmr_xapp) + self.logger.set_level(Level.INFO) + + def decode_ran_function_description_hex_str(self, ran_func_description_hex_str: str): + hex_bytes = self.hex2str(ran_func_description_hex_str) + e2sm_kpm_ran_function_description = E2SmKpmRanfunctionDescription() + print(e2sm_kpm_ran_function_description.to_dict()) + try: + # Parse the bytes using the protobuf message class + e2sm_kpm_ran_function_description = e2sm_kpm_ran_function_description.parse(hex_bytes) + except Exception as e: + self.logger.error(f"[decode_ran_function_description_hex_str] decode error {e}") + return None + + print(e2sm_kpm_ran_function_description.ran_function_name.ran_function_e2_sm_oid) + return e2sm_kpm_ran_function_description + + @staticmethod + def hex2str(hex_str): + return binascii.unhexlify(hex_str) + diff --git a/src/manager/MetricManager.py b/src/manager/MetricManager.py new file mode 100644 index 0000000..81848e9 --- /dev/null +++ b/src/manager/MetricManager.py @@ -0,0 +1,40 @@ +# ================================================================================== +# +# Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +from ricxappframe.xapp_frame import RMRXapp +from ricxappframe.metric import metric +from ._BaseManager import _BaseManager +from datetime import datetime + +# noinspection PyProtectedMember,PyProtectedMember +class MetricManager(_BaseManager): + + def __init__(self, rmr_xapp: RMRXapp): + super().__init__(rmr_xapp) + self.metric_mgr = metric.MetricsManager(self._rmr_xapp._mrc, "system-time", "hw-python") + + def send_metric(self): + + # datetime object containing current date and time + now = datetime.now() + dt_string = now.strftime("%d/%m/%Y %H:%M:%S") + metric_list = [dt_string] + self.logger.info("MetricManager:: metric time {}".format(metric_list)) + self.metric_mgr.send_metrics(metric_list) + self.logger.info("MetricManager:: metric sent") + diff --git a/src/manager/SdlAlarmManager.py b/src/manager/SdlAlarmManager.py new file mode 100644 index 0000000..dc5bd7b --- /dev/null +++ b/src/manager/SdlAlarmManager.py @@ -0,0 +1,48 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +from ricxappframe.xapp_frame import RMRXapp +from ricxappframe.alarm import alarm +from ._BaseManager import _BaseManager + + +# noinspection PyProtectedMember,PyProtectedMember +class SdlAlarmManager(_BaseManager): + + def __init__(self, rmr_xapp: RMRXapp): + super().__init__(rmr_xapp) + self.alarm_mgr = alarm.AlarmManager(self._rmr_xapp._mrc, "ric-xapp", "hw-python") + self.alarm_sdl = None + + def checkSdl(self): + if self._rmr_xapp._sdl.healthcheck(): + # healthy, so clear the alarm if it was raised + if self.alarm_sdl: + self.logger.info("SdlAlarmManager:: clearing alarm") + self.alarm_mgr.clear_alarm(self.alarm_sdl) + self.alarm_sdl = None + else: + # not healthy, so (re-)raise the alarm + self.logger.info("SdlAlarmManager:: connection to SDL is not healthy, raising alarm") + if self.alarm_sdl: + self.alarm_mgr.reraise_alarm(self.alarm_sdl) + else: + self.alarm_sdl = self.alarm_mgr.create_alarm(1, alarm.AlarmSeverity.CRITICAL, + "SdlAlarmManager:: SDL failure") + self.alarm_mgr.raise_alarm(self.alarm_sdl) + self.logger.warning("SdlAlarmManager:: dropping request!") diff --git a/src/manager/SdlManager.py b/src/manager/SdlManager.py new file mode 100644 index 0000000..5ce0698 --- /dev/null +++ b/src/manager/SdlManager.py @@ -0,0 +1,69 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +import json +import requests +from typing import List, Dict +from ricxappframe.xapp_frame import RMRXapp +from ricxappframe.entities.rnib.nb_identity_pb2 import NbIdentity +from ._BaseManager import _BaseManager +from mdclogpy import Level + + +class SdlManager(_BaseManager): + + __namespace = "e2Manager" + __endpoint = "http://service-ricplt-e2mgr-http.ricplt.svc.cluster.local:3800/v1/nodeb/" + + def __init__(self, rmr_xapp: RMRXapp) -> None: + super().__init__(rmr_xapp) + self.logger.set_level(Level.INFO) + + def get_sdl_keys(self) -> List: + return self._rmr_xapp.sdl.find_keys(self.__namespace, "") + + def get_sdl_with_key(self, ns): + return self._rmr_xapp.sdl_find_and_get(self.__namespace, ns, usemsgpack=False) + + def get_gnb_list(self) -> List[NbIdentity]: + return self._rmr_xapp.get_list_gnb_ids() + + def get_enb_list(self) -> List[NbIdentity]: + return self._rmr_xapp.get_list_enb_ids() + + def get_nodeb_info_by_inventory_name(self, inventory_name) -> Dict: + url = self.__endpoint + inventory_name + response = requests.get(url) + if response.status_code == 200: + return json.loads(response.text) + else: + self.logger.error('SdlManager [get_nodeb_info_by_id] Error:', response.status_code) + return dict() + + # def sdlGetGnbList(self): + # gnblist = self._rmr_xapp.sdl_find_and_get(self.__namespace, "GNB") + # self.logger.info("SdlManager.sdlGetGnbList:: Processed request: {}".format(json.dumps(gnblist))) + # + # def sdlGetEnbList(self): + # enblist = self._rmr_xapp.sdl_find_and_get(self.__namespace, "ENB") + # self.logger.info("SdlManager.sdlGetGnbList:: Handler processed request: {}".format(json.dumps(enblist))) + + + + + diff --git a/src/manager/SubscriptionManager.py b/src/manager/SubscriptionManager.py new file mode 100644 index 0000000..799e400 --- /dev/null +++ b/src/manager/SubscriptionManager.py @@ -0,0 +1,167 @@ +# ================================================================================== +# +# Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== +""" + +""" + +import requests +import json +import threading +import http.server +import socketserver +from ricxappframe.xapp_frame import RMRXapp +from ricxappframe.entities.rnib.nb_identity_pb2 import NbIdentity +from ..utils.constants import Constants +from ._BaseManager import _BaseManager +from mdclogpy import Level + + +class SubscriptionManager(_BaseManager): + + __namespace = "e2Manager" + __SUBSCRIPTION_URL = "http://service-ricplt-submgr-http.ricplt:8088/ric/v1/subscriptions/" + __CLIENT_END_POINT = "service-ricxapp-mobiflow-auditor-http.ricxapp" + + def __init__(self, rmr_xapp: RMRXapp, local_address="0.0.0.0", http_port=8080, rmr_port=4560) -> None: + super().__init__(rmr_xapp) + self.gnb_list = [] + self.enb_list = [] + self.logger.set_level(Level.INFO) + self.subscription_list = {} + self.local_address = local_address + self.client_http_port = http_port + self.client_rmr_port = rmr_port + # subscription response + self.subscription_resp_handler = None + self.responseCB = None + self.regsiter_subscription_handler(response_cb=self.handle_subscription_response) + + def query_subscriptions(self): + try: + response = requests.get(self.__SUBSCRIPTION_URL) + + if response.status_code == 201: # subscription request success + resp = json.loads(response.text) + self.logger.info(f"Subscription query succeeded, response: {resp}") + else: + self.logger.error(f"Subscription query failed {response.status_code} {response.text} ") + + except requests.exceptions.HTTPError as err_h: + return "An Http Error occurred:" + repr(err_h) + except requests.exceptions.ConnectionError as err_c: + return "An Error Connecting to the API occurred:" + repr(err_c) + except requests.exceptions.Timeout as err_t: + return "A Timeout Error occurred:" + repr(err_t) + except requests.exceptions.RequestException as err: + return "An Unknown Error occurred" + repr(err) + + def send_subscription_request(self, nb_identity: NbIdentity, ran_function_id: int): + me_id = nb_identity.inventory_name + subscription_params = {"ClientEndPoint": {"Host": self.__CLIENT_END_POINT, + "HTTPPort": self.client_http_port, + "RMRPort": self.client_rmr_port + }, + "Meid": me_id, + "RANFunctionID": ran_function_id, + "E2SubscriptionDirectives":{ + "E2TimeoutTimerValue": 10, + "E2RetryCount": 2, + "RMRRoutingNeeded": True + }, + "SubscriptionDetails": [{"XappEventInstanceID": 1, # ?? + "EventTriggers": [0x08, 0x03, 0xe7], # 0x3e7 -> 1000 + "ActionToBeSetupList": [ + {"ActionID": 0, + "ActionType": "report", + "ActionDefinition": self.encode_action_definition(), + "SubsequentActionType": "continue", + "TimeToWait": "w10ms"} + ]}] + } + + self.logger.info(f"Sending subscription request: {self.__SUBSCRIPTION_URL}") + try: + response = requests.post(self.__SUBSCRIPTION_URL, json=subscription_params) + + self.logger.info(f"Subscription response {response.status_code} {response.text}") + if response.status_code == 201: # subscription request success + resp = json.loads(response.text) + # save subscription info + if "SubscriptionId" in resp.keys(): + self.subscription_list[resp["SubscriptionId"]] = me_id + return None + + except requests.exceptions.HTTPError as err_h: + return "An Http Error occurred:" + repr(err_h) + except requests.exceptions.ConnectionError as err_c: + return "An Error Connecting to the API occurred:" + repr(err_c) + except requests.exceptions.Timeout as err_t: + return "A Timeout Error occurred:" + repr(err_t) + except requests.exceptions.RequestException as err: + return "An Unknown Error occurred" + repr(err) + + def handle_subscription_response(self, resp_data): + if resp_data is None: + self.logger.info(f"Empty subscription response received") + return + + resp_json = json.loads(resp_data) + self.logger.info(f"Handling subscription response: {resp_json}") + + def regsiter_subscription_handler(self, response_cb=None): + # Create the thread HTTP server + if self.subscription_resp_handler is None: + # Create the server handler if not provided + class SubscriptionRespHandler(http.server.BaseHTTPRequestHandler): + def do_POST(self): + # Check if the request path is /ric/v1/subscriptions/response + if self.path == "/ric/v1/subscriptions/response": + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + if response_cb: + response_cb(post_data) + self.send_response(200) + self.end_headers() + else: + self.send_response(404) + self.end_headers() + self.wfile.write(b"404 Not Found") + + class ThreadedHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): + pass + + self.subscription_resp_handler = ThreadedHTTPServer((self.local_address, self.client_http_port), SubscriptionRespHandler) + + # Start the server in a separate thread + server_thread = threading.Thread(target=self.subscription_resp_handler.serve_forever) + server_thread.daemon = True # Terminate thread when main program exits + server_thread.start() + + if self.subscription_resp_handler is not None: + if response_cb is not None: + self.responseCB = response_cb + return True + else: + return False + + def encode_action_definition(self): + # hack for now until we find ways to encode / decode ASN1, this action def is dedicated for mobiflow kpm + action_def_hex = "000600000131001D003055452E524E5449003855452E494D534931003855452E494D534932002855452E524154004055452E4D5F544D5349006055452E4349504845525F414C47007855452E494E544547524954595F414C47005855452E454D4D5F4341555345007855452E52454C454153455F54494D4552008855452E45535441424C4953485F434155534500186D73673100186D73673200186D73673300186D73673400186D73673500186D73673600186D73673700186D73673800186D73673900206D7367313000206D7367313100206D7367313200206D7367313300206D7367313400206D7367313500206D7367313600206D7367313700206D7367313800206D7367313900206D736732304003E70000" + action_def_encoded = [int(action_def_hex[i:i + 2], 16) for i in range(0, len(action_def_hex), 2)] + return action_def_encoded + diff --git a/src/manager/_BaseManager.py b/src/manager/_BaseManager.py new file mode 100644 index 0000000..be8df7c --- /dev/null +++ b/src/manager/_BaseManager.py @@ -0,0 +1,33 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +from ricxappframe.xapp_frame import RMRXapp +from abc import ABC + + +class _BaseManager(ABC): + """ + Represents base Manager Abstract class + Here initialize variables which will be common to all xapp + + Parameters: + rmr_xapp: Reference to original RMRxappframe object + """ + def __init__(self, rmr_xapp: RMRXapp): + self._rmr_xapp = rmr_xapp + self.logger = self._rmr_xapp.logger diff --git a/src/manager/__init__.py b/src/manager/__init__.py new file mode 100644 index 0000000..9bb9412 --- /dev/null +++ b/src/manager/__init__.py @@ -0,0 +1,27 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +from .A1PolicyManager import A1PolicyManager +from .SdlAlarmManager import SdlAlarmManager +from .SdlManager import SdlManager +from .SubscriptionManager import SubscriptionManager +from .MetricManager import MetricManager +from .E2Manager import E2Manager +from .onos_e2_sm import e2sm_kpm_v2 + + diff --git a/src/manager/onos_e2_sm/__init__.py b/src/manager/onos_e2_sm/__init__.py new file mode 100644 index 0000000..dffd7ed --- /dev/null +++ b/src/manager/onos_e2_sm/__init__.py @@ -0,0 +1 @@ +from .e2smkpmv2 import v2 diff --git a/mobiflow-auditor/__init__.py b/src/manager/onos_e2_sm/asn1/__init__.py similarity index 100% rename from mobiflow-auditor/__init__.py rename to src/manager/onos_e2_sm/asn1/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/asn1/v1/__init__.py b/src/manager/onos_e2_sm/asn1/v1/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/asn1/v1/__init__.py rename to src/manager/onos_e2_sm/asn1/v1/__init__.py diff --git a/mobiflow-auditor/secsm/__init__.py b/src/manager/onos_e2_sm/e2sm_kpm/__init__.py similarity index 100% rename from mobiflow-auditor/secsm/__init__.py rename to src/manager/onos_e2_sm/e2sm_kpm/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/e2sm_kpm/v1beta1/__init__.py b/src/manager/onos_e2_sm/e2sm_kpm/v1beta1/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/e2sm_kpm/v1beta1/__init__.py rename to src/manager/onos_e2_sm/e2sm_kpm/v1beta1/__init__.py diff --git a/mobiflow-auditor/secsm/mobiflow/__init__.py b/src/manager/onos_e2_sm/e2sm_kpm_v2/__init__.py similarity index 100% rename from mobiflow-auditor/secsm/mobiflow/__init__.py rename to src/manager/onos_e2_sm/e2sm_kpm_v2/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/e2sm_kpm_v2/v2/__init__.py b/src/manager/onos_e2_sm/e2sm_kpm_v2/v2/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/e2sm_kpm_v2/v2/__init__.py rename to src/manager/onos_e2_sm/e2sm_kpm_v2/v2/__init__.py diff --git a/mobiflow-auditor/secsm/rpc/__init__.py b/src/manager/onos_e2_sm/e2sm_mho/__init__.py similarity index 100% rename from mobiflow-auditor/secsm/rpc/__init__.py rename to src/manager/onos_e2_sm/e2sm_mho/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/e2sm_mho/v1/__init__.py b/src/manager/onos_e2_sm/e2sm_mho/v1/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/e2sm_mho/v1/__init__.py rename to src/manager/onos_e2_sm/e2sm_mho/v1/__init__.py diff --git a/mobiflow-auditor/secsm/rpc/protos/__init__.py b/src/manager/onos_e2_sm/e2sm_ni/__init__.py similarity index 100% rename from mobiflow-auditor/secsm/rpc/protos/__init__.py rename to src/manager/onos_e2_sm/e2sm_ni/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/e2sm_ni/v1beta1/__init__.py b/src/manager/onos_e2_sm/e2sm_ni/v1beta1/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/e2sm_ni/v1beta1/__init__.py rename to src/manager/onos_e2_sm/e2sm_ni/v1beta1/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/__init__.py b/src/manager/onos_e2_sm/e2sm_rc_pre/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/__init__.py rename to src/manager/onos_e2_sm/e2sm_rc_pre/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/e2sm_rc_pre/v2/__init__.py b/src/manager/onos_e2_sm/e2sm_rc_pre/v2/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/e2sm_rc_pre/v2/__init__.py rename to src/manager/onos_e2_sm/e2sm_rc_pre/v2/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/asn1/__init__.py b/src/manager/onos_e2_sm/e2smkpmv2/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/asn1/__init__.py rename to src/manager/onos_e2_sm/e2smkpmv2/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/e2smkpmv2/v2/__init__.py b/src/manager/onos_e2_sm/e2smkpmv2/v2/__init__.py similarity index 99% rename from onos_e2_sm/onos_e2_sm/e2smkpmv2/v2/__init__.py rename to src/manager/onos_e2_sm/e2smkpmv2/v2/__init__.py index 2712723..3958b13 100644 --- a/onos_e2_sm/onos_e2_sm/e2smkpmv2/v2/__init__.py +++ b/src/manager/onos_e2_sm/e2smkpmv2/v2/__init__.py @@ -5,7 +5,6 @@ from typing import List, Optional import betterproto -from betterproto.grpc.grpclib_server import ServiceBase class TestCondExpression(betterproto.Enum): diff --git a/onos_e2_sm/onos_e2_sm/e2sm_kpm/__init__.py b/src/manager/onos_e2_sm/validate/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/e2sm_kpm/__init__.py rename to src/manager/onos_e2_sm/validate/__init__.py diff --git a/onos_e2_sm/onos_e2_sm/validate/v1/__init__.py b/src/manager/onos_e2_sm/validate/v1/__init__.py similarity index 100% rename from onos_e2_sm/onos_e2_sm/validate/v1/__init__.py rename to src/manager/onos_e2_sm/validate/v1/__init__.py diff --git a/src/utils/__init__.py b/src/utils/__init__.py new file mode 100644 index 0000000..f035acd --- /dev/null +++ b/src/utils/__init__.py @@ -0,0 +1,17 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== diff --git a/src/utils/constants.py b/src/utils/constants.py new file mode 100644 index 0000000..2fb7096 --- /dev/null +++ b/src/utils/constants.py @@ -0,0 +1,34 @@ +# ================================================================================== +# +# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# +# ================================================================================== + +class Constants: + A1_POLICY_QUERY = 20012 + HELLOWORLD_POLICY_ID = 2 + RIC_HEALTH_CHECK_REQ = 100 + RIC_HEALTH_CHECK_RESP = 101 + A1_POLICY_REQ = 20010 + A1_POLICY_RESP = 20011 + RIC_ALARM_UPDATE = 110 + ACTION_TYPE = "REPORT" + SUBSCRIPTION_PATH = "http://service-{}-{}-http:{}" + PLT_NAMESPACE = "ricplt" + SUBSCRIPTION_SERVICE = "submgr" + SUBSCRIPTION_PORT = "3800" + SUBSCRIPTION_REQ = 12011 + INDICATION_REQ = 12050 # TODO: need to figure how these constant values are defined + diff --git a/undeploy.sh b/undeploy.sh new file mode 100755 index 0000000..1e0bb42 --- /dev/null +++ b/undeploy.sh @@ -0,0 +1,2 @@ +#!/bin/bash +sudo -E dms_cli uninstall mobiflow-auditor ricxapp diff --git a/uninstall_xapp.sh b/uninstall_xapp.sh deleted file mode 100755 index a5c2267..0000000 --- a/uninstall_xapp.sh +++ /dev/null @@ -1 +0,0 @@ -helm uninstall mobiflow-auditor -n riab