Initial QP Driver Xapp skeleton. 28/2828/5
authorTommy Carpenter <tc677g@att.com>
Tue, 17 Mar 2020 14:32:38 +0000 (10:32 -0400)
committerTommy Carpenter <tc677g@att.com>
Tue, 17 Mar 2020 17:39:31 +0000 (17:39 +0000)
Issue-ID: RICAPP-47
Change-Id: Ifed1dcdc6a6ee5eaa54b978c61776055c646569e
Signed-off-by: Tommy Carpenter <tc677g@att.com>
22 files changed:
.gitignore [new file with mode: 0644]
.readthedocs.yaml [new file with mode: 0644]
Dockerfile [new file with mode: 0644]
LICENSE.txt [new file with mode: 0644]
container-tag.yaml [new file with mode: 0644]
docs/_static/logo.png [new file with mode: 0644]
docs/conf.py [new file with mode: 0644]
docs/conf.yaml [new file with mode: 0644]
docs/favicon.ico [new file with mode: 0644]
docs/index.rst [new file with mode: 0644]
docs/installation-guide.rst [new file with mode: 0755]
docs/overview.rst [new file with mode: 0644]
docs/release-notes.rst [new file with mode: 0644]
docs/requirements-docs.txt [new file with mode: 0644]
qpdriver/__init__.py [new file with mode: 0644]
qpdriver/main.py [new file with mode: 0644]
rmr-version.yaml [new file with mode: 0644]
setup.py [new file with mode: 0644]
tests/__init__.py [new file with mode: 0644]
tests/fixtures/test_local.rt [new file with mode: 0644]
tests/test_qpd.py [new file with mode: 0644]
tox.ini [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..9d5f770
--- /dev/null
@@ -0,0 +1,108 @@
+# 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
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644 (file)
index 0000000..3797dc8
--- /dev/null
@@ -0,0 +1,20 @@
+---
+# .readthedocs.yml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+# Required
+version: 2
+
+formats:
+  - htmlzip
+
+build:
+  image: latest
+
+python:
+  version: 3.7
+  install:
+    - requirements: docs/requirements-docs.txt
+
+sphinx:
+  configuration: docs/conf.py
diff --git a/Dockerfile b/Dockerfile
new file mode 100644 (file)
index 0000000..d7fd8ae
--- /dev/null
@@ -0,0 +1,35 @@
+# ==================================================================================
+#       Copyright (c) 2018-2020 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.
+# ==================================================================================
+FROM python:3.7-alpine
+
+# RMR setup
+RUN mkdir -p /opt/route/
+COPY --from=nexus3.o-ran-sc.org:10004/bldr-alpine3-go:1-rmr1.13.1 /usr/local/lib64/libnng.so /usr/local/lib64/libnng.so
+COPY --from=nexus3.o-ran-sc.org:10004/bldr-alpine3-go:1-rmr1.13.1 /usr/local/lib64/librmr_nng.so /usr/local/lib64/librmr_nng.so
+ENV LD_LIBRARY_PATH /usr/local/lib/:/usr/local/lib64
+
+# sdl needs gcc
+RUN apk update && apk add gcc musl-dev bash
+
+# Install
+COPY setup.py /tmp
+COPY LICENSE.txt /tmp/
+COPY qpdriver/ /tmp/qpdriver
+RUN pip install /tmp
+
+# Run
+ENV PYTHONUNBUFFERED 1
+CMD start.py
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..69a2cef
--- /dev/null
@@ -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/container-tag.yaml b/container-tag.yaml
new file mode 100644 (file)
index 0000000..48c5b97
--- /dev/null
@@ -0,0 +1,4 @@
+# The Jenkins job uses this string for the tag in the image name
+# for example nexus3.o-ran-sc.org:10004/my-image-name:my-tag
+---
+tag: 0.0.1
diff --git a/docs/_static/logo.png b/docs/_static/logo.png
new file mode 100644 (file)
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 (file)
index 0000000..974c309
--- /dev/null
@@ -0,0 +1,3 @@
+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 (file)
index 0000000..f3ca79a
--- /dev/null
@@ -0,0 +1,3 @@
+---
+project_cfg: oran
+project: ric-app-qp-driver
diff --git a/docs/favicon.ico b/docs/favicon.ico
new file mode 100644 (file)
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 (file)
index 0000000..8fe809b
--- /dev/null
@@ -0,0 +1,19 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+.. Copyright (C) 2020 AT&T Intellectual Property
+
+
+Welcome to O-RAN SC qp-driver Documentation
+============================================
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   overview.rst
+   release-notes.rst
+   installation-guide.rst
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/installation-guide.rst b/docs/installation-guide.rst
new file mode 100755 (executable)
index 0000000..dafaacd
--- /dev/null
@@ -0,0 +1,16 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+.. Copyright (C) 2020 AT&T Intellectual Property
+
+
+Installation Guide
+==================
+
+.. contents::
+   :depth: 3
+   :local:
+
+Installation
+------------
+
+[to come]
diff --git a/docs/overview.rst b/docs/overview.rst
new file mode 100644 (file)
index 0000000..e7512d3
--- /dev/null
@@ -0,0 +1,8 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+.. Copyright (C) 2020 AT&T Intellectual Property
+
+qp-driver Overview
+==================
+
+[to come]
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
new file mode 100644 (file)
index 0000000..585465d
--- /dev/null
@@ -0,0 +1,21 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+.. Copyright (C) 2020 AT&T Intellectual Property
+
+Release Notes
+===============
+
+All notable changes to this project will be documented in this file.
+
+The format is based on `Keep a Changelog <http://keepachangelog.com/>`__
+and this project adheres to `Semantic Versioning <http://semver.org/>`__.
+
+.. contents::
+   :depth: 3
+   :local:
+
+[0.0.1] - 3/17/2020
+-------------------
+::
+
+    * inital skeleton creation
diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt
new file mode 100644 (file)
index 0000000..09a0c1c
--- /dev/null
@@ -0,0 +1,5 @@
+sphinx
+sphinx-rtd-theme
+sphinxcontrib-httpdomain
+recommonmark
+lfdocs-conf
diff --git a/qpdriver/__init__.py b/qpdriver/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/qpdriver/main.py b/qpdriver/main.py
new file mode 100644 (file)
index 0000000..3cfe42b
--- /dev/null
@@ -0,0 +1,58 @@
+# ==================================================================================
+#       Copyright (c) 2020 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.
+# ==================================================================================
+from ricxappframe.xapp_frame import RMRXapp
+
+
+"""
+This is only a stencil for now, will be filled in!
+What is currently here was only for initial skeleton and test creation.
+"""
+
+
+def post_init(self):
+    self.def_hand_called = 0
+    self.traffic_steering_requests = 0
+
+
+def default_handler(self, summary, sbuf):
+    self.def_hand_called += 1
+    print(summary)
+    self.rmr_free(sbuf)
+
+
+def steering_req_handler(self, summary, sbuf):
+    self.traffic_steering_requests += 1
+    print(summary)
+    self.rmr_free(sbuf)
+
+
+# obv some of these flags have to change
+rmr_xapp = RMRXapp(default_handler, post_init=post_init, rmr_port=4562, use_fake_sdl=True)
+rmr_xapp.register_callback(steering_req_handler, 60000)  # no idea (yet) what the real int is here
+
+
+def start(thread=False):
+    rmr_xapp.run(thread)
+
+
+def stop():
+    """can only be called if thread=True when started"""
+    rmr_xapp.stop()
+
+
+def get_stats():
+    # hacky for now, will evolve
+    return {"DefCalled": rmr_xapp.def_hand_called, "SteeringRequests": rmr_xapp.traffic_steering_requests}
diff --git a/rmr-version.yaml b/rmr-version.yaml
new file mode 100644 (file)
index 0000000..5808bc4
--- /dev/null
@@ -0,0 +1,3 @@
+# CI script installs RMR from PackageCloud using this version
+---
+version: 1.13.1
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..8ad0259
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,56 @@
+# ==================================================================================
+#       Copyright (c) 2020 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.
+# ==================================================================================
+from os.path import dirname, abspath, join as path_join
+from setuptools import setup, find_packages
+
+SETUP_DIR = abspath(dirname(__file__))
+
+
+def _long_descr():
+    """Yields the content of documentation files for the long description"""
+    try:
+        doc_path = path_join(SETUP_DIR, "docs/overview.rst")
+        with open(doc_path) as f:
+            return f.read()
+    except FileNotFoundError:  # this happens during unit testing, we don't need it
+        return ""
+
+
+setup(
+    name="qpdriver",
+    version="0.0.1",
+    packages=find_packages(exclude=["tests.*", "tests"]),
+    author="Tommy Carpenter",
+    description="QP Driver Xapp for traffic steering",
+    url="https://gerrit.o-ran-sc.org/r/admin/repos/ric-app/qp-driver",
+    install_requires=["ricxappframe>=0.4.0"],
+    entry_points={"console_scripts": ["start.py=qpdriver.main:start"]},  # adds a magical entrypoint for Docker
+    classifiers=[
+        "Development Status :: 4 - Beta",
+        "Intended Audience :: Telecommunications Industry",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.7",
+        "License :: OSI Approved :: Apache Software License",
+        "Operating System :: POSIX :: Linux",
+        "Topic :: Communications",
+    ],
+    python_requires=">=3.7",
+    keywords="RIC xapp",
+    license="Apache 2.0",
+    data_files=[("", ["LICENSE.txt"])],
+    long_description=_long_descr(),
+    long_description_content_type="text/x-rst",
+)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/fixtures/test_local.rt b/tests/fixtures/test_local.rt
new file mode 100644 (file)
index 0000000..2d12e18
--- /dev/null
@@ -0,0 +1,5 @@
+# do NOT use localhost, seems unresolved on jenkins VMs
+newrt|start
+mse| 60000 | -1 | 127.0.0.1:4562
+mse| 60001 | -1 | 127.0.0.1:4562
+newrt|end
diff --git a/tests/test_qpd.py b/tests/test_qpd.py
new file mode 100644 (file)
index 0000000..1d70821
--- /dev/null
@@ -0,0 +1,60 @@
+# ==================================================================================
+#       Copyright (c) 2020 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.
+# ==================================================================================
+import json
+import time
+from contextlib import suppress
+from qpdriver import main
+from ricxappframe.xapp_frame import Xapp
+
+test_sender = None
+
+
+def test_flow():
+    """
+    just a skeleton for now.. this will evolve when qpd evolves
+    """
+
+    # start qpd
+    main.start(thread=True)
+
+    # define a test sender
+    def entry(self):
+
+        val = json.dumps({"test send 60000": 1}).encode()
+        self.rmr_send(val, 60000)
+
+        val = json.dumps({"test send 60001": 2}).encode()
+        self.rmr_send(val, 60001)
+
+    global test_sender
+    test_sender = Xapp(entrypoint=entry, rmr_port=4564, use_fake_sdl=True)
+    test_sender.run()
+
+    time.sleep(1)
+
+    assert main.get_stats() == {"DefCalled": 1, "SteeringRequests": 1}
+
+
+def teardown_module():
+    """
+    this is like a "finally"; the name of this function is pytest magic
+    safer to put down here since certain failures above can lead to pytest never returning
+    for example if an exception gets raised before stop is called in any test function above, pytest will hang forever
+    """
+    with suppress(Exception):
+        test_sender.stop()
+    with suppress(Exception):
+        main.stop()
diff --git a/tox.ini b/tox.ini
new file mode 100644 (file)
index 0000000..122ac82
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,68 @@
+# ==================================================================================
+#       Copyright (c) 2020 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.
+# ==================================================================================
+[tox]
+envlist = code,flake8,docs,docs-linkcheck
+minversion = 2.0
+
+[testenv:code]
+basepython = python3.7
+deps=
+    pytest
+    coverage
+    pytest-cov
+setenv =
+    LD_LIBRARY_PATH = /usr/local/lib/:/usr/local/lib64
+    RMR_SEED_RT = tests/fixtures/test_local.rt
+    RMR_ASYNC_CONN = 0
+
+commands =
+    pytest --cov qpdriver --cov-report xml --cov-report term-missing --cov-report html
+# --cov-fail-under=70
+    coverage xml -i
+
+[testenv:flake8]
+basepython = python3.7
+skip_install = true
+deps = flake8
+commands = flake8 setup.py qpdriver tests
+
+[flake8]
+extend-ignore = E501,E741,E731
+
+# doc jobs
+[testenv:docs]
+whitelist_externals = echo
+skipsdist = true
+basepython = python3.7
+deps =
+    sphinx
+    sphinx-rtd-theme
+    sphinxcontrib-httpdomain
+    recommonmark
+    lfdocs-conf
+commands =
+    sphinx-build -W -b html -n -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/html
+    echo "Generated docs available in {toxinidir}/docs/_build/html"
+
+[testenv:docs-linkcheck]
+skipsdist = true
+basepython = python3.7
+deps = sphinx
+       sphinx-rtd-theme
+       sphinxcontrib-httpdomain
+       recommonmark
+       lfdocs-conf
+commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/linkcheck