- clean_indata = indata
- if not indata:
- return {}
- if item == "vnfds":
- if clean_indata.get('vnfd:vnfd-catalog'):
- clean_indata = clean_indata['vnfd:vnfd-catalog']
- elif clean_indata.get('vnfd-catalog'):
- clean_indata = clean_indata['vnfd-catalog']
- if clean_indata.get('vnfd'):
- if not isinstance(clean_indata['vnfd'], list) or len(clean_indata['vnfd']) != 1:
- raise EngineException("'vnfd' must be a list only one element")
- clean_indata = clean_indata['vnfd'][0]
- elif item == "nsds":
- if clean_indata.get('nsd:nsd-catalog'):
- clean_indata = clean_indata['nsd:nsd-catalog']
- elif clean_indata.get('nsd-catalog'):
- clean_indata = clean_indata['nsd-catalog']
- if clean_indata.get('nsd'):
- if not isinstance(clean_indata['nsd'], list) or len(clean_indata['nsd']) != 1:
- raise EngineException("'nsd' must be a list only one element")
- clean_indata = clean_indata['nsd'][0]
- return clean_indata
-
- def _validate_new_data(self, session, item, indata):
- if item == "users":
- if not indata.get("username"):
- raise EngineException("missing 'username'", HTTPStatus.UNPROCESSABLE_ENTITY)
- if not indata.get("password"):
- raise EngineException("missing 'password'", HTTPStatus.UNPROCESSABLE_ENTITY)
- if not indata.get("projects"):
- raise EngineException("missing 'projects'", HTTPStatus.UNPROCESSABLE_ENTITY)
- # check username not exist
- if self.db.get_one(item, {"username": indata.get("username")}, fail_on_empty=False, fail_on_more=False):
- raise EngineException("username '{}' exist".format(indata["username"]), HTTPStatus.CONFLICT)
- elif item == "projects":
- if not indata.get("name"):
- raise EngineException("missing 'name'")
- # check name not exist
- if self.db.get_one(item, {"name": indata.get("name")}, fail_on_empty=False, fail_on_more=False):
- raise EngineException("name '{}' exist".format(indata["name"]), HTTPStatus.CONFLICT)
- elif item == "vnfds" or item == "nsds":
- filter = {"id": indata["id"]}
- # TODO add admin to filter, validate rights
- self._add_read_filter(session, item, filter)
- if self.db.get_one(item, filter, fail_on_empty=False):
- raise EngineException("{} with id '{}' already exist for this tenant".format(item[:-1], indata["id"]),
- HTTPStatus.CONFLICT)
-
- # TODO validate with pyangbind
- elif item == "nsrs":
- pass
-
- def _format_new_data(self, session, item, indata, admin=None):
- now = time()
- if not "_admin" in indata:
- indata["_admin"] = {}
- indata["_admin"]["created"] = now
- indata["_admin"]["modified"] = now
- if item == "users":
- _id = indata["username"]
- salt = uuid4().hex
- indata["_admin"]["salt"] = salt
- indata["password"] = sha256(indata["password"].encode('utf-8') + salt.encode('utf-8')).hexdigest()
- elif item == "projects":
- _id = indata["name"]
- else:
- _id = None
- storage = None
- if admin:
- _id = admin.get("_id")
- storage = admin.get("storage")
- if not _id:
- _id = str(uuid4())
- if item == "vnfds" or item == "nsds":
- if not indata["_admin"].get("projects_read"):
- indata["_admin"]["projects_read"] = [session["project_id"]]
- if not indata["_admin"].get("projects_write"):
- indata["_admin"]["projects_write"] = [session["project_id"]]
- if storage:
- indata["_admin"]["storage"] = storage
- indata["_id"] = _id
-
- def _new_item_partial(self, session, item, indata, headers):
- """
- Used for recieve content by chunks (with a transaction_id header and/or gzip file. It will store and extract
- :param session: session
- :param item:
- :param indata: http body request
- :param headers: http request headers
- :return: a dict with::
- _id: <transaction_id>
- storage: <path>: where it is saving
- desc: <dict>: descriptor: Only present when all the content is received, extracted and read the descriptor
- """
- content_range_text = headers.get("Content-Range")
- transaction_id = headers.get("Transaction-Id")
- filename = headers.get("Content-Filename", "pkg")
- # TODO change to Content-Disposition filename https://tools.ietf.org/html/rfc6266
- expected_md5 = headers.get("Content-File-MD5")
- compressed = None
- if "application/gzip" in headers.get("Content-Type") or "application/x-gzip" in headers.get("Content-Type") or \
- "application/zip" in headers.get("Content-Type"):
- compressed = "gzip"
- file_pkg = None
- error_text = ""
- try:
- if content_range_text:
- content_range = content_range_text.replace("-", " ").replace("/", " ").split()
- if content_range[0] != "bytes": # TODO check x<y not negative < total....
- raise IndexError()
- start = int(content_range[1])
- end = int(content_range[2]) + 1
- total = int(content_range[3])
- if len(indata) != end-start:
- raise EngineException("Mismatch between Content-Range header {}-{} and body length of {}".format(
- start, end-1, len(indata)), HTTPStatus.BAD_REQUEST)
- else:
- start = 0
- total = end = len(indata)
- if not transaction_id:
- # generate transaction
- transaction_id = str(uuid4())
- self.fs.mkdir(transaction_id)
- # control_file = open(self.storage["path"] + transaction_id + "/.osm.yaml", 'wb')
- # control = {"received": 0}
- elif not self.fs.file_exists(transaction_id):
- raise EngineException("invalid Transaction-Id header", HTTPStatus.NOT_FOUND)
- else:
- pass
- # control_file = open(self.storage["path"] + transaction_id + "/.osm.yaml", 'rw')
- # control = yaml.load(control_file)
- # control_file.seek(0, 0)
- storage = self.fs.get_params()
- storage["folder"] = transaction_id
- storage["file"] = filename
-
- file_path = (transaction_id, filename)
- if self.fs.file_exists(file_path):
- file_size = self.fs.file_size(file_path)
- else:
- file_size = 0
- if file_size != start:
- raise EngineException("invalid upload transaction sequence, expected '{}' but received '{}'".format(
- file_size, start), HTTPStatus.BAD_REQUEST)
- file_pkg = self.fs.file_open(file_path, 'a+b')
- file_pkg.write(indata)
- if end != total:
- return {"_id": transaction_id, "storage": storage}
- if expected_md5:
- file_pkg.seek(0, 0)
- file_md5 = md5()
- chunk_data = file_pkg.read(1024)
- while chunk_data:
- file_md5.update(chunk_data)
- chunk_data = file_pkg.read(1024)
- if expected_md5 != file_md5.hexdigest():
- raise EngineException("Error, MD5 mismatch", HTTPStatus.CONFLICT)
- file_pkg.seek(0, 0)
- if compressed == "gzip":
- # TODO unzip,
- storage["tarfile"] = filename
- tar = tarfile.open(mode='r', fileobj=file_pkg)
- descriptor_file_name = None
- for tarinfo in tar:
- tarname = tarinfo.name
- tarname_path = tarname.split("/")
- if not tarname_path[0] or ".." in tarname_path: # if start with "/" means absolute path
- raise EngineException("Absolute path or '..' are not allowed for package descriptor tar.gz")
- if len(tarname_path) == 1 and not tarinfo.isdir():
- raise EngineException("All files must be inside a dir for package descriptor tar.gz")
- if tarname.endswith(".yaml") or tarname.endswith(".json") or tarname.endswith(".yml"):
- storage["file"] = tarname_path[0]
- if len(tarname_path) == 2:
- if descriptor_file_name:
- raise EngineException("Found more than one descriptor file at package descriptor tar.gz")
- descriptor_file_name = tarname
- if not descriptor_file_name:
- raise EngineException("Not found any descriptor file at package descriptor tar.gz")
- self.fs.file_extract(tar, transaction_id)
- with self.fs.file_open((transaction_id, descriptor_file_name), "r") as descriptor_file:
- content = descriptor_file.read()
- else:
- content = file_pkg.read()
- tarname = ""
-
- if tarname.endswith(".json"):
- error_text = "Invalid json format "
- indata = json.load(content)
- else:
- error_text = "Invalid yaml format "
- indata = yaml.load(content)
- return {"_id": transaction_id, "storage": storage, "desc": indata}
- except EngineException:
- raise
- except IndexError:
- raise EngineException("invalid Content-Range header format. Expected 'bytes start-end/total'",
- HTTPStatus.BAD_REQUEST)
- except IOError as e:
- raise EngineException("invalid upload transaction sequence: '{}'".format(e), HTTPStatus.BAD_REQUEST)
- except (ValueError, yaml.YAMLError) as e:
- raise EngineException(error_text + str(e))
- finally:
- if file_pkg:
- file_pkg.close()
-
- def new_nsr(self, session, ns_request):
- """
- Creates a new nsr into database
- :param session: contains the used login username and working project
- :param ns_request: params to be used for the nsr
- :return: nsr descriptor to be stored at database and the _id