X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;ds=inline;f=osm_nbi%2Fnbi.py;h=1d53396f05321122f1400c26f5b772b1b4247f6e;hb=ef4e224eb67d7b6711206591b8e1b5f8790c694e;hp=0a4c4027ef1165a81bbeb0f89549c915c3c102c9;hpb=cb83c941ebdf6a8807ffe3b1c3346c61e085b0bf;p=osm%2FNBI.git diff --git a/osm_nbi/nbi.py b/osm_nbi/nbi.py index 0a4c402..1d53396 100644 --- a/osm_nbi/nbi.py +++ b/osm_nbi/nbi.py @@ -259,9 +259,11 @@ class Server(object): if "application/json" in cherrypy.request.headers["Content-Type"]: error_text = "Invalid json format " indata = json.load(self.reader(cherrypy.request.body)) + cherrypy.request.headers.pop("Content-File-MD5", None) elif "application/yaml" in cherrypy.request.headers["Content-Type"]: error_text = "Invalid yaml format " indata = yaml.load(cherrypy.request.body) + cherrypy.request.headers.pop("Content-File-MD5", None) elif "application/binary" in cherrypy.request.headers["Content-Type"] or \ "application/gzip" in cherrypy.request.headers["Content-Type"] or \ "application/zip" in cherrypy.request.headers["Content-Type"] or \ @@ -281,9 +283,11 @@ class Server(object): # 'application/yaml' for input format are available") error_text = "Invalid yaml format " indata = yaml.load(cherrypy.request.body) + cherrypy.request.headers.pop("Content-File-MD5", None) else: error_text = "Invalid yaml format " indata = yaml.load(cherrypy.request.body) + cherrypy.request.headers.pop("Content-File-MD5", None) if not indata: indata = {} @@ -483,7 +487,7 @@ class Server(object): return f elif len(args) == 2 and args[0] == "db-clear": - return self.engine.del_item_list({"project_id": "admin", "admin": True}, args[1], kwargs) + return self.engine.db.del_list(args[1], kwargs) elif args and args[0] == "prune": return self.engine.prune() elif args and args[0] == "login": @@ -506,17 +510,17 @@ class Server(object): time.sleep(sleep_time) # thread_info elif len(args) >= 2 and args[0] == "message": - topic = args[1] - return_text = "
{} ->\n".format(topic) + main_topic = args[1] + return_text = "{} ->\n".format(main_topic) try: if cherrypy.request.method == 'POST': to_send = yaml.load(cherrypy.request.body) for k, v in to_send.items(): - self.engine.msg.write(topic, k, v) + self.engine.msg.write(main_topic, k, v) return_text += " {}: {}\n".format(k, v) elif cherrypy.request.method == 'GET': for k, v in kwargs.items(): - self.engine.msg.write(topic, k, yaml.load(v)) + self.engine.msg.write(main_topic, k, yaml.load(v)) return_text += " {}: {}\n".format(k, yaml.load(v)) except Exception as e: return_text += "Error: " + str(e) @@ -545,7 +549,7 @@ class Server(object): def _check_valid_url_method(self, method, *args): if len(args) < 3: - raise NbiException("URL must contain at least 'topic/version/item'", HTTPStatus.METHOD_NOT_ALLOWED) + raise NbiException("URL must contain at least 'main_topic/version/topic'", HTTPStatus.METHOD_NOT_ALLOWED) reference = self.valid_methods for arg in args: @@ -571,33 +575,35 @@ class Server(object): return @staticmethod - def _set_location_header(topic, version, item, id): + def _set_location_header(main_topic, version, topic, id): """ Insert response header Location with the URL of created item base on URL params - :param topic: + :param main_topic: :param version: - :param item: + :param topic: :param id: :return: None """ # Use cherrypy.request.base for absoluted path and make use of request.header HOST just in case behind aNAT - cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(topic, version, item, id) + cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(main_topic, version, topic, id) return @cherrypy.expose - def default(self, topic=None, version=None, item=None, _id=None, item2=None, *args, **kwargs): + def default(self, main_topic=None, version=None, topic=None, _id=None, item=None, *args, **kwargs): session = None outdata = None _format = None method = "DONE" - engine_item = None + engine_topic = None rollback = [] session = 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) - if topic not in ("admin", "vnfpkgm", "nsd", "nslcm"): - raise NbiException("URL topic '{}' not supported".format(topic), HTTPStatus.METHOD_NOT_ALLOWED) + if not main_topic or not version or not topic: + raise NbiException("URL must contain at least 'main_topic/version/topic'", + HTTPStatus.METHOD_NOT_ALLOWED) + if main_topic not in ("admin", "vnfpkgm", "nsd", "nslcm"): + raise NbiException("URL main_topic '{}' not supported".format(main_topic), + HTTPStatus.METHOD_NOT_ALLOWED) if version != 'v1': raise NbiException("URL version '{}' not supported".format(version), HTTPStatus.METHOD_NOT_ALLOWED) @@ -610,97 +616,107 @@ class Server(object): else: force = False - self._check_valid_url_method(method, topic, version, item, _id, item2, *args) + self._check_valid_url_method(method, main_topic, version, topic, _id, item, *args) - if topic == "admin" and item == "tokens": + if main_topic == "admin" and topic == "tokens": return self.token(method, _id, kwargs) # self.engine.load_dbase(cherrypy.request.app.config) session = self.authenticator.authorize() indata = self._format_in(kwargs) - engine_item = item - if item == "subscriptions": - engine_item = topic + "_" + item - if item2: - engine_item = item2 - - if topic == "nsd": - engine_item = "nsds" - elif topic == "vnfpkgm": - engine_item = "vnfds" - elif topic == "nslcm": - engine_item = "nsrs" - if item == "ns_lcm_op_occs": - engine_item = "nslcmops" - if item == "vnfrs" or item == "vnf_instances": - engine_item = "vnfrs" - elif topic == "pdu": - engine_item = "pdus" - if engine_item == "vims": # TODO this is for backward compatibility, it will remove in the future - engine_item = "vim_accounts" + engine_topic = topic + if topic == "subscriptions": + engine_topic = main_topic + "_" + topic + if item: + engine_topic = item + + if main_topic == "nsd": + engine_topic = "nsds" + elif main_topic == "vnfpkgm": + engine_topic = "vnfds" + elif main_topic == "nslcm": + engine_topic = "nsrs" + if topic == "ns_lcm_op_occs": + engine_topic = "nslcmops" + if topic == "vnfrs" or topic == "vnf_instances": + engine_topic = "vnfrs" + elif main_topic == "pdu": + engine_topic = "pdus" + if engine_topic == "vims": # TODO this is for backward compatibility, it will remove in the future + engine_topic = "vim_accounts" if method == "GET": - if item2 in ("nsd_content", "package_content", "artifacts", "vnfd", "nsd"): - if item2 in ("vnfd", "nsd"): + if item in ("nsd_content", "package_content", "artifacts", "vnfd", "nsd"): + if item in ("vnfd", "nsd"): path = "$DESCRIPTOR" elif args: path = args - elif item2 == "artifacts": + elif item == "artifacts": path = () else: path = None - file, _format = self.engine.get_file(session, engine_item, _id, path, + file, _format = self.engine.get_file(session, engine_topic, _id, path, cherrypy.request.headers.get("Accept")) outdata = file elif not _id: - outdata = self.engine.get_item_list(session, engine_item, kwargs) + outdata = self.engine.get_item_list(session, engine_topic, kwargs) else: - outdata = self.engine.get_item(session, engine_item, _id) + outdata = self.engine.get_item(session, engine_topic, _id) elif method == "POST": - if item in ("ns_descriptors_content", "vnf_packages_content"): + if topic in ("ns_descriptors_content", "vnf_packages_content"): _id = cherrypy.request.headers.get("Transaction-Id") if not _id: - _id = self.engine.new_item(rollback, session, engine_item, {}, None, cherrypy.request.headers, + _id = self.engine.new_item(rollback, session, engine_topic, {}, None, cherrypy.request.headers, force=force) - completed = self.engine.upload_content(session, engine_item, _id, indata, kwargs, - cherrypy.request.headers) + completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs, + cherrypy.request.headers, force=force) if completed: - self._set_location_header(topic, version, item, _id) + self._set_location_header(main_topic, version, topic, _id) else: cherrypy.response.headers["Transaction-Id"] = _id outdata = {"id": _id} - elif item == "ns_instances_content": - _id = self.engine.new_item(rollback, session, engine_item, indata, kwargs, force=force) - self.engine.ns_operation(rollback, session, _id, "instantiate", indata, None) - self._set_location_header(topic, version, item, _id) + elif topic == "ns_instances_content": + # creates NSR + _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs, force=force) + # creates nslcmop + indata["lcmOperationType"] = "instantiate" + indata["nsInstanceId"] = _id + self.engine.new_item(rollback, session, "nslcmops", indata, None) + self._set_location_header(main_topic, version, topic, _id) outdata = {"id": _id} - elif item == "ns_instances" and item2: - _id = self.engine.ns_operation(rollback, session, _id, item2, indata, kwargs) - self._set_location_header(topic, version, "ns_lcm_op_occs", _id) + elif topic == "ns_instances" and item: + indata["lcmOperationType"] = item + indata["nsInstanceId"] = _id + _id = self.engine.new_item(rollback, session, "nslcmops", indata, kwargs) + self._set_location_header(main_topic, version, "ns_lcm_op_occs", _id) outdata = {"id": _id} cherrypy.response.status = HTTPStatus.ACCEPTED.value else: - _id = self.engine.new_item(rollback, session, engine_item, indata, kwargs, cherrypy.request.headers, - force=force) - self._set_location_header(topic, version, item, _id) + _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs, + cherrypy.request.headers, force=force) + self._set_location_header(main_topic, version, topic, _id) outdata = {"id": _id} - # TODO form NsdInfo when item in ("ns_descriptors", "vnf_packages") + # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages") cherrypy.response.status = HTTPStatus.CREATED.value elif method == "DELETE": if not _id: - outdata = self.engine.del_item_list(session, engine_item, kwargs) + outdata = self.engine.del_item_list(session, engine_topic, kwargs) cherrypy.response.status = HTTPStatus.OK.value else: # len(args) > 1 - if item == "ns_instances_content" and not force: - opp_id = self.engine.ns_operation(rollback, session, _id, "terminate", {"autoremove": True}, - None) + if topic == "ns_instances_content" and not force: + nslcmop_desc = { + "lcmOperationType": "terminate", + "nsInstanceId": _id, + "autoremove": True + } + opp_id = self.engine.new_item(rollback, session, "nslcmops", nslcmop_desc, None) outdata = {"_id": opp_id} cherrypy.response.status = HTTPStatus.ACCEPTED.value else: - self.engine.del_item(session, engine_item, _id, force) + self.engine.del_item(session, engine_topic, _id, force) cherrypy.response.status = HTTPStatus.NO_CONTENT.value - if engine_item in ("vim_accounts", "sdns"): + if engine_topic in ("vim_accounts", "sdns"): cherrypy.response.status = HTTPStatus.ACCEPTED.value elif method in ("PUT", "PATCH"): @@ -708,35 +724,48 @@ class Server(object): if not indata and not kwargs: raise NbiException("Nothing to update. Provide payload and/or query string", HTTPStatus.BAD_REQUEST) - if item2 in ("nsd_content", "package_content") and method == "PUT": - completed = self.engine.upload_content(session, engine_item, _id, indata, kwargs, - cherrypy.request.headers) + if item in ("nsd_content", "package_content") and method == "PUT": + completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs, + cherrypy.request.headers, force=force) if not completed: cherrypy.response.headers["Transaction-Id"] = id else: - self.engine.edit_item(session, engine_item, _id, indata, kwargs, force=force) + self.engine.edit_item(session, engine_topic, _id, indata, kwargs, force=force) cherrypy.response.status = HTTPStatus.NO_CONTENT.value 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, AuthException) as e: - cherrypy.log("Exception {}".format(e)) - cherrypy.response.status = e.http_code.value + except Exception as e: + if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException)): + http_code_value = cherrypy.response.status = e.http_code.value + http_code_name = e.http_code.name + cherrypy.log("Exception {}".format(e)) + else: + http_code_value = cherrypy.response.status = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR + cherrypy.log("CRITICAL: Exception {}".format(e)) + http_code_name = HTTPStatus.BAD_REQUEST.name if hasattr(outdata, "close"): # is an open file outdata.close() + error_text = str(e) + rollback.reverse() for rollback_item in rollback: try: - self.engine.del_item(**rollback_item, session=session, force=True) + if rollback_item.get("operation") == "set": + self.engine.db.set_one(rollback_item["topic"], {"_id": rollback_item["_id"]}, + rollback_item["content"], fail_on_empty=False) + else: + self.engine.del_item(**rollback_item, session=session, force=True) except Exception as e2: - cherrypy.log("Rollback Exception {}: {}".format(rollback_item, 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( - engine_item[:-1], method, error_text) + rollback_error_text = "Rollback Exception {}: {}".format(rollback_item, e2) + cherrypy.log(rollback_error_text) + error_text += ". " + rollback_error_text + # if isinstance(e, MsgException): + # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format( + # engine_topic[:-1], method, error_text) problem_details = { - "code": e.http_code.name, - "status": e.http_code.value, - "detail": str(e), + "code": http_code_name, + "status": http_code_value, + "detail": error_text, } return self._format_out(problem_details, session) # raise cherrypy.HTTPError(e.http_code.value, str(e)) @@ -808,16 +837,16 @@ def _start_service(): file_handler.setFormatter(log_formatter_simple) logger_cherry.addHandler(file_handler) logger_nbi.addHandler(file_handler) - else: - for format_, logger in {"nbi.server": logger_server, - "nbi.access": logger_access, - "%(name)s %(filename)s:%(lineno)s": logger_nbi - }.items(): - log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_) - log_formatter_cherry = logging.Formatter(log_format_cherry, datefmt='%Y-%m-%dT%H:%M:%S') - str_handler = logging.StreamHandler() - str_handler.setFormatter(log_formatter_cherry) - logger.addHandler(str_handler) + # log always to standard output + for format_, logger in {"nbi.server %(filename)s:%(lineno)s": logger_server, + "nbi.access %(filename)s:%(lineno)s": logger_access, + "%(name)s %(filename)s:%(lineno)s": logger_nbi + }.items(): + log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_) + log_formatter_cherry = logging.Formatter(log_format_cherry, datefmt='%Y-%m-%dT%H:%M:%S') + str_handler = logging.StreamHandler() + str_handler.setFormatter(log_formatter_cherry) + logger.addHandler(str_handler) if engine_config["global"].get("log.level"): logger_cherry.setLevel(engine_config["global"]["log.level"]) @@ -837,11 +866,8 @@ def _start_service(): # TODO add more entries, e.g.: storage cherrypy.tree.apps['/osm'].root.engine.start(engine_config) cherrypy.tree.apps['/osm'].root.authenticator.start(engine_config) - try: - cherrypy.tree.apps['/osm'].root.engine.init_db(target_version=database_version) - cherrypy.tree.apps['/osm'].root.authenticator.init_db(target_version=auth_database_version) - except (EngineException, AuthException): - pass + cherrypy.tree.apps['/osm'].root.engine.init_db(target_version=database_version) + cherrypy.tree.apps['/osm'].root.authenticator.init_db(target_version=auth_database_version) # getenv('OSMOPENMANO_TENANT', None)