fixed database init version issue
[osm/NBI.git] / osm_nbi / engine.py
index a15420f..e7bf7c9 100644 (file)
@@ -18,6 +18,7 @@ from msgbase import MsgException
 from http import HTTPStatus
 from time import time
 from copy import deepcopy
 from http import HTTPStatus
 from time import time
 from copy import deepcopy
+from validation import validate_input, ValidationError
 
 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
 
 
 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
 
@@ -276,6 +277,11 @@ class Engine(object):
 
         elif item == "nsrs":
             pass
 
         elif item == "nsrs":
             pass
+        elif item == "vims" or item == "sdns":
+            if self.db.get_one(item, {"name": indata.get("name")}, fail_on_empty=False, fail_on_more=False):
+                raise EngineException("name '{}' already exist for {}".format(indata["name"], item),
+                                      HTTPStatus.CONFLICT)
+
 
     def _format_new_data(self, session, item, indata, admin=None):
         now = time()
 
     def _format_new_data(self, session, item, indata, admin=None):
         now = time()
@@ -306,6 +312,9 @@ class Engine(object):
                 indata["_admin"]["onboardingState"] = "CREATED"
                 indata["_admin"]["operationalState"] = "DISABLED"
                 indata["_admin"]["usageSate"] = "NOT_IN_USE"
                 indata["_admin"]["onboardingState"] = "CREATED"
                 indata["_admin"]["operationalState"] = "DISABLED"
                 indata["_admin"]["usageSate"] = "NOT_IN_USE"
+            elif item in ("vims", "sdns"):
+                indata["_admin"]["operationalState"] = "PROCESSING"
+
             if storage:
                 indata["_admin"]["storage"] = storage
         indata["_id"] = _id
             if storage:
                 indata["_admin"]["storage"] = storage
         indata["_id"] = _id
@@ -500,25 +509,14 @@ class Engine(object):
         Creates a new entry into database. For nsds and vnfds it creates an almost empty DISABLED  entry,
         that must be completed with a call to method upload_content
         :param session: contains the used login username and working project
         Creates a new entry into database. For nsds and vnfds it creates an almost empty DISABLED  entry,
         that must be completed with a call to method upload_content
         :param session: contains the used login username and working project
-        :param item: it can be: users, projects, nsrs, nsds, vnfds
+        :param item: it can be: users, projects, vims, sdns, nsrs, nsds, vnfds
         :param indata: data to be inserted
         :param kwargs: used to override the indata descriptor
         :param headers: http request headers
         :return: _id, transaction_id: identity of the inserted data. or transaction_id if Content-Range is used
         """
         :param indata: data to be inserted
         :param kwargs: used to override the indata descriptor
         :param headers: http request headers
         :return: _id, transaction_id: identity of the inserted data. or transaction_id if Content-Range is used
         """
-        # TODO validate input. Check not exist at database
-        # TODO add admin and status
 
         transaction = None
 
         transaction = None
-        # if headers.get("Content-Range") or "application/gzip" in headers.get("Content-Type") or \
-        #     "application/x-gzip" in headers.get("Content-Type") or "application/zip" in headers.get("Content-Type") or \
-        #     "text/plain" in headers.get("Content-Type"):
-        #     if not indata:
-        #         raise EngineException("Empty payload")
-        #     transaction = self._new_item_partial(session, item, indata, headers)
-        #     if "desc" not in transaction:
-        #         return transaction["_id"], False
-        #     indata = transaction["desc"]
 
         item_envelop = item
         if item in ("nsds", "vnfds"):
 
         item_envelop = item
         if item in ("nsds", "vnfds"):
@@ -552,6 +550,11 @@ class Engine(object):
             except IndexError:
                 raise EngineException(
                     "Invalid query string '{}'. Index '{}' out of  range".format(k, kitem_old))
             except IndexError:
                 raise EngineException(
                     "Invalid query string '{}'. Index '{}' out of  range".format(k, kitem_old))
+        try:
+            validate_input(content, item, new=True)
+        except ValidationError as e:
+            raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
+
         if not indata and item not in ("nsds", "vnfds"):
             raise EngineException("Empty payload")
 
         if not indata and item not in ("nsds", "vnfds"):
             raise EngineException("Empty payload")
 
@@ -568,6 +571,14 @@ class Engine(object):
         _id = self.db.create(item, content)
         if item == "nsrs":
             self.msg.write("ns", "create", _id)
         _id = self.db.create(item, content)
         if item == "nsrs":
             self.msg.write("ns", "create", _id)
+        elif item == "vims":
+            msg_data = self.db.get_one(item, {"_id": _id})
+            msg_data.pop("_admin", None)
+            self.msg.write("vim_account", "create", msg_data)
+        elif item == "sdns":
+            msg_data = self.db.get_one(item, {"_id": _id})
+            msg_data.pop("_admin", None)
+            self.msg.write("sdn", "create", msg_data)
         return _id
 
     def _add_read_filter(self, session, item, filter):
         return _id
 
     def _add_read_filter(self, session, item, filter):
@@ -645,7 +656,6 @@ class Engine(object):
                                       "", http_code=HTTPStatus.NOT_ACCEPTABLE)
             return self.fs.file_open((storage['folder'], storage['zipfile']), "rb"), "application/zip"
 
                                       "", http_code=HTTPStatus.NOT_ACCEPTABLE)
             return self.fs.file_open((storage['folder'], storage['zipfile']), "rb"), "application/zip"
 
-
     def get_item_list(self, session, item, filter={}):
         """
         Get a list of items
     def get_item_list(self, session, item, filter={}):
         """
         Get a list of items
@@ -701,17 +711,20 @@ class Engine(object):
         filter = {"_id": _id}
         self._add_delete_filter(session, item, filter)
 
         filter = {"_id": _id}
         self._add_delete_filter(session, item, filter)
 
-        if item == "nsrs":
+        if item in ("nsrs", "vims", "sdns"):
             desc = self.db.get_one(item, filter)
             desc["_admin"]["to_delete"] = True
             self.db.replace(item, _id, desc)   # TODO change to set_one
             desc = self.db.get_one(item, filter)
             desc["_admin"]["to_delete"] = True
             self.db.replace(item, _id, desc)   # TODO change to set_one
-            self.msg.write("ns", "delete", _id)
-            return {"deleted": 1}
+            if item == "nsrs":
+                self.msg.write("ns", "delete", _id)
+            elif item == "vims":
+                self.msg.write("vim_account", "delete", {"_id": _id})
+            elif item == "sdns":
+                self.msg.write("sdn", "delete", {"_id": _id})
+            return {"deleted": 1}  # TODO indicate an offline operation to return 202 ACCEPTED
 
         v = self.db.del_one(item, filter)
         self.fs.file_delete(_id, ignore_non_exist=True)
 
         v = self.db.del_one(item, filter)
         self.fs.file_delete(_id, ignore_non_exist=True)
-        if item == "nsrs":
-            self.msg.write("ns", "delete", _id)
         return v
 
     def prune(self):
         return v
 
     def prune(self):
@@ -723,18 +736,47 @@ class Engine(object):
 
     def create_admin(self):
         """
 
     def create_admin(self):
         """
-        Creates a new user admin/admin into database. Only allowed if database is empty. Useful for initialization
-        :return: _id identity of the inserted data.
+        Creates a new user admin/admin into database if database is empty. Useful for initialization
+        :return: _id identity of the inserted data, or None
         """
         users = self.db.get_one("users", fail_on_empty=False, fail_on_more=False)
         if users:
         """
         users = self.db.get_one("users", fail_on_empty=False, fail_on_more=False)
         if users:
-            raise EngineException("Unauthorized. Database users is not empty", HTTPStatus.UNAUTHORIZED)
+            return None
+            # raise EngineException("Unauthorized. Database users is not empty", HTTPStatus.UNAUTHORIZED)
         indata = {"username": "admin", "password": "admin", "projects": ["admin"]}
         fake_session = {"project_id": "admin", "username": "admin"}
         self._format_new_data(fake_session, "users", indata)
         _id = self.db.create("users", indata)
         return _id
 
         indata = {"username": "admin", "password": "admin", "projects": ["admin"]}
         fake_session = {"project_id": "admin", "username": "admin"}
         self._format_new_data(fake_session, "users", indata)
         _id = self.db.create("users", indata)
         return _id
 
+    def init_db(self, target_version='1.0'):
+        """
+        Init database if empty. If not empty it checks that database version is ok.
+        If empty, it creates a new user admin/admin at 'users' and a new entry at 'version'
+        :return: None if ok, exception if error or if the version is different.
+        """
+        version = self.db.get_one("version", fail_on_empty=False, fail_on_more=False)
+        if not version:
+            # create user admin
+            self.create_admin()
+            # create database version
+            version_data = {
+                "_id": '1.0',                     # version text
+                "version": 1000,                  # version number
+                "date": "2018-04-12",             # version date
+                "description": "initial design",  # changes in this version
+                'status': 'ENABLED'               # ENABLED, DISABLED (migration in process), ERROR,
+            }
+            self.db.create("version", version_data)
+        elif version["_id"] != target_version:
+            # TODO implement migration process
+            raise EngineException("Wrong database version '{}'. Expected '{}'".format(
+                version["_id"], target_version), HTTPStatus.INTERNAL_SERVER_ERROR)
+        elif version["status"] != 'ENABLED':
+            raise EngineException("Wrong database status '{}'".format(
+                version["status"]), HTTPStatus.INTERNAL_SERVER_ERROR)
+        return
+
     def _edit_item(self, session, item, id, content, indata={}, kwargs=None):
         if indata:
             indata = self._remove_envelop(item, indata)
     def _edit_item(self, session, item, id, content, indata={}, kwargs=None):
         if indata:
             indata = self._remove_envelop(item, indata)
@@ -766,11 +808,22 @@ class Engine(object):
             except IndexError:
                 raise EngineException(
                     "Invalid query string '{}'. Index '{}' out of  range".format(k, kitem_old))
             except IndexError:
                 raise EngineException(
                     "Invalid query string '{}'. Index '{}' out of  range".format(k, kitem_old))
+        try:
+            validate_input(content, item, new=False)
+        except ValidationError as e:
+            raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
 
         _deep_update(content, indata)
         self._validate_new_data(session, item, content, id)
         # self._format_new_data(session, item, content)
         self.db.replace(item, id, content)
 
         _deep_update(content, indata)
         self._validate_new_data(session, item, content, id)
         # self._format_new_data(session, item, content)
         self.db.replace(item, id, content)
+        if item in ("vims", "sdns"):
+            indata.pop("_admin", None)
+            indata["_id"] = id
+            if item == "vims":
+                self.msg.write("vim_account", "edit", indata)
+            elif item == "sdns":
+                self.msg.write("sdn", "edit", indata)
         return id
 
     def edit_item(self, session, item, _id, indata={}, kwargs=None):
         return id
 
     def edit_item(self, session, item, _id, indata={}, kwargs=None):