Xapp Registration reading configuration from env 49/6649/10
authornaman.gupta <naman.gupta@samsung.com>
Fri, 3 Sep 2021 08:15:45 +0000 (13:45 +0530)
committernaman.gupta <naman.gupta@samsung.com>
Mon, 13 Sep 2021 08:14:40 +0000 (13:44 +0530)
Xapp Registration reading configuration from env instead of
configuration as part of the configuration related to xapp comes from
xapp descriptor and rest of the general configuration is part of the xappframework

Issue-ID: RIC-706
Signed-off-by: naman.gupta <naman.gupta@samsung.com>
Change-Id: Ib6d3a73654806820973afa77dc18dd7469255909

examples/xappConfig.json [deleted file]
ricxappframe/util/__init__.py [new file with mode: 0644]
ricxappframe/util/constants.py [new file with mode: 0644]
ricxappframe/xapp_frame.py
tests/test_config.py
tests/test_xapps.py

diff --git a/examples/xappConfig.json b/examples/xappConfig.json
deleted file mode 100644 (file)
index 35218b0..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "SERVICE_HTTP" : "SERVICE_{}_{}_HTTP_PORT",
-  "SERVICE_RMR" : "SERVICE_{}_{}_RMR_PORT",
-  "CONFIG_PATH" : "/ric/v1/config",
-  "REGISTER_PATH" : "http://service-{}-appmgr-http.{}:8080/ric/v1/register",
-  "DEREGISTER_PATH" : "http://service-{}-appmgr-http.{}:8080/ric/v1/deregister",
-  "DEFAULT_PLT_NS" : "ricplt",
-  "DEFAULT_XAPP_NS" : "ricxapp",
-  "hostname" : "xapp",
-  "name" : "xappRegistration",
-  "version" : "1",
-  "PLT_NAMESPACE" : "ricplt",
-  "APP_NAMESPACE" : "ricxapp"
-}
diff --git a/ricxappframe/util/__init__.py b/ricxappframe/util/__init__.py
new file mode 100644 (file)
index 0000000..986cd47
--- /dev/null
@@ -0,0 +1,17 @@
+# ==================================================================================
+#
+#       Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#          http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# ==================================================================================
diff --git a/ricxappframe/util/constants.py b/ricxappframe/util/constants.py
new file mode 100644 (file)
index 0000000..ceb074f
--- /dev/null
@@ -0,0 +1,36 @@
+# ==================================================================================
+#
+#       Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#          http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# ==================================================================================
+
+
+class Constants:
+
+    # xapp registration constants
+    SERVICE_HTTP = "SERVICE_{}_{}_HTTP_PORT"
+    SERVICE_RMR = "SERVICE_{}_{}_RMR_PORT"
+    CONFIG_PATH = "/ric/v1/config"
+    REGISTER_PATH = "http://service-{}-appmgr-http.{}:8080/ric/v1/register"
+    DEREGISTER_PATH = "http://service-{}-appmgr-http.{}:8080/ric/v1/deregister"
+    DEFAULT_PLT_NS = "ricplt"
+    DEFAULT_XAPP_NS = "ricxapp"
+
+    # message-type constants
+    RIC_HEALTH_CHECK_REQ = 100
+    RIC_HEALTH_CHECK_RESP = 101
+
+    # environment variable with path to configuration file
+    CONFIG_FILE_ENV = "CONFIG_FILE"
index 623bd37..feb5d66 100644 (file)
@@ -34,15 +34,9 @@ from ricxappframe.constants import sdl_namespaces
 from ricxappframe.entities.rnib.nb_identity_pb2 import NbIdentity
 from ricxappframe.entities.rnib.nodeb_info_pb2 import Node
 from ricxappframe.rmr import rmr
+from ricxappframe.util.constants import Constants
 from ricxappframe.xapp_sdl import SDLWrapper
 import requests
-# message-type constants
-RIC_HEALTH_CHECK_REQ = 100
-RIC_HEALTH_CHECK_RESP = 101
-
-# environment variable with path to configuration file
-CONFIG_FILE_ENV = "CONFIG_FILE"
-CONFIG_FILE_PATH = "CONFIG_FILE_PATH"
 
 
 class _BaseXapp:
@@ -95,7 +89,7 @@ class _BaseXapp:
 
         # Config
         # The environment variable specifies the path to the Xapp config file
-        self._config_path = os.environ.get(CONFIG_FILE_ENV, None)
+        self._config_path = os.environ.get(Constants.CONFIG_FILE_ENV, None)
         if self._config_path and os.path.isfile(self._config_path):
             self._inotify = inotify_simple.INotify()
             self._inotify.add_watch(self._config_path, inotify_simple.flags.MODIFY)
@@ -109,13 +103,13 @@ class _BaseXapp:
 
         # configuration data  for xapp registration and deregistration
         self._config_data = None
-        self._configfile_path = os.environ.get(CONFIG_FILE_PATH, None)
-        if self._configfile_path and os.path.isfile(self._configfile_path):
-            with open(self._configfile_path) as json_file:
+        if self._config_path and os.path.isfile(self._config_path):
+            with open(self._config_path) as json_file:
                 self._config_data = json.load(json_file)
         else:
             self._keep_registration = False
-            self.logger.warning("__init__: Cannot Read config file for xapp Registration")
+            self.logger.error("__init__: Cannot Read config file for xapp Registration")
+            self._config_data = {}
 
         Thread(target=self.registerXapp).start()
 
@@ -140,13 +134,16 @@ class _BaseXapp:
             url for the service
         """
         app_namespace = self._config_data.get("APP_NAMESPACE")
-        if app_namespace == "":
-            app_namespace = self._config_data.get("DEFAULT_XAPP_NS")
-        svc = service.format(app_namespace.upper(), host.upper())
-        url = svc.replace("-", "_").split("//")
-
-        if len(url) > 1:
-            return url[1]
+        if app_namespace is None:
+            app_namespace = Constants.DEFAULT_XAPP_NS
+        self.logger.debug("service : {} host : {},appnamespace : {}".format(service, host, app_namespace))
+        if app_namespace is not None and host is not None:
+            svc = service.format(app_namespace.upper(), host.upper())
+            urlkey = svc.replace("-", "_")
+            url = os.environ.get(urlkey).split("//")
+            self.logger.debug("Service urlkey : {} and url: {}".format(urlkey, url))
+            if len(url) > 1:
+                return url[1]
         return ""
 
     def do_post(self, plt_namespace, url, msg):
@@ -167,10 +164,17 @@ class _BaseXapp:
         bool
             whether or not the xapp is registered
         """
+        if url is None:
+            self.logger.error("url is empty ")
+            return False
+        if plt_namespace is None:
+            self.logger.error("plt_namespace is empty")
+            return False
         try:
             request_url = url.format(plt_namespace, plt_namespace)
             resp = requests.post(request_url, json=msg)
             self.logger.debug("Post to '{}' done, status : {}".format(request_url, resp.status_code))
+            self.logger.debug("Response Text : {}".format(resp.text))
             return resp.status_code == 200 or resp.status_code == 201
         except requests.exceptions.RequestException as err:
             self.logger.error("Error : {}".format(err))
@@ -194,49 +198,58 @@ class _BaseXapp:
         bool
             whether or not the xapp is registered
         """
-        hostname = self._config_data.get("hostname")
+        hostname = os.environ.get("HOSTNAME")
         xappname = self._config_data.get("name")
         xappversion = self._config_data.get("version")
-        pltnamespace = self._config_data.get("PLT_NAMESPACE")
-        if pltnamespace == "":
-            pltnamespace = self._config_data.get("DEFAULT_PLT_NS")
-        http_endpoint = self.get_service(hostname, self._config_data.get("SERVICE_HTTP"))
-        rmr_endpoint = self.get_service(hostname, self._config_data.get("SERVICE_RMR"))
+        pltnamespace = os.environ.get("PLT_NAMESPACE")
+        if pltnamespace is None:
+            pltnamespace = Constants.DEFAULT_PLT_NS
+        self.logger.debug("config details hostname : {} xappname: {} xappversion : {} pltnamespace : {}".format(
+            hostname, xappname, xappversion, pltnamespace))
+
+        http_endpoint = self.get_service(hostname, Constants.SERVICE_HTTP)
+        rmr_endpoint = self.get_service(hostname, Constants.SERVICE_RMR)
         if http_endpoint == "" or rmr_endpoint == "":
-            self.logger.warning("Couldn't resolve service endpoints: http_endpoint={} rmr_endpoint={}".format(http_endpoint, rmr_endpoint))
-            return None
-        try:
-            request_string = {
-                "appName": hostname,
-                "httpEndpoint": http_endpoint,
-                "rmrEndpoint": rmr_endpoint,
-                "appInstanceName": xappname,
-                "appVersion": xappversion,
-                "configPath": self._config_data.get("CONFIG_PATH")
-            }
-            request_body = json.dumps(request_string)
-        except TypeError:
-            self.logger.error("Unable to serialize the object")
-            return "Error searializing the object"
-
-        return self.do_post(pltnamespace, self._config_data.get("REGISTER_PATH"), request_body)
+            self.logger.error(
+                "Couldn't resolve service endpoints: http_endpoint={} rmr_endpoint={}".format(http_endpoint,
+                                                                                              rmr_endpoint))
+            return False
+        self.logger.debug(
+            "config details hostname : {} xappname: {} xappversion : {} pltnamespace : {} http_endpoint : {} rmr_endpoint "
+            ": {} configpath : {}".format(hostname, xappname, xappversion, pltnamespace, http_endpoint, rmr_endpoint,
+                                          self._config_data.get("CONFIG_PATH")))
+        request_string = {
+            "appName": hostname,
+            "appVersion": xappversion,
+            "configPath": "",
+            "appInstanceName": xappname,
+            "httpEndpoint": http_endpoint,
+            "rmrEndpoint": rmr_endpoint,
+            "config": json.dumps(self._config_data)
+        }
+        self.logger.info("REQUEST STRING :{}".format(request_string))
+        return self.do_post(pltnamespace, Constants.REGISTER_PATH, request_string)
 
     def registerXapp(self):
         """
             registers the xapp
         """
-        while self._keep_registration:
-            time.sleep(5)
+        retries = 5
+        while self._keep_registration and retries > 0:
+            time.sleep(2)
+            retries = retries-1
             # checking for rmr/sdl/xapp health
             healthy = self.healthcheck()
             if not healthy:
-                self.logger.warning("Application='{}' is not ready yet, waiting ...".format(self._config_data.get("name")))
+                self.logger.warning(
+                    "Application='{}' is not ready yet, waiting ...".format(self._config_data.get("name")))
                 continue
 
-            self.logger.debug("Application='{}'  is now up and ready, continue with registration ...".format(self._config_data.get("name")))
-            self.register()
-            self.logger.debug("Registration done, proceeding with startup ...")
-            break
+            self.logger.debug("Application='{}'  is now up and ready, continue with registration ...".format(
+                self._config_data.get("name")))
+            if self.register():
+                self.logger.debug("Registration done, proceeding with startup ...")
+                break
 
     def deregister(self):
         """
@@ -253,22 +266,17 @@ class _BaseXapp:
             return None
         if self._config_data is None:
             return None
-        name = self._config_data.get("hostname")
+        name = os.environ.get("HOSTNAME")
         xappname = self._config_data.get("name")
-        pltnamespace = self._config_data.get("PLT_NAMESPACE")
-        if pltnamespace == "":
-            pltnamespace = self._config_data.get("PLT_NAMESPACE")
-        try:
-            request_string = {
+        pltnamespace = os.environ.get("PLT_NAMESPACE")
+        if pltnamespace is None:
+            pltnamespace = Constants.DEFAULT_PLT_NS
+        request_string = {
                 "appName": name,
                 "appInstanceName": xappname,
-            }
-            request_body = json.dumps(request_string)
-        except TypeError:
-            self.logger.error("Error Serializing the object")
-            return "Error serializing the object"
+        }
 
-        return self.do_post(pltnamespace, self._config_data.get("DEREGISTER_PATH"), request_body)
+        return self.do_post(pltnamespace, Constants.DEREGISTER_PATH, request_string)
 
     def xapp_shutdown(self):
         """
@@ -310,7 +318,8 @@ class _BaseXapp:
         bool
             whether or not the send worked after retries attempts
         """
-        sbuf = rmr.rmr_alloc_msg(vctx=self._mrc, size=len(payload), payload=payload, gen_transaction_id=True, mtype=mtype)
+        sbuf = rmr.rmr_alloc_msg(vctx=self._mrc, size=len(payload), payload=payload, gen_transaction_id=True,
+                                 mtype=mtype)
 
         for _ in range(retries):
             sbuf = rmr.rmr_send_msg(self._mrc, sbuf)
@@ -637,7 +646,8 @@ class RMRXapp(_BaseXapp):
         its signature should be post_init(self)
     """
 
-    def __init__(self, default_handler, config_handler=None, rmr_port=4562, rmr_wait_for_ready=True, use_fake_sdl=False, post_init=None):
+    def __init__(self, default_handler, config_handler=None, rmr_port=4562, rmr_wait_for_ready=True, use_fake_sdl=False,
+                 post_init=None):
         """
         Also see _BaseXapp
         """
@@ -661,15 +671,16 @@ class RMRXapp(_BaseXapp):
         def handle_healthcheck(self, summary, sbuf):
             healthy = self.healthcheck()
             payload = b"OK\n" if healthy else b"ERROR [RMR or SDL is unhealthy]\n"
-            self.rmr_rts(sbuf, new_payload=payload, new_mtype=RIC_HEALTH_CHECK_RESP)
+            self.rmr_rts(sbuf, new_payload=payload, new_mtype=Constants.RIC_HEALTH_CHECK_RESP)
             self.rmr_free(sbuf)
 
-        self.register_callback(handle_healthcheck, RIC_HEALTH_CHECK_REQ)
+        self.register_callback(handle_healthcheck, Constants.RIC_HEALTH_CHECK_REQ)
 
         # define a default configuration-change handler if none was provided.
         if not config_handler:
             def handle_config_change(self, config):
                 self.logger.debug("xapp_frame: default config handler invoked")
+
             self._config_handler = handle_config_change
 
         # call the config handler at startup if prereqs were met
index 8eef73b..e1cbb8f 100644 (file)
@@ -19,7 +19,9 @@ import time
 import os
 from contextlib import suppress
 from mdclogpy import Logger
-from ricxappframe.xapp_frame import RMRXapp, CONFIG_FILE_ENV
+
+from ricxappframe.util.constants import Constants
+from ricxappframe.xapp_frame import RMRXapp
 
 mdc_logger = Logger(name=__name__)
 rmr_xapp_config = None
@@ -41,7 +43,7 @@ def write_config_file():
 
 def test_config_no_env(monkeypatch):
     init_config_file()
-    monkeypatch.delenv(CONFIG_FILE_ENV, raising=False)
+    monkeypatch.delenv(Constants.CONFIG_FILE_ENV, raising=False)
 
     def default_rmr_handler(self, summary, sbuf):
         pass
@@ -67,7 +69,7 @@ def test_config_no_env(monkeypatch):
 def test_default_config_handler(monkeypatch):
     """Just for coverage"""
     init_config_file()
-    monkeypatch.setenv(CONFIG_FILE_ENV, config_file_path)
+    monkeypatch.setenv(Constants.CONFIG_FILE_ENV, config_file_path)
 
     def default_rmr_handler(self, summary, sbuf):
         pass
@@ -86,7 +88,7 @@ def test_default_config_handler(monkeypatch):
 def test_custom_config_handler(monkeypatch):
     # point watcher at the file
     init_config_file()
-    monkeypatch.setenv(CONFIG_FILE_ENV, config_file_path)
+    monkeypatch.setenv(Constants.CONFIG_FILE_ENV, config_file_path)
 
     def default_handler(self, summary, sbuf):
         pass
index 6aa32da..3706f30 100644 (file)
@@ -17,7 +17,9 @@
 import json
 import time
 from contextlib import suppress
-from ricxappframe.xapp_frame import _BaseXapp, Xapp, RMRXapp, RIC_HEALTH_CHECK_REQ, RIC_HEALTH_CHECK_RESP
+
+from ricxappframe.util.constants import Constants
+from ricxappframe.xapp_frame import _BaseXapp, Xapp, RMRXapp
 from ricxappframe.constants import sdl_namespaces
 
 rmr_xapp = None
@@ -87,7 +89,7 @@ def test_rmr_healthcheck():
     health_pay = None
 
     def post_init(self):
-        self.rmr_send(b"", RIC_HEALTH_CHECK_REQ)
+        self.rmr_send(b"", Constants.RIC_HEALTH_CHECK_REQ)
 
     def default_handler(self, summary, sbuf):
         pass
@@ -100,7 +102,7 @@ def test_rmr_healthcheck():
         health_pay = summary["payload"]
         self.rmr_free(sbuf)
 
-    rmr_xapp_health.register_callback(health_handler, RIC_HEALTH_CHECK_RESP)
+    rmr_xapp_health.register_callback(health_handler, Constants.RIC_HEALTH_CHECK_RESP)
     rmr_xapp_health.run(thread=True)  # in unit tests we need to thread here or else execution is not returned!
 
     time.sleep(1)