X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FNBI.git;a=blobdiff_plain;f=osm_nbi%2Fnbi.py;h=467f3b9a87bb194a3f66a9b5075b5bcedd138fe3;hp=a72fa58440ed4f215685fbadae36459e165d0676;hb=f5298be0fd06ca35e827fca738f8ef5747da12fd;hpb=09c073edf3929913df6c57cac12aca06f1ce297b diff --git a/osm_nbi/nbi.py b/osm_nbi/nbi.py index a72fa58..467f3b9 100644 --- a/osm_nbi/nbi.py +++ b/osm_nbi/nbi.py @@ -7,16 +7,19 @@ import json import yaml import html_out as html import logging +import logging.handlers +import getopt +import sys from engine import Engine, EngineException -from dbbase import DbException -from fsbase import FsException -from msgbase import MsgException +from osm_common.dbbase import DbException +from osm_common.fsbase import FsException +from osm_common.msgbase import MsgException from base64 import standard_b64decode #from os import getenv from http import HTTPStatus #from http.client import responses as http_responses from codecs import getreader -from os import environ +from os import environ, path __author__ = "Alfonso Tierno " @@ -67,6 +70,8 @@ URL: /osm GET POST /ns_lcm_op_occs 5 5 / 5 5 5 TO BE COMPLETED 5 5 + /vnfrs O + / O /subscriptions 5 5 / 5 X /admin/v1 @@ -150,10 +155,10 @@ class Server(object): "": {"METHODS": ("GET", "DELETE")} }, "vim_accounts": {"METHODS": ("GET", "POST"), - "": {"METHODS": ("GET", "DELETE")} + "": {"METHODS": ("GET", "DELETE", "PATCH")} }, "sdns": {"METHODS": ("GET", "POST"), - "": {"METHODS": ("GET", "DELETE")} + "": {"METHODS": ("GET", "DELETE", "PATCH")} }, } }, @@ -215,7 +220,10 @@ class Server(object): }, "ns_lcm_op_occs": {"METHODS": "GET", "": {"METHODS": "GET"}, - } + }, + "vnfrs": {"METHODS": ("GET"), + "": {"METHODS": ("GET")} + }, } }, } @@ -338,6 +346,8 @@ class Server(object): raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST) except KeyError as exc: raise NbiException("Query string error: " + str(exc), HTTPStatus.BAD_REQUEST) + except Exception as exc: + raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST) @staticmethod def _format_out(data, session=None, _format=None): @@ -603,6 +613,7 @@ class Server(object): _format = None method = "DONE" engine_item = None + rollback = None try: if not topic or not version or not item: raise NbiException("URL must contain at least 'topic/version/item'", HTTPStatus.METHOD_NOT_ALLOWED) @@ -615,6 +626,10 @@ class Server(object): method = kwargs.pop("METHOD") else: method = cherrypy.request.method + if kwargs and "FORCE" in kwargs: + force = kwargs.pop("FORCE") + else: + force = False self._check_valid_url_method(method, topic, version, item, _id, item2, *args) @@ -638,6 +653,8 @@ class Server(object): engine_item = "nsrs" if item == "ns_lcm_op_occs": engine_item = "nslcmops" + if item == "vnfrs": + engine_item = "vnfrs" if engine_item == "vims": # TODO this is for backward compatibility, it will remove in the future engine_item = "vim_accounts" @@ -662,7 +679,9 @@ class Server(object): if item in ("ns_descriptors_content", "vnf_packages_content"): _id = cherrypy.request.headers.get("Transaction-Id") if not _id: - _id = self.engine.new_item(session, engine_item, {}, None, cherrypy.request.headers) + _id = self.engine.new_item(session, engine_item, {}, None, cherrypy.request.headers, + force=force) + rollback = {"session": session, "item": engine_item, "_id": _id, "force": True} completed = self.engine.upload_content(session, engine_item, _id, indata, kwargs, cherrypy.request.headers) if completed: self._set_location_header(topic, version, item, _id) @@ -670,17 +689,19 @@ class Server(object): cherrypy.response.headers["Transaction-Id"] = _id outdata = {"id": _id} elif item == "ns_instances_content": - _id = self.engine.new_item(session, engine_item, indata, kwargs) - self.engine.ns_action(session, _id, "instantiate", {}, None) + _id = self.engine.new_item(session, engine_item, indata, kwargs, force=force) + rollback = {"session": session, "item": engine_item, "_id": _id, "force": True} + self.engine.ns_operation(session, _id, "instantiate", {}, None) self._set_location_header(topic, version, item, _id) outdata = {"id": _id} elif item == "ns_instances" and item2: - _id = self.engine.ns_action(session, _id, item2, indata, kwargs) + _id = self.engine.ns_operation(session, _id, item2, indata, kwargs) self._set_location_header(topic, version, "ns_lcm_op_occs", _id) outdata = {"id": _id} cherrypy.response.status = HTTPStatus.ACCEPTED.value else: - _id = self.engine.new_item(session, engine_item, indata, kwargs, cherrypy.request.headers) + _id = self.engine.new_item(session, engine_item, indata, kwargs, cherrypy.request.headers, + force=force) self._set_location_header(topic, version, item, _id) outdata = {"id": _id} # TODO form NsdInfo when item in ("ns_descriptors", "vnf_packages") @@ -692,10 +713,10 @@ class Server(object): cherrypy.response.status = HTTPStatus.OK.value else: # len(args) > 1 if item == "ns_instances_content": - self.engine.ns_action(session, _id, "terminate", {"autoremove": True}, None) + opp_id = self.engine.ns_operation(session, _id, "terminate", {"autoremove": True}, None) + outdata = {"_id": opp_id} cherrypy.response.status = HTTPStatus.ACCEPTED.value else: - force = kwargs.get("FORCE") self.engine.del_item(session, engine_item, _id, force) cherrypy.response.status = HTTPStatus.NO_CONTENT.value if engine_item in ("vim_accounts", "sdns"): @@ -712,15 +733,25 @@ class Server(object): cherrypy.response.status = HTTPStatus.NO_CONTENT.value outdata = None else: - outdata = {"id": self.engine.edit_item(session, engine_item, args[1], indata, kwargs)} + outdata = {"id": self.engine.edit_item(session, engine_item, _id, indata, kwargs, force=force)} + elif method == "PATCH": + if not indata and not kwargs: + raise NbiException("Nothing to update. Provide payload and/or query string", + HTTPStatus.BAD_REQUEST) + outdata = {"id": self.engine.edit_item(session, engine_item, _id, indata, kwargs, force=force)} else: raise NbiException("Method {} not allowed".format(method), HTTPStatus.METHOD_NOT_ALLOWED) return self._format_out(outdata, session, _format) except (NbiException, EngineException, DbException, FsException, MsgException) as e: - if hasattr(outdata, "close"): # is an open file - outdata.close() cherrypy.log("Exception {}".format(e)) cherrypy.response.status = e.http_code.value + if hasattr(outdata, "close"): # is an open file + outdata.close() + if rollback: + try: + self.engine.del_item(**rollback) + except Exception as e2: + cherrypy.log("Rollback Exception {}: {}".format(rollback, e2)) error_text = str(e) if isinstance(e, MsgException): error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format( @@ -768,10 +799,10 @@ def _start_service(): update_dict['server.socket_port'] = int(v) elif k == 'OSMNBI_SOCKET_HOST' or k == 'OSMNBI_SERVER_HOST': update_dict['server.socket_host'] = v - elif k1 == "server": - update_dict['server' + k2] = v - # TODO add more entries + elif k1 in ("server", "test", "auth", "log"): + update_dict[k1 + '.' + k2] = v elif k1 in ("message", "database", "storage"): + # k2 = k2.replace('_', '.') if k2 == "port": engine_config[k1][k2] = int(v) else: @@ -783,6 +814,7 @@ def _start_service(): if update_dict: cherrypy.config.update(update_dict) + engine_config["global"].update(update_dict) # logging cherrypy log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s" @@ -792,8 +824,8 @@ def _start_service(): logger_cherry = logging.getLogger("cherrypy") logger_nbi = logging.getLogger("nbi") - if "logfile" in engine_config["global"]: - file_handler = logging.handlers.RotatingFileHandler(engine_config["global"]["logfile"], + if "log.file" in engine_config["global"]: + file_handler = logging.handlers.RotatingFileHandler(engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0) file_handler.setFormatter(log_formatter_simple) logger_cherry.addHandler(file_handler) @@ -809,9 +841,9 @@ def _start_service(): str_handler.setFormatter(log_formatter_cherry) logger.addHandler(str_handler) - if engine_config["global"].get("loglevel"): - logger_cherry.setLevel(engine_config["global"]["loglevel"]) - logger_nbi.setLevel(engine_config["global"]["loglevel"]) + if engine_config["global"].get("log.level"): + logger_cherry.setLevel(engine_config["global"]["log.level"]) + logger_nbi.setLevel(engine_config["global"]["log.level"]) # logging other modules for k1, logname in {"message": "nbi.msg", "database": "nbi.db", "storage": "nbi.fs"}.items(): @@ -841,7 +873,7 @@ def _stop_service(): cherrypy.tree.apps['/osm'].root.engine.stop() cherrypy.log.error("Stopping osm_nbi") -def nbi(): +def nbi(config_file): # conf = { # '/': { # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(), @@ -861,8 +893,51 @@ def nbi(): # 'tools.auth_basic.checkpassword': validate_password}) cherrypy.engine.subscribe('start', _start_service) cherrypy.engine.subscribe('stop', _stop_service) - cherrypy.quickstart(Server(), '/osm', "nbi.cfg") + cherrypy.quickstart(Server(), '/osm', config_file) + + +def usage(): + print("""Usage: {} [options] + -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg) + -h|--help: shows this help + """.format(sys.argv[0])) + # --log-socket-host HOST: send logs to this host") + # --log-socket-port PORT: send logs using this port (default: 9022)") if __name__ == '__main__': - nbi() + try: + # load parameters and configuration + opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"]) + # TODO add "log-socket-host=", "log-socket-port=", "log-file=" + config_file = None + for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit() + elif o in ("-c", "--config"): + config_file = a + # elif o == "--log-socket-port": + # log_socket_port = a + # elif o == "--log-socket-host": + # log_socket_host = a + # elif o == "--log-file": + # log_file = a + else: + assert False, "Unhandled option" + if config_file: + if not path.isfile(config_file): + print("configuration file '{}' that not exist".format(config_file), file=sys.stderr) + exit(1) + else: + for config_file in (__file__[:__file__.rfind(".")] + ".cfg", "./nbi.cfg", "/etc/osm/nbi.cfg"): + if path.isfile(config_file): + break + else: + print("No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/", file=sys.stderr) + exit(1) + nbi(config_file) + except getopt.GetoptError as e: + print(str(e), file=sys.stderr) + # usage() + exit(1)