From bac18952225035e44ae7672b81dacc2793944a16 Mon Sep 17 00:00:00 2001 From: Naman Gupta Date: Thu, 3 Jun 2021 21:49:17 +0530 Subject: [PATCH] Upadated Xappframework for Xapp registration/deregistration to RIC Issue-ID: RIC-706 Change-Id: Ie1363d6f937b6412753439a409f17aa850e3a462 Signed-off-by: naman.gupta --- docs/developer-guide.rst | 9 +++ examples/xappConfig.json | 14 ++++ ricxappframe/xapp_frame.py | 177 ++++++++++++++++++++++++++++++++++++++++++++- setup.py | 2 +- 4 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 examples/xappConfig.json diff --git a/docs/developer-guide.rst b/docs/developer-guide.rst index 6cfe369..dc03c77 100644 --- a/docs/developer-guide.rst +++ b/docs/developer-guide.rst @@ -38,6 +38,15 @@ These items in this repo must be kept in sync with the RMR version: #. ``rmr-version.yaml`` controls what version of RMR is installed for unit testing in Jenkins CI +Registration/Deregistartion of Xapp +----------------------------------- + +For registration and deregistration of Xapp following items need to be defined: + +#. CONFIG_FILE_PATH variable as a environment variable in Dockerfile if running + Xapp as a docker container or in configmap in case of Xapp as a pod. +#. Copy the xappConfig.json into the docker image in Dockerfile. + Unit Testing ------------ diff --git a/examples/xappConfig.json b/examples/xappConfig.json new file mode 100644 index 0000000..35218b0 --- /dev/null +++ b/examples/xappConfig.json @@ -0,0 +1,14 @@ +{ + "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/xapp_frame.py b/ricxappframe/xapp_frame.py index 8abc443..5428ce9 100644 --- a/ricxappframe/xapp_frame.py +++ b/ricxappframe/xapp_frame.py @@ -22,19 +22,21 @@ should instantiate and/or subclass depending on their needs. import json import os import queue +import time from threading import Thread import inotify_simple from mdclogpy import Logger from ricxappframe import xapp_rmr from ricxappframe.rmr import rmr 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: @@ -96,10 +98,180 @@ class _BaseXapp: self._inotify = None self.logger.warning("__init__: NOT watching any config file") + # used for thread control of Registration of Xapp + self._keep_registration = True + + # 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: + self._config_data = json.load(json_file) + else: + self._keep_registration = False + self.logger.warning("__init__: Cannot Read config file for xapp Registration") + + Thread(target=self.registerXapp).start() + # run the optionally provided user post init if post_init: post_init(self) + def get_service(self, host, service): + """ + To find the url for connecting to the service + + Parameters + ---------- + host: string + defines the hostname in the url + service: string + defines the servicename in the url + + Returns + ------- + string + 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] + return "" + + def do_post(self, plt_namespace, url, msg): + """ + registration of the xapp using the url and json msg + + Parameters + ---------- + plt_namespace: string + platform namespace where the xapp is running + url: string + url for xapp registration + msg: string + json msg containing the xapp details + + Returns + ------- + bool + whether or not the xapp is registered + """ + 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)) + return resp.status_code == 200 or resp.status_code == 201 + except requests.exceptions.RequestException as err: + self.logger.error("Error : {}".format(err)) + return format(err) + except requests.exceptions.HTTPError as errh: + self.logger.error("Http Error: {}".format(errh)) + return errh + except requests.exceptions.ConnectionError as errc: + self.logger.error("Error Connecting: {}".format(errc)) + return errc + except requests.exceptions.Timeout as errt: + self.logger.error("Timeout Error: {}".format(errt)) + return errt + + def register(self): + """ + function to registers the xapp + + Returns + ------- + bool + whether or not the xapp is registered + """ + hostname = self._config_data.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")) + 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) + + def registerXapp(self): + """ + registers the xapp + """ + while self._keep_registration: + time.sleep(5) + # 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"))) + 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 + + def deregister(self): + """ + Deregisters the xapp + + Returns + ------- + bool + whether or not the xapp is registered + """ + healthy = self.healthcheck() + if not healthy: + self.logger.error("RMR or SDL or xapp == Not Healthy") + return None + if self._config_data is None: + return None + name = self._config_data.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 = { + "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) + + def xapp_shutdown(self): + """ + Deregisters the xapp while shutting down + """ + self.deregister() + self.logger.debug("Wait for xapp to get unregistered") + time.sleep(10) + # Public rmr methods def rmr_get_messages(self): @@ -335,6 +507,9 @@ class _BaseXapp: TODO: can we register a ctrl-c handler so this gets called on ctrl-c? Because currently two ctrl-c are needed to stop. """ + + self.xapp_shutdown() + self._rmr_loop.stop() diff --git a/setup.py b/setup.py index 6a0a862..b46715e 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ setup( author="O-RAN Software Community", description="Xapp and RMR framework for Python", url="https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/xapp-frame-py", - install_requires=["inotify_simple", "msgpack", "mdclogpy", "ricsdl>=2.1.0,<3.0.0"], + install_requires=["inotify_simple", "msgpack", "mdclogpy", "ricsdl>=2.1.0,<3.0.0", "requests"], classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Telecommunications Industry", -- 2.16.6