Fix age key generation to convert pubkey to string
[osm/NBI.git] / osm_nbi / nbi.py
index ff2f6de..3728943 100644 (file)
@@ -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[/<artifactPath>]                 O
+            /ns_config_template                                 O       O
+                /<nsConfigTemplateId>                           O                       O
+                    /template_content                           O               O
             /pnf_descriptors                                    5       5
                 /<pnfdInfoId>                                   5                       5       5
                     /pnfd_content                               5               5
@@ -85,11 +91,12 @@ URL: /osm                                                       GET     POST
                     terminate                                           O5
                     action                                              O
                     scale                                               O5
-                    heal                                                5
+                    migrate                                             O
                     update                                              05
+                    heal                                                O5
             /ns_lcm_op_occs                                     5       5
                 /<nsLcmOpOccId>                                 5                       5       5
-                    TO BE COMPLETED                             5               5
+                    cancel                                              05
             /vnf_instances  (also vnfrs for compatibility)      O
                 /<vnfInstanceId>                                O
             /subscriptions                                      5       5
@@ -143,6 +150,24 @@ URL: /osm                                                       GET     POST
             /subscriptions                                      X       X
                 /<subscriptionId>                               X                       X
 
+        /k8scluster/v1
+            /clusters                                           O       O
+                /<clustersId>                                   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
+                /<app_profilesId>                               O                       O       O
+            /infra_controller_profiles                          O       O
+                /<infra_controller_profilesId>                  O                       O       O
+            /infra_config_profiles                              O       O
+                /<infra_config_profilesId>                      O                       O       O
+            /resource_profiles                                  O       O
+                /<resource_profilesID>                          O                       O       O
+
 query string:
     Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
         simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
@@ -171,7 +196,7 @@ query string:
         ADMIN: To act as an administrator or a different project
         PUBLIC: To get public descriptors or set a descriptor as public
         SET_PROJECT: To make a descriptor available for other project
-        
+
 Header field name      Reference       Example Descriptions
     Accept     IETF RFC 7231 [19]      application/json        Content-Types that are acceptable for the response.
     This header field shall be present if the response is expected to have a non-empty message body.
@@ -352,6 +377,18 @@ valid_url_methods = {
                     },
                 },
             },
+            "ns_config_template": {
+                "METHODS": ("GET", "POST"),
+                "ROLE_PERMISSION": "ns_config_template:content:",
+                "<ID>": {
+                    "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"),
                 "<ID>": {
@@ -418,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:",
@@ -432,6 +473,10 @@ valid_url_methods = {
                 "<ID>": {
                     "METHODS": ("GET", "DELETE"),
                     "ROLE_PERMISSION": "ns_instances:id:",
+                    "heal": {
+                        "METHODS": ("POST",),
+                        "ROLE_PERMISSION": "ns_instances:id:heal:",
+                    },
                     "scale": {
                         "METHODS": ("POST",),
                         "ROLE_PERMISSION": "ns_instances:id:scale:",
@@ -444,6 +489,10 @@ valid_url_methods = {
                         "METHODS": ("POST",),
                         "ROLE_PERMISSION": "ns_instances:id:instantiate:",
                     },
+                    "migrate": {
+                        "METHODS": ("POST",),
+                        "ROLE_PERMISSION": "ns_instances:id:migrate:",
+                    },
                     "action": {
                         "METHODS": ("POST",),
                         "ROLE_PERMISSION": "ns_instances:id:action:",
@@ -460,6 +509,10 @@ valid_url_methods = {
                 "<ID>": {
                     "METHODS": ("GET",),
                     "ROLE_PERMISSION": "ns_instances:opps:id:",
+                    "cancel": {
+                        "METHODS": ("POST",),
+                        "ROLE_PERMISSION": "ns_instances:opps:cancel:",
+                    },
                 },
             },
             "vnfrs": {
@@ -484,27 +537,42 @@ valid_url_methods = {
     },
     "vnflcm": {
         "v1": {
-            "vnf_instances": {"METHODS": ("GET", "POST"),
-                              "ROLE_PERMISSION": "vnflcm_instances:",
-                              "<ID>": {"METHODS": ("GET", "DELETE"),
-                                       "ROLE_PERMISSION": "vnflcm_instances:id:",
-                                       "scale": {"METHODS": ("POST",),
-                                                 "ROLE_PERMISSION": "vnflcm_instances:id:scale:"
-                                                },
-                                       "terminate": {"METHODS": ("POST",),
-                                                     "ROLE_PERMISSION": "vnflcm_instances:id:terminate:"
-                                                    },
-                                       "instantiate": {"METHODS": ("POST",),
-                                                       "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:"
-                                                      },
-                                       }
-                            },
-            "vnf_lcm_op_occs": {"METHODS": ("GET",),
-                               "ROLE_PERMISSION": "vnf_instances:opps:",
-                               "<ID>": {"METHODS": ("GET",),
-                                        "ROLE_PERMISSION": "vnf_instances:opps:id:"
-                                        },
-                               },
+            "vnf_instances": {
+                "METHODS": ("GET", "POST"),
+                "ROLE_PERMISSION": "vnflcm_instances:",
+                "<ID>": {
+                    "METHODS": ("GET", "DELETE"),
+                    "ROLE_PERMISSION": "vnflcm_instances:id:",
+                    "scale": {
+                        "METHODS": ("POST",),
+                        "ROLE_PERMISSION": "vnflcm_instances:id:scale:",
+                    },
+                    "terminate": {
+                        "METHODS": ("POST",),
+                        "ROLE_PERMISSION": "vnflcm_instances:id:terminate:",
+                    },
+                    "instantiate": {
+                        "METHODS": ("POST",),
+                        "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:",
+                    },
+                },
+            },
+            "vnf_lcm_op_occs": {
+                "METHODS": ("GET",),
+                "ROLE_PERMISSION": "vnf_instances:opps:",
+                "<ID>": {
+                    "METHODS": ("GET",),
+                    "ROLE_PERMISSION": "vnf_instances:opps:id:",
+                },
+            },
+            "subscriptions": {
+                "METHODS": ("GET", "POST"),
+                "ROLE_PERMISSION": "vnflcm_subscriptions:",
+                "<ID>": {
+                    "METHODS": ("GET", "DELETE"),
+                    "ROLE_PERMISSION": "vnflcm_subscriptions:id:",
+                },
+            },
         }
     },
     "nst": {
@@ -601,14 +669,136 @@ valid_url_methods = {
     },
     "nsfm": {
         "v1": {
-            "alarms": {"METHODS": ("GET", "PATCH"),
-                       "ROLE_PERMISSION": "alarms:",
-                       "<ID>": {"METHODS": ("GET", "PATCH"),
-                                "ROLE_PERMISSION": "alarms:id:",
-                                },
-                       }
+            "alarms": {
+                "METHODS": ("GET", "PATCH"),
+                "ROLE_PERMISSION": "alarms:",
+                "<ID>": {
+                    "METHODS": ("GET", "PATCH"),
+                    "ROLE_PERMISSION": "alarms:id:",
+                },
+            }
         },
     },
+    "k8scluster": {
+        "v1": {
+            "clusters": {
+                "METHODS": ("GET", "POST"),
+                "ROLE_PERMISSION": "k8scluster:",
+                "<ID>": {
+                    "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:",
+                "<ID>": {
+                    "METHODS": ("GET", "PATCH", "DELETE"),
+                    "ROLE_PERMISSION": "k8scluster:app_profiles:id:",
+                },
+            },
+            "infra_controller_profiles": {
+                "METHODS": ("POST", "GET"),
+                "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:",
+                "<ID>": {
+                    "METHODS": ("GET", "PATCH", "DELETE"),
+                    "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:",
+                },
+            },
+            "infra_config_profiles": {
+                "METHODS": ("POST", "GET"),
+                "ROLE_PERMISSION": "k8scluster:infra_config_profiles:",
+                "<ID>": {
+                    "METHODS": ("GET", "PATCH", "DELETE"),
+                    "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:",
+                },
+            },
+            "resource_profiles": {
+                "METHODS": ("POST", "GET"),
+                "ROLE_PERMISSION": "k8scluster:resource_profiles:",
+                "<ID>": {
+                    "METHODS": ("GET", "PATCH", "DELETE"),
+                    "ROLE_PERMISSION": "k8scluster:resource_profiles:id:",
+                },
+            },
+        }
+    },
+    "ksu": {
+        "v1": {
+            "ksus": {
+                "METHODS": ("GET", "POST"),
+                "ROLE_PERMISSION": "ksu:",
+                "<ID>": {
+                    "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:",
+                "<ID>": {
+                    "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
+                    "ROLE_PERMISSION": "oka_pkg:id:",
+                },
+            }
+        }
+    },
 }
 
 
@@ -627,8 +817,10 @@ 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
         try:
             indata = None
             if cherrypy.request.body.length:
@@ -641,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"]
@@ -657,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
+                            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.file  # .read()
+                            indata = filecontent
                             if filecontent.content_type.value:
                                 cherrypy.request.headers[
                                     "Content-Type"
@@ -673,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
@@ -694,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 (
@@ -718,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
 
@@ -865,55 +1071,88 @@ class Server(object):
 
     # NS Fault Management
     @cherrypy.expose
-    def nsfm(self, version=None, topic=None, uuid=None, project_name=None, ns_id=None, *args, **kwargs):
-        if topic == 'alarms':
+    def nsfm(
+        self,
+        version=None,
+        topic=None,
+        uuid=None,
+        project_name=None,
+        ns_id=None,
+        *args,
+        **kwargs
+    ):
+        if topic == "alarms":
             try:
                 method = cherrypy.request.method
-                role_permission = self._check_valid_url_method(method, "nsfm", version, topic, None, None, *args)
-                query_string_operations = self._extract_query_string_operations(kwargs, method)
+                role_permission = self._check_valid_url_method(
+                    method, "nsfm", version, topic, None, None, *args
+                )
+                query_string_operations = self._extract_query_string_operations(
+                    kwargs, method
+                )
 
-                self.authenticator.authorize(role_permission, query_string_operations, None)
+                self.authenticator.authorize(
+                    role_permission, query_string_operations, None
+                )
 
                 # to handle get request
-                if cherrypy.request.method == 'GET':
+                if cherrypy.request.method == "GET":
                     # if request is on basis of uuid
-                    if uuid and uuid != 'None':
+                    if uuid and uuid != "None":
                         try:
                             alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
-                            alarm_action = self.engine.db.get_one("alarms_action", {"uuid": uuid})
+                            alarm_action = self.engine.db.get_one(
+                                "alarms_action", {"uuid": uuid}
+                            )
                             alarm.update(alarm_action)
-                            vnf = self.engine.db.get_one("vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]})
+                            vnf = self.engine.db.get_one(
+                                "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
+                            )
                             alarm["vnf-id"] = vnf["_id"]
                             return self._format_out(str(alarm))
                         except Exception:
                             return self._format_out("Please provide valid alarm uuid")
-                    elif ns_id and ns_id != 'None':
+                    elif ns_id and ns_id != "None":
                         # if request is on basis of ns_id
                         try:
-                            alarms = self.engine.db.get_list("alarms", {"tags.ns_id": ns_id})
+                            alarms = self.engine.db.get_list(
+                                "alarms", {"tags.ns_id": ns_id}
+                            )
                             for alarm in alarms:
-                                alarm_action = self.engine.db.get_one("alarms_action", {"uuid": alarm['uuid']})
+                                alarm_action = self.engine.db.get_one(
+                                    "alarms_action", {"uuid": alarm["uuid"]}
+                                )
                                 alarm.update(alarm_action)
                             return self._format_out(str(alarms))
                         except Exception:
                             return self._format_out("Please provide valid ns id")
                     else:
                         # to return only alarm which are related to given project
-                        project = self.engine.db.get_one("projects", {"name": project_name})
-                        project_id = project.get('_id')
-                        ns_list = self.engine.db.get_list("nsrs", {"_admin.projects_read": project_id})
+                        project = self.engine.db.get_one(
+                            "projects", {"name": project_name}
+                        )
+                        project_id = project.get("_id")
+                        ns_list = self.engine.db.get_list(
+                            "nsrs", {"_admin.projects_read": project_id}
+                        )
                         ns_ids = []
                         for ns in ns_list:
                             ns_ids.append(ns.get("_id"))
                         alarms = self.engine.db.get_list("alarms")
-                        alarm_list = [alarm for alarm in alarms if alarm["tags"]["ns_id"] in ns_ids]
+                        alarm_list = [
+                            alarm
+                            for alarm in alarms
+                            if alarm["tags"]["ns_id"] in ns_ids
+                        ]
                         for alrm in alarm_list:
-                            action = self.engine.db.get_one("alarms_action", {"uuid": alrm.get("uuid")})
+                            action = self.engine.db.get_one(
+                                "alarms_action", {"uuid": alrm.get("uuid")}
+                            )
                             alrm.update(action)
                         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)
+                elif cherrypy.request.method == "PATCH":
+                    data = yaml.safe_load(cherrypy.request.body)
                     try:
                         # check if uuid is valid
                         self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
@@ -921,24 +1160,42 @@ class Server(object):
                         return self._format_out("Please provide valid alarm uuid.")
                     if data.get("is_enable") is not None:
                         if data.get("is_enable"):
-                            alarm_status = 'ok'
+                            alarm_status = "ok"
                         else:
-                            alarm_status = 'disabled'
-                        self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
-                                               {"alarm_status": alarm_status})
+                            alarm_status = "disabled"
+                        self.engine.db.set_one(
+                            "alarms",
+                            {"uuid": data.get("uuid")},
+                            {"alarm_status": alarm_status},
+                        )
                     else:
-                        self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
-                                               {"threshold": data.get("threshold")})
+                        self.engine.db.set_one(
+                            "alarms",
+                            {"uuid": data.get("uuid")},
+                            {"threshold": data.get("threshold")},
+                        )
                     return self._format_out("Alarm updated")
             except Exception as e:
-                cherrypy.response.status = e.http_code.value
-                if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
-                              ValidationError, AuthconnException)):
+                if isinstance(
+                    e,
+                    (
+                        NbiException,
+                        EngineException,
+                        DbException,
+                        FsException,
+                        MsgException,
+                        AuthException,
+                        ValidationError,
+                        AuthconnException,
+                    ),
+                ):
                     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
+                    http_code_value = (
+                        cherrypy.response.status
+                    ) = HTTPStatus.BAD_REQUEST.value  # INTERNAL_SERVER_ERROR
                     cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
                     http_code_name = HTTPStatus.BAD_REQUEST.name
                 problem_details = {
@@ -979,18 +1236,30 @@ class Server(object):
             outdata = token_info = self.authenticator.new_token(
                 token_info, indata, cherrypy.request.remote
             )
-            cherrypy.session["Authorization"] = outdata["_id"]
+            cherrypy.session["Authorization"] = outdata["_id"]  # pylint: disable=E1101
             self._set_location_header("admin", "v1", "tokens", outdata["_id"])
             # for logging
             self._format_login(token_info)
             # password expiry check
             if self.authenticator.check_password_expiry(outdata):
-                outdata = {"id": outdata["id"],
-                           "message": "change_password",
-                           "user_id": outdata["user_id"]
-                           }
+                outdata = {
+                    "id": outdata["id"],
+                    "message": "change_password",
+                    "user_id": outdata["user_id"],
+                }
             # 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"]
@@ -999,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"
+            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:
@@ -1029,7 +1316,8 @@ class Server(object):
         elif args and args[0] == "init":
             try:
                 # self.engine.load_dbase(cherrypy.request.app.config)
-                self.engine.create_admin()
+                pid = self.authenticator.create_admin_project()
+                self.authenticator.create_admin_user(pid)
                 return "Done. User 'admin', password 'admin' created"
             except Exception:
                 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
@@ -1087,13 +1375,13 @@ class Server(object):
             return_text = "<html><pre>{} ->\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:
@@ -1107,10 +1395,12 @@ class Server(object):
             + "  headers: {}\n".format(cherrypy.request.headers)
             + "  path_info: {}\n".format(cherrypy.request.path_info)
             + "  query_string: {}\n".format(cherrypy.request.query_string)
-            + "  session: {}\n".format(cherrypy.session)
+            + "  session: {}\n".format(cherrypy.session)  # pylint: disable=E1101
             + "  cookie: {}\n".format(cherrypy.request.cookie)
             + "  method: {}\n".format(cherrypy.request.method)
-            + "  session: {}\n".format(cherrypy.session.get("fieldname"))
+            + "  session: {}\n".format(
+                cherrypy.session.get("fieldname")  # pylint: disable=E1101
+            )
             + "  body:\n"
         )
         return_text += "    length: {}\n".format(cherrypy.request.body.length)
@@ -1302,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(
@@ -1324,6 +1622,9 @@ class Server(object):
                 "nsilcm",
                 "nspm",
                 "vnflcm",
+                "k8scluster",
+                "ksu",
+                "oka",
             ):
                 raise NbiException(
                     "URL main_topic '{}' not supported".format(main_topic),
@@ -1334,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
@@ -1366,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":
@@ -1389,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
@@ -1406,6 +1731,7 @@ class Server(object):
                     "nsd",
                     "nst",
                     "nst_content",
+                    "ns_config_template",
                 ):
                     if item in ("vnfd", "nsd", "nst"):
                         path = "$DESCRIPTOR"
@@ -1427,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
@@ -1434,7 +1780,9 @@ class Server(object):
                     filter_q = None
                     if "vcaStatusRefresh" in kwargs:
                         filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
-                    outdata = self.engine.get_item(engine_session, engine_topic, _id, filter_q, True)
+                    outdata = self.engine.get_item(
+                        engine_session, engine_topic, _id, filter_q, True
+                    )
 
             elif method == "POST":
                 cherrypy.response.status = HTTPStatus.CREATED.value
@@ -1442,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,
@@ -1466,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(
@@ -1474,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)
@@ -1541,10 +1941,78 @@ class Server(object):
                 elif topic == "vnf_instances" and item:
                     indata["lcmOperationType"] = item
                     indata["vnfInstanceId"] = _id
-                    _id, _ = 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}
+                    _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, "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,
@@ -1576,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"]
@@ -1595,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
@@ -1614,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(
@@ -1627,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
@@ -1656,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(
@@ -1697,7 +2310,6 @@ class Server(object):
                         self.engine.db.del_list(
                             rollback_item["topic"],
                             rollback_item["filter"],
-                            fail_on_empty=False,
                         )
                     else:
                         self.engine.db.del_one(
@@ -1719,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:
@@ -1741,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("_")
@@ -1773,7 +2431,9 @@ def _start_service():
         except ValueError as e:
             cherrypy.log.error("Ignoring environ '{}': " + str(e))
         except Exception as e:
-            cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
+            cherrypy.log(
+                "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
+            )
 
     if update_dict:
         cherrypy.config.update(update_dict)
@@ -1840,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
@@ -1848,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