From 545e6b6bb093235c2f8dab8d171f30c6ae8682d3 Mon Sep 17 00:00:00 2001 From: Bin Yang Date: Tue, 26 Jan 2021 08:51:16 +0800 Subject: [PATCH] Add Notification Services and Notification Client Sidecar Story: 2008529 Task: 41688 Signed-off-by: Bin Yang Change-Id: Ib276520605cc624a9976f804a1721ba2c5909403 --- centos_stable_docker_images.inc | 3 + locationservice-base/centos/docker/Dockerfile | 22 + .../locationservice/apiserver/__init__.py | 0 .../docker/locationservice/apiserver/app.py | 15 + .../apiserver/controllers/__init__.py | 0 .../apiserver/controllers/root.py | 20 + .../apiserver/model/__init__.py | 17 + .../apiserver/repository/__init__.py | 0 .../repository/notification_control.py | 26 + .../apiserver/templates/layout.html | 22 + .../apiserver/tests/__init__.py | 22 + .../locationservice/apiserver/tests/config.py | 25 + .../apiserver/tests/test_functional.py | 22 + .../apiserver/tests/test_units.py | 7 + .../centos/docker/locationservice/config.py | 54 ++ .../locationservice.egg-info/PKG-INFO | 11 + .../locationservice.egg-info/SOURCES.txt | 20 + .../dependency_links.txt | 1 + .../locationservice.egg-info/not-zip-safe | 1 + .../locationservice.egg-info/requires.txt | 1 + .../locationservice.egg-info/top_level.txt | 1 + .../locationservicesdk/__init__.py | 0 .../locationservicesdk/client/base.py | 107 +++ .../client/locationproducer.py | 84 +++ .../common/helpers/log_helper.py | 12 + .../common/helpers/rpc_helper.py | 22 + .../locationservicesdk/model/__init__.py | 0 .../locationservicesdk/model/dto/__init__.py | 0 .../locationservicesdk/model/dto/location.py | 10 + .../model/dto/resourcetype.py | 9 + .../model/dto/rpc_endpoint.py | 34 + .../locationservicesdk/services/__init__.py | 0 .../locationservicesdk/services/daemon.py | 131 ++++ .../locationservice/public/css/style.css | 43 ++ .../locationservice/public/images/logo.png | Bin 0 -> 9095 bytes .../centos/docker/locationservice/setup.cfg | 6 + .../centos/docker/locationservice/setup.py | 22 + .../locationservice-base.stable_docker_image | 2 + .../centos/docker/Dockerfile | 23 + .../notificationclient-sidecar/MANIFEST.in | 1 + .../notificationclient-sidecar/config.py | 66 ++ .../notificationclientsdk/__init__.py | 0 .../notificationclientsdk/client/base.py | 109 +++ .../client/locationservice.py | 149 ++++ .../client/notificationservice.py | 85 +++ .../common/helpers/hostfile_helper.py | 29 + .../common/helpers/log_helper.py | 12 + .../common/helpers/nodeinfo_helper.py | 71 ++ .../common/helpers/rpc_helper.py | 22 + .../common/helpers/subscription_helper.py | 49 ++ .../notificationclientsdk/model/__init__.py | 0 .../model/dto/__init__.py | 16 + .../model/dto/location.py | 30 + .../model/dto/resourcetype.py | 9 + .../model/dto/rpc_endpoint.py | 34 + .../model/dto/subscription.py | 94 +++ .../model/orm/__init__.py | 0 .../notificationclientsdk/model/orm/base.py | 10 + .../notificationclientsdk/model/orm/node.py | 20 + .../model/orm/subscription.py | 17 + .../repository/__init__.py | 0 .../repository/dbcontext.py | 75 ++ .../repository/node_repo.py | 81 +++ .../repository/subscription_repo.py | 86 +++ .../services/__init__.py | 0 .../notificationclientsdk/services/daemon.py | 666 ++++++++++++++++++ .../notificationclientsdk/services/ptp.py | 107 +++ .../public/css/style.css | 43 ++ .../public/images/logo.png | Bin 0 -> 9095 bytes .../notificationclient-sidecar/setup.cfg | 6 + .../notificationclient-sidecar/setup.py | 22 + .../sidecar.egg-info/PKG-INFO | 11 + .../sidecar.egg-info/SOURCES.txt | 20 + .../sidecar.egg-info/dependency_links.txt | 1 + .../sidecar.egg-info/not-zip-safe | 1 + .../sidecar.egg-info/requires.txt | 2 + .../sidecar.egg-info/top_level.txt | 1 + .../sidecar/__init__.py | 0 .../notificationclient-sidecar/sidecar/app.py | 29 + .../sidecar/controllers/__init__.py | 0 .../sidecar/controllers/root.py | 65 ++ .../sidecar/controllers/v1/__init__.py | 0 .../controllers/v1/resource/__init__.py | 0 .../sidecar/controllers/v1/resource/ptp.py | 55 ++ .../sidecar/controllers/v1/subscriptions.py | 157 +++++ .../sidecar/model/__init__.py | 17 + .../sidecar/model/jsonify.py | 15 + .../sidecar/repository/__init__.py | 0 .../sidecar/repository/dbcontext_default.py | 12 + .../repository/notification_control.py | 36 + .../sidecar/tests/__init__.py | 22 + .../sidecar/tests/config.py | 25 + .../sidecar/tests/test_functional.py | 22 + .../sidecar/tests/test_units.py | 7 + ...otificationclient-base.stable_docker_image | 2 + .../centos/docker/Dockerfile | 22 + .../docker/ptptrackingfunction/setup.cfg | 6 + .../docker/ptptrackingfunction/setup.py | 22 + .../trackingfunctionsdk/__init__.py | 0 .../trackingfunctionsdk/client/base.py | 107 +++ .../client/ptpeventproducer.py | 192 +++++ .../common/helpers/log_helper.py | 12 + .../common/helpers/ptpsync.py | 5 + .../common/helpers/rpc_helper.py | 19 + .../trackingfunctionsdk/model/__init__.py | 0 .../trackingfunctionsdk/model/dto/__init__.py | 0 .../trackingfunctionsdk/model/dto/ptpstate.py | 11 + .../model/dto/ptpstatus.py | 24 + .../model/dto/resourcetype.py | 9 + .../model/dto/rpc_endpoint.py | 34 + .../trackingfunctionsdk/services/__init__.py | 0 .../trackingfunctionsdk/services/daemon.py | 213 ++++++ ...tificationservice-base.stable_docker_image | 2 + 113 files changed, 3882 insertions(+) create mode 100644 locationservice-base/centos/docker/Dockerfile create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/app.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/controllers/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/controllers/root.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/model/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/repository/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/repository/notification_control.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/templates/layout.html create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/tests/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/tests/config.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/tests/test_functional.py create mode 100644 locationservice-base/centos/docker/locationservice/apiserver/tests/test_units.py create mode 100644 locationservice-base/centos/docker/locationservice/config.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservice.egg-info/PKG-INFO create mode 100644 locationservice-base/centos/docker/locationservice/locationservice.egg-info/SOURCES.txt create mode 100644 locationservice-base/centos/docker/locationservice/locationservice.egg-info/dependency_links.txt create mode 100644 locationservice-base/centos/docker/locationservice/locationservice.egg-info/not-zip-safe create mode 100644 locationservice-base/centos/docker/locationservice/locationservice.egg-info/requires.txt create mode 100644 locationservice-base/centos/docker/locationservice/locationservice.egg-info/top_level.txt create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/client/base.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/client/locationproducer.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/common/helpers/log_helper.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/common/helpers/rpc_helper.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/model/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/location.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/resourcetype.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/rpc_endpoint.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/services/__init__.py create mode 100644 locationservice-base/centos/docker/locationservice/locationservicesdk/services/daemon.py create mode 100644 locationservice-base/centos/docker/locationservice/public/css/style.css create mode 100644 locationservice-base/centos/docker/locationservice/public/images/logo.png create mode 100644 locationservice-base/centos/docker/locationservice/setup.cfg create mode 100644 locationservice-base/centos/docker/locationservice/setup.py create mode 100644 locationservice-base/centos/locationservice-base.stable_docker_image create mode 100644 notificationclient-base/centos/docker/Dockerfile create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/MANIFEST.in create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/config.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/base.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/locationservice.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/notificationservice.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/hostfile_helper.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/log_helper.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/nodeinfo_helper.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/rpc_helper.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/subscription_helper.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/location.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/resourcetype.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/rpc_endpoint.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/subscription.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/base.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/node.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/subscription.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/dbcontext.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/node_repo.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/subscription_repo.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/services/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/services/daemon.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/services/ptp.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/public/css/style.css create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/public/images/logo.png create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/setup.cfg create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/setup.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/PKG-INFO create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/SOURCES.txt create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/dependency_links.txt create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/not-zip-safe create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/requires.txt create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/top_level.txt create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/app.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/root.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/resource/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/resource/ptp.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/subscriptions.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/model/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/model/jsonify.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/dbcontext_default.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/notification_control.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/__init__.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/config.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/test_functional.py create mode 100644 notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/test_units.py create mode 100644 notificationclient-base/centos/notificationclient-base.stable_docker_image create mode 100644 notificationservice-base/centos/docker/Dockerfile create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/setup.cfg create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/setup.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/__init__.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/client/base.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/client/ptpeventproducer.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/log_helper.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/rpc_helper.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/__init__.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/__init__.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/ptpstate.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/ptpstatus.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/resourcetype.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/rpc_endpoint.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/services/__init__.py create mode 100644 notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py create mode 100644 notificationservice-base/centos/notificationservice-base.stable_docker_image diff --git a/centos_stable_docker_images.inc b/centos_stable_docker_images.inc index e69de29..ff7bc4f 100644 --- a/centos_stable_docker_images.inc +++ b/centos_stable_docker_images.inc @@ -0,0 +1,3 @@ +notificationservice-base +locationservice-base +notificationclient-base diff --git a/locationservice-base/centos/docker/Dockerfile b/locationservice-base/centos/docker/Dockerfile new file mode 100644 index 0000000..a91644a --- /dev/null +++ b/locationservice-base/centos/docker/Dockerfile @@ -0,0 +1,22 @@ +ARG BASE +FROM ${BASE} + +ARG STX_REPO_FILE=/etc/yum.repos.d/stx.repo + +ENV KUBE_LATEST_VERSION="v1.18.3" + +RUN set -ex ;\ + yum install --disablerepo=* \ + $(grep '^name=' ${STX_REPO_FILE} | awk -F '=' '{printf "--enablerepo=" $2 " "}') \ + -y \ + gcc python3-devel python3-pip \ + && pip3 install --user pecan \ + && pip3 install oslo-config \ + && pip3 install oslo-messaging \ + && pip3 install WSME + +WORKDIR /opt/ +COPY ./locationservice /opt/locationservice +RUN cd /opt/locationservice && python3 setup.py develop + +CMD ["bash"] diff --git a/locationservice-base/centos/docker/locationservice/apiserver/__init__.py b/locationservice-base/centos/docker/locationservice/apiserver/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locationservice-base/centos/docker/locationservice/apiserver/app.py b/locationservice-base/centos/docker/locationservice/apiserver/app.py new file mode 100644 index 0000000..ae7479a --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/app.py @@ -0,0 +1,15 @@ +from pecan import make_app +from apiserver.repository.notification_control import notification_control + +from pecan import conf + +def setup_app(config): + + notification_control.refresh() + app_conf = dict(config.app) + + return make_app( + app_conf.pop('root'), + logging=getattr(config, 'logging', {}), + **app_conf + ) diff --git a/locationservice-base/centos/docker/locationservice/apiserver/controllers/__init__.py b/locationservice-base/centos/docker/locationservice/apiserver/controllers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locationservice-base/centos/docker/locationservice/apiserver/controllers/root.py b/locationservice-base/centos/docker/locationservice/apiserver/controllers/root.py new file mode 100644 index 0000000..21c947d --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/controllers/root.py @@ -0,0 +1,20 @@ +#coding=utf-8 + +from pecan import expose, redirect, rest, route, response +from webob.exc import status_map + +from wsme import types as wtypes +from wsmeext.pecan import wsexpose + +class HealthController(rest.RestController): + + @wsexpose(wtypes.text) + def get(self): + return {'health': True} + + +class RootController(object): + pass + + +route(RootController, 'health', HealthController()) diff --git a/locationservice-base/centos/docker/locationservice/apiserver/model/__init__.py b/locationservice-base/centos/docker/locationservice/apiserver/model/__init__.py new file mode 100644 index 0000000..71f7c51 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/model/__init__.py @@ -0,0 +1,17 @@ +from pecan import conf # noqa + +def init_model(): + """ + This is a stub method which is called at application startup time. + + If you need to bind to a parsed database configuration, set up tables or + ORM classes, or perform any database initialization, this is the + recommended place to do it. + + For more information working with databases, and some common recipes, + see https://pecan.readthedocs.io/en/latest/databases.html + """ + + pass + + diff --git a/locationservice-base/centos/docker/locationservice/apiserver/repository/__init__.py b/locationservice-base/centos/docker/locationservice/apiserver/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locationservice-base/centos/docker/locationservice/apiserver/repository/notification_control.py b/locationservice-base/centos/docker/locationservice/apiserver/repository/notification_control.py new file mode 100644 index 0000000..c4a56a9 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/repository/notification_control.py @@ -0,0 +1,26 @@ +import os +import time +import json +from pecan import conf +from locationservicesdk.services.daemon import DaemonControl + +REGISTRATION_USER = os.environ.get("REGISTRATION_USER", "admin") +REGISTRATION_PASS = os.environ.get("REGISTRATION_PASS", "admin") +REGISTRATION_PORT = os.environ.get("REGISTRATION_PORT", "5672") +REGISTRATION_HOST = os.environ.get("REGISTRATION_HOST",'registration.notification.svc.cluster.local') +THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0') +THIS_POD_IP = os.environ.get("THIS_POD_IP",'127.0.0.1') + +REGISTRATION_TRANSPORT_ENDPOINT = 'rabbit://{0}:{1}@{2}:{3}'.format( + REGISTRATION_USER, REGISTRATION_PASS, REGISTRATION_HOST, REGISTRATION_PORT) + +sqlalchemy_conf_json=json.dumps({}) +LocationInfo = { + 'NodeName': THIS_NODE_NAME, + 'PodIP': THIS_POD_IP, + 'ResourceTypes': ['PTP'], + 'Timestamp': time.time() + } +location_info_json = json.dumps(LocationInfo) +notification_control = DaemonControl( + sqlalchemy_conf_json, REGISTRATION_TRANSPORT_ENDPOINT, location_info_json) diff --git a/locationservice-base/centos/docker/locationservice/apiserver/templates/layout.html b/locationservice-base/centos/docker/locationservice/apiserver/templates/layout.html new file mode 100644 index 0000000..2bdfe88 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/templates/layout.html @@ -0,0 +1,22 @@ + + + ${self.title()} + ${self.style()} + ${self.javascript()} + + + ${self.body()} + + + +<%def name="title()"> + Default Title + + +<%def name="style()"> + + + +<%def name="javascript()"> + + diff --git a/locationservice-base/centos/docker/locationservice/apiserver/tests/__init__.py b/locationservice-base/centos/docker/locationservice/apiserver/tests/__init__.py new file mode 100644 index 0000000..78ea527 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/tests/__init__.py @@ -0,0 +1,22 @@ +import os +from unittest import TestCase +from pecan import set_config +from pecan.testing import load_test_app + +__all__ = ['FunctionalTest'] + + +class FunctionalTest(TestCase): + """ + Used for functional tests where you need to test your + literal application and its integration with the framework. + """ + + def setUp(self): + self.app = load_test_app(os.path.join( + os.path.dirname(__file__), + 'config.py' + )) + + def tearDown(self): + set_config({}, overwrite=True) diff --git a/locationservice-base/centos/docker/locationservice/apiserver/tests/config.py b/locationservice-base/centos/docker/locationservice/apiserver/tests/config.py new file mode 100644 index 0000000..8f66b60 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/tests/config.py @@ -0,0 +1,25 @@ +# Server Specific Configurations +server = { + 'port': '8080', + 'host': '0.0.0.0' +} + +# Pecan Application Configurations +app = { + 'root': 'notificationclient.controllers.root.RootController', + 'modules': ['notificationclient'], + 'static_root': '%(confdir)s/../../public', + 'template_path': '%(confdir)s/../templates', + 'debug': True, + 'errors': { + '404': '/error/404', + '__force_dict__': True + } +} + +# Custom Configurations must be in Python dictionary format:: +# +# foo = {'bar':'baz'} +# +# All configurations are accessible at:: +# pecan.conf diff --git a/locationservice-base/centos/docker/locationservice/apiserver/tests/test_functional.py b/locationservice-base/centos/docker/locationservice/apiserver/tests/test_functional.py new file mode 100644 index 0000000..252c3aa --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/tests/test_functional.py @@ -0,0 +1,22 @@ +from unittest import TestCase +from webtest import TestApp +from notificationclient.tests import FunctionalTest + + +class TestRootController(FunctionalTest): + + def test_get(self): + response = self.app.get('/') + assert response.status_int == 200 + + def test_search(self): + response = self.app.post('/', params={'q': 'RestController'}) + assert response.status_int == 302 + assert response.headers['Location'] == ( + 'https://pecan.readthedocs.io/en/latest/search.html' + '?q=RestController' + ) + + def test_get_not_found(self): + response = self.app.get('/a/bogus/url', expect_errors=True) + assert response.status_int == 404 diff --git a/locationservice-base/centos/docker/locationservice/apiserver/tests/test_units.py b/locationservice-base/centos/docker/locationservice/apiserver/tests/test_units.py new file mode 100644 index 0000000..573fb68 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/apiserver/tests/test_units.py @@ -0,0 +1,7 @@ +from unittest import TestCase + + +class TestUnits(TestCase): + + def test_units(self): + assert 5 * 5 == 25 diff --git a/locationservice-base/centos/docker/locationservice/config.py b/locationservice-base/centos/docker/locationservice/config.py new file mode 100644 index 0000000..c0a9c3e --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/config.py @@ -0,0 +1,54 @@ +# Server Specific Configurations +server = { + 'port': '8080', + 'host': '0.0.0.0' +} + +# Pecan Application Configurations +app = { + 'root': 'apiserver.controllers.root.RootController', + 'modules': ['apiserver'], + 'static_root': '%(confdir)s/public', + 'template_path': '%(confdir)s/apiserver/templates', + 'debug': True, + 'errors': { + 404: '/error/404', + '__force_dict__': True + } +} + +logging = { + 'root': {'level': 'INFO', 'handlers': ['console']}, + 'loggers': { + 'apiserver': {'level': 'DEBUG', 'handlers': ['console'], 'propagate': False}, + 'pecan': {'level': 'DEBUG', 'handlers': ['console'], 'propagate': False}, + 'py.warnings': {'handlers': ['console']}, + '__force_dict__': True + }, + 'handlers': { + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'color' + } + }, + 'formatters': { + 'simple': { + 'format': ('%(asctime)s %(levelname)-5.5s [%(name)s]' + '[%(threadName)s] %(message)s') + }, + 'color': { + '()': 'pecan.log.ColorFormatter', + 'format': ('%(asctime)s [%(padded_color_levelname)s] [%(name)s]' + '[%(threadName)s] %(message)s'), + '__force_dict__': True + } + } +} + +# Custom Configurations must be in Python dictionary format:: +# +# foo = {'bar':'baz'} +# +# All configurations are accessible at:: +# pecan.conf diff --git a/locationservice-base/centos/docker/locationservice/locationservice.egg-info/PKG-INFO b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/PKG-INFO new file mode 100644 index 0000000..464fa9c --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/PKG-INFO @@ -0,0 +1,11 @@ +Metadata-Version: 1.0 +Name: locationservice +Version: 0.1 +Summary: locationservice offers a container image for notificationservice +to manage location information +Home-page: UNKNOWN +Author: Bin Yang +Author-email: bin.yang@windriver.com +License: Apache License 2.0 +Description: UNKNOWN +Platform: UNKNOWN diff --git a/locationservice-base/centos/docker/locationservice/locationservice.egg-info/SOURCES.txt b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/SOURCES.txt new file mode 100644 index 0000000..6e37901 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/SOURCES.txt @@ -0,0 +1,20 @@ +MANIFEST.in +setup.cfg +setup.py +apiserver/__init__.py +apiserver/app.py +apiserver.egg-info/PKG-INFO +apiserver.egg-info/SOURCES.txt +apiserver.egg-info/dependency_links.txt +apiserver.egg-info/not-zip-safe +apiserver.egg-info/requires.txt +apiserver.egg-info/top_level.txt +apiserver/controllers/__init__.py +apiserver/controllers/root.py +apiserver/model/__init__.py +apiserver/tests/__init__.py +apiserver/tests/config.py +apiserver/tests/test_functional.py +apiserver/tests/test_units.py +public/css/style.css +public/images/logo.png \ No newline at end of file diff --git a/locationservice-base/centos/docker/locationservice/locationservice.egg-info/dependency_links.txt b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/locationservice-base/centos/docker/locationservice/locationservice.egg-info/not-zip-safe b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/locationservice-base/centos/docker/locationservice/locationservice.egg-info/requires.txt b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/requires.txt new file mode 100644 index 0000000..ea2fd7b --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/requires.txt @@ -0,0 +1 @@ +pecan \ No newline at end of file diff --git a/locationservice-base/centos/docker/locationservice/locationservice.egg-info/top_level.txt b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/top_level.txt new file mode 100644 index 0000000..e6f379c --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservice.egg-info/top_level.txt @@ -0,0 +1 @@ +locationservice diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/__init__.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/client/base.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/client/base.py new file mode 100644 index 0000000..e3c8a47 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservicesdk/client/base.py @@ -0,0 +1,107 @@ +import os +import json +import time +import oslo_messaging +from oslo_config import cfg +from locationservicesdk.common.helpers import rpc_helper +from locationservicesdk.model.dto.rpc_endpoint import RpcEndpointInfo + +import logging + +LOG = logging.getLogger(__name__) + +from locationservicesdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +class BrokerClientBase(object): + def __init__(self, broker_name, broker_transport_endpoint): + self.broker_name = broker_name + self.listeners = {} + self.broker_endpoint = RpcEndpointInfo(broker_transport_endpoint) + self.transport = rpc_helper.get_transport(self.broker_endpoint) + LOG.debug("Created Broker client:{0}".format(broker_name)) + + def __del__(self): + self.transport.cleanup() + del self.transport + return + + def __create_listener(self, context): + target = oslo_messaging.Target( + topic=context['topic'], + server=context['server']) + endpoints = context['endpoints'] + server = oslo_messaging.get_rpc_server( + self.transport, target, endpoints, executor=None) + return server + + def _refresh(self): + for topic, servers in self.listeners.items(): + for servername, context in servers.items(): + try: + rpcserver = context.get('rpcserver', None) + isactive = context.get('active', False) + if isactive and not rpcserver: + rpcserver = self.__create_listener(context) + rpcserver.start() + context['rpcserver'] = rpcserver + LOG.debug("Started rpcserver@{0}@{1}".format(context['topic'], context['server'])) + elif not isactive and rpcserver: + rpcserver.stop() + rpcserver.wait() + context.pop('rpcserver') + LOG.debug("Stopped rpcserver@{0}@{1}".format(context['topic'], context['server'])) + except: + LOG.error("Failed to update listener for topic/server:{0}/{1}" + .format(topic, servername)) + continue + + def add_listener(self, topic, server, listener_endpoints=None): + context = self.listeners.get(topic,{}).get(server, {}) + if not context: + context = { + 'endpoints': listener_endpoints, + 'topic': topic, + 'server': server, + 'active': True + } + if not self.listeners.get(topic, None): + self.listeners[topic] = {} + self.listeners[topic][server] = context + else: + context['endpoints'] = listener_endpoints + context['active'] = True + + self._refresh() + + def remove_listener(self, topic, server): + context = self.listeners.get(topic,{}).get(server, {}) + if context: + context['active'] = False + self._refresh() + + def is_listening(self, topic, server): + context = self.listeners.get(topic,{}).get(server, {}) + return context.get('active', False) + + def any_listener(self): + for topic, servers in self.listeners.items(): + for servername, context in servers.items(): + isactive = context.get('active', False) + if isactive: + return True + return False + + def call(self, topic, server, api_name, **api_kwargs): + target = oslo_messaging.Target( + topic=topic, server=server, version=self.broker_endpoint.Version, + namespace=self.broker_endpoint.Namespace) + queryclient = oslo_messaging.RPCClient(self.transport, target, timeout = 2, retry = 0) + return queryclient.call({}, api_name, **api_kwargs) + + def cast(self, topic, api_name, **api_kwargs): + target = oslo_messaging.Target( + topic=topic, fanout=True, version=self.broker_endpoint.Version, + namespace=self.broker_endpoint.Namespace) + queryclient = oslo_messaging.RPCClient(self.transport, target) + queryclient.cast({}, api_name, **api_kwargs) diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/client/locationproducer.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/client/locationproducer.py new file mode 100644 index 0000000..bdf614d --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservicesdk/client/locationproducer.py @@ -0,0 +1,84 @@ +import os +import json +import time +import oslo_messaging +from oslo_config import cfg + +from locationservicesdk.client.base import BrokerClientBase + +import logging + +LOG = logging.getLogger(__name__) + +from locationservicesdk.common.helpers import log_helper +log_helper.config_logger(LOG) + + +class LocationProducer(BrokerClientBase): + class ListenerEndpoint(object): + target = oslo_messaging.Target(namespace='notification', version='1.0') + + def __init__(self, location_info, handler=None): + self.location_info = location_info + self.handler = handler + pass + + def QueryLocation(self, ctx, **rpc_kwargs): + LOG.debug ("LocationProducer QueryLocation called %s" %rpc_kwargs) + return self.location_info + + def TriggerAnnouncement(self, ctx, **rpc_kwargs): + LOG.debug ("LocationProducer TriggerAnnouncement called %s" %rpc_kwargs) + if self.handler: + return self.handler.handle(**rpc_kwargs) + else: + return False + + def __init__(self, node_name, registrationservice_transport_endpoint): + self.Id = id(self) + self.node_name = node_name + super(LocationProducer, self).__init__( + 'locationproducer', registrationservice_transport_endpoint) + return + + def __del__(self): + super(LocationProducer, self).__del__() + return + + def announce_location(self, LocationInfo): + location_topic_all='LocationListener-*' + location_topic='LocationListener-{0}'.format(self.node_name) + server = None + while True: + try: + self.cast(location_topic_all, 'NotifyLocation', location_info=LocationInfo) + LOG.debug("Broadcast location info:{0}@Topic:{1}".format(LocationInfo, location_topic)) + except Exception as ex: + LOG.debug("Failed to publish location due to: {0}".format(str(ex))) + continue + else: + break + + def start_location_listener(self, location_info, handler=None): + + topic='LocationQuery' + server="LocationService-{0}".format(self.node_name) + endpoints = [LocationProducer.ListenerEndpoint(location_info, handler)] + + super(LocationProducer, self).add_listener( + topic, server, endpoints) + return True + + def stop_location_listener(self): + topic='LocationQuery' + server="LocationService-{0}".format(self.node_name) + super(LocationProducer, self).remove_listener( + topic, server) + + def is_listening(self): + topic='LocationQuery' + server="LocationService-{0}".format(self.node_name) + return super(LocationProducer, self).is_listening( + topic, server) + + diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/common/helpers/log_helper.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/common/helpers/log_helper.py new file mode 100644 index 0000000..d1a16e7 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservicesdk/common/helpers/log_helper.py @@ -0,0 +1,12 @@ +import logging + +def get_logger(module_name): + logger = logging.getLogger(module_name) + return config_logger(logger) + +def config_logger(logger): + ''' + configure the logger: uncomment following lines for debugging + ''' + # logger.setLevel(level=logging.DEBUG) + return logger diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/common/helpers/rpc_helper.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/common/helpers/rpc_helper.py new file mode 100644 index 0000000..ed98405 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservicesdk/common/helpers/rpc_helper.py @@ -0,0 +1,22 @@ +#coding=utf-8 + +import os +import json + +import oslo_messaging +from oslo_config import cfg + + +def setup_client(rpc_endpoint_info, topic, server): + oslo_messaging.set_transport_defaults(rpc_endpoint_info.Exchange) + transport = oslo_messaging.get_rpc_transport(cfg.CONF, url=rpc_endpoint_info.TransportEndpoint) + target = oslo_messaging.Target(topic=topic, + version=rpc_endpoint_info.Version, + server=server, + namespace=rpc_endpoint_info.Namespace) + client = oslo_messaging.RPCClient(transport, target) + return client + +def get_transport(rpc_endpoint_info): + oslo_messaging.set_transport_defaults(rpc_endpoint_info.Exchange) + return oslo_messaging.get_rpc_transport(cfg.CONF, url=rpc_endpoint_info.TransportEndpoint) diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/model/__init__.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/__init__.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/location.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/location.py new file mode 100644 index 0000000..13ca17d --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/location.py @@ -0,0 +1,10 @@ +#coding=utf-8 + +from wsme import types as wtypes +from locationservicesdk.model.dto.resourcetype import EnumResourceType + +class LocationInfo(wtypes.Base): + NodeName = wtypes.text + PodIP = wtypes.text + Timestamp = float + ResourceTypes = [EnumResourceType] diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/resourcetype.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/resourcetype.py new file mode 100644 index 0000000..a04f96b --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/resourcetype.py @@ -0,0 +1,9 @@ +#coding=utf-8 + +from wsme import types as wtypes + +EnumResourceType = wtypes.Enum(str, 'PTP', 'FPGA') + +class ResourceType(object): + TypePTP = "PTP" + TypeFPGA = 'FPGA' diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/rpc_endpoint.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/rpc_endpoint.py new file mode 100644 index 0000000..150f57d --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservicesdk/model/dto/rpc_endpoint.py @@ -0,0 +1,34 @@ +#coding=utf-8 + +from wsme import types as wtypes + +RPC_ENDPOINT_BASE = { + 'Version': '1.0', + 'Namespace': 'notification', + 'Exchange': 'notification_exchange', + 'TransportEndpoint': '', + 'Topic': '', + 'Server': '' +} + +class RpcEndpointInfo(wtypes.Base): + TransportEndpoint = wtypes.text + Exchange = wtypes.text + Topic = wtypes.text + Server = wtypes.text + Version = wtypes.text + Namespace = wtypes.text + + def __init__(self, transport_endpoint): + self.endpoint_json = { + 'Version': RPC_ENDPOINT_BASE['Version'], + 'Namespace': RPC_ENDPOINT_BASE['Namespace'], + 'Exchange': RPC_ENDPOINT_BASE['Exchange'], + 'TransportEndpoint': transport_endpoint, + 'Topic': RPC_ENDPOINT_BASE['Topic'], + 'Server': RPC_ENDPOINT_BASE['Server'] + } + super(RpcEndpointInfo, self).__init__(**self.endpoint_json) + + def to_dict(self): + return self.endpoint_json diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/services/__init__.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locationservice-base/centos/docker/locationservice/locationservicesdk/services/daemon.py b/locationservice-base/centos/docker/locationservice/locationservicesdk/services/daemon.py new file mode 100644 index 0000000..02455bd --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/locationservicesdk/services/daemon.py @@ -0,0 +1,131 @@ + +import os +import json +import time +import oslo_messaging +from oslo_config import cfg +import logging + +import multiprocessing as mp + +from locationservicesdk.common.helpers import rpc_helper +from locationservicesdk.model.dto.rpc_endpoint import RpcEndpointInfo +from locationservicesdk.model.dto.resourcetype import ResourceType + +from locationservicesdk.client.locationproducer import LocationProducer + +LOG = logging.getLogger(__name__) + +from locationservicesdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +'''Entry point of Default Process Worker''' +def ProcessWorkerDefault(event, sqlalchemy_conf_json, registration_endpoint, location_info_json): + worker = LocationWatcherDefault(event, sqlalchemy_conf_json, registration_endpoint, location_info_json) + worker.run() + return + + +class LocationWatcherDefault: + class LocationRequestHandlerDefault(object): + def __init__(self, watcher): + self.watcher = watcher + + def handle(self, **rpc_kwargs): + self.watcher.signal_location_event() + + def __init__(self, event, sqlalchemy_conf_json, registration_transport_endpoint, location_info_json): + self.sqlalchemy_conf = json.loads(sqlalchemy_conf_json) + self.event = event + self.event_timeout = float(2.0) + self.event_iteration = 0 + self.location_info = json.loads(location_info_json) + this_node_name = self.location_info['NodeName'] + + self.registration_endpoint = RpcEndpointInfo(registration_transport_endpoint) + self.LocationProducer = LocationProducer( + this_node_name, + self.registration_endpoint.TransportEndpoint) + + def signal_location_event(self): + if self.event: + self.event.set() + else: + LOG.warning("Unable to assert location event") + pass + + def run(self): + # start location listener + self.__start_listener() + while True: + # annouce the location + self.__announce_location() + if self.event.wait(self.event_timeout): + LOG.debug("daemon control event is asserted") + self.event.clear() + else: + # max timeout: 1 hour + if self.event_timeout < float(3600): + self.event_timeout = self.event_timeout + self.event_timeout + LOG.debug("daemon control event is timeout") + continue + self.__stop_listener() + + '''Start listener to answer querying from clients''' + def __start_listener(self): + LOG.debug("start listener to answer location querying") + + self.LocationProducer.start_location_listener( + self.location_info, + LocationWatcherDefault.LocationRequestHandlerDefault(self) + ) + return + + def __stop_listener(self): + LOG.debug("stop listener to answer location querying") + + self.LocationProducer.stop_location_listener(self.location_info) + return + + '''announce location''' + def __announce_location(self): + LOG.debug("announce location info to clients") + self.LocationProducer.announce_location(self.location_info) + return + +class DaemonControl(object): + + def __init__( + self, sqlalchemy_conf_json, registration_transport_endpoint, + location_info, process_worker = None, daemon_mode=True): + + self.daemon_mode = daemon_mode + self.event = mp.Event() + self.registration_endpoint = RpcEndpointInfo(registration_transport_endpoint) + self.registration_transport = rpc_helper.get_transport(self.registration_endpoint) + self.location_info = location_info + self.sqlalchemy_conf_json = sqlalchemy_conf_json + + if not process_worker: + process_worker = ProcessWorkerDefault + self.process_worker = process_worker + + if not self.daemon_mode: + return + + self.mpinstance = mp.Process( + target=process_worker, + args=(self.event, self.sqlalchemy_conf_json, + self.registration_endpoint.TransportEndpoint, + self.location_info)) + self.mpinstance.start() + + pass + + def refresh(self): + if not self.daemon_mode: + self.process_worker( + self.event, self.sqlalchemy_conf_json, + self.registration_endpoint.TransportEndpoint, self.location_info) + + self.event.set() diff --git a/locationservice-base/centos/docker/locationservice/public/css/style.css b/locationservice-base/centos/docker/locationservice/public/css/style.css new file mode 100644 index 0000000..55c9db5 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/public/css/style.css @@ -0,0 +1,43 @@ +body { + background: #311F00; + color: white; + font-family: 'Helvetica Neue', 'Helvetica', 'Verdana', sans-serif; + padding: 1em 2em; +} + +a { + color: #FAFF78; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +div#content { + width: 800px; + margin: 0 auto; +} + +form { + margin: 0; + padding: 0; + border: 0; +} + +fieldset { + border: 0; +} + +input.error { + background: #FAFF78; +} + +header { + text-align: center; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Futura-CondensedExtraBold', 'Futura', 'Helvetica', sans-serif; + text-transform: uppercase; +} diff --git a/locationservice-base/centos/docker/locationservice/public/images/logo.png b/locationservice-base/centos/docker/locationservice/public/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..39de6e549257922dcc10ac870d2fd4d67bd8a110 GIT binary patch literal 9095 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 EKPrEkOaK4? literal 0 HcmV?d00001 diff --git a/locationservice-base/centos/docker/locationservice/setup.cfg b/locationservice-base/centos/docker/locationservice/setup.cfg new file mode 100644 index 0000000..9935a8c --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/setup.cfg @@ -0,0 +1,6 @@ +[nosetests] +match=^test +where=apiserver +nocapture=1 +cover-package=apiserver +cover-erase=1 diff --git a/locationservice-base/centos/docker/locationservice/setup.py b/locationservice-base/centos/docker/locationservice/setup.py new file mode 100644 index 0000000..73c70f4 --- /dev/null +++ b/locationservice-base/centos/docker/locationservice/setup.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +try: + from setuptools import setup, find_packages +except ImportError: + from ez_setup import use_setuptools + use_setuptools() + from setuptools import setup, find_packages + +setup( + name='apiserver', + version='0.1', + description='', + author='', + author_email='', + install_requires=[ + "pecan", + ], + test_suite='apiserver', + zip_safe=False, + include_package_data=True, + packages=find_packages(exclude=['ez_setup']) +) diff --git a/locationservice-base/centos/locationservice-base.stable_docker_image b/locationservice-base/centos/locationservice-base.stable_docker_image new file mode 100644 index 0000000..fe18da2 --- /dev/null +++ b/locationservice-base/centos/locationservice-base.stable_docker_image @@ -0,0 +1,2 @@ +BUILDER=docker +LABEL=locationservice-base \ No newline at end of file diff --git a/notificationclient-base/centos/docker/Dockerfile b/notificationclient-base/centos/docker/Dockerfile new file mode 100644 index 0000000..0c0addf --- /dev/null +++ b/notificationclient-base/centos/docker/Dockerfile @@ -0,0 +1,23 @@ +ARG BASE +FROM ${BASE} + +ARG STX_REPO_FILE=/etc/yum.repos.d/stx.repo + +ENV KUBE_LATEST_VERSION="v1.18.3" + +RUN set -ex ;\ + yum install --disablerepo=* \ + $(grep '^name=' ${STX_REPO_FILE} | awk -F '=' '{printf "--enablerepo=" $2 " "}') \ + -y \ + gcc python3-devel python3-pip \ + && pip3 install --user pecan \ + && pip3 install oslo-config \ + && pip3 install oslo-messaging \ + && pip3 install WSME \ + && pip3 install sqlalchemy + +WORKDIR /opt/ +COPY ./notificationclient-sidecar /opt/notificationclient +RUN cd /opt/notificationclient && python3 setup.py develop + +CMD ["bash"] diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/MANIFEST.in b/notificationclient-base/centos/docker/notificationclient-sidecar/MANIFEST.in new file mode 100644 index 0000000..c922f11 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/MANIFEST.in @@ -0,0 +1 @@ +recursive-include public * diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/config.py b/notificationclient-base/centos/docker/notificationclient-sidecar/config.py new file mode 100644 index 0000000..8a7714a --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/config.py @@ -0,0 +1,66 @@ +import os +SIDECAR_API_PORT = os.environ.get("SIDECAR_API_PORT", "8080") +SIDECAR_API_HOST = os.environ.get("SIDECAR_API_HOST", "127.0.0.1") +# Server Specific Configurations +server = { + 'port': SIDECAR_API_PORT, + 'host': SIDECAR_API_HOST +} + +# Pecan Application Configurations +app = { + 'root': 'sidecar.controllers.root.RootController', + 'modules': ['sidecar'], + 'static_root': '%(confdir)s/public', + 'template_path': '%(confdir)s/sidecar/templates', + 'debug': True, + 'errors': { + 404: '/error/404', + '__force_dict__': True + } +} + +logging = { + 'root': {'level': 'INFO', 'handlers': ['console']}, + 'loggers': { + 'sidecar': {'level': 'DEBUG', 'handlers': ['console'], 'propagate': False}, + 'pecan': {'level': 'DEBUG', 'handlers': ['console'], 'propagate': False}, + 'py.warnings': {'handlers': ['console']}, + '__force_dict__': True + }, + 'handlers': { + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'color' + } + }, + 'formatters': { + 'simple': { + 'format': ('%(asctime)s %(levelname)-5.5s [%(name)s]' + '[%(threadName)s] %(message)s') + }, + 'color': { + '()': 'pecan.log.ColorFormatter', + 'format': ('%(asctime)s [%(padded_color_levelname)s] [%(name)s]' + '[%(threadName)s] %(message)s'), + '__force_dict__': True + } + } +} + +# Bindings and options to pass to SQLAlchemy's ``create_engine`` +sqlalchemy = { + 'url' : 'sqlite:///sidecar.db', + 'echo' : False, + 'echo_pool' : False, + 'pool_recycle' : 3600, + 'encoding' : 'utf-8' +} + +# Custom Configurations must be in Python dictionary format:: +# +# foo = {'bar':'baz'} +# +# All configurations are accessible at:: +# pecan.conf diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/base.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/base.py new file mode 100644 index 0000000..8cccacf --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/base.py @@ -0,0 +1,109 @@ +import os +import json +import time +import oslo_messaging +from oslo_config import cfg +from notificationclientsdk.common.helpers import rpc_helper +from notificationclientsdk.model.dto.rpc_endpoint import RpcEndpointInfo + +import logging + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +class BrokerClientBase(object): + def __init__(self, broker_name, broker_transport_endpoint): + self.broker_name = broker_name + self.listeners = {} + self.broker_endpoint = RpcEndpointInfo(broker_transport_endpoint) + self.transport = rpc_helper.get_transport(self.broker_endpoint) + LOG.debug("Created Broker client:{0}".format(broker_name)) + + def __del__(self): + self.transport.cleanup() + del self.transport + return + + def __create_listener(self, context): + target = oslo_messaging.Target( + topic=context['topic'], + server=context['server']) + endpoints = context['endpoints'] + server = oslo_messaging.get_rpc_server( + self.transport, target, endpoints, executor=None) + return server + + def _refresh(self): + for topic, servers in self.listeners.items(): + for servername, context in servers.items(): + try: + rpcserver = context.get('rpcserver', None) + isactive = context.get('active', False) + if isactive and not rpcserver: + rpcserver = self.__create_listener(context) + rpcserver.start() + context['rpcserver'] = rpcserver + LOG.debug("Started rpcserver@{0}@{1}".format(context['topic'], context['server'])) + elif not isactive and rpcserver: + rpcserver.stop() + rpcserver.wait() + context.pop('rpcserver') + LOG.debug("Stopped rpcserver@{0}@{1}".format(context['topic'], context['server'])) + except: + LOG.error("Failed to update listener for topic/server:{0}/{1}" + .format(topic, servername)) + continue + + def add_listener(self, topic, server, listener_endpoints=None): + context = self.listeners.get(topic,{}).get(server, {}) + if not context: + context = { + 'endpoints': listener_endpoints, + 'topic': topic, + 'server': server, + 'active': True + } + if not self.listeners.get(topic, None): + self.listeners[topic] = {} + self.listeners[topic][server] = context + else: + context['endpoints'] = listener_endpoints + context['active'] = True + + self._refresh() + + def remove_listener(self, topic, server): + context = self.listeners.get(topic,{}).get(server, {}) + if context: + context['active'] = False + self._refresh() + + def is_listening(self, topic, server): + context = self.listeners.get(topic,{}).get(server, {}) + return context.get('active', False) + + def any_listener(self): + for topic, servers in self.listeners.items(): + for servername, context in servers.items(): + isactive = context.get('active', False) + if isactive: + return True + return False + + def call(self, topic, server, api_name, timeout=None, retry=None, **api_kwargs): + target = oslo_messaging.Target( + topic=topic, server=server, version=self.broker_endpoint.Version, + namespace=self.broker_endpoint.Namespace) + # note: the call might stuck here on 'Connection failed' and retry forever + # due to the tcp connection is unreachable: 'AMQP server on : is unreachable: timed out' + queryclient = oslo_messaging.RPCClient(self.transport, target, timeout = timeout, retry = retry) + return queryclient.call({}, api_name, **api_kwargs) + + def cast(self, topic, api_name, timeout=None, retry=None, **api_kwargs): + target = oslo_messaging.Target( + topic=topic, fanout=True, version=self.broker_endpoint.Version, + namespace=self.broker_endpoint.Namespace) + queryclient = oslo_messaging.RPCClient(self.transport, target, timeout = timeout, retry = retry) + queryclient.cast({}, api_name, **api_kwargs) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/locationservice.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/locationservice.py new file mode 100644 index 0000000..53c18b5 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/locationservice.py @@ -0,0 +1,149 @@ +import os +import json +import time +import oslo_messaging +from oslo_config import cfg + +from notificationclientsdk.common.helpers import hostfile_helper + +from notificationclientsdk.client.base import BrokerClientBase + +from notificationclientsdk.client.notificationservice import NotificationServiceClient, NotificationHandlerBase + +import logging + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +class LocationHandlerBase(object): + + def __init__(self): + self.NOTIFICATIONSERVICE_HOSTNAME = 'notificationservice-{0}' + pass + + def handle(self, location_info): + pass + +class LocationHandlerDefault(LocationHandlerBase): + def __init__(self, host_file_path='/etc/hosts'): + self.hostfile = host_file_path + super(LocationHandlerDefault, self).__init__() + + def handle(self, location_info): + LOG.debug("Received location info:{0}".format(location_info)) + nodename = location_info.get('NodeName', None) + podip = location_info.get("PodIP", None) + if not nodename or not podip: + LOG.warning("Mising NodeName or PodIP inside location info") + return False + + hostfile_helper.update_host( + self.NOTIFICATIONSERVICE_HOSTNAME.format(nodename), + podip) + LOG.debug("Updated location with IP:{0}".format(podip)) + return True + +class LocationServiceClient(BrokerClientBase): + class ListenerEndpoint(object): + target = oslo_messaging.Target(namespace='notification', version='1.0') + + def __init__(self, handler): + self.handler = handler + + def NotifyLocation(self, ctx, location_info): + LOG.debug("LocationServiceClient NotifyLocation called %s" % location_info) + self.handler.handle(location_info) + return time.time() + + def __init__(self, registrationservice_transport_endpoint): + self.Id = id(self) + super(LocationServiceClient, self).__init__( + 'locationservice', registrationservice_transport_endpoint) + return + + def __del__(self): + super(LocationServiceClient, self).__del__() + return + + def update_location(self, target_node_name, location_handler=None, timeout=None, retry=None): + if not location_handler: + location_handler = LocationHandlerDefault('/etc/hosts') + location_info = self.query_location(target_node_name, timeout=timeout, retry=retry) + if location_info: + location_handler.handle(location_info) + return True + else: + return False + + def query_location(self, target_node_name, timeout=None, retry=None): + topic = 'LocationQuery' + server = 'LocationService-{0}'.format(target_node_name) + return self.call(topic, server, 'QueryLocation', timeout=timeout, retry=retry) + + def trigger_location_annoucement(self, timeout=None, retry=None): + topic = 'LocationQuery' + return self.cast(topic, 'TriggerAnnouncement', timeout=timeout, retry=retry) + + def add_location_listener(self, target_node_name, location_handler=None): + if not location_handler: + location_handler = LocationHandlerDefault('/etc/hosts') + + topic='LocationListener-{0}'.format(target_node_name) + server="LocationListener-{0}".format(self.Id) + endpoints = [LocationServiceClient.ListenerEndpoint(location_handler)] + + super(LocationServiceClient, self).add_listener( + topic, server, endpoints) + return True + + def remove_location_listener(self, target_node_name): + topic='LocationListener-{0}'.format(target_node_name) + server="LocationListener-{0}".format(self.Id) + super(LocationServiceClient, self).remove_listener( + topic, server) + + def is_listening_on_location(self, target_node_name): + topic='LocationListener-{0}'.format(target_node_name) + server="LocationListener-{0}".format(self.Id) + return super(LocationServiceClient, self).is_listening( + topic, server) + + ### extensions + def trigger_publishing_status(self, resource_type, + timeout=None, retry=None, resource_qualifier_json=None): + topic = '{0}-Status'.format(resource_type) + try: + self.cast( + topic, 'TriggerDelivery', timeout=timeout, retry=retry, + QualifierJson=resource_qualifier_json) + except Exception as ex: + LOG.warning("Fail to trigger_publishing_status: {0}".format(str(ex))) + return False + return True + + def add_resource_status_listener(self, resource_type, status_handler=None): + if not status_handler: + status_handler = NotificationHandlerBase() + + topic='{0}-Event-*'.format(resource_type) + server="{0}-EventListener-{1}".format(resource_type, self.Id) + endpoints = [NotificationServiceClient.ListenerEndpoint(status_handler)] + + super(LocationServiceClient, self).add_listener( + topic, server, endpoints) + return True + + def remove_resource_status_listener(self, resource_type): + topic='{0}-Event-*'.format(resource_type) + server="{0}-EventListener-{1}".format(resource_type, self.Id) + super(LocationServiceClient, self).remove_listener( + topic, server) + pass + + def is_listening_on_resource(self, resource_type): + topic='{0}-Event-*'.format(resource_type) + server="{0}-EventListener-{1}".format(resource_type, self.Id) + return super(LocationServiceClient, self).is_listening( + topic, server) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/notificationservice.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/notificationservice.py new file mode 100644 index 0000000..52a8030 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/client/notificationservice.py @@ -0,0 +1,85 @@ +import os +import json +import time +import oslo_messaging +from oslo_config import cfg + +from notificationclientsdk.model.dto.rpc_endpoint import RpcEndpointInfo + +from notificationclientsdk.client.base import BrokerClientBase + +from notificationclientsdk.model.dto.subscription import SubscriptionInfo +from notificationclientsdk.repository.subscription_repo import SubscriptionRepo + +import logging + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +class NotificationHandlerBase(object): + + def __init__(self): + pass + + def handle(self, notification_info): + return False + +class NotificationServiceClient(BrokerClientBase): + class ListenerEndpoint(object): + target = oslo_messaging.Target(namespace='notification', version='1.0') + + def __init__(self, handler): + self.handler = handler + + def NotifyStatus(self, ctx, notification): + LOG.debug("NotificationServiceClient NotifyStatus called %s" % notification) + self.handler.handle(notification) + return time.time() + + '''Init client to notification service''' + def __init__(self, target_node_name, notificationservice_transport_endpoint): + self.Id = id(self) + self.target_node_name = target_node_name + super(NotificationServiceClient, self).__init__( + '{0}'.format(target_node_name), + notificationservice_transport_endpoint) + return + + def __del__(self): + super(NotificationServiceClient, self).__del__() + return + + def query_resource_status(self, resource_type, + timeout=None, retry=None, resource_qualifier_json=None): + topic = '{0}-Status'.format(resource_type) + server = '{0}-Tracking-{1}'.format(resource_type, self.target_node_name) + return self.call( + topic, server, 'QueryStatus', timeout=timeout, retry=retry, + QualifierJson=resource_qualifier_json) + + def add_resource_status_listener(self, resource_type, status_handler=None): + if not status_handler: + status_handler = NotificationHandlerBase() + + topic='{0}-Event-{1}'.format(resource_type, self.broker_name) + server="{0}-EventListener-{1}".format(resource_type, self.Id) + endpoints = [NotificationServiceClient.ListenerEndpoint(status_handler)] + + super(NotificationServiceClient, self).add_listener( + topic, server, endpoints) + return True + + def remove_resource_status_listener(self, resource_type): + topic='{0}-Event-{1}'.format(resource_type, self.broker_name) + server="{0}-EventListener-{1}".format(resource_type, self.Id) + super(NotificationServiceClient, self).remove_listener( + topic, server) + pass + + def is_listening_on_resource(self, resource_type): + topic='{0}-Event-{1}'.format(resource_type, self.broker_name) + server="{0}-EventListener-{1}".format(resource_type, self.Id) + return super(NotificationServiceClient, self).is_listening( + topic, server) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/hostfile_helper.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/hostfile_helper.py new file mode 100644 index 0000000..d78d14a --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/hostfile_helper.py @@ -0,0 +1,29 @@ +#coding:utf8 + +import os +import sys +import re + +def update_host(hostname, ip): + hostsfile="/etc/hosts" + Lines=[] + replaced = False + + with open(hostsfile) as fd: + for line in fd.readlines(): + if line.strip() == '': + Lines.append(line) + else: + h_name = line.strip().split()[1] + if h_name == hostname: + lin = "{0} {1}".format(ip, hostname) + Lines.append(lin) + replaced = True + else: + Lines.append(line) + + if replaced == False: + Lines.append("{0} {1}".format(ip, hostname)) + + with open(hostsfile, 'w') as fc: + fc.writelines(Lines) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/log_helper.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/log_helper.py new file mode 100644 index 0000000..edaa55a --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/log_helper.py @@ -0,0 +1,12 @@ +import logging + +def get_logger(module_name): + logger = logging.getLogger(module_name) + return config_logger(logger) + +def config_logger(logger): + ''' + configure the logger: uncomment following lines for debugging + ''' + # logger.setLevel(level=logging.DEBUG) + return logger diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/nodeinfo_helper.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/nodeinfo_helper.py new file mode 100644 index 0000000..33773d5 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/nodeinfo_helper.py @@ -0,0 +1,71 @@ +import json +from notificationclientsdk.repository.node_repo import NodeRepo + +class NodeInfoHelper(object): + BROKER_NODE_ALL = '*' + residing_node_name = None + + @staticmethod + def set_residing_node(residing_node_name): + NodeInfoHelper.residing_node_name = residing_node_name + + @staticmethod + def get_residing_node(): + residing_node_name = NodeInfoHelper.residing_node_name + return residing_node_name + + @staticmethod + def expand_node_name(node_name_pattern): + if node_name_pattern == '.': + return NodeInfoHelper.residing_node_name + elif node_name_pattern == NodeInfoHelper.BROKER_NODE_ALL: + return NodeInfoHelper.BROKER_NODE_ALL + else: + return node_name_pattern + + @staticmethod + def default_node_name(node_name_pattern): + if node_name_pattern == '.' or node_name_pattern == '*': + return NodeInfoHelper.residing_node_name + else: + return node_name_pattern + + @staticmethod + def match_node_name(node_name_pattern, target_node_name): + if node_name_pattern == '*': + return True + elif node_name_pattern == '.': + return NodeInfoHelper.residing_node_name == target_node_name + else: + return node_name_pattern == target_node_name + + @staticmethod + def enumerate_nodes(node_name_pattern): + ''' + enumerate nodes from node repo by pattern + ''' + nodeinfos = [] + if not node_name_pattern: + raise ValueError("node name pattern is invalid") + + nodeinfo_repo = None + try: + nodeinfo_repo = NodeRepo(autocommit=True) + filter = {} + if node_name_pattern == '*': + pass + elif not node_name_pattern or node_name_pattern == '.': + filter = { 'NodeName': NodeInfoHelper.residing_node_name } + else: + filter = { 'NodeName': node_name_pattern } + + nodeinfos = [x.NodeName for x in nodeinfo_repo.get(Status=1, **filter)] + + except Exception as ex: + LOG.warning("Failed to enumerate nodes:{0}".format(str(ex))) + nodeinfos = None + finally: + if nodeinfo_repo: + del nodeinfo_repo + + return nodeinfos diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/rpc_helper.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/rpc_helper.py new file mode 100644 index 0000000..f159b74 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/rpc_helper.py @@ -0,0 +1,22 @@ +#coding=utf-8 + +import os +import json + +import oslo_messaging +from oslo_config import cfg + + +def setup_client(rpc_endpoint_info, topic, server): + oslo_messaging.set_transport_defaults(rpc_endpoint_info.Exchange) + transport = oslo_messaging.get_rpc_transport(cfg.CONF, url=rpc_endpoint_info.TransportEndpoint) + target = oslo_messaging.Target(topic=topic, + version=rpc_endpoint_info.Version, + server=server, + namespace=rpc_endpoint_info.Namespace) + client = oslo_messaging.RPCClient(transport, target) + return client + +def get_transport(rpc_endpoint_info): + oslo_messaging.set_transport_defaults(rpc_endpoint_info.Exchange) + return oslo_messaging.get_rpc_transport(cfg.CONF, url=rpc_endpoint_info.TransportEndpoint) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/subscription_helper.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/subscription_helper.py new file mode 100644 index 0000000..6c1fc18 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/common/helpers/subscription_helper.py @@ -0,0 +1,49 @@ +#coding=utf-8 + +import os +import json + +import requests +import logging + +from notificationclientsdk.common.helpers.nodeinfo_helper import NodeInfoHelper + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +def notify(subscriptioninfo, notification, timeout=2, retry=3): + result = False + while True: + retry = retry - 1 + try: + headers = {'Content-Type': 'application/json'} + data = json.dumps(notification) + url = subscriptioninfo.EndpointUri + response = requests.post(url, data=data, headers=headers, timeout=timeout) + response.raise_for_status() + result = True + return response + except requests.exceptions.ConnectionError as errc: + if retry > 0: + LOG.warning("Retry notifying due to: {0}".format(str(errc))) + continue + raise errc + except requests.exceptions.Timeout as errt: + if retry > 0: + LOG.warning("Retry notifying due to: {0}".format(str(errt))) + continue + raise errt + except requests.exceptions.RequestException as ex: + LOG.warning("Failed to notify due to: {0}".format(str(ex))) + raise ex + except requests.exceptions.HTTPError as ex: + LOG.warning("Failed to notify due to: {0}".format(str(ex))) + raise ex + except Exception as ex: + LOG.warning("Failed to notify due to: {0}".format(str(ex))) + raise ex + + return result + diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/__init__.py new file mode 100644 index 0000000..1216f3f --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/__init__.py @@ -0,0 +1,16 @@ +from notificationclientsdk.model.dto.subscription import SubscriptionInfo +from notificationclientsdk.model.dto.subscription import ResourceQualifierPtp + +from wsme.rest.json import tojson + +@tojson.when_object(SubscriptionInfo) +def subscriptioninfo_tojson(datatype, value): + if value is None: + return None + return value.to_dict() + +@tojson.when_object(ResourceQualifierPtp) +def resourcequalifierptp_tojson(datatype, value): + if value is None: + return None + return value.to_dict() diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/location.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/location.py new file mode 100644 index 0000000..f9f7e0b --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/location.py @@ -0,0 +1,30 @@ +#coding=utf-8 + +import json + +from wsme import types as wtypes +from notificationclientsdk.model.dto.resourcetype import EnumResourceType + +class LocationInfo(wtypes.Base): + NodeName = wtypes.text + PodIP = wtypes.text + Timestamp = float + ResourceTypes = [EnumResourceType] + + def to_dict(self): + d = { + 'NodeName': self.NodeName, + 'PodIP': self.PodIP, + 'Timestamp': self.Timestamp, + 'ResourceTypes': [x for x in self.ResourceTypes] + } + return d + + def to_orm(self): + d = { + 'NodeName': self.NodeName, + 'PodIP': self.PodIP or '', + 'Timestamp': self.Timestamp, + 'ResourceTypes': json.dumps([x for x in self.ResourceTypes]) if self.ResourceTypes else '' + } + return d diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/resourcetype.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/resourcetype.py new file mode 100644 index 0000000..5e80cf3 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/resourcetype.py @@ -0,0 +1,9 @@ +#coding=utf-8 + +from wsme import types as wtypes + +EnumResourceType = wtypes.Enum(str, 'PTP', 'FPGA') + +class ResourceType(object): + TypePTP = "PTP" + TypeFPGA = "FPGA" diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/rpc_endpoint.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/rpc_endpoint.py new file mode 100644 index 0000000..a955655 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/rpc_endpoint.py @@ -0,0 +1,34 @@ +#coding=utf-8 + +from wsme import types as wtypes + +RPC_ENDPOINT_BASE = { + 'Version': '1.0', + 'Namespace': 'notification', + 'Exchange': 'notification_exchange', + 'TransportEndpoint': '', + 'Topic': '', + 'Server': '' +} + +class RpcEndpointInfo(wtypes.Base): + TransportEndpoint = wtypes.text + Exchange = wtypes.text + Topic = wtypes.text + Server = wtypes.text + Version = wtypes.text + Namespace = wtypes.text + + def __init__(self, transport_endpoint): + self.endpoint_json = { + 'Version': RPC_ENDPOINT_BASE['Version'], + 'Namespace': RPC_ENDPOINT_BASE['Namespace'], + 'Exchange': RPC_ENDPOINT_BASE['Exchange'], + 'TransportEndpoint': transport_endpoint, + 'Topic': RPC_ENDPOINT_BASE['Topic'], + 'Server': RPC_ENDPOINT_BASE['Server'] + } + super(RpcEndpointInfo, self).__init__(**self.endpoint_json) + + def to_dict(self): + return self.endpoint_json diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/subscription.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/subscription.py new file mode 100644 index 0000000..12714af --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/dto/subscription.py @@ -0,0 +1,94 @@ +#coding=utf-8 + +import os +import json +from wsme import types as wtypes +import datetime +import time + +import uuid + +from notificationclientsdk.model.dto.resourcetype import EnumResourceType, ResourceType + +''' +Base for Resource Qualifiers +''' +class ResourceQualifierBase(wtypes.Base): + def __init__(self, **kw): + super(ResourceQualifierBase, self).__init__(**kw) + + def to_dict(self): + pass + + +''' +Resource Qualifiers PTP +''' +class ResourceQualifierPtp(ResourceQualifierBase): + NodeName = wtypes.text + + def __init__(self, **kw): + self.NodeName = kw.pop('NodeName', None) + super(ResourceQualifierPtp, self).__init__(**kw) + + def to_dict(self): + d = { + 'NodeName': self.NodeName + } + return d + +''' +ViewModel of Subscription +''' +class SubscriptionInfo(wtypes.Base): + SubscriptionId = wtypes.text + UriLocation = wtypes.text + ResourceType = EnumResourceType + EndpointUri = wtypes.text + + # dynamic type depending on ResourceType + def set_resource_qualifier(self, value): + if isinstance(value, wtypes.Base): + self._ResourceQualifer = value + else: + self._ResourceQualifierJson = value + self._ResourceQualifer = None + + def get_resource_qualifier(self): + if not self._ResourceQualifer: + if self.ResourceType == ResourceType.TypePTP: + self._ResourceQualifer = ResourceQualifierPtp(**self._ResourceQualifierJson) + + return self._ResourceQualifer + + ResourceQualifier = wtypes.wsproperty(wtypes.Base, + get_resource_qualifier, set_resource_qualifier, mandatory=True) + + + def __init__(self, orm_entry=None): + if orm_entry: + self.SubscriptionId = orm_entry.SubscriptionId + self.ResourceType = orm_entry.ResourceType + self.UriLocation = orm_entry.UriLocation + self.ResourceQualifier = json.loads(orm_entry.ResourceQualifierJson) + self.EndpointUri = orm_entry.EndpointUri + + def to_dict(self): + d = { + 'SubscriptionId': self.SubscriptionId, + 'ResourceType': self.ResourceType, + 'UriLocation': self.UriLocation, + 'EndpointUri': self.EndpointUri, + 'ResourceQualifier': self.ResourceQualifier.to_dict() + } + return d + + def to_orm(self): + d = { + 'SubscriptionId': self.SubscriptionId, + 'ResourceType': self.ResourceType or '', + 'UriLocation': self.UriLocation, + 'EndpointUri': self.EndpointUri or '', + 'ResourceQualifierJson': json.dumps(self.ResourceQualifier.to_dict()) or '' + } + return d diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/base.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/base.py new file mode 100644 index 0000000..b00c202 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/base.py @@ -0,0 +1,10 @@ +import sqlalchemy +from sqlalchemy import MetaData +from sqlalchemy.ext.declarative import declarative_base + +DefaultMetaData = MetaData() +OrmBase = declarative_base(metadata = DefaultMetaData) #生成orm基类 + +def create_tables(orm_engine): + OrmBase.metadata.create_all(orm_engine) #创建表结构 + return OrmBase.metadata diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/node.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/node.py new file mode 100644 index 0000000..19aa8b9 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/node.py @@ -0,0 +1,20 @@ +from sqlalchemy import Float, Integer, ForeignKey, String, Column + +from notificationclientsdk.model.orm.base import OrmBase + +''' +NodeName: literal Node Name +ResourceTypes: json dump of Enumerate string list: PTP, FPGA, etc +''' +class NodeInfo(OrmBase): + __tablename__ = 'nodeinfo' + NodeName = Column(String(128), primary_key=True) + PodIP = Column(String(256)) + ResourceTypes = Column(String(1024)) + Timestamp = Column(Float) + Status = Column(Integer) + CreateTime = Column(Float) + LastUpdateTime = Column(Float) + +def create_tables(orm_engine): + NodeInfo.metadata.create_all(orm_engine) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/subscription.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/subscription.py new file mode 100644 index 0000000..0e2be72 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/model/orm/subscription.py @@ -0,0 +1,17 @@ +from sqlalchemy import Float, Integer, ForeignKey, String, Column + +from notificationclientsdk.model.orm.base import OrmBase + +class Subscription(OrmBase): + __tablename__ = 'subscription' + SubscriptionId = Column(String(128), primary_key=True) + UriLocation = Column(String(512)) + ResourceType = Column(String(64)) + EndpointUri = Column(String(512)) + Status = Column(Integer) + CreateTime = Column(Float) + LastUpdateTime = Column(Float) + ResourceQualifierJson = Column(String) + +def create_tables(orm_engine): + Subscription.metadata.create_all(orm_engine) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/dbcontext.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/dbcontext.py new file mode 100644 index 0000000..a3a3660 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/dbcontext.py @@ -0,0 +1,75 @@ +import logging + +from sqlalchemy import create_engine, MetaData +from sqlalchemy.orm import scoped_session, sessionmaker + +from notificationclientsdk.model.orm import base +from notificationclientsdk.model.orm import subscription +from notificationclientsdk.model.orm import node + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + + +class DbContext(object): + # static properties + DBSession = None + metadata = None + engine = None + + @staticmethod + def _engine_from_config(configuration): + configuration = dict(configuration) + url = configuration.pop('url') + return create_engine(url, **configuration) + + @staticmethod + def init_dbcontext(sqlalchemy_conf): + """ + This is a stub method which is called at application startup time. + + If you need to bind to a parsed database configuration, set up tables or + ORM classes, or perform any database initialization, this is the + recommended place to do it. + + For more information working with databases, and some common recipes, + see https://pecan.readthedocs.io/en/latest/databases.html + """ + + DbContext.engine = DbContext._engine_from_config(sqlalchemy_conf) + DbContext.DbSession = sessionmaker(bind=DbContext.engine) + + DbContext.metadata = base.create_tables(DbContext.engine) + DbContext.metadata.bind = DbContext.engine + + def __init__(self, session=None): + LOG.debug("initing DbContext ...") + if not session: + if not DbContext.engine: + raise Exception("DbContext must be inited with DbContext.init_dbcontext() first") + session = scoped_session(DbContext.DbSession) + self.session = session + + def __del__(self): + LOG.debug("deleting DbContext ...") + pass + + def get_session(self): + return self.session + + def start(self): + pass + + def start_read_only(self): + self.start() + + def commit(self): + self.session.commit() + + def rollback(self): + self.session.rollback() + + def clear(self): + self.session.remove() diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/node_repo.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/node_repo.py new file mode 100644 index 0000000..8eac79f --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/node_repo.py @@ -0,0 +1,81 @@ +import time, uuid +import logging + +from sqlalchemy.orm import scoped_session, sessionmaker + +from notificationclientsdk.model.orm.node import NodeInfo as NodeInfoOrm +from notificationclientsdk.repository.dbcontext import DbContext + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +class NodeRepo(DbContext): + def __init__(self, session=None, autocommit=False): + self.autocommit = autocommit + super(NodeRepo, self).__init__(session) + if session: + self.own_session = False + else: + self.own_session = True + + def __del__(self): + if self.own_session: + self.clear() + + def add(self, nodeinfo): + try: + nodeinfo.Status = 1 + nodeinfo.CreateTime = time.time() + nodeinfo.LastUpdateTime = nodeinfo.CreateTime + self.session.add(nodeinfo) + except Exception as ex: + if self.autocommit: + self.rollback() + raise ex + else: + if self.autocommit: + self.commit() + return nodeinfo + + def update(self, node_name, **data): + try: + data['LastUpdateTime'] = time.time() + self.session.query(NodeInfoOrm).filter_by(NodeName=node_name).update(data) + except Exception as ex: + if self.autocommit: + self.rollback() + raise ex + else: + if self.autocommit: + self.commit() + + def get_one(self, **filter): + return self.session.query(NodeInfoOrm).filter_by(**filter).first() + + def get(self, **filter): + return self.session.query(NodeInfoOrm).filter_by(**filter) + + def delete_one(self, **filter): + try: + entry = self.session.query(NodeInfoOrm).filter_by(**filter).first() + self.session.delete(entry) + except Exception as ex: + if self.autocommit: + self.rollback() + raise ex + else: + if self.autocommit: + self.commit() + + def delete(self, **filter): + try: + entry = self.session.query(NodeInfoOrm).filter_by(**filter).delete() + except Exception as ex: + if self.autocommit: + self.rollback() + raise ex + else: + if self.autocommit: + self.commit() diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/subscription_repo.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/subscription_repo.py new file mode 100644 index 0000000..dc305e3 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/repository/subscription_repo.py @@ -0,0 +1,86 @@ +import time, uuid +import logging + +from sqlalchemy.orm import scoped_session, sessionmaker + +from notificationclientsdk.model.orm.subscription import Subscription as SubscriptionOrm +from notificationclientsdk.repository.dbcontext import DbContext + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +class SubscriptionRepo(DbContext): + + def __init__(self, session=None, autocommit=False): + self.autocommit = autocommit + super(SubscriptionRepo, self).__init__(session) + if session: + self.own_session = False + else: + self.own_session = True + + def __del__(self): + if self.own_session: + self.clear() + + def add(self, subscription): + try: + subscription.SubscriptionId = str(uuid.uuid1()) + subscription.Status = 1 + subscription.CreateTime = time.time() + subscription.LastUpdateTime = subscription.CreateTime + subscription.UriLocation = "{0}/{1}".format( + subscription.UriLocation, subscription.SubscriptionId) + + self.session.add(subscription) + except Exception as ex: + if self.autocommit: + self.rollback() + raise ex + else: + if self.autocommit: + self.commit() + return subscription + + def update(self, subscriptionid, **data): + try: + data['LastUpdateTime'] = time.time() + self.session.query(SubscriptionOrm).filter_by(SubscriptionId=subscriptionid).update(data) + except Exception as ex: + if self.autocommit: + self.rollback() + raise ex + else: + if self.autocommit: + self.commit() + + def get_one(self, **filter): + return self.session.query(SubscriptionOrm).filter_by(**filter).first() + + def get(self, **filter): + return self.session.query(SubscriptionOrm).filter_by(**filter) + + def delete_one(self, **filter): + try: + entry = self.session.query(SubscriptionOrm).filter_by(**filter).first() + self.session.delete(entry) + except Exception as ex: + if self.autocommit: + self.rollback() + raise ex + else: + if self.autocommit: + self.commit() + + def delete(self, **filter): + try: + entry = self.session.query(SubscriptionOrm).filter_by(**filter).delete() + except Exception as ex: + if self.autocommit: + self.rollback() + raise ex + else: + if self.autocommit: + self.commit() diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/services/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/services/daemon.py b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/services/daemon.py new file mode 100644 index 0000000..99a2648 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/notificationclientsdk/services/daemon.py @@ -0,0 +1,666 @@ + +import os +import json +import time +import oslo_messaging +from oslo_config import cfg +import logging + +import multiprocessing as mp +import threading +import sys +if sys.version > '3': + import queue as Queue +else: + import Queue + +from notificationclientsdk.common.helpers import subscription_helper +from notificationclientsdk.common.helpers import rpc_helper, hostfile_helper +from notificationclientsdk.common.helpers.nodeinfo_helper import NodeInfoHelper + +from notificationclientsdk.model.dto.rpc_endpoint import RpcEndpointInfo +from notificationclientsdk.model.dto.subscription import SubscriptionInfo +from notificationclientsdk.model.dto.resourcetype import ResourceType +from notificationclientsdk.model.dto.location import LocationInfo + +from notificationclientsdk.repository.dbcontext import DbContext +from notificationclientsdk.repository.subscription_repo import SubscriptionRepo + +from notificationclientsdk.model.orm.node import NodeInfo as NodeInfoOrm + +from notificationclientsdk.repository.node_repo import NodeRepo + +from notificationclientsdk.client.locationservice import LocationServiceClient +from notificationclientsdk.client.notificationservice import NotificationServiceClient +from notificationclientsdk.client.notificationservice import NotificationHandlerBase + +from notificationclientsdk.client.locationservice import LocationHandlerDefault + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +'''Entry point of Default Process Worker''' +def ProcessWorkerDefault(event, subscription_event, daemon_context): + worker = NotificationWorker(event, subscription_event, daemon_context) + worker.run() + return + + +class NotificationWorker: + + class NotificationWatcher(NotificationHandlerBase): + def __init__(self, notification_watcher): + self.notification_watcher = notification_watcher + super(NotificationWorker.NotificationWatcher, self).__init__() + + def handle(self, notification_info): + LOG.debug("Received notification:{0}".format(notification_info)) + result = self.notification_watcher.handle_notification_delivery(notification_info) + return result + + class NodeInfoWatcher(LocationHandlerDefault): + def __init__(self, notification_watcher): + self.notification_watcher = notification_watcher + super(NotificationWorker.NodeInfoWatcher, self).__init__() + + def handle(self, location_info): + LOG.debug("Received location info:{0}".format(location_info)) + return self.notification_watcher.produce_location_event(location_info) + + def __init__( + self, event, subscription_event, daemon_context): + + self.daemon_context = daemon_context + self.residing_node_name = daemon_context['THIS_NODE_NAME'] + NodeInfoHelper.set_residing_node(self.residing_node_name) + + self.sqlalchemy_conf = json.loads(daemon_context['SQLALCHEMY_CONF_JSON']) + DbContext.init_dbcontext(self.sqlalchemy_conf) + self.event = event + self.subscription_event = subscription_event + + self.registration_endpoint = RpcEndpointInfo(daemon_context['REGISTRATION_TRANSPORT_ENDPOINT']) + self.locationservice_client = LocationServiceClient(self.registration_endpoint.TransportEndpoint) + # dict,key: node name, value , notificationservice client + self.notificationservice_clients = {} + + # Watcher callback + self.__NotificationWatcher = NotificationWorker.NotificationWatcher(self) + self.__NodeInfoWatcher = NotificationWorker.NodeInfoWatcher(self) + + self.__init_node_resources_map() + self.__init_node_info_channel() + self.__init_location_channel() + self.__init_notification_channel() + self.__init_node_sync_channel() + + def __init_node_resources_map(self): + self.node_resources_map = {} + self.node_resources_iteration = 0 + self.__node_resources_event = mp.Event() + + def __init_node_info_channel(self): + self.__node_info_event = mp.Event() + + def __init_location_channel(self): + self.location_event = mp.Event() + self.location_lock = threading.Lock() + # map index by node name + # only cache the latest loation info + self.location_channel = {} + self.location_keys_q = Queue.Queue() + + def __init_notification_channel(self): + self.notification_lock = threading.Lock() + self.notification_stat = {} + + def __init_node_sync_channel(self): + self.__node_sync_event = mp.Event() + # initial to be set + self.__node_sync_event.set() + + def __del__(self): + del self.locationservice_client + + def signal_location_event(self): + self.location_event.set() + + def signal_subscription_event(self): + self.subscription_event.set() + + def signal_node_sync_event(self): + self.__node_sync_event.set() + + def signal_nodeinfo_event(self): + self.__node_info_event.set() + + def signal_node_resources_event(self): + self.__node_resources_event.set() + + def signal_events(self): + self.event.set() + + def produce_location_event(self, location_info): + node_name = location_info.get('NodeName', None) + podip = location_info.get("PodIP", None) + if not node_name or not podip: + LOG.warning("Missing PodIP inside location info:{0}".format(location_info)) + return False + + result = True + timestamp = location_info.get('Timestamp', 0) + # acquire lock to sync threads which invoke this method + self.location_lock.acquire() + try: + current = self.location_channel.get(node_name, {}) + if current.get('Timestamp', 0) < timestamp: + if current.get('PodIP', None) != podip: + # update /etc/hosts must happen in threads to avoid blocking by the main thread + NOTIFICATIONSERVICE_HOSTNAME = 'notificationservice-{0}' + hostfile_helper.update_host( + NOTIFICATIONSERVICE_HOSTNAME.format(node_name), podip) + LOG.debug("Updated location with IP:{0}".format(podip)) + + # replace the location_info + self.location_channel[node_name] = location_info + self.location_keys_q.put(node_name) + # notify the consumer to process the update + self.signal_location_event() + self.signal_events() + result = True + except Exception as ex: + LOG.warning("failed to produce location event:{0}".format(str(ex))) + result = False + finally: + # release lock + self.location_lock.release() + + return result + + def consume_location_event(self): + LOG.debug("Start consuming location event") + need_to_sync_node = False + node_changed = False + node_resource_updated = False + nodeinfo_repo = NodeRepo(autocommit=True) + + while not self.location_keys_q.empty(): + node_name = self.location_keys_q.get(False) + location_info = self.location_channel.get(node_name, None) + if not location_info: + LOG.warning("consume location@{0} without location info".format(node_name)) + continue + + LOG.debug("process location event@{0}:{1}".format(node_name, location_info)) + + location_info2 = LocationInfo(**location_info) + + entry = nodeinfo_repo.get_one(NodeName=location_info['NodeName'], Status=1) + if not entry: + entry = NodeInfoOrm(**location_info2.to_orm()) + nodeinfo_repo.add(entry) + node_resource_updated = True + node_changed = True + LOG.debug("Add NodeInfo: {0}".format(entry.NodeName)) + elif not entry.Timestamp or entry.Timestamp < location_info['Timestamp']: + # update the entry + if entry.ResourceTypes != location_info2.ResourceTypes: + node_resource_updated = True + nodeinfo_repo.update(entry.NodeName, **location_info2.to_orm()) + LOG.debug("Update NodeInfo: {0}".format(entry.NodeName)) + else: + # do nothing + LOG.debug("Ignore the location for: {0}".format(entry.NodeName)) + continue + need_to_sync_node = True + continue + + del nodeinfo_repo + LOG.debug("Finished consuming location event") + if need_to_sync_node or node_resource_updated: + if node_changed: + LOG.debug("signal node changed event") + # node changes triggers rebuild map from subscription + # due to the potential subscriptions to all nodes + self.signal_subscription_event() + if node_resource_updated: + # signal the potential changes on node resources + LOG.debug("signal node resources updating event") + self.signal_nodeinfo_event() + if need_to_sync_node: + LOG.debug("signal node syncing event") + self.signal_node_sync_event() + self.signal_events() + pass + + def handle_notification_delivery(self, notification_info): + LOG.debug("start notification delivery") + result = True + subscription_repo = None + try: + self.notification_lock.acquire() + subscription_repo = SubscriptionRepo(autocommit=True) + resource_type = notification_info.get('ResourceType', None) + node_name = notification_info.get('ResourceQualifier', {}).get('NodeName', None) + if not resource_type: + raise Exception("abnormal notification@{0}".format(node_name)) + + if resource_type == ResourceType.TypePTP: + pass + else: + raise Exception("notification with unsupported resource type:{0}".format(resource_type)) + + this_delivery_time = notification_info['EventTimestamp'] + + entries = subscription_repo.get(ResourceType=resource_type, Status=1) + for entry in entries: + subscriptionid = entry.SubscriptionId + ResourceQualifierJson = entry.ResourceQualifierJson or '{}' + ResourceQualifier = json.loads(ResourceQualifierJson) + # qualify by NodeName + entry_node_name = ResourceQualifier.get('NodeName', None) + node_name_matched = NodeInfoHelper.match_node_name(entry_node_name, node_name) + if not node_name_matched: + continue + + subscription_dto2 = SubscriptionInfo(entry) + try: + last_delivery_stat = self.notification_stat.get(node_name,{}).get(subscriptionid,{}) + last_delivery_time = last_delivery_stat.get('EventTimestamp', None) + if last_delivery_time and last_delivery_time >= this_delivery_time: + # skip this entry since already delivered + LOG.debug("Ignore the notification for: {0}".format(entry.SubscriptionId)) + raise Exception("notification timestamp indicate it is not lastest") + + subscription_helper.notify(subscription_dto2, notification_info) + LOG.debug("notification is delivered successfully to {0}".format( + entry.SubscriptionId)) + + if not self.notification_stat.get(node_name, None): + self.notification_stat[node_name] = { + subscriptionid: { + 'EventTimestamp': this_delivery_time + } + } + LOG.debug("delivery time @node: {0},subscription:{1} is added".format( + node_name, subscriptionid)) + elif not self.notification_stat[node_name].get(subscriptionid, None): + self.notification_stat[node_name][subscriptionid] = { + 'EventTimestamp': this_delivery_time + } + LOG.debug("delivery time @node: {0},subscription:{1} is added".format( + node_name, subscriptionid)) + else: + last_delivery_stat['EventTimestamp'] = this_delivery_time + LOG.debug("delivery time @node: {0},subscription:{1} is updated".format( + node_name, subscriptionid)) + + except Exception as ex: + LOG.warning("notification is not delivered to {0}:{1}".format( + entry.SubscriptionId, str(ex))) + # remove the entry + continue + finally: + pass + except Exception as ex: + LOG.warning("Failed to delivery notification:{0}".format(str(ex))) + result = False + finally: + self.notification_lock.release() + if not subscription_repo: + del subscription_repo + + if result: + LOG.debug("Finished notification delivery") + else: + LOG.warning("Failed on notification delivery") + return result + + def process_sync_node_event(self): + LOG.debug("Start processing sync node event") + need_to_sync_node_again = False + for broker_node_name, node_resources in self.node_resources_map.items(): + try: + result = self.syncup_node(broker_node_name) + if not result: + need_to_sync_node_again = True + except Exception as ex: + LOG.warning("Failed to syncup node{0}:{1}".format(broker_node_name, str(ex))) + continue + + if need_to_sync_node_again: + # continue try in to next loop + self.signal_node_sync_event() + self.signal_events() + LOG.debug("Finished processing sync node event") + + def run(self): + # start location listener + self.__start_watch_all_nodes() + while True: + self.event.wait() + self.event.clear() + LOG.debug("daemon control event is asserted") + + if self.location_event.is_set(): + self.location_event.clear() + # process location notifications + self.consume_location_event() + + if self.subscription_event.is_set(): + self.subscription_event.clear() + # build node resources map from subscriptions + self.process_subscription_event() + + if self.__node_info_event.is_set(): + self.__node_info_event.clear() + # update node_resources_map from node info + self.__update_map_from_nodeinfos() + + if self.__node_resources_event.is_set(): + self.__node_resources_event.clear() + # update watchers from node_resources_map + self.__refresh_watchers_from_map() + + if self.__node_sync_event.is_set(): + self.__node_sync_event.clear() + # compensate for the possible loss of notification during reconnection + self.process_sync_node_event() + + continue + return + + def syncup_resource(self, broker_node_name, resource_type): + # check to sync up resource status on a node + LOG.debug("sync up resource@{0} :{1}".format(broker_node_name, resource_type)) + try: + if broker_node_name == NodeInfoHelper.BROKER_NODE_ALL: + self.locationservice_client.trigger_publishing_status( + resource_type, timeout=5, retry=10) + return True + + # 1, query resource status + broker_client = self.notificationservice_clients.get(broker_node_name, None) + if not broker_client: + raise Exception("notification service client is not setup for node {0}".format(broker_node_name)) + resource_status = broker_client.query_resource_status( + resource_type, timeout=5, retry=10) + + # 2, deliver resource by comparing LastDelivery time with EventTimestamp + # 3, update the LastDelivery with EventTimestamp + self.__NotificationWatcher.handle(resource_status) + except oslo_messaging.exceptions.MessagingTimeout as ex: + LOG.warning("Fail to syncup resource {0}@{1}, due to {2}".format( + resource_type, broker_node_name, str(ex))) + return False + except Exception as ex: + LOG.warning("Fail to syncup resource {0}@{1}, due to {2}".format( + resource_type, broker_node_name, str(ex))) + raise ex + finally: + pass + return True + + def syncup_node(self, broker_node_name): + all_resource_synced = True + # check to sync up resources status on a node + node_resources = self.node_resources_map.get(broker_node_name, None) + if node_resources: + LOG.debug("sync up resources@{0} :{1}".format(broker_node_name, node_resources)) + for resource_type, iteration in node_resources.items(): + if iteration == self.node_resources_iteration: + result = self.syncup_resource(broker_node_name, resource_type) + if not result: + all_resource_synced = False + return all_resource_synced + + def __cleanup_map(self): + for broker_node_name, node_resources in self.node_resources_map.items(): + resourcetypelist = [r for (r, i) in node_resources.items() if id<$pvYIU8MW@ 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 EKPrEkOaK4? literal 0 HcmV?d00001 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/setup.cfg b/notificationclient-base/centos/docker/notificationclient-sidecar/setup.cfg new file mode 100644 index 0000000..2c93fb1 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/setup.cfg @@ -0,0 +1,6 @@ +[nosetests] +match=^test +where=sidecar +nocapture=1 +cover-package=sidecar +cover-erase=1 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/setup.py b/notificationclient-base/centos/docker/notificationclient-sidecar/setup.py new file mode 100644 index 0000000..3477a92 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/setup.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +try: + from setuptools import setup, find_packages +except ImportError: + from ez_setup import use_setuptools + use_setuptools() + from setuptools import setup, find_packages + +setup( + name='sidecar', + version='0.1', + description='', + author='', + author_email='', + install_requires=[ + "pecan", + ], + test_suite='sidecar', + zip_safe=False, + include_package_data=True, + packages=find_packages(exclude=['ez_setup']) +) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/PKG-INFO b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/PKG-INFO new file mode 100644 index 0000000..aaffd3f --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/PKG-INFO @@ -0,0 +1,11 @@ +Metadata-Version: 1.0 +Name: sidecar +Version: 0.1 +Summary: sidecar offers a container image for vdu to + interact with notification service to get updates of resource status +Home-page: UNKNOWN +Author: Bin Yang +Author-email: bin.yang@windriver.com +License: Apache License 2.0 +Description: UNKNOWN +Platform: UNKNOWN diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/SOURCES.txt b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/SOURCES.txt new file mode 100644 index 0000000..05ddadb --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/SOURCES.txt @@ -0,0 +1,20 @@ +MANIFEST.in +setup.cfg +setup.py +sidecar/__init__.py +sidecar/app.py +sidecar.egg-info/PKG-INFO +sidecar.egg-info/SOURCES.txt +sidecar.egg-info/dependency_links.txt +sidecar.egg-info/not-zip-safe +sidecar.egg-info/requires.txt +sidecar.egg-info/top_level.txt +sidecar/controllers/__init__.py +sidecar/controllers/root.py +sidecar/model/__init__.py +sidecar/tests/__init__.py +sidecar/tests/config.py +sidecar/tests/test_functional.py +sidecar/tests/test_units.py +public/css/style.css +public/images/logo.png \ No newline at end of file diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/dependency_links.txt b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/not-zip-safe b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/requires.txt b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/requires.txt new file mode 100644 index 0000000..8e76aff --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/requires.txt @@ -0,0 +1,2 @@ +pecan +sqlalchemy diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/top_level.txt b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/top_level.txt new file mode 100644 index 0000000..49f321f --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar.egg-info/top_level.txt @@ -0,0 +1 @@ +sidecar diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/app.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/app.py new file mode 100644 index 0000000..00d74ae --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/app.py @@ -0,0 +1,29 @@ +from pecan import make_app +from pecan.hooks import TransactionHook +from pecan import conf + +from sidecar.repository.dbcontext_default import init_default_dbcontext, defaults +from sidecar.model import jsonify + +def setup_app(config): + + # important to register jsonify for models + jsonify.__init__() + + default_dbcontext = init_default_dbcontext(conf.sqlalchemy) + app_conf = dict(config.app) + + return make_app( + app_conf.pop('root'), + logging=getattr(config, 'logging', {}), + hooks=[ + TransactionHook( + default_dbcontext.start, + default_dbcontext.start_read_only, + default_dbcontext.commit, + default_dbcontext.rollback, + default_dbcontext.clear + ) + ], + **app_conf + ) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/root.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/root.py new file mode 100644 index 0000000..3dc39b9 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/root.py @@ -0,0 +1,65 @@ +#coding=utf-8 + +from pecan import expose, redirect, rest, route, response +from webob.exc import status_map +import os + +from wsme import types as wtypes +from wsmeext.pecan import wsexpose + +THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0') + +from sidecar.controllers.v1.subscriptions import SubscriptionsController +from sidecar.controllers.v1.resource.ptp import PtpController + +class HealthController(rest.RestController): + + @wsexpose(wtypes.text) + def get(self): + return {'health': True} + +class V1Controller(rest.RestController): + + @wsexpose(wtypes.text) + def get(self): + return 'v1controller' + + +class ocloudDaemonController(rest.RestController): + + # All supported API versions + _versions = ['v1'] + + # The default API version + _default_version = 'v1' + v1 = V1Controller() + + @wsexpose(wtypes.text) + def get(self): + return 'ocloudNotification' + + +class RootController(object): + + @expose(generic=True, template='json') + def index(self): + return dict() + + @expose('json') + def error(self, status): + try: + status = int(status) + except ValueError: # pragma: no cover + status = 500 + message = getattr(status_map.get(status), 'explanation', '') + return dict(status=status, message=message) + + +route(RootController, 'health', HealthController()) + +route(RootController, 'ocloudNotifications', ocloudDaemonController()) +route(RootController, 'ocloudnotifications', ocloudDaemonController()) + +route(V1Controller, 'PTP', PtpController()) +route(V1Controller, 'ptp', PtpController()) +route(V1Controller, 'subscriptions', SubscriptionsController()) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/resource/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/resource/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/resource/ptp.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/resource/ptp.py new file mode 100644 index 0000000..6254a1a --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/resource/ptp.py @@ -0,0 +1,55 @@ +#coding=utf-8 + +from pecan import expose, redirect, rest, route, response, abort +from webob.exc import HTTPException, HTTPNotFound, HTTPBadRequest, HTTPClientError, HTTPServerError + +from wsme import types as wtypes +from wsmeext.pecan import wsexpose + +import os +import logging + +from notificationclientsdk.services.ptp import PtpService + +from sidecar.repository.notification_control import notification_control + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0') + +class CurrentStateController(rest.RestController): + def __init__(self): + pass + + @expose('json') + def get(self): + try: + ptpservice = PtpService(notification_control) + ptpstatus = ptpservice.query(THIS_NODE_NAME) + # response.status = 200 + return ptpstatus + except HTTPException as ex: + LOG.warning("Client side error:{0},{1}".format(type(ex), str(ex))) + # raise ex + abort(400) + except HTTPServerError as ex: + LOG.error("Server side error:{0},{1}".format(type(ex), str(ex))) + # raise ex + abort(500) + except Exception as ex: + LOG.error("Exception:{0}@{1}".format(type(ex),str(ex))) + abort(500) + +class PtpController(rest.RestController): + def __init__(self): + pass + + @wsexpose(wtypes.text) + def get(self): + return 'ptp' + +route(PtpController, 'CurrentState', CurrentStateController()) +route(PtpController, 'currentstate', CurrentStateController()) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/subscriptions.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/subscriptions.py new file mode 100644 index 0000000..246a073 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/controllers/v1/subscriptions.py @@ -0,0 +1,157 @@ +#coding=utf-8 +from pecan import conf +from pecan import expose, redirect, rest, route, response, abort +from webob.exc import HTTPException, HTTPNotFound, HTTPBadRequest, HTTPClientError, HTTPServerError + +import os +import logging + +import oslo_messaging + +from wsme import types as wtypes +from wsmeext.pecan import wsexpose + +from notificationclientsdk.model.dto.resourcetype import ResourceType +from notificationclientsdk.model.dto.subscription import SubscriptionInfo + +from notificationclientsdk.repository.subscription_repo import SubscriptionRepo +from notificationclientsdk.services.ptp import PtpService + +from sidecar.repository.notification_control import notification_control +from sidecar.repository.dbcontext_default import defaults + +LOG = logging.getLogger(__name__) + +from notificationclientsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0') + +class SubscriptionsController(rest.RestController): + @wsexpose(SubscriptionInfo, body=SubscriptionInfo, status_code=201) + def post(self, subscription): + # decode the request body + try: + if subscription.ResourceType == ResourceType.TypePTP: + LOG.info(' subscribe: {0}, {1} with callback uri {2}'.format( + subscription.ResourceType, + subscription.ResourceQualifier.NodeName, + subscription.EndpointUri)) + else: + LOG.warning(' Subscribe with unsupported ResourceType:{0}'.format( + subscription.ResourceType)) + abort(404) + + if not self._validate(subscription): + LOG.warning(' Invalid Request data:{0}'.format(subscription.to_dict())) + abort(400) + + subscription.UriLocation = "{0}://{1}:{2}/ocloudNotifications/v1/subscriptions".format( + conf.server.get('protocol','http'), + conf.server.get('host', '127.0.01'), + conf.server.get('port', '8080') + ) + if subscription.ResourceType == ResourceType.TypePTP: + ptpservice = PtpService(notification_control) + entry = ptpservice.add_subscription(subscription) + del ptpservice + if not entry: + abort(404) + + subscription.SubscriptionId = entry.SubscriptionId + subscription.UriLocation = entry.UriLocation + LOG.info('created subscription: {0}'.format(subscription.to_dict())) + + return subscription + except oslo_messaging.exceptions.MessagingTimeout as ex: + abort(404) + except HTTPException as ex: + LOG.warning("Client side error:{0},{1}".format(type(ex), str(ex))) + raise ex + except HTTPServerError as ex: + LOG.error("Server side error:{0},{1}".format(type(ex), str(ex))) + raise ex + except Exception as ex: + LOG.error("Exception:{0}@{1}".format(type(ex),str(ex))) + abort(500) + + @expose('json') + def get(self): + try: + repo = SubscriptionRepo(defaults['dbcontext'].get_session(), autocommit = False) + entries = repo.get(Status=1) + + response.status = 200 + return [SubscriptionInfo(x).to_dict() for x in entries if x.Status == 1] + except HTTPException as ex: + LOG.warning("Client side error:{0},{1}".format(type(ex), str(ex))) + raise ex + except HTTPServerError as ex: + LOG.error("Server side error:{0},{1}".format(type(ex), str(ex))) + raise ex + except Exception as ex: + LOG.error("Exception:{0}@{1}".format(type(ex),str(ex))) + abort(500) + + @expose() + def _lookup(self, subscription_id, *remainder): + return SubscriptionController(subscription_id), remainder + + def _validate(self, subscription_request): + try: + assert subscription_request.ResourceType == 'PTP' + assert subscription_request.EndpointUri + + return True + except: + return False + +class SubscriptionController(rest.RestController): + def __init__(self, subscription_id): + self.subscription_id = subscription_id + + @expose('json') + def get(self): + try: + repo = SubscriptionRepo(defaults['dbcontext'].get_session(), autocommit = False) + entry = repo.get_one(SubscriptionId=self.subscription_id, Status=1) + + if not entry: + abort(404) + else: + response.status = 200 + return SubscriptionInfo(entry).to_dict() + except HTTPException as ex: + LOG.warning("Client side error:{0},{1}".format(type(ex), str(ex))) + raise ex + except HTTPServerError as ex: + LOG.error("Server side error:{0},{1}".format(type(ex), str(ex))) + raise ex + except Exception as ex: + LOG.error("Exception:{0}@{1}".format(type(ex),str(ex))) + abort(500) + + @wsexpose(status_code=204) + def delete(self): + try: + repo = SubscriptionRepo(defaults['dbcontext'].get_session(), autocommit = False) + entry = repo.get_one(SubscriptionId=self.subscription_id) + if entry: + if entry.ResourceType == ResourceType.TypePTP: + ptpservice = PtpService(notification_control) + ptpservice.remove_subscription(entry.SubscriptionId) + del ptpservice + return + else: + repo.delete_one(SubscriptionId=self.subscription_id) + return + abort(404) + except HTTPException as ex: + LOG.warning("Client side error:{0},{1}".format(type(ex), str(ex))) + raise ex + except HTTPServerError as ex: + LOG.error("Server side error:{0},{1}".format(type(ex), str(ex))) + raise ex + except Exception as ex: + LOG.error("Exception:{0}@{1}".format(type(ex),str(ex))) + abort(500) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/model/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/model/__init__.py new file mode 100644 index 0000000..71f7c51 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/model/__init__.py @@ -0,0 +1,17 @@ +from pecan import conf # noqa + +def init_model(): + """ + This is a stub method which is called at application startup time. + + If you need to bind to a parsed database configuration, set up tables or + ORM classes, or perform any database initialization, this is the + recommended place to do it. + + For more information working with databases, and some common recipes, + see https://pecan.readthedocs.io/en/latest/databases.html + """ + + pass + + diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/model/jsonify.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/model/jsonify.py new file mode 100644 index 0000000..a5036e7 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/model/jsonify.py @@ -0,0 +1,15 @@ +from notificationclientsdk.model.dto.subscription import SubscriptionInfo +from notificationclientsdk.model.dto.subscription import ResourceQualifierPtp + +from pecan.jsonify import jsonify + +@jsonify.register(SubscriptionInfo) +def jsonify_subscriptioninfo(subscriptionInfo): + return subscriptionInfo.to_dict() + +@jsonify.register(ResourceQualifierPtp) +def jsonify_resourcequalifierptp(resourceQualifierPtp): + return resourceQualifierPtp.to_dict() + +def __init__(): + pass diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/dbcontext_default.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/dbcontext_default.py new file mode 100644 index 0000000..504191f --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/dbcontext_default.py @@ -0,0 +1,12 @@ +from notificationclientsdk.repository.dbcontext import DbContext + +defaults = { + 'dbcontext': None +} + +def init_default_dbcontext(sqlalchemy_conf): + global defaults + DbContext.init_dbcontext(sqlalchemy_conf) + default_dbcontext = DbContext() + defaults['dbcontext'] = default_dbcontext + return default_dbcontext diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/notification_control.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/notification_control.py new file mode 100644 index 0000000..2c981d3 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/repository/notification_control.py @@ -0,0 +1,36 @@ +import os +import json +from pecan import conf +from notificationclientsdk.services.daemon import DaemonControl +from notificationclientsdk.common.helpers.nodeinfo_helper import NodeInfoHelper + + +REGISTRATION_USER = os.environ.get("REGISTRATION_USER", "admin") +REGISTRATION_PASS = os.environ.get("REGISTRATION_PASS", "admin") +REGISTRATION_PORT = os.environ.get("REGISTRATION_PORT", "5672") +REGISTRATION_HOST = os.environ.get("REGISTRATION_HOST",'registration.notification.svc.cluster.local') +THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0') +THIS_POD_IP = os.environ.get("THIS_POD_IP",'127.0.0.1') + +NOTIFICATION_BROKER_USER = os.environ.get("NOTIFICATIONSERVICE_USER", "admin") +NOTIFICATION_BROKER_PASS = os.environ.get("NOTIFICATIONSERVICE_PASS", "admin") +NOTIFICATION_BROKER_PORT = os.environ.get("NOTIFICATIONSERVICE_PORT", "5672") + +REGISTRATION_TRANSPORT_ENDPOINT = 'rabbit://{0}:{1}@{2}:{3}'.format( + REGISTRATION_USER, REGISTRATION_PASS, REGISTRATION_HOST, REGISTRATION_PORT) + +sqlalchemy_conf = dict(conf.sqlalchemy) +if sqlalchemy_conf.get('engine', None): + sqlalchemy_conf.pop('engine') +sqlalchemy_conf_json = json.dumps(sqlalchemy_conf) +daemon_context = { + 'SQLALCHEMY_CONF_JSON': sqlalchemy_conf_json, + 'THIS_NODE_NAME': THIS_NODE_NAME, + 'REGISTRATION_TRANSPORT_ENDPOINT': REGISTRATION_TRANSPORT_ENDPOINT, + 'NOTIFICATION_BROKER_USER': NOTIFICATION_BROKER_USER, + 'NOTIFICATION_BROKER_PASS': NOTIFICATION_BROKER_PASS, + 'NOTIFICATION_BROKER_PORT': NOTIFICATION_BROKER_PORT +} +notification_control = DaemonControl(daemon_context) + +NodeInfoHelper.set_residing_node(THIS_NODE_NAME) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/__init__.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/__init__.py new file mode 100644 index 0000000..78ea527 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/__init__.py @@ -0,0 +1,22 @@ +import os +from unittest import TestCase +from pecan import set_config +from pecan.testing import load_test_app + +__all__ = ['FunctionalTest'] + + +class FunctionalTest(TestCase): + """ + Used for functional tests where you need to test your + literal application and its integration with the framework. + """ + + def setUp(self): + self.app = load_test_app(os.path.join( + os.path.dirname(__file__), + 'config.py' + )) + + def tearDown(self): + set_config({}, overwrite=True) diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/config.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/config.py new file mode 100644 index 0000000..8f66b60 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/config.py @@ -0,0 +1,25 @@ +# Server Specific Configurations +server = { + 'port': '8080', + 'host': '0.0.0.0' +} + +# Pecan Application Configurations +app = { + 'root': 'notificationclient.controllers.root.RootController', + 'modules': ['notificationclient'], + 'static_root': '%(confdir)s/../../public', + 'template_path': '%(confdir)s/../templates', + 'debug': True, + 'errors': { + '404': '/error/404', + '__force_dict__': True + } +} + +# Custom Configurations must be in Python dictionary format:: +# +# foo = {'bar':'baz'} +# +# All configurations are accessible at:: +# pecan.conf diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/test_functional.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/test_functional.py new file mode 100644 index 0000000..252c3aa --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/test_functional.py @@ -0,0 +1,22 @@ +from unittest import TestCase +from webtest import TestApp +from notificationclient.tests import FunctionalTest + + +class TestRootController(FunctionalTest): + + def test_get(self): + response = self.app.get('/') + assert response.status_int == 200 + + def test_search(self): + response = self.app.post('/', params={'q': 'RestController'}) + assert response.status_int == 302 + assert response.headers['Location'] == ( + 'https://pecan.readthedocs.io/en/latest/search.html' + '?q=RestController' + ) + + def test_get_not_found(self): + response = self.app.get('/a/bogus/url', expect_errors=True) + assert response.status_int == 404 diff --git a/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/test_units.py b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/test_units.py new file mode 100644 index 0000000..573fb68 --- /dev/null +++ b/notificationclient-base/centos/docker/notificationclient-sidecar/sidecar/tests/test_units.py @@ -0,0 +1,7 @@ +from unittest import TestCase + + +class TestUnits(TestCase): + + def test_units(self): + assert 5 * 5 == 25 diff --git a/notificationclient-base/centos/notificationclient-base.stable_docker_image b/notificationclient-base/centos/notificationclient-base.stable_docker_image new file mode 100644 index 0000000..8bc8e33 --- /dev/null +++ b/notificationclient-base/centos/notificationclient-base.stable_docker_image @@ -0,0 +1,2 @@ +BUILDER=docker +LABEL=notificationclient-base \ No newline at end of file diff --git a/notificationservice-base/centos/docker/Dockerfile b/notificationservice-base/centos/docker/Dockerfile new file mode 100644 index 0000000..6e7cec9 --- /dev/null +++ b/notificationservice-base/centos/docker/Dockerfile @@ -0,0 +1,22 @@ +ARG BASE +FROM ${BASE} + +ARG STX_REPO_FILE=/etc/yum.repos.d/stx.repo + +ENV KUBE_LATEST_VERSION="v1.18.3" + +RUN set -ex ;\ + yum install --disablerepo=* \ + $(grep '^name=' ${STX_REPO_FILE} | awk -F '=' '{printf "--enablerepo=" $2 " "}') \ + -y \ + gcc python3-devel python3-pip \ + && pip3 install --user pecan \ + && pip3 install oslo-config \ + && pip3 install oslo-messaging \ + && pip3 install WSME + +WORKDIR /opt/ +COPY ./ptptrackingfunction /opt/ptptrackingfunction +RUN cd /opt/ptptrackingfunction && python3 setup.py develop + +CMD ["bash"] diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/setup.cfg b/notificationservice-base/centos/docker/ptptrackingfunction/setup.cfg new file mode 100644 index 0000000..19c8281 --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/setup.cfg @@ -0,0 +1,6 @@ +[nosetests] +match=^test +where=ptptrackingfunction +nocapture=1 +cover-package=ptptrackingfunction +cover-erase=1 diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/setup.py b/notificationservice-base/centos/docker/ptptrackingfunction/setup.py new file mode 100644 index 0000000..5ea2a7b --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/setup.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +try: + from setuptools import setup, find_packages +except ImportError: + from ez_setup import use_setuptools + use_setuptools() + from setuptools import setup, find_packages + +setup( + name='ptptrackingfunction', + version='0.1', + description='', + author='', + author_email='', + install_requires=[ + "", + ], + test_suite='ptptrackingfunction', + zip_safe=False, + include_package_data=True, + packages=find_packages(exclude=['ez_setup']) +) diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/__init__.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/client/base.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/client/base.py new file mode 100644 index 0000000..8a41e64 --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/client/base.py @@ -0,0 +1,107 @@ +import os +import json +import time +import oslo_messaging +from oslo_config import cfg +from trackingfunctionsdk.common.helpers import rpc_helper +from trackingfunctionsdk.model.dto.rpc_endpoint import RpcEndpointInfo + +import logging + +LOG = logging.getLogger(__name__) + +from trackingfunctionsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +class BrokerClientBase(object): + def __init__(self, broker_name, broker_transport_endpoint): + self.broker_name = broker_name + self.listeners = {} + self.broker_endpoint = RpcEndpointInfo(broker_transport_endpoint) + self.transport = rpc_helper.get_transport(self.broker_endpoint) + LOG.debug("Created Broker client:{0}".format(broker_name)) + + def __del__(self): + self.transport.cleanup() + del self.transport + return + + def __create_listener(self, context): + target = oslo_messaging.Target( + topic=context['topic'], + server=context['server']) + endpoints = context['endpoints'] + server = oslo_messaging.get_rpc_server( + self.transport, target, endpoints, executor=None) + return server + + def _refresh(self): + for topic, servers in self.listeners.items(): + for servername, context in servers.items(): + try: + rpcserver = context.get('rpcserver', None) + isactive = context.get('active', False) + if isactive and not rpcserver: + rpcserver = self.__create_listener(context) + rpcserver.start() + context['rpcserver'] = rpcserver + LOG.debug("Started rpcserver@{0}@{1}".format(context['topic'], context['server'])) + elif not isactive and rpcserver: + rpcserver.stop() + rpcserver.wait() + context.pop('rpcserver') + LOG.debug("Stopped rpcserver@{0}@{1}".format(context['topic'], context['server'])) + except: + LOG.error("Failed to update listener for topic/server:{0}/{1}" + .format(topic, servername)) + continue + + def add_listener(self, topic, server, listener_endpoints=None): + context = self.listeners.get(topic,{}).get(server, {}) + if not context: + context = { + 'endpoints': listener_endpoints, + 'topic': topic, + 'server': server, + 'active': True + } + if not self.listeners.get(topic, None): + self.listeners[topic] = {} + self.listeners[topic][server] = context + else: + context['endpoints'] = listener_endpoints + context['active'] = True + + self._refresh() + + def remove_listener(self, topic, server): + context = self.listeners.get(topic,{}).get(server, {}) + if context: + context['active'] = False + self._refresh() + + def is_listening(self, topic, server): + context = self.listeners.get(topic,{}).get(server, {}) + return context.get('active', False) + + def any_listener(self): + for topic, servers in self.listeners.items(): + for servername, context in servers.items(): + isactive = context.get('active', False) + if isactive: + return True + return False + + def call(self, topic, server, api_name, timeout=2, retry=0, **api_kwargs): + target = oslo_messaging.Target( + topic=topic, server=server, version=self.broker_endpoint.Version, + namespace=self.broker_endpoint.Namespace) + queryclient = oslo_messaging.RPCClient(self.transport, target, timeout = timeout, retry = retry) + return queryclient.call({}, api_name, **api_kwargs) + + def cast(self, topic, api_name, **api_kwargs): + target = oslo_messaging.Target( + topic=topic, fanout=True, version=self.broker_endpoint.Version, + namespace=self.broker_endpoint.Namespace) + queryclient = oslo_messaging.RPCClient(self.transport, target) + queryclient.cast({}, api_name, **api_kwargs) diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/client/ptpeventproducer.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/client/ptpeventproducer.py new file mode 100644 index 0000000..ab52aad --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/client/ptpeventproducer.py @@ -0,0 +1,192 @@ +import os +import json +import time +import oslo_messaging +from oslo_config import cfg + +from trackingfunctionsdk.client.base import BrokerClientBase + +import logging + +LOG = logging.getLogger(__name__) + +from trackingfunctionsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +class PtpEventProducer(object): + class ListenerEndpoint(object): + target = oslo_messaging.Target(namespace='notification', version='1.0') + + def __init__(self, handler=None): + + self.handler = handler + self.init_time = time.time() + pass + + def QueryStatus(self, ctx, **rpc_kwargs): + LOG.debug("PtpEventProducer QueryStatus called %s" %rpc_kwargs) + if self.handler: + return self.handler.query_status(**rpc_kwargs) + else: + return None + + def TriggerDelivery(self, ctx, **rpc_kwargs): + LOG.debug ("PtpEventProducer TriggerDelivery called %s" %rpc_kwargs) + if self.handler: + return self.handler.trigger_delivery(**rpc_kwargs) + else: + return None + + def __init__(self, node_name, local_broker_transport_endpoint, + registration_broker_transport_endpoint=None): + self.Id = id(self) + self.node_name = node_name + self.local_broker_client = BrokerClientBase( + 'LocalPtpEventProducer', local_broker_transport_endpoint) + if registration_broker_transport_endpoint: + self.registration_broker_client = BrokerClientBase( + 'AllPtpEventProducer', registration_broker_transport_endpoint) + else: + self.registration_broker_client = None + return + + def __del__(self): + if self.local_broker_client: + del self.local_broker_client + self.local_broker_client = None + if self.registration_broker_client: + del self.registration_broker_client + self.registration_broker_client = None + return + + def publish_status(self, ptpstatus, retry=3): + result = False + result1 = self.publish_status_local(ptpstatus, retry) if self.local_broker_client else result + result2 = self.publish_status_all(ptpstatus, retry) if self.registration_broker_client else result + return result1, result2 + + def publish_status_local(self, ptpstatus, retry=3): + if not self.local_broker_client: + return False + topic='PTP-Event-{0}'.format(self.node_name) + server = None + isretrystopped = False + while not isretrystopped: + try: + self.local_broker_client.cast( + topic, 'NotifyStatus', notification=ptpstatus) + LOG.debug("Published ptp status:{0}@Topic:{1}".format(ptpstatus, topic)) + break + except Exception as ex: + LOG.warning("Failed to publish ptp status:{0}@Topic:{1} due to: {2}".format( + ptpstatus, topic, str(ex))) + retry = retry - 1 + isretrystopped = False if retry > 0 else True + + if isretrystopped: + LOG.error("Failed to publish ptp status:{0}@Topic:{1}".format( + ptpstatus, topic)) + return isretrystopped == False + + def publish_status_all(self, ptpstatus, retry=3): + if not self.registration_broker_client: + return False + topic_all='PTP-Event-*' + server = None + isretrystopped = False + while not isretrystopped: + try: + self.registration_broker_client.cast( + topic_all, 'NotifyStatus', notification=ptpstatus) + LOG.debug("Published ptp status:{0}@Topic:{1}".format(ptpstatus, topic_all)) + break + except Exception as ex: + LOG.warning("Failed to publish ptp status:{0}@Topic:{1} due to: {2}".format( + ptpstatus, topic, str(ex))) + retry = retry - 1 + isretrystopped = False if retry > 0 else True + + if isretrystopped: + LOG.error("Failed to publish ptp status:{0}@Topic:{1}".format( + ptpstatus, topic)) + return isretrystopped == False + + def start_status_listener(self, handler=None): + result = False + result1 = self.start_status_listener_local(handler) if self.local_broker_client else result + result2 = self.start_status_listener_all(handler) if self.registration_broker_client else result + result = result1 and result2 + return result + + def start_status_listener_local(self, handler=None): + if not self.local_broker_client: + return False + + topic='PTP-Status' + server='PTP-Tracking-{0}'.format(self.node_name) + endpoints = [PtpEventProducer.ListenerEndpoint(handler)] + + self.local_broker_client.add_listener( + topic, server, endpoints) + return True + + def start_status_listener_all(self, handler=None): + if not self.registration_broker_client: + return False + + topic='PTP-Status' + server='PTP-Tracking-{0}'.format(self.node_name) + endpoints = [PtpEventProducer.ListenerEndpoint(handler)] + + self.registration_broker_client.add_listener( + topic, server, endpoints) + return True + + def stop_status_listener(self): + result = False + result1 = self.stop_status_listener_local() if self.local_broker_client else result + result2 = self.stop_status_listener_all() if self.registration_broker_client else result + result = result1 and result2 + return result + + def stop_status_listener_local(self): + if not self.local_broker_client: + return False + + topic='PTP-Status' + server="PTP-Tracking-{0}".format(self.node_name) + self.local_broker_client.remove_listener( + topic, server) + + def stop_status_listener_all(self): + if not self.registration_broker_client: + return False + + topic='PTP-Status' + server="PTP-Tracking-{0}".format(self.node_name) + self.registration_broker_client.remove_listener( + topic, server) + + def is_listening(self): + result = False + result1 = self.is_listening_local() if self.local_broker_client else result + result2 = self.is_listening_all() if self.registration_broker_client else result + result = result1 and result2 + return result + + def is_listening_local(self): + if not self.local_broker_client: + return False + + topic='PTP-Status' + server="PTP-Tracking-{0}".format(self.node_name) + return self.local_broker_client.is_listening( + topic, server) + + def is_listening_all(self): + if not self.registration_broker_client: + return False + topic='PTP-Status' + server="PTP-Tracking-{0}".format(self.node_name) + return self.registration_broker_client.is_listening( + topic, server) diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/log_helper.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/log_helper.py new file mode 100644 index 0000000..d1a16e7 --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/log_helper.py @@ -0,0 +1,12 @@ +import logging + +def get_logger(module_name): + logger = logging.getLogger(module_name) + return config_logger(logger) + +def config_logger(logger): + ''' + configure the logger: uncomment following lines for debugging + ''' + # logger.setLevel(level=logging.DEBUG) + return logger diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py new file mode 100644 index 0000000..fe96d0b --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/ptpsync.py @@ -0,0 +1,5 @@ +#! /usr/bin/python3 + +'''ptp_status API stub''' +def ptp_status(holdover_time, freq, sync_state, event_time): + return new_event, sync_state, event_time diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/rpc_helper.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/rpc_helper.py new file mode 100644 index 0000000..71e5cbf --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/common/helpers/rpc_helper.py @@ -0,0 +1,19 @@ +#coding=utf-8 + +import oslo_messaging +from oslo_config import cfg + + +def setup_client(rpc_endpoint_info, topic, server): + oslo_messaging.set_transport_defaults(rpc_endpoint_info.Exchange) + transport = oslo_messaging.get_rpc_transport(cfg.CONF, url=rpc_endpoint_info.TransportEndpoint) + target = oslo_messaging.Target(topic=topic, + version=rpc_endpoint_info.Version, + server=server, + namespace=rpc_endpoint_info.Namespace) + client = oslo_messaging.RPCClient(transport, target) + return client + +def get_transport(rpc_endpoint_info): + oslo_messaging.set_transport_defaults(rpc_endpoint_info.Exchange) + return oslo_messaging.get_rpc_transport(cfg.CONF, url=rpc_endpoint_info.TransportEndpoint) diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/__init__.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/__init__.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/ptpstate.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/ptpstate.py new file mode 100644 index 0000000..5415ac4 --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/ptpstate.py @@ -0,0 +1,11 @@ +#coding=utf-8 + +from wsme import types as wtypes + +EnumPtpState = wtypes.Enum(str, 'Locked', 'Freerun', 'Holdover') + +class PtpState(object): + Locked = "Locked" + Freerun = "Freerun" + Holdover = "Holdover" + diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/ptpstatus.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/ptpstatus.py new file mode 100644 index 0000000..0be94fc --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/ptpstatus.py @@ -0,0 +1,24 @@ +#coding=utf-8 + +from wsme import types as wtypes +from trackingfunctionsdk.model.dto.resourcetype import EnumResourceType +from trackingfunctionsdk.model.dto.ptpstate import PtpState + +class PtpStatus(wtypes.Base): + EventTimestamp = float + ResourceType = EnumResourceType + EventData_State = PtpState + ResourceQualifier_NodeName = wtypes.text + + def to_dict(self): + d = { + 'EventTimestamp': self.EventTimestamp, + 'ResourceType': self.ResourceType, + 'EventData': { + 'State': self.EventData_State + }, + 'ResourceQualifier': { + 'NodeName': self.ResourceQualifier_NodeName + } + } + return d diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/resourcetype.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/resourcetype.py new file mode 100644 index 0000000..788f477 --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/resourcetype.py @@ -0,0 +1,9 @@ +#coding=utf-8 + +from wsme import types as wtypes + +EnumResourceType = wtypes.Enum(str, 'PTP', 'FPGA') + +class ResourceType(object): + TypePTP = "PTP" + TypeFPGA = "FPGA" diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/rpc_endpoint.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/rpc_endpoint.py new file mode 100644 index 0000000..e4dcceb --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/model/dto/rpc_endpoint.py @@ -0,0 +1,34 @@ +#coding=utf-8 + +from wsme import types as wtypes + +RPC_ENDPOINT_BASE = { + 'Version': '1.0', + 'Namespace': 'notification', + 'Exchange': 'notification_exchange', + 'TransportEndpoint': '', + 'Topic': '', + 'Server': '' +} + +class RpcEndpointInfo(wtypes.Base): + TransportEndpoint = wtypes.text + Exchange = wtypes.text + Topic = wtypes.text + Server = wtypes.text + Version = wtypes.text + Namespace = wtypes.text + + def __init__(self, transport_endpoint): + self.endpoint_json = { + 'Version': RPC_ENDPOINT_BASE['Version'], + 'Namespace': RPC_ENDPOINT_BASE['Namespace'], + 'Exchange': RPC_ENDPOINT_BASE['Exchange'], + 'TransportEndpoint': transport_endpoint, + 'Topic': RPC_ENDPOINT_BASE['Topic'], + 'Server': RPC_ENDPOINT_BASE['Server'] + } + super(RpcEndpointInfo, self).__init__(**self.endpoint_json) + + def to_dict(self): + return self.endpoint_json diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/services/__init__.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py new file mode 100644 index 0000000..5c08bc5 --- /dev/null +++ b/notificationservice-base/centos/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py @@ -0,0 +1,213 @@ + +import os +import json +import time +import oslo_messaging +from oslo_config import cfg +import logging + +import multiprocessing as mp +import threading + +from trackingfunctionsdk.common.helpers import rpc_helper +from trackingfunctionsdk.model.dto.rpc_endpoint import RpcEndpointInfo +from trackingfunctionsdk.model.dto.resourcetype import ResourceType +from trackingfunctionsdk.model.dto.ptpstate import PtpState + +from trackingfunctionsdk.client.ptpeventproducer import PtpEventProducer + +from trackingfunctionsdk.common.helpers import ptpsync as ptpsync + +LOG = logging.getLogger(__name__) + +from trackingfunctionsdk.common.helpers import log_helper +log_helper.config_logger(LOG) + +THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0') + + +'''Entry point of Default Process Worker''' +def ProcessWorkerDefault(event, sqlalchemy_conf_json, broker_transport_endpoint): + worker = PtpWatcherDefault(event, sqlalchemy_conf_json, broker_transport_endpoint) + worker.run() + return + + +class PtpWatcherDefault: + DEFAULT_PTPTRACKER_CONTEXT = { + 'holdover_seconds': 30, + 'poll_freq_seconds': 2 + } + + class PtpRequestHandlerDefault(object): + def __init__(self, watcher): + self.watcher = watcher + self.init_time = time.time() + + def query_status(self, **rpc_kwargs): + self.watcher.ptptracker_context_lock.acquire() + sync_state = self.watcher.ptptracker_context.get('sync_state', PtpState.Freerun) + last_event_time = self.watcher.ptptracker_context.get('last_event_time', time.time()) + self.watcher.ptptracker_context_lock.release() + + lastStatus = { + 'ResourceType': ResourceType.TypePTP, + 'EventData': { + 'State': sync_state + }, + 'ResourceQualifier': { + 'NodeName': self.watcher.node_name + }, + 'EventTimestamp': last_event_time + } + return lastStatus + + def trigger_delivery(self, **rpc_kwargs): + self.watcher.forced_publishing = True + self.watcher.signal_ptp_event() + pass + + def __init__(self, event, sqlalchemy_conf_json, daemon_context_json): + self.sqlalchemy_conf = json.loads(sqlalchemy_conf_json) + self.event = event + self.init_time = time.time() + + self.daemon_context = json.loads(daemon_context_json) + self.ptptracker_context = self.daemon_context.get( + 'ptptracker_context', PtpWatcherDefault.DEFAULT_PTPTRACKER_CONTEXT) + self.ptptracker_context['sync_state'] = PtpState.Freerun + self.ptptracker_context['last_event_time'] = self.init_time + self.ptptracker_context_lock = threading.Lock() + + self.ptp_device_simulated = "true" == self.ptptracker_context.get('device_simulated', "False").lower() + + self.event_timeout = float(self.ptptracker_context['poll_freq_seconds']) + + self.node_name = self.daemon_context['THIS_NODE_NAME'] + self.namespace = self.daemon_context.get('THIS_NAMESPACE', 'notification') + + broker_transport_endpoint = self.daemon_context['NOTIFICATION_TRANSPORT_ENDPOINT'] + + registration_transport_endpoint = self.daemon_context['REGISTRATION_TRANSPORT_ENDPOINT'] + + self.broker_endpoint = RpcEndpointInfo(broker_transport_endpoint) + self.registration_broker_endpoint = RpcEndpointInfo(registration_transport_endpoint) + self.ptpeventproducer = PtpEventProducer( + self.node_name, + self.broker_endpoint.TransportEndpoint, + self.registration_broker_endpoint.TransportEndpoint) + + self.__ptprequest_handler = PtpWatcherDefault.PtpRequestHandlerDefault(self) + self.forced_publishing = False + + def signal_ptp_event(self): + if self.event: + self.event.set() + else: + LOG.warning("Unable to assert ptp event") + pass + + def run(self): + # start location listener + self.__start_listener() + while True: + # annouce the location + forced = self.forced_publishing + self.forced_publishing = False + self.__publish_ptpstatus(forced) + if self.event.wait(self.event_timeout): + LOG.debug("daemon control event is asserted") + self.event.clear() + else: + LOG.debug("daemon control event is timeout") + pass + continue + self.__stop_listener() + + '''Start listener to answer querying from clients''' + def __start_listener(self): + LOG.debug("start listener to answer location querying") + + self.ptpeventproducer.start_status_listener( + self.__ptprequest_handler + ) + return + + def __stop_listener(self): + LOG.debug("stop listener to answer location querying") + + self.ptpeventproducer.stop_status_listener(self.location_info) + return + + def __get_ptp_status(self, holdover_time, freq, sync_state, last_event_time): + new_event = False + new_event_time = last_event_time + if self.ptp_device_simulated: + now = time.time() + timediff = now - last_event_time + if timediff > holdover_time: + new_event = True + new_event_time = now + if sync_state == PtpState.Freerun: + sync_state = PtpState.Locked + elif sync_state == PtpState.Locked: + sync_state = PtpState.Holdover + elif sync_state == PtpState.Holdover: + sync_state = PtpState.Freerun + else: + sync_state = PtpState.Freerun + else: + new_event, sync_state, new_event_time = ptpsync.ptp_status( + holdover_time, freq, sync_state, last_event_time) + return new_event, sync_state, new_event_time + + '''announce location''' + def __publish_ptpstatus(self, forced=False): + holdover_time = float(self.ptptracker_context['holdover_seconds']) + freq = float(self.ptptracker_context['poll_freq_seconds']) + sync_state = self.ptptracker_context.get('sync_state', 'Unknown') + last_event_time = self.ptptracker_context.get('last_event_time', time.time()) + + new_event, sync_state, new_event_time = self.__get_ptp_status( + holdover_time, freq, sync_state, last_event_time) + + if new_event or forced: + # update context + self.ptptracker_context_lock.acquire() + self.ptptracker_context['sync_state'] = sync_state + self.ptptracker_context['last_event_time'] = new_event_time + self.ptptracker_context_lock.release() + + # publish new event + LOG.debug("publish ptp status to clients") + lastStatus = { + 'ResourceType': 'PTP', + 'EventData': { + 'State': sync_state + }, + 'ResourceQualifier': { + 'NodeName': self.node_name + }, + 'EventTimestamp': new_event_time + } + self.ptpeventproducer.publish_status(lastStatus) + return + + +class DaemonControl(object): + + def __init__(self, sqlalchemy_conf_json, daemon_context_json, process_worker = None): + self.event = mp.Event() + self.daemon_context = json.loads(daemon_context_json) + self.node_name = self.daemon_context['THIS_NODE_NAME'] + if not process_worker: + process_worker = ProcessWorkerDefault + + self. sqlalchemy_conf_json = sqlalchemy_conf_json + self.daemon_context_json = daemon_context_json + self.process_worker = process_worker + return + + def refresh(self): + self.process_worker(self.event, self.sqlalchemy_conf_json, self.daemon_context_json) + self.event.set() diff --git a/notificationservice-base/centos/notificationservice-base.stable_docker_image b/notificationservice-base/centos/notificationservice-base.stable_docker_image new file mode 100644 index 0000000..2bd26d6 --- /dev/null +++ b/notificationservice-base/centos/notificationservice-base.stable_docker_image @@ -0,0 +1,2 @@ +BUILDER=docker +LABEL=notificationservice-base \ No newline at end of file