From 2650675e23003fa2d2dca319a72ba04e92316634 Mon Sep 17 00:00:00 2001 From: "naman.gupta" Date: Fri, 3 Sep 2021 13:45:45 +0530 Subject: [PATCH] Xapp Registration reading configuration from env 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 Change-Id: Ib6d3a73654806820973afa77dc18dd7469255909 --- examples/xappConfig.json | 14 ---- ricxappframe/util/__init__.py | 17 +++++ ricxappframe/util/constants.py | 36 +++++++++++ ricxappframe/xapp_frame.py | 141 ++++++++++++++++++++++------------------- tests/test_config.py | 10 +-- tests/test_xapps.py | 8 ++- 6 files changed, 140 insertions(+), 86 deletions(-) delete mode 100644 examples/xappConfig.json create mode 100644 ricxappframe/util/__init__.py create mode 100644 ricxappframe/util/constants.py diff --git a/examples/xappConfig.json b/examples/xappConfig.json deleted file mode 100644 index 35218b0..0000000 --- a/examples/xappConfig.json +++ /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 index 0000000..986cd47 --- /dev/null +++ b/ricxappframe/util/__init__.py @@ -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 index 0000000..ceb074f --- /dev/null +++ b/ricxappframe/util/constants.py @@ -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" diff --git a/ricxappframe/xapp_frame.py b/ricxappframe/xapp_frame.py index 623bd37..feb5d66 100644 --- a/ricxappframe/xapp_frame.py +++ b/ricxappframe/xapp_frame.py @@ -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 diff --git a/tests/test_config.py b/tests/test_config.py index 8eef73b..e1cbb8f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -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 diff --git a/tests/test_xapps.py b/tests/test_xapps.py index 6aa32da..3706f30 100644 --- a/tests/test_xapps.py +++ b/tests/test_xapps.py @@ -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) -- 2.16.6