From: tierno Date: Wed, 16 May 2018 12:43:57 +0000 (+0200) Subject: Allow logging at file. Check member-vnf-index at ns action X-Git-Tag: v4.0.0~3 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FNBI.git;a=commitdiff_plain;h=f5298be0fd06ca35e827fca738f8ef5747da12fd Allow logging at file. Check member-vnf-index at ns action Change-Id: I4ff3ac3b5a5addd69af7908fd490d31cb24ee7fc Signed-off-by: tierno --- diff --git a/Dockerfile.local b/Dockerfile.local index 7f1a02b..154e4ae 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -51,6 +51,9 @@ ENV OSMNBI_LOG_LEVEL DEBUG ENV OSMNBI_MESSAGE_DRIVER kafka ENV OSMNBI_MESSAGE_HOST kafka ENV OSMNBI_MESSAGE_PORT 9092 +# logs +ENV OSMNBI_LOG_FILE /app/log/nbi.log +ENV OSMNBI_LOG_LEVEL DEBUG # Run app.py when the container launches CMD ["python3", "nbi.py"] diff --git a/osm_nbi/engine.py b/osm_nbi/engine.py index c4a2619..4d611cc 100644 --- a/osm_nbi/engine.py +++ b/osm_nbi/engine.py @@ -325,6 +325,25 @@ class Engine(object): raise EngineException("name '{}' already exists for {}".format(indata["name"], item), HTTPStatus.CONFLICT) + def _check_ns_operation(self, session, nsr, operation, indata): + """ + Check that user has enter right parameters for the operation + :param session: + :param operation: it can be: instantiate, terminate, action, TODO: update, heal + :param indata: descriptor with the parameters of the operation + :return: None + """ + if operation == "action": + if indata.get("vnf_member_index"): + indata["member_vnf_index"] = indata.pop("vnf_member_index") # for backward compatibility + for vnf in nsr["nsd"]["constituent-vnfd"]: + if indata["member_vnf_index"] == vnf["member-vnf-index"]: + # TODO get vnfd, check primitives + break + else: + raise EngineException("Invalid parameter member_vnf_index='{}' is not one of the nsd " + "constituent-vnfd".format(indata["member_vnf_index"])) + def _format_new_data(self, session, item, indata): now = time() if not "_admin" in indata: @@ -711,7 +730,7 @@ class Engine(object): except ValidationError as e: raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY) - def new_nslcmop(self, session, nsInstanceId, action, params): + def new_nslcmop(self, session, nsInstanceId, operation, params): now = time() _id = str(uuid4()) nslcmop = { @@ -720,7 +739,7 @@ class Engine(object): "operationState": "PROCESSING", # COMPLETED,PARTIALLY_COMPLETED,FAILED_TEMP,FAILED,ROLLING_BACK,ROLLED_BACK "statusEnteredTime": now, "nsInstanceId": nsInstanceId, - "lcmOperationType": action, + "lcmOperationType": operation, "startTime": now, "isAutomaticInvocation": False, "operationParams": params, @@ -732,40 +751,40 @@ class Engine(object): } return nslcmop - def ns_action(self, session, nsInstanceId, action, indata, kwargs=None): + def ns_operation(self, session, nsInstanceId, operation, indata, kwargs=None): """ - Performs a new action over a ns + Performs a new operation over a ns :param session: contains the used login username and working project - :param nsInstanceId: _id of the nsr to perform the action - :param action: it can be: instantiate, terminate, action, TODO: update, heal - :param indata: descriptor with the parameters of the action + :param nsInstanceId: _id of the nsr to perform the operation + :param operation: it can be: instantiate, terminate, action, TODO: update, heal + :param indata: descriptor with the parameters of the operation :param kwargs: used to override the indata descriptor :return: id of the nslcmops """ try: # Override descriptor with query string kwargs self._update_descriptor(indata, kwargs) - validate_input(indata, "ns_" + action, new=True) + validate_input(indata, "ns_" + operation, new=True) # get ns from nsr_id nsr = self.get_item(session, "nsrs", nsInstanceId) if not nsr["_admin"].get("nsState") or nsr["_admin"]["nsState"] == "NOT_INSTANTIATED": - if action == "terminate" and indata.get("autoremove"): + if operation == "terminate" and indata.get("autoremove"): # NSR must be deleted return self.del_item(session, "nsrs", nsInstanceId) - if action != "instantiate": + if operation != "instantiate": raise EngineException("ns_instance '{}' cannot be '{}' because it is not instantiated".format( - nsInstanceId, action), HTTPStatus.CONFLICT) + nsInstanceId, operation), HTTPStatus.CONFLICT) else: - if action == "instantiate" and not indata.get("force"): + if operation == "instantiate" and not indata.get("force"): raise EngineException("ns_instance '{}' cannot be '{}' because it is already instantiated".format( - nsInstanceId, action), HTTPStatus.CONFLICT) + nsInstanceId, operation), HTTPStatus.CONFLICT) indata["nsInstanceId"] = nsInstanceId - # TODO - nslcmop = self.new_nslcmop(session, nsInstanceId, action, indata) + self._check_ns_operation(session, nsr, operation, indata) + nslcmop = self.new_nslcmop(session, nsInstanceId, operation, indata) self._format_new_data(session, "nslcmops", nslcmop) _id = self.db.create("nslcmops", nslcmop) indata["_id"] = _id - self.msg.write("ns", action, nslcmop) + self.msg.write("ns", operation, nslcmop) return _id except ValidationError as e: raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY) @@ -911,7 +930,7 @@ class Engine(object): nsr = self.db.get_one(item, filter) if nsr["_admin"]["nsState"] == "INSTANTIATED" and not force: raise EngineException("nsr '{}' cannot be deleted because it is in 'INSTANTIATED' state. " - "Launch 'terminate' action first; or force deletion".format(_id), + "Launch 'terminate' operation first; or force deletion".format(_id), http_code=HTTPStatus.CONFLICT) v = self.db.del_one(item, {"_id": _id}) self.db.del_list("nslcmops", {"nsInstanceId": _id}) diff --git a/osm_nbi/html_public/version b/osm_nbi/html_public/version index ff270ea..b390c9b 100644 --- a/osm_nbi/html_public/version +++ b/osm_nbi/html_public/version @@ -1,3 +1,3 @@ -0.1.5 -2018-05-14 +0.1.6 +2018-05-16 diff --git a/osm_nbi/nbi.cfg b/osm_nbi/nbi.cfg index e75643c..ffe407d 100644 --- a/osm_nbi/nbi.cfg +++ b/osm_nbi/nbi.cfg @@ -39,8 +39,8 @@ log.screen: False log.access_file: "" log.error_file: "" -loglevel: "DEBUG" -#logfile: /var/log/osm/nbi.log +log.level: "DEBUG" +#log.file: /var/log/osm/nbi.log [database] diff --git a/osm_nbi/nbi.py b/osm_nbi/nbi.py index 566fb2b..467f3b9 100644 --- a/osm_nbi/nbi.py +++ b/osm_nbi/nbi.py @@ -7,6 +7,9 @@ import json import yaml import html_out as html import logging +import logging.handlers +import getopt +import sys from engine import Engine, EngineException from osm_common.dbbase import DbException from osm_common.fsbase import FsException @@ -16,7 +19,7 @@ from base64 import standard_b64decode 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 " @@ -688,11 +691,11 @@ class Server(object): elif item == "ns_instances_content": _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_action(session, _id, "instantiate", {}, None) + 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 @@ -710,7 +713,7 @@ class Server(object): cherrypy.response.status = HTTPStatus.OK.value else: # len(args) > 1 if item == "ns_instances_content": - opp_id = 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: @@ -796,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: @@ -811,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" @@ -820,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) @@ -837,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(): @@ -869,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(), @@ -889,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) diff --git a/osm_nbi/validation.py b/osm_nbi/validation.py index ba78c55..70003a9 100644 --- a/osm_nbi/validation.py +++ b/osm_nbi/validation.py @@ -55,11 +55,12 @@ ns_action = { # TODO for the moment it is only contemplated the vnfd primitive "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { - "vnf_member_index": name_schema, + "member_vnf_index": name_schema, + "vnf_member_index": name_schema, # TODO for backward compatibility. To remove in future "primitive": name_schema, "primitive_params": {"type": "object"}, }, - "required": ["vnf_member_index", "primitive", "primitive_params"], + "required": ["primitive", "primitive_params"], # TODO add member_vnf_index "additionalProperties": False }