Fix Bug 2229 Set fixed IP address for VDU through VNFD and the instantiation params
[osm/NBI.git] / osm_nbi / nbi.py
index bec0cfa..8fa6152 100644 (file)
@@ -85,7 +85,9 @@ 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
@@ -170,7 +172,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.
@@ -431,6 +433,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:",
@@ -443,10 +449,22 @@ 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:",
                     },
+                    "update": {
+                        "METHODS": ("POST",),
+                        "ROLE_PERMISSION": "ns_instances:id:update:",
+                    },
+                    "verticalscale": {
+                        "METHODS": ("POST",),
+                        "ROLE_PERMISSION": "ns_instances:id:verticalscale:",
+                    },
                 },
             },
             "ns_lcm_op_occs": {
@@ -477,6 +495,46 @@ 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:",
+                },
+            },
+            "subscriptions": {
+                "METHODS": ("GET", "POST"),
+                "ROLE_PERMISSION": "vnflcm_subscriptions:",
+                "<ID>": {
+                    "METHODS": ("GET", "DELETE"),
+                    "ROLE_PERMISSION": "vnflcm_subscriptions:id:",
+                },
+            },
+        }
+    },
     "nst": {
         "v1": {
             "netslice_templates_content": {
@@ -569,6 +627,18 @@ valid_url_methods = {
             },
         },
     },
+    "nsfm": {
+        "v1": {
+            "alarms": {
+                "METHODS": ("GET", "PATCH"),
+                "ROLE_PERMISSION": "alarms:",
+                "<ID>": {
+                    "METHODS": ("GET", "PATCH"),
+                    "ROLE_PERMISSION": "alarms:id:",
+                },
+            }
+        },
+    },
 }
 
 
@@ -589,6 +659,7 @@ class Server(object):
         self.engine = Engine(self.authenticator)
 
     def _format_in(self, kwargs):
+        error_text = ""  # error_text must be initialized outside try
         try:
             indata = None
             if cherrypy.request.body.length:
@@ -601,9 +672,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"]
@@ -633,13 +702,11 @@ 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 = {}
@@ -654,7 +721,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 (
@@ -678,7 +745,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
 
@@ -823,6 +890,142 @@ class Server(object):
         if token_info.get("id"):
             cherrypy.request.login += ";session=" + token_info["id"][0:12]
 
+    # 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":
+            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
+                )
+
+                self.authenticator.authorize(
+                    role_permission, query_string_operations, None
+                )
+
+                # to handle get request
+                if cherrypy.request.method == "GET":
+                    # if request is on basis of uuid
+                    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.update(alarm_action)
+                            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":
+                        # if request is on basis of ns_id
+                        try:
+                            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.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}
+                        )
+                        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
+                        ]
+                        for alrm in alarm_list:
+                            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.safe_load(cherrypy.request.body)
+                    try:
+                        # check if uuid is valid
+                        self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
+                    except Exception:
+                        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"
+                        else:
+                            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")},
+                        )
+                    return self._format_out("Alarm updated")
+            except Exception as e:
+                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
+                    cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
+                    http_code_name = HTTPStatus.BAD_REQUEST.name
+                problem_details = {
+                    "code": http_code_name,
+                    "status": http_code_value,
+                    "detail": str(e),
+                }
+                return self._format_out(problem_details)
+
     @cherrypy.expose
     def token(self, method, token_id=None, kwargs=None):
         token_info = None
@@ -854,11 +1057,17 @@ 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"],
+                }
             # cherrypy.response.cookie["Authorization"] = outdata["id"]
             # cherrypy.response.cookie["Authorization"]['expires'] = 3600
         elif method == "DELETE":
@@ -871,7 +1080,7 @@ class Server(object):
                 token_id = token_info["_id"]
             outdata = self.authenticator.del_token(token_id)
             token_info = None
-            cherrypy.session["Authorization"] = "logout"
+            cherrypy.session["Authorization"] = "logout"  # pylint: disable=E1101
             # cherrypy.response.cookie["Authorization"] = token_id
             # cherrypy.response.cookie["Authorization"]['expires'] = 0
         else:
@@ -899,7 +1108,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
@@ -957,13 +1167,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:
@@ -977,10 +1187,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)
@@ -1193,6 +1405,7 @@ class Server(object):
                 "nst",
                 "nsilcm",
                 "nspm",
+                "vnflcm",
             ):
                 raise NbiException(
                     "URL main_topic '{}' not supported".format(main_topic),
@@ -1247,6 +1460,9 @@ class Server(object):
                     engine_topic = "nslcmops"
                 if topic == "vnfrs" or topic == "vnf_instances":
                     engine_topic = "vnfrs"
+            elif main_topic == "vnflcm":
+                if topic == "vnf_lcm_op_occs":
+                    engine_topic = "vnflcmops"
             elif main_topic == "nst":
                 engine_topic = "nsts"
             elif main_topic == "nsilcm":
@@ -1297,8 +1513,11 @@ class Server(object):
                     if item == "reports":
                         # TODO check that project_id (_id in this context) has permissions
                         _id = args[0]
+                    filter_q = None
+                    if "vcaStatusRefresh" in kwargs:
+                        filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
                     outdata = self.engine.get_item(
-                        engine_session, engine_topic, _id, True
+                        engine_session, engine_topic, _id, filter_q, True
                     )
 
             elif method == "POST":
@@ -1403,6 +1622,17 @@ class Server(object):
                         "_links": link,
                     }
                     cherrypy.response.status = HTTPStatus.CREATED.value
+                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}
+                    cherrypy.response.status = HTTPStatus.ACCEPTED.value
                 else:
                     _id, op_id = self.engine.new_item(
                         rollback,
@@ -1555,7 +1785,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(
@@ -1631,7 +1860,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)