X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=osm_nbi%2Fnbi.py;h=d78379f77103b8b70629183333300361acf35944;hb=HEAD;hp=fa414f053384983fa4b1c786697f9f1bd27c8846;hpb=f2af4a100d308e07f355d61b94fb27d1ccc97aa2;p=osm%2FNBI.git diff --git a/osm_nbi/nbi.py b/osm_nbi/nbi.py index fa414f0..3728943 100644 --- a/osm_nbi/nbi.py +++ b/osm_nbi/nbi.py @@ -28,6 +28,7 @@ from osm_nbi.authconn import AuthException, AuthconnException from osm_nbi.auth import Authenticator from osm_nbi.engine import Engine, EngineException from osm_nbi.subscriptions import SubscriptionThread +from osm_nbi.utils import cef_event, cef_event_builder from osm_nbi.validation import ValidationError from osm_common.dbbase import DbException from osm_common.fsbase import FsException @@ -46,6 +47,8 @@ database_version = "1.2" auth_database_version = "1.0" nbi_server = None # instance of Server class subscription_thread = None # instance of SubscriptionThread class +cef_logger = None +logger = logging.getLogger("nbi.nbi") """ North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented) @@ -58,6 +61,9 @@ URL: /osm GET POST /nsd_content O5 O5 /nsd O /artifacts[/] O + /ns_config_template O O + / O O + /template_content O O /pnf_descriptors 5 5 / 5 5 5 /pnfd_content 5 5 @@ -90,7 +96,7 @@ URL: /osm GET POST heal O5 /ns_lcm_op_occs 5 5 / 5 5 5 - TO BE COMPLETED 5 5 + cancel 05 /vnf_instances (also vnfrs for compatibility) O / O /subscriptions 5 5 @@ -144,6 +150,24 @@ URL: /osm GET POST /subscriptions X X / X X + /k8scluster/v1 + /clusters O O + / O O + app_profiles O O + infra_controller_profiles O O + infra_config_profiles O O + resource_profiles O O + deregister O + /register O + /app_profiles O O + / O O O + /infra_controller_profiles O O + / O O O + /infra_config_profiles O O + / O O O + /resource_profiles O O + / O O O + query string: Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force. simpleFilterExpr := ["."]*["."]"="[","]* @@ -353,6 +377,18 @@ valid_url_methods = { }, }, }, + "ns_config_template": { + "METHODS": ("GET", "POST"), + "ROLE_PERMISSION": "ns_config_template:content:", + "": { + "METHODS": ("GET", "DELETE"), + "ROLE_PERMISSION": "ns_config_template:id:", + "template_content": { + "METHODS": ("GET", "PUT"), + "ROLE_PERMISSION": "ns_config_template:id:content:", + }, + }, + }, "pnf_descriptors": { "TODO": ("GET", "POST"), "": { @@ -419,6 +455,10 @@ valid_url_methods = { }, "nslcm": { "v1": { + "ns_instances_terminate": { + "METHODS": ("POST"), + "ROLE_PERMISSION": "ns_instances:", + }, "ns_instances_content": { "METHODS": ("GET", "POST"), "ROLE_PERMISSION": "ns_instances:", @@ -461,10 +501,6 @@ valid_url_methods = { "METHODS": ("POST",), "ROLE_PERMISSION": "ns_instances:id:update:", }, - "verticalscale": { - "METHODS": ("POST",), - "ROLE_PERMISSION": "ns_instances:id:verticalscale:", - }, }, }, "ns_lcm_op_occs": { @@ -473,6 +509,10 @@ valid_url_methods = { "": { "METHODS": ("GET",), "ROLE_PERMISSION": "ns_instances:opps:id:", + "cancel": { + "METHODS": ("POST",), + "ROLE_PERMISSION": "ns_instances:opps:cancel:", + }, }, }, "vnfrs": { @@ -639,6 +679,126 @@ valid_url_methods = { } }, }, + "k8scluster": { + "v1": { + "clusters": { + "METHODS": ("GET", "POST"), + "ROLE_PERMISSION": "k8scluster:", + "": { + "METHODS": ("GET", "DELETE"), + "ROLE_PERMISSION": "k8scluster:id:", + "app_profiles": { + "METHODS": ("PATCH", "GET"), + "ROLE_PERMISSION": "k8scluster:id:app_profiles:", + }, + "infra_controller_profiles": { + "METHODS": ("PATCH", "GET"), + "ROLE_PERMISSION": "k8scluster:id:infra_profiles:", + }, + "infra_config_profiles": { + "METHODS": ("PATCH", "GET"), + "ROLE_PERMISSION": "k8scluster:id:infra_profiles:", + }, + "resource_profiles": { + "METHODS": ("PATCH", "GET"), + "ROLE_PERMISSION": "k8scluster:id:infra_profiles:", + }, + "deregister": { + "METHODS": ("DELETE",), + "ROLE_PERMISSION": "k8scluster:id:deregister:", + }, + "get_creds": { + "METHODS": ("GET",), + "ROLE_PERMISSION": "k8scluster:id:get_creds:", + }, + "scale": { + "METHODS": ("POST",), + "ROLE_PERMISSION": "k8scluster:id:scale:", + }, + "upgrade": { + "METHODS": ("POST",), + "ROLE_PERMISSION": "k8scluster:id:upgrade:", + }, + }, + "register": { + "METHODS": ("POST",), + "ROLE_PERMISSION": "k8scluster:register:", + }, + }, + "app_profiles": { + "METHODS": ("POST", "GET"), + "ROLE_PERMISSION": "k8scluster:app_profiles:", + "": { + "METHODS": ("GET", "PATCH", "DELETE"), + "ROLE_PERMISSION": "k8scluster:app_profiles:id:", + }, + }, + "infra_controller_profiles": { + "METHODS": ("POST", "GET"), + "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:", + "": { + "METHODS": ("GET", "PATCH", "DELETE"), + "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:", + }, + }, + "infra_config_profiles": { + "METHODS": ("POST", "GET"), + "ROLE_PERMISSION": "k8scluster:infra_config_profiles:", + "": { + "METHODS": ("GET", "PATCH", "DELETE"), + "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:", + }, + }, + "resource_profiles": { + "METHODS": ("POST", "GET"), + "ROLE_PERMISSION": "k8scluster:resource_profiles:", + "": { + "METHODS": ("GET", "PATCH", "DELETE"), + "ROLE_PERMISSION": "k8scluster:resource_profiles:id:", + }, + }, + } + }, + "ksu": { + "v1": { + "ksus": { + "METHODS": ("GET", "POST"), + "ROLE_PERMISSION": "ksu:", + "": { + "METHODS": ("GET", "PATCH", "DELETE"), + "ROLE_PERMISSION": "ksu:id:", + "clone": { + "METHODS": ("POST",), + "ROLE_PERMISSION": "ksu:id:clone:", + }, + "move": { + "METHODS": ("POST",), + "ROLE_PERMISSION": "ksu:id:move:", + }, + }, + "update": { + "METHODS": ("POST",), + "ROLE_PERMISSION": "ksu:", + }, + "delete": { + "METHODS": ("POST",), + "ROLE_PERMISSION": "ksu:", + }, + }, + } + }, + "oka": { + "v1": { + "oka_packages": { + "METHODS": ("GET", "POST"), + "ROLE_PERMISSION": "oka_pkg:", + "": { + "METHODS": ("GET", "PATCH", "DELETE", "PUT"), + "ROLE_PERMISSION": "oka_pkg:id:", + }, + } + } + }, } @@ -657,6 +817,7 @@ class Server(object): self.instance += 1 self.authenticator = Authenticator(valid_url_methods, valid_query_string) self.engine = Engine(self.authenticator) + self.logger = logging.getLogger("nbi.server") def _format_in(self, kwargs): error_text = "" # error_text must be initialized outside try @@ -672,9 +833,7 @@ class Server(object): 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, Loader=yaml.SafeLoader - ) + indata = yaml.safe_load(cherrypy.request.body) cherrypy.request.headers.pop("Content-File-MD5", None) elif ( "application/binary" in cherrypy.request.headers["Content-Type"] @@ -688,13 +847,32 @@ class Server(object): "multipart/form-data" in cherrypy.request.headers["Content-Type"] ): - if "descriptor_file" in kwargs: - filecontent = kwargs.pop("descriptor_file") + if ( + "descriptor_file" in kwargs + or "package" in kwargs + and "name" in kwargs + ): + filecontent = "" + if "descriptor_file" in kwargs: + filecontent = kwargs.pop("descriptor_file") + if "package" in kwargs: + filecontent = kwargs.pop("package") if not filecontent.file: raise NbiException( "empty file or content", HTTPStatus.BAD_REQUEST ) - indata = filecontent.file # .read() + indata = filecontent + if filecontent.content_type.value: + cherrypy.request.headers[ + "Content-Type" + ] = filecontent.content_type.value + elif "package" in kwargs: + filecontent = kwargs.pop("package") + if not filecontent.file: + raise NbiException( + "empty file or content", HTTPStatus.BAD_REQUEST + ) + indata = filecontent if filecontent.content_type.value: cherrypy.request.headers[ "Content-Type" @@ -704,17 +882,14 @@ class Server(object): # "Only 'Content-Type' of type 'application/json' or # 'application/yaml' for input format are available") error_text = "Invalid yaml format " - indata = yaml.load( - cherrypy.request.body, Loader=yaml.SafeLoader - ) + indata = yaml.safe_load(cherrypy.request.body) cherrypy.request.headers.pop("Content-File-MD5", None) else: error_text = "Invalid yaml format " - indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader) + indata = yaml.safe_load(cherrypy.request.body) cherrypy.request.headers.pop("Content-File-MD5", None) if not indata: indata = {} - format_yaml = False if cherrypy.request.headers.get("Query-String-Format") == "yaml": format_yaml = True @@ -725,7 +900,7 @@ class Server(object): kwargs[k] = None elif format_yaml: try: - kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader) + kwargs[k] = yaml.safe_load(v) except Exception: pass elif ( @@ -749,7 +924,7 @@ class Server(object): v[index] = None elif format_yaml: try: - v[index] = yaml.load(v[index], Loader=yaml.SafeLoader) + v[index] = yaml.safe_load(v[index]) except Exception: pass @@ -977,7 +1152,7 @@ class Server(object): return self._format_out(str(alarm_list)) # to handle patch request for alarm update elif cherrypy.request.method == "PATCH": - data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader) + data = yaml.safe_load(cherrypy.request.body) try: # check if uuid is valid self.engine.db.get_one("alarms", {"uuid": data.get("uuid")}) @@ -1074,6 +1249,17 @@ class Server(object): } # cherrypy.response.cookie["Authorization"] = outdata["id"] # cherrypy.response.cookie["Authorization"]['expires'] = 3600 + cef_event( + cef_logger, + { + "name": "User Login", + "sourceUserName": token_info.get("username"), + "message": "User Logged In, Project={} Outcome=Success".format( + token_info.get("project_name") + ), + }, + ) + cherrypy.log("{}".format(cef_logger)) elif method == "DELETE": if not token_id and "id" in kwargs: token_id = kwargs["id"] @@ -1082,9 +1268,27 @@ class Server(object): # for logging self._format_login(token_info) token_id = token_info["_id"] + if current_backend != "keystone": + token_details = self.engine.db.get_one("tokens", {"_id": token_id}) + current_user = token_details.get("username") + current_project = token_details.get("project_name") + else: + current_user = "keystone backend" + current_project = "keystone backend" outdata = self.authenticator.del_token(token_id) token_info = None cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101 + cef_event( + cef_logger, + { + "name": "User Logout", + "sourceUserName": current_user, + "message": "User Logged Out, Project={} Outcome=Success".format( + current_project + ), + }, + ) + cherrypy.log("{}".format(cef_logger)) # cherrypy.response.cookie["Authorization"] = token_id # cherrypy.response.cookie["Authorization"]['expires'] = 0 else: @@ -1171,13 +1375,13 @@ class Server(object): return_text = "
{} ->\n".format(main_topic)
             try:
                 if cherrypy.request.method == "POST":
-                    to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
+                    to_send = yaml.safe_load(cherrypy.request.body)
                     for k, v in to_send.items():
                         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():
-                        v_dict = yaml.load(v, Loader=yaml.SafeLoader)
+                        v_dict = yaml.safe_load(v)
                         self.engine.msg.write(main_topic, k, v_dict)
                         return_text += "  {}: {}\n".format(k, v_dict)
             except Exception as e:
@@ -1388,12 +1592,20 @@ class Server(object):
         **kwargs
     ):
         token_info = None
-        outdata = None
+        outdata = {}
         _format = None
         method = "DONE"
         engine_topic = None
         rollback = []
         engine_session = None
+        url_id = ""
+        log_mapping = {
+            "POST": "Creating",
+            "GET": "Fetching",
+            "DELETE": "Deleting",
+            "PUT": "Updating",
+            "PATCH": "Updating",
+        }
         try:
             if not main_topic or not version or not topic:
                 raise NbiException(
@@ -1410,6 +1622,9 @@ class Server(object):
                 "nsilcm",
                 "nspm",
                 "vnflcm",
+                "k8scluster",
+                "ksu",
+                "oka",
             ):
                 raise NbiException(
                     "URL main_topic '{}' not supported".format(main_topic),
@@ -1420,6 +1635,8 @@ class Server(object):
                     "URL version '{}' not supported".format(version),
                     HTTPStatus.METHOD_NOT_ALLOWED,
                 )
+            if _id is not None:
+                url_id = _id
 
             if (
                 kwargs
@@ -1452,6 +1669,8 @@ class Server(object):
 
             if main_topic == "nsd":
                 engine_topic = "nsds"
+                if topic == "ns_config_template":
+                    engine_topic = "nsconfigtemps"
             elif main_topic == "vnfpkgm":
                 engine_topic = "vnfds"
                 if topic == "vnfpkg_op_occs":
@@ -1475,6 +1694,26 @@ class Server(object):
                     engine_topic = "nsilcmops"
             elif main_topic == "pdu":
                 engine_topic = "pdus"
+            elif main_topic == "k8scluster":
+                engine_topic = "k8s"
+                if topic == "clusters" and _id == "register" or item == "deregister":
+                    engine_topic = "k8sops"
+                elif topic == "infra_controller_profiles":
+                    engine_topic = "infras_cont"
+                elif topic == "infra_config_profiles":
+                    engine_topic = "infras_conf"
+                elif topic == "resource_profiles":
+                    engine_topic = "resources"
+                elif topic == "app_profiles":
+                    engine_topic = "apps"
+            elif main_topic == "k8scluster" and item in (
+                "upgrade",
+                "get_creds",
+                "scale",
+            ):
+                engine_topic = "k8s"
+            elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
+                engine_topic = "ksus"
             if (
                 engine_topic == "vims"
             ):  # TODO this is for backward compatibility, it will be removed in the future
@@ -1492,6 +1731,7 @@ class Server(object):
                     "nsd",
                     "nst",
                     "nst_content",
+                    "ns_config_template",
                 ):
                     if item in ("vnfd", "nsd", "nst"):
                         path = "$DESCRIPTOR"
@@ -1513,6 +1753,26 @@ class Server(object):
                     outdata = self.engine.get_item_list(
                         engine_session, engine_topic, kwargs, api_req=True
                     )
+                elif topic == "clusters" and item in (
+                    "infra_controller_profiles",
+                    "infra_config_profiles",
+                    "app_profiles",
+                    "resource_profiles",
+                ):
+                    profile = item
+                    filter_q = None
+                    outdata = self.engine.get_one_item(
+                        engine_session,
+                        engine_topic,
+                        _id,
+                        profile,
+                        filter_q,
+                        api_req=True,
+                    )
+                elif topic == "clusters" and item == "get_creds":
+                    outdata = self.engine.get_cluster_info(
+                        engine_session, engine_topic, _id, item
+                    )
                 else:
                     if item == "reports":
                         # TODO check that project_id (_id in this context) has permissions
@@ -1530,8 +1790,10 @@ class Server(object):
                     "ns_descriptors_content",
                     "vnf_packages_content",
                     "netslice_templates_content",
+                    "ns_config_template",
                 ):
                     _id = cherrypy.request.headers.get("Transaction-Id")
+
                     if not _id:
                         _id, _ = self.engine.new_item(
                             rollback,
@@ -1554,6 +1816,33 @@ class Server(object):
                     else:
                         cherrypy.response.headers["Transaction-Id"] = _id
                     outdata = {"id": _id}
+                elif topic == "oka_packages":
+                    _id = cherrypy.request.headers.get("Transaction-Id")
+
+                    if not _id:
+                        _id, _ = self.engine.new_item(
+                            rollback,
+                            engine_session,
+                            engine_topic,
+                            {},
+                            kwargs,
+                            cherrypy.request.headers,
+                        )
+                    cherrypy.request.headers["method"] = cherrypy.request.method
+                    if indata:
+                        completed = self.engine.upload_content(
+                            engine_session,
+                            engine_topic,
+                            _id,
+                            indata,
+                            None,
+                            cherrypy.request.headers,
+                        )
+                    if completed:
+                        self._set_location_header(main_topic, version, topic, _id)
+                    else:
+                        cherrypy.response.headers["Transaction-Id"] = _id
+                    outdata = {"_id": _id}
                 elif topic == "ns_instances_content":
                     # creates NSR
                     _id, _ = self.engine.new_item(
@@ -1562,21 +1851,44 @@ class Server(object):
                     # creates nslcmop
                     indata["lcmOperationType"] = "instantiate"
                     indata["nsInstanceId"] = _id
-                    nslcmop_id, _ = self.engine.new_item(
+                    nslcmop_id, nsName, _ = self.engine.new_item(
                         rollback, engine_session, "nslcmops", indata, None
                     )
                     self._set_location_header(main_topic, version, topic, _id)
-                    outdata = {"id": _id, "nslcmop_id": nslcmop_id}
+                    outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
+                elif topic == "ns_instances_terminate":
+                    if indata.get("ns_ids"):
+                        for ns_id in indata.get("ns_ids"):
+                            nslcmop_desc = {
+                                "lcmOperationType": "terminate",
+                                "nsInstanceId": ns_id,
+                                "autoremove": indata.get("autoremove")
+                                if "autoremove" in indata
+                                else True,
+                            }
+                            op_id, _, _ = self.engine.new_item(
+                                rollback,
+                                engine_session,
+                                "nslcmops",
+                                nslcmop_desc,
+                                kwargs,
+                            )
+                            if not op_id:
+                                _ = self.engine.del_item(
+                                    engine_session, engine_topic, ns_id
+                                )
+                    outdata = {"ns_ids": indata.get("ns_ids")}
+                    cherrypy.response.status = HTTPStatus.ACCEPTED.value
                 elif topic == "ns_instances" and item:
                     indata["lcmOperationType"] = item
                     indata["nsInstanceId"] = _id
-                    _id, _ = self.engine.new_item(
+                    _id, nsName, _ = self.engine.new_item(
                         rollback, engine_session, "nslcmops", indata, kwargs
                     )
                     self._set_location_header(
                         main_topic, version, "ns_lcm_op_occs", _id
                     )
-                    outdata = {"id": _id}
+                    outdata = {"id": _id, "nsName": nsName}
                     cherrypy.response.status = HTTPStatus.ACCEPTED.value
                 elif topic == "netslice_instances_content":
                     # creates NetSlice_Instance_record (NSIR)
@@ -1629,14 +1941,78 @@ class Server(object):
                 elif topic == "vnf_instances" and item:
                     indata["lcmOperationType"] = item
                     indata["vnfInstanceId"] = _id
-                    _id, _ = self.engine.new_item(
+                    _id, nsName, _ = self.engine.new_item(
                         rollback, engine_session, "vnflcmops", indata, kwargs
                     )
                     self._set_location_header(
                         main_topic, version, "vnf_lcm_op_occs", _id
                     )
-                    outdata = {"id": _id}
+                    outdata = {"id": _id, "nsName": nsName}
+                    cherrypy.response.status = HTTPStatus.ACCEPTED.value
+                elif topic == "ns_lcm_op_occs" and item == "cancel":
+                    indata["nsLcmOpOccId"] = _id
+                    self.engine.cancel_item(
+                        rollback, engine_session, "nslcmops", indata, None
+                    )
+                    self._set_location_header(main_topic, version, topic, _id)
                     cherrypy.response.status = HTTPStatus.ACCEPTED.value
+                elif topic == "clusters" and _id == "register":
+                    # To register a cluster
+                    _id, _ = self.engine.add_item(
+                        rollback, engine_session, engine_topic, indata, kwargs
+                    )
+                    self._set_location_header(main_topic, version, topic, _id)
+                    outdata = {"id": _id}
+                elif (
+                    topic
+                    in (
+                        "clusters",
+                        "infra_controller_profiles",
+                        "infra_config_profiles",
+                        "app_profiles",
+                        "resource_profiles",
+                    )
+                    and item is None
+                ):
+                    # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
+                    _id, _ = self.engine.new_item(
+                        rollback, engine_session, engine_topic, indata, kwargs
+                    )
+                    self._set_location_header(main_topic, version, topic, _id)
+                    outdata = {"_id": _id}
+                elif topic == "ksus" and item:
+                    if item == "clone":
+                        _id = self.engine.clone(
+                            rollback,
+                            engine_session,
+                            engine_topic,
+                            _id,
+                            indata,
+                            kwargs,
+                            cherrypy.request.headers,
+                        )
+                        self._set_location_header(main_topic, version, topic, _id)
+                        outdata = {"id": _id}
+                    if item == "move":
+                        op_id = self.engine.move_ksu(
+                            engine_session, engine_topic, _id, indata, kwargs
+                        )
+                        outdata = {"op_id": op_id}
+                elif topic == "ksus" and _id == "delete":
+                    op_id = self.engine.delete_ksu(
+                        engine_session, engine_topic, _id, indata
+                    )
+                    outdata = {"op_id": op_id}
+                elif topic == "ksus" and _id == "update":
+                    op_id = self.engine.edit_item(
+                        engine_session, engine_topic, _id, indata, kwargs
+                    )
+                    outdata = {"op_id": op_id}
+                elif topic == "clusters" and item in ("upgrade", "scale"):
+                    op_id = self.engine.update_cluster(
+                        engine_session, engine_topic, _id, item, indata
+                    )
+                    outdata = {"op_id": op_id}
                 else:
                     _id, op_id = self.engine.new_item(
                         rollback,
@@ -1668,11 +2044,11 @@ class Server(object):
                             "nsInstanceId": _id,
                             "autoremove": True,
                         }
-                        op_id, _ = self.engine.new_item(
+                        op_id, nsName, _ = self.engine.new_item(
                             rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
                         )
                         if op_id:
-                            outdata = {"_id": op_id}
+                            outdata = {"_id": op_id, "nsName": nsName}
                     elif (
                         topic == "netslice_instances_content"
                         and not engine_session["force"]
@@ -1687,11 +2063,28 @@ class Server(object):
                         )
                         if op_id:
                             outdata = {"_id": op_id}
+                    elif topic == "clusters" and item == "deregister":
+                        if not op_id:
+                            op_id = self.engine.remove(
+                                engine_session, engine_topic, _id
+                            )
+                            if op_id:
+                                outdata = {"_id": op_id}
+                        cherrypy.response.status = (
+                            HTTPStatus.ACCEPTED.value
+                            if op_id
+                            else HTTPStatus.NO_CONTENT.value
+                        )
+                    elif topic == "ksus":
+                        op_id = self.engine.delete_ksu(
+                            engine_session, engine_topic, _id, indata
+                        )
+                        outdata = {"op_id": op_id}
                     # if there is not any deletion in process, delete
-                    if not op_id:
+                    elif not op_id:
                         op_id = self.engine.del_item(engine_session, engine_topic, _id)
                         if op_id:
-                            outdata = {"op_id": op_id}
+                            outdata = {"_id": op_id}
                     cherrypy.response.status = (
                         HTTPStatus.ACCEPTED.value
                         if op_id
@@ -1706,7 +2099,13 @@ class Server(object):
                         HTTPStatus.BAD_REQUEST,
                     )
                 if (
-                    item in ("nsd_content", "package_content", "nst_content")
+                    item
+                    in (
+                        "nsd_content",
+                        "package_content",
+                        "nst_content",
+                        "template_content",
+                    )
                     and method == "PUT"
                 ):
                     completed = self.engine.upload_content(
@@ -1719,6 +2118,50 @@ class Server(object):
                     )
                     if not completed:
                         cherrypy.response.headers["Transaction-Id"] = id
+                elif item in (
+                    "app_profiles",
+                    "resource_profiles",
+                    "infra_controller_profiles",
+                    "infra_config_profiles",
+                ):
+                    op_id = self.engine.edit(
+                        engine_session, engine_topic, _id, item, indata, kwargs
+                    )
+                elif topic == "oka_packages" and method == "PATCH":
+                    if kwargs:
+                        op_id = self.engine.edit_item(
+                            engine_session, engine_topic, _id, None, kwargs
+                        )
+                    if indata:
+                        if isinstance(indata, dict):
+                            op_id = self.engine.edit_item(
+                                engine_session, engine_topic, _id, indata, kwargs
+                            )
+                        else:
+                            cherrypy.request.headers["method"] = cherrypy.request.method
+                            completed = self.engine.upload_content(
+                                engine_session,
+                                engine_topic,
+                                _id,
+                                indata,
+                                {},
+                                cherrypy.request.headers,
+                            )
+                            if not completed:
+                                cherrypy.response.headers["Transaction-Id"] = id
+                elif topic == "oka_packages" and method == "PUT":
+                    if indata:
+                        cherrypy.request.headers["method"] = cherrypy.request.method
+                        completed = self.engine.upload_content(
+                            engine_session,
+                            engine_topic,
+                            _id,
+                            indata,
+                            {},
+                            cherrypy.request.headers,
+                        )
+                        if not completed:
+                            cherrypy.response.headers["Transaction-Id"] = id
                 else:
                     op_id = self.engine.edit_item(
                         engine_session, engine_topic, _id, indata, kwargs
@@ -1748,6 +2191,84 @@ class Server(object):
             ):
                 self.authenticator.remove_token_from_cache()
 
+            cef_event(
+                cef_logger,
+                {
+                    "name": "User Operation",
+                    "sourceUserName": token_info.get("username"),
+                },
+            )
+            if topic == "ns_instances_content" and url_id:
+                nsName = (
+                    outdata.get("name") if method == "GET" else outdata.get("nsName")
+                )
+                cef_event(
+                    cef_logger,
+                    {
+                        "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
+                            log_mapping[method],
+                            topic,
+                            nsName,
+                            outdata.get("id"),
+                            token_info.get("project_name"),
+                        ),
+                    },
+                )
+                cherrypy.log("{}".format(cef_logger))
+            elif topic == "ns_instances_content" and method == "POST":
+                cef_event(
+                    cef_logger,
+                    {
+                        "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
+                            log_mapping[method],
+                            topic,
+                            outdata.get("nsName"),
+                            outdata.get("id"),
+                            token_info.get("project_name"),
+                        ),
+                    },
+                )
+                cherrypy.log("{}".format(cef_logger))
+            elif topic in ("ns_instances", "vnf_instances") and item:
+                cef_event(
+                    cef_logger,
+                    {
+                        "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
+                            log_mapping[method],
+                            topic,
+                            outdata.get("nsName"),
+                            url_id,
+                            token_info.get("project_name"),
+                        ),
+                    },
+                )
+                cherrypy.log("{}".format(cef_logger))
+            elif item is not None:
+                cef_event(
+                    cef_logger,
+                    {
+                        "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
+                            item,
+                            topic,
+                            url_id,
+                            token_info.get("project_name"),
+                        ),
+                    },
+                )
+                cherrypy.log("{}".format(cef_logger))
+            else:
+                cef_event(
+                    cef_logger,
+                    {
+                        "message": "{} {} {}, Project={} Outcome=Success".format(
+                            log_mapping[method],
+                            topic,
+                            url_id,
+                            token_info.get("project_name"),
+                        ),
+                    },
+                )
+                cherrypy.log("{}".format(cef_logger))
             return self._format_out(outdata, token_info, _format)
         except Exception as e:
             if isinstance(
@@ -1810,6 +2331,38 @@ class Server(object):
                 "status": http_code_value,
                 "detail": error_text,
             }
+            if item is not None and token_info is not None:
+                cef_event(
+                    cef_logger,
+                    {
+                        "name": "User Operation",
+                        "sourceUserName": token_info.get("username", None),
+                        "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
+                            item,
+                            topic,
+                            url_id,
+                            token_info.get("project_name", None),
+                        ),
+                        "severity": "2",
+                    },
+                )
+                cherrypy.log("{}".format(cef_logger))
+            elif token_info is not None:
+                cef_event(
+                    cef_logger,
+                    {
+                        "name": "User Operation",
+                        "sourceUserName": token_info.get("username", None),
+                        "message": "{} {} {}, Project={} Outcome=Failure".format(
+                            item,
+                            topic,
+                            url_id,
+                            token_info.get("project_name", None),
+                        ),
+                        "severity": "2",
+                    },
+                )
+                cherrypy.log("{}".format(cef_logger))
             return self._format_out(problem_details, token_info)
             # raise cherrypy.HTTPError(e.http_code.value, str(e))
         finally:
@@ -1832,12 +2385,26 @@ def _start_service():
     """
     global nbi_server
     global subscription_thread
+    global cef_logger
+    global current_backend
     cherrypy.log.error("Starting osm_nbi")
     # update general cherrypy configuration
     update_dict = {}
 
     engine_config = cherrypy.tree.apps["/osm"].config
     for k, v in environ.items():
+        if k == "OSMNBI_USER_MANAGEMENT":
+            feature_state = eval(v.title())
+            engine_config["authentication"]["user_management"] = feature_state
+        elif k == "OSMNBI_PWD_EXPIRE_DAYS":
+            pwd_expire_days = int(v)
+            engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
+        elif k == "OSMNBI_MAX_PWD_ATTEMPT":
+            max_pwd_attempt = int(v)
+            engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
+        elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
+            account_expire_days = int(v)
+            engine_config["authentication"]["account_expire_days"] = account_expire_days
         if not k.startswith("OSMNBI_"):
             continue
         k1, _, k2 = k[7:].lower().partition("_")
@@ -1933,6 +2500,8 @@ def _start_service():
         target_version=auth_database_version
     )
 
+    cef_logger = cef_event_builder(engine_config["authentication"])
+
     # start subscriptions thread:
     subscription_thread = SubscriptionThread(
         config=engine_config, engine=nbi_server.engine
@@ -1941,6 +2510,7 @@ def _start_service():
     # Do not capture except SubscriptionException
 
     backend = engine_config["authentication"]["backend"]
+    current_backend = backend
     cherrypy.log.error(
         "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
             nbi_version, nbi_version_date, backend