topic_msg = "users"
schema_new = user_new_schema
schema_edit = user_edit_schema
+ multiproject = False
def __init__(self, db, fs, msg):
BaseTopic.__init__(self, db, fs, msg)
@staticmethod
- def _get_project_filter(session, write=False, show_all=True):
+ def _get_project_filter(session):
"""
Generates a filter dictionary for querying database users.
Current policy is admin can show all, non admin, only its own user.
- :param session: contains "username", if user is "admin" and the working "project_id"
- :param write: if operation is for reading (False) or writing (True)
- :param show_all: if True it will show public or
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:return:
"""
if session["admin"]: # allows all
else:
return {"username": session["username"]}
- def check_conflict_on_new(self, session, indata, force=False):
+ def check_conflict_on_new(self, session, indata):
# check username not exists
if self.db.get_one(self.topic, {"username": indata.get("username")}, fail_on_empty=False, fail_on_more=False):
raise EngineException("username '{}' exists".format(indata["username"]), HTTPStatus.CONFLICT)
# check projects
- if not force:
- for p in indata["projects"]:
- if p == "admin":
- continue
+ if not session["force"]:
+ for p in indata.get("projects"):
# To allow project addressing by Name as well as ID
if not self.db.get_one("projects", {BaseTopic.id_field("projects", p): p}, fail_on_empty=False,
fail_on_more=False):
raise EngineException("project '{}' does not exist".format(p), HTTPStatus.CONFLICT)
- def check_conflict_on_del(self, session, _id, force=False):
+ def check_conflict_on_del(self, session, _id):
if _id == session["username"]:
raise EngineException("You cannot delete your own user", http_code=HTTPStatus.CONFLICT)
final_content["password"] = sha256(edit_content["password"].encode('utf-8') +
salt.encode('utf-8')).hexdigest()
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
if not session["admin"]:
raise EngineException("needed admin privileges", http_code=HTTPStatus.UNAUTHORIZED)
# Names that look like UUIDs are not allowed
if is_valid_uuid(name):
raise EngineException("Usernames that look like UUIDs are not allowed",
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
- return BaseTopic.edit(self, session, _id, indata=indata, kwargs=kwargs, force=force, content=content)
+ return BaseTopic.edit(self, session, _id, indata=indata, kwargs=kwargs, content=content)
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
if not session["admin"]:
raise EngineException("needed admin privileges", http_code=HTTPStatus.UNAUTHORIZED)
# Names that look like UUIDs are not allowed
if is_valid_uuid(name):
raise EngineException("Usernames that look like UUIDs are not allowed",
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
- return BaseTopic.new(self, rollback, session, indata=indata, kwargs=kwargs, headers=headers, force=force,
- make_public=make_public)
+ return BaseTopic.new(self, rollback, session, indata=indata, kwargs=kwargs, headers=headers)
class ProjectTopic(BaseTopic):
topic_msg = "projects"
schema_new = project_new_schema
schema_edit = project_edit_schema
+ multiproject = False
def __init__(self, db, fs, msg):
BaseTopic.__init__(self, db, fs, msg)
- def check_conflict_on_new(self, session, indata, force=False):
+ @staticmethod
+ def _get_project_filter(session):
+ """
+ Generates a filter dictionary for querying database users.
+ Current policy is admin can show all, non admin, only its own user.
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+ :return:
+ """
+ if session["admin"]: # allows all
+ return {}
+ else:
+ return {"_id.cont": session["project_id"]}
+
+ def check_conflict_on_new(self, session, indata):
if not indata.get("name"):
raise EngineException("missing 'name'")
# check name not exists
# Removed so that the UUID is kept, to allow Project Name modification
# content["_id"] = content["name"]
- def check_conflict_on_del(self, session, _id, force=False):
- if _id == session["project_id"]:
+ def check_conflict_on_del(self, session, _id):
+ if _id in session["project_id"]:
raise EngineException("You cannot delete your own project", http_code=HTTPStatus.CONFLICT)
- if force:
+ if session["force"]:
return
_filter = {"projects": _id}
if self.db.get_list("users", _filter):
raise EngineException("There is some USER that contains this project", http_code=HTTPStatus.CONFLICT)
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
if not session["admin"]:
raise EngineException("needed admin privileges", http_code=HTTPStatus.UNAUTHORIZED)
# Names that look like UUIDs are not allowed
if is_valid_uuid(name):
raise EngineException("Project names that look like UUIDs are not allowed",
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
- return BaseTopic.edit(self, session, _id, indata=indata, kwargs=kwargs, force=force, content=content)
+ return BaseTopic.edit(self, session, _id, indata=indata, kwargs=kwargs, content=content)
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
if not session["admin"]:
raise EngineException("needed admin privileges", http_code=HTTPStatus.UNAUTHORIZED)
# Names that look like UUIDs are not allowed
if is_valid_uuid(name):
raise EngineException("Project names that look like UUIDs are not allowed",
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
- return BaseTopic.new(self, rollback, session, indata=indata, kwargs=kwargs, headers=headers, force=force,
- make_public=make_public)
+ return BaseTopic.new(self, rollback, session, indata=indata, kwargs=kwargs, headers=headers)
class VimAccountTopic(BaseTopic):
schema_new = vim_account_new_schema
schema_edit = vim_account_edit_schema
vim_config_encrypted = ("admin_password", "nsx_password", "vcenter_password")
+ multiproject = True
def __init__(self, db, fs, msg):
BaseTopic.__init__(self, db, fs, msg)
- def check_conflict_on_new(self, session, indata, force=False):
+ def check_conflict_on_new(self, session, indata):
self.check_unique_name(session, indata["name"], _id=None)
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
- if not force and edit_content.get("name"):
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
+ if not session["force"] and edit_content.get("name"):
self.check_unique_name(session, edit_content["name"], _id=_id)
# encrypt passwords
content["_admin"]["operationalState"] = "PROCESSING"
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
"""
Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
:param dry_run: make checking but do not delete
:return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
"""
# TODO add admin to filter, validate rights
- if dry_run or force: # delete completely
- return BaseTopic.delete(self, session, _id, force, dry_run)
+ if dry_run or session["force"]: # delete completely
+ return BaseTopic.delete(self, session, _id, dry_run)
else: # if not, sent to kafka
- v = BaseTopic.delete(self, session, _id, force, dry_run=True)
+ v = BaseTopic.delete(self, session, _id, dry_run=True)
self.db.set_one("vim_accounts", {"_id": _id}, {"_admin.to_delete": True}) # TODO change status
self._send_msg("delete", {"_id": _id})
return v # TODO indicate an offline operation to return 202 ACCEPTED
topic_msg = "wim_account"
schema_new = wim_account_new_schema
schema_edit = wim_account_edit_schema
+ multiproject = True
wim_config_encrypted = ()
def __init__(self, db, fs, msg):
BaseTopic.__init__(self, db, fs, msg)
- def check_conflict_on_new(self, session, indata, force=False):
+ def check_conflict_on_new(self, session, indata):
self.check_unique_name(session, indata["name"], _id=None)
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
- if not force and edit_content.get("name"):
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
+ if not session["force"] and edit_content.get("name"):
self.check_unique_name(session, edit_content["name"], _id=_id)
# encrypt passwords
content["_admin"]["operationalState"] = "PROCESSING"
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
"""
Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
:param dry_run: make checking but do not delete
:return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
"""
# TODO add admin to filter, validate rights
- if dry_run or force: # delete completely
- return BaseTopic.delete(self, session, _id, force, dry_run)
+ if dry_run or session["force"]: # delete completely
+ return BaseTopic.delete(self, session, _id, dry_run)
else: # if not, sent to kafka
- v = BaseTopic.delete(self, session, _id, force, dry_run=True)
+ v = BaseTopic.delete(self, session, _id, dry_run=True)
self.db.set_one("wim_accounts", {"_id": _id}, {"_admin.to_delete": True}) # TODO change status
self._send_msg("delete", {"_id": _id})
return v # TODO indicate an offline operation to return 202 ACCEPTED
topic_msg = "sdn"
schema_new = sdn_new_schema
schema_edit = sdn_edit_schema
+ multiproject = True
def __init__(self, db, fs, msg):
BaseTopic.__init__(self, db, fs, msg)
- def check_conflict_on_new(self, session, indata, force=False):
+ def check_conflict_on_new(self, session, indata):
self.check_unique_name(session, indata["name"], _id=None)
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
- if not force and edit_content.get("name"):
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
+ if not session["force"] and edit_content.get("name"):
self.check_unique_name(session, edit_content["name"], _id=_id)
# encrypt passwords
content["_admin"]["operationalState"] = "PROCESSING"
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
"""
Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
:param dry_run: make checking but do not delete
:return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
"""
- if dry_run or force: # delete completely
- return BaseTopic.delete(self, session, _id, force, dry_run)
+ if dry_run or session["force"]: # delete completely
+ return BaseTopic.delete(self, session, _id, dry_run)
else: # if not sent to kafka
- v = BaseTopic.delete(self, session, _id, force, dry_run=True)
+ v = BaseTopic.delete(self, session, _id, dry_run=True)
self.db.set_one("sdns", {"_id": _id}, {"_admin.to_delete": True}) # TODO change status
self._send_msg("delete", {"_id": _id})
return v # TODO indicate an offline operation to return 202 ACCEPTED
class UserTopicAuth(UserTopic):
- topic = "users"
- topic_msg = "users"
- schema_new = user_new_schema
- schema_edit = user_edit_schema
+ # topic = "users"
+ # topic_msg = "users"
+ # schema_new = user_new_schema
+ # schema_edit = user_edit_schema
def __init__(self, db, fs, msg, auth):
UserTopic.__init__(self, db, fs, msg)
self.auth = auth
- def check_conflict_on_new(self, session, indata, force=False):
+ def check_conflict_on_new(self, session, indata):
"""
Check that the data to be inserted is valid
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
- :param force: boolean. With force it is more tolerant
:return: None or raises EngineException
"""
username = indata.get("username")
if username in user_list:
raise EngineException("username '{}' exists".format(username), HTTPStatus.CONFLICT)
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
"""
Check that the data to be edited/uploaded is valid
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param final_content: data once modified
:param edit_content: incremental data that contains the modifications to apply
:param _id: internal _id
- :param force: boolean. With force it is more tolerant
:return: None or raises EngineException
"""
users = self.auth.get_user_list()
raise EngineException("You cannot remove system_admin role from admin user",
http_code=HTTPStatus.FORBIDDEN)
- def check_conflict_on_del(self, session, _id, force=False):
+ def check_conflict_on_del(self, session, _id):
"""
Check if deletion can be done because of dependencies if it is not force. To override
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: internal _id
- :param force: Avoid this checking
:return: None if ok or raises EngineException with the conflict
"""
if _id == session["username"]:
else:
final_content["project_role_mappings"] = edit_content["project_role_mappings"]
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
"""
Creates a new entry into the authentication backend.
NOTE: Overrides BaseTopic functionality because it doesn't require access to database.
:param rollback: list to append created items at database in case a rollback may to be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created item public to all projects
:return: _id: identity of the inserted data.
"""
try:
# Override descriptor with query string kwargs
BaseTopic._update_input_with_kwargs(content, kwargs)
- content = self._validate_input_new(content, force)
- self.check_conflict_on_new(session, content, force=force)
- self.format_on_new(content, project_id=session["project_id"], make_public=make_public)
+ content = self._validate_input_new(content, session["force"])
+ self.check_conflict_on_new(session, content)
+ self.format_on_new(content, session["project_id"], make_public=session["public"])
_id = self.auth.create_user(content["username"], content["password"])
rollback.append({"topic": self.topic, "_id": _id})
del content["password"]
"""
Get complete information on an topic
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
:return: dictionary, raise exception if not found.
"""
else:
raise EngineException("User not found", HTTPStatus.NOT_FOUND)
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
"""
Updates an user entry.
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id:
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
- :param force: If True avoid some dependence checks
:param content:
:return: _id: identity of the inserted data.
"""
if kwargs:
BaseTopic._update_input_with_kwargs(indata, kwargs)
try:
- indata = self._validate_input_edit(indata, force=force)
+ indata = self._validate_input_edit(indata, force=session["force"])
if not content:
content = self.show(session, _id)
- self.check_conflict_on_edit(session, content, indata, _id=_id, force=force)
+ self.check_conflict_on_edit(session, content, indata, _id=_id)
self.format_on_edit(content, indata)
if "password" in content:
def list(self, session, filter_q=None):
"""
Get a list of the topic that matches a filter
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param filter_q: filter of data to be applied
:return: The list, it can be empty if no one match the filter.
"""
return self.auth.get_user_list()
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
"""
Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
:param force: indicates if deletion must be forced in case of conflict
:param dry_run: make checking but do not delete
:return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
"""
- self.check_conflict_on_del(session, _id, force)
+ self.check_conflict_on_del(session, _id)
if not dry_run:
v = self.auth.delete_user(_id)
return v
class ProjectTopicAuth(ProjectTopic):
- topic = "projects"
- topic_msg = "projects"
- schema_new = project_new_schema
- schema_edit = project_edit_schema
+ # topic = "projects"
+ # topic_msg = "projects"
+ # schema_new = project_new_schema
+ # schema_edit = project_edit_schema
def __init__(self, db, fs, msg, auth):
ProjectTopic.__init__(self, db, fs, msg)
self.auth = auth
- def check_conflict_on_new(self, session, indata, force=False):
+ def check_conflict_on_new(self, session, indata):
"""
Check that the data to be inserted is valid
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
- :param force: boolean. With force it is more tolerant
:return: None or raises EngineException
"""
project = indata.get("name")
if project in project_list:
raise EngineException("project '{}' exists".format(project), HTTPStatus.CONFLICT)
- def check_conflict_on_del(self, session, _id, force=False):
+ def check_conflict_on_del(self, session, _id):
"""
Check if deletion can be done because of dependencies if it is not force. To override
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: internal _id
- :param force: Avoid this checking
:return: None if ok or raises EngineException with the conflict
"""
projects = self.auth.get_project_list()
if _id == current_project["_id"]:
raise EngineException("You cannot delete your own project", http_code=HTTPStatus.CONFLICT)
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
"""
Creates a new entry into the authentication backend.
NOTE: Overrides BaseTopic functionality because it doesn't require access to database.
:param rollback: list to append created items at database in case a rollback may to be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created item public to all projects
:return: _id: identity of the inserted data.
"""
try:
# Override descriptor with query string kwargs
BaseTopic._update_input_with_kwargs(content, kwargs)
- content = self._validate_input_new(content, force)
- self.check_conflict_on_new(session, content, force=force)
- self.format_on_new(content, project_id=session["project_id"], make_public=make_public)
+ content = self._validate_input_new(content, session["force"])
+ self.check_conflict_on_new(session, content)
+ self.format_on_new(content, project_id=session["project_id"], make_public=session["public"])
_id = self.auth.create_project(content["name"])
rollback.append({"topic": self.topic, "_id": _id})
# self._send_msg("create", content)
"""
Get complete information on an topic
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
:return: dictionary, raise exception if not found.
"""
"""
Get a list of the topic that matches a filter
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param filter_q: filter of data to be applied
:return: The list, it can be empty if no one match the filter.
"""
return self.auth.get_project_list()
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
"""
Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
:param dry_run: make checking but do not delete
:return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
"""
- self.check_conflict_on_del(session, _id, force)
+ self.check_conflict_on_del(session, _id)
if not dry_run:
v = self.auth.delete_project(_id)
return v
topic_msg = "roles"
schema_new = roles_new_schema
schema_edit = roles_edit_schema
+ multiproject = False
def __init__(self, db, fs, msg, auth, ops):
BaseTopic.__init__(self, db, fs, msg)
self.validate_role_definition(self.operations, input["definition"])
return input
- def check_conflict_on_new(self, session, indata, force=False):
+ def check_conflict_on_new(self, session, indata):
"""
Check that the data to be inserted is valid
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
- :param force: boolean. With force it is more tolerant
:return: None or raises EngineException
"""
role = indata.get("name")
if role in role_list:
raise EngineException("role '{}' exists".format(role), HTTPStatus.CONFLICT)
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
"""
Check that the data to be edited/uploaded is valid
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param final_content: data once modified
:param edit_content: incremental data that contains the modifications to apply
:param _id: internal _id
- :param force: boolean. With force it is more tolerant
:return: None or raises EngineException
"""
roles = self.auth.get_role_list()
if _id == system_admin_role["_id"]:
raise EngineException("You cannot edit system_admin role", http_code=HTTPStatus.FORBIDDEN)
- def check_conflict_on_del(self, session, _id, force=False):
+ def check_conflict_on_del(self, session, _id):
"""
Check if deletion can be done because of dependencies if it is not force. To override
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: internal _id
- :param force: Avoid this checking
:return: None if ok or raises EngineException with the conflict
"""
roles = self.auth.get_role_list()
"""
Get complete information on an topic
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
:return: dictionary, raise exception if not found.
"""
"""
Get a list of the topic that matches a filter
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param filter_q: filter of data to be applied
:return: The list, it can be empty if no one match the filter.
"""
return new_roles
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
"""
Creates a new entry into database.
:param rollback: list to append created items at database in case a rollback may to be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created item public to all projects
:return: _id: identity of the inserted data.
"""
try:
# Override descriptor with query string kwargs
BaseTopic._update_input_with_kwargs(content, kwargs)
- content = self._validate_input_new(content, force)
- self.check_conflict_on_new(session, content, force=force)
- self.format_on_new(content, project_id=session["project_id"], make_public=make_public)
+ content = self._validate_input_new(content, session["force"])
+ self.check_conflict_on_new(session, content)
+ self.format_on_new(content, project_id=session["project_id"], make_public=session["public"])
role_name = content["name"]
role = self.auth.create_role(role_name)
content["_id"] = role["_id"]
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
"""
Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
:param dry_run: make checking but do not delete
:return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
"""
- self.check_conflict_on_del(session, _id, force)
+ self.check_conflict_on_del(session, _id)
filter_q = self._get_project_filter(session, write=True, show_all=True)
filter_q["_id"] = _id
if not dry_run:
return v
return None
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
"""
Updates a role entry.
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id:
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
- :param force: If True avoid some dependence checks
:param content:
:return: _id: identity of the inserted data.
"""
if kwargs:
BaseTopic._update_input_with_kwargs(indata, kwargs)
try:
- indata = self._validate_input_edit(indata, force=force)
+ indata = self._validate_input_edit(indata, force=session["force"])
if not content:
content = self.show(session, _id)
- self.check_conflict_on_edit(session, content, indata, _id=_id, force=force)
+ self.check_conflict_on_edit(session, content, indata, _id=_id)
self.format_on_edit(content, indata)
self.db.replace(self.topic, _id, content)
return id
Authorization. Initially it should support Openstack Keystone as a
backend through a plugin model where more backends can be added and a
RBAC model to manage permissions on operations.
+ This class must be threading safe
"""
periodin_db_pruning = 60 * 30 # for the internal backend only. every 30 minutes expired tokens will be pruned
now = time()
session = self.tokens_cache.get(token_id)
if session and session["expires"] < now:
- del self.tokens_cache[token_id]
+ # delete token. MUST be done with care, as another thread maybe already delete it. Do not use del
+ self.tokens_cache.pop(token_id, None)
session = None
if session:
return session
if self.config["global"].get("test.user_not_authorized"):
return {"id": "fake-token-id-for-test",
"project_id": self.config["global"].get("test.project_not_authorized", "admin"),
- "username": self.config["global"]["test.user_not_authorized"]}
+ "username": self.config["global"]["test.user_not_authorized"], "admin": True}
else:
raise
topic_msg = None # to_override
schema_new = None # to_override
schema_edit = None # to_override
+ multiproject = True # True if this Topic can be shared by several projects. Then it contains _admin.projects_read
# Alternative ID Fields for some Topics
alt_id_field = {
@staticmethod
def id_field(topic, value):
- "Returns ID Field for given topic and field value"
+ """Returns ID Field for given topic and field value"""
if topic in ["projects", "users"] and not is_valid_uuid(value):
return BaseTopic.alt_id_field[topic]
else:
return input
@staticmethod
- def _get_project_filter(session, write=False, show_all=True):
+ def _get_project_filter(session):
"""
Generates a filter dictionary for querying database, so that only allowed items for this project can be
addressed. Only propietary or public can be used. Allowed projects are at _admin.project_read/write. If it is
not present or contains ANY mean public.
- :param session: contains "username", if user is "admin" and the working "project_id"
- :param write: if operation is for reading (False) or writing (True)
- :param show_all: if True it will show public or
- :return:
- """
- if write:
- k = "_admin.projects_write.cont"
- else:
- k = "_admin.projects_read.cont"
- if not show_all:
- return {k: session["project_id"]}
- elif session["admin"]: # and show_all: # allows all
- return {}
- else:
- return {k: ["ANY", session["project_id"], None]}
-
- def check_conflict_on_new(self, session, indata, force=False):
+ :param session: contains:
+ project_id: project list this session has rights to access. Can be empty, one or several
+ set_project: items created will contain this project list
+ force: True or False
+ public: True, False or None
+ method: "list", "show", "write", "delete"
+ admin: True or False
+ :return: dictionary with project filter
+ """
+ p_filter = {}
+ project_filter_n = []
+ project_filter = list(session["project_id"])
+
+ if session["method"] not in ("list", "delete"):
+ if project_filter:
+ project_filter.append("ANY")
+ elif session["public"] is not None:
+ if session["public"]:
+ project_filter.append("ANY")
+ else:
+ project_filter_n.append("ANY")
+
+ if session.get("PROJECT.ne"):
+ project_filter_n.append(session["PROJECT.ne"])
+
+ if project_filter:
+ if session["method"] in ("list", "show", "delete") or session.get("set_project"):
+ p_filter["_admin.projects_read.cont"] = project_filter
+ else:
+ p_filter["_admin.projects_write.cont"] = project_filter
+ if project_filter_n:
+ if session["method"] in ("list", "show", "delete") or session.get("set_project"):
+ p_filter["_admin.projects_read.ncont"] = project_filter_n
+ else:
+ p_filter["_admin.projects_write.ncont"] = project_filter_n
+
+ return p_filter
+
+ def check_conflict_on_new(self, session, indata):
"""
Check that the data to be inserted is valid
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
- :param force: boolean. With force it is more tolerant
:return: None or raises EngineException
"""
pass
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
"""
Check that the data to be edited/uploaded is valid
- :param session: contains "username", if user is "admin" and the working "project_id"
- :param final_content: data once modified
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+ :param final_content: data once modified. This methdo may change it.
:param edit_content: incremental data that contains the modifications to apply
:param _id: internal _id
- :param force: boolean. With force it is more tolerant
:return: None or raises EngineException
"""
- pass
+ if not self.multiproject:
+ return
+ # Change public status
+ if session["public"] is not None:
+ if session["public"] and "ANY" not in final_content["_admin"]["projects_read"]:
+ final_content["_admin"]["projects_read"].append("ANY")
+ final_content["_admin"]["projects_write"].clear()
+ if not session["public"] and "ANY" in final_content["_admin"]["projects_read"]:
+ final_content["_admin"]["projects_read"].remove("ANY")
+
+ # Change project status
+ if session.get("set_project"):
+ for p in session["set_project"]:
+ if p not in final_content["_admin"]["projects_read"]:
+ final_content["_admin"]["projects_read"].append(p)
def check_unique_name(self, session, name, _id=None):
"""
Check that the name is unique for this project
- :param session: contains "username", if user is "admin" and the working "project_id"
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param name: name to be checked
:param _id: If not None, ignore this entry that are going to change
:return: None or raises EngineException
"""
- _filter = self._get_project_filter(session, write=False, show_all=False)
+ _filter = self._get_project_filter(session)
_filter["name"] = name
if _id:
_filter["_id.neq"] = _id
"""
Modifies content descriptor to include _admin
:param content: descriptor to be modified
- :param project_id: if included, it add project read/write permissions
+ :param project_id: if included, it add project read/write permissions. Can be None or a list
:param make_public: if included it is generated as public for reading.
:return: None, but content is modified
"""
content["_admin"]["modified"] = now
if not content.get("_id"):
content["_id"] = str(uuid4())
- if project_id:
+ if project_id is not None:
if not content["_admin"].get("projects_read"):
- content["_admin"]["projects_read"] = [project_id]
+ content["_admin"]["projects_read"] = list(project_id)
if make_public:
content["_admin"]["projects_read"].append("ANY")
if not content["_admin"].get("projects_write"):
- content["_admin"]["projects_write"] = [project_id]
+ content["_admin"]["projects_write"] = list(project_id)
@staticmethod
def format_on_edit(final_content, edit_content):
content.pop("_admin", None)
self.msg.write(self.topic_msg, action, content)
- def check_conflict_on_del(self, session, _id, force=False):
+ def check_conflict_on_del(self, session, _id):
"""
Check if deletion can be done because of dependencies if it is not force. To override
- :param session: contains "username", if user is "admin" and the working "project_id"
- :param _id: itnernal _id
- :param force: Avoid this checking
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+ :param _id: internal _id
:return: None if ok or raises EngineException with the conflict
"""
pass
def show(self, session, _id):
"""
Get complete information on an topic
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
:return: dictionary, raise exception if not found.
"""
- filter_db = self._get_project_filter(session, write=False, show_all=True)
+ filter_db = self._get_project_filter(session)
# To allow project&user addressing by name AS WELL AS _id
filter_db[BaseTopic.id_field(self.topic, _id)] = _id
return self.db.get_one(self.topic, filter_db)
def get_file(self, session, _id, path=None, accept_header=None):
"""
Only implemented for descriptor topics. Return the file content of a descriptor
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: Identity of the item to get content
:param path: artifact path or "$DESCRIPTOR" or None
:param accept_header: Content of Accept header. Must contain applition/zip or/and text/plain
if not filter_q:
filter_q = {}
- filter_q.update(self._get_project_filter(session, write=False, show_all=True))
+ filter_q.update(self._get_project_filter(session))
# TODO transform data for SOL005 URL requests. Transform filtering
# TODO implement "field-type" query string SOL005
return self.db.get_list(self.topic, filter_q)
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
"""
Creates a new entry into database.
:param rollback: list to append created items at database in case a rollback may to be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created item public to all projects
:return: _id: identity of the inserted data.
"""
try:
# Override descriptor with query string kwargs
self._update_input_with_kwargs(content, kwargs)
- content = self._validate_input_new(content, force=force)
- self.check_conflict_on_new(session, content, force=force)
- self.format_on_new(content, project_id=session["project_id"], make_public=make_public)
+ content = self._validate_input_new(content, force=session["force"])
+ self.check_conflict_on_new(session, content)
+ self.format_on_new(content, project_id=session["project_id"], make_public=session["public"])
_id = self.db.create(self.topic, content)
rollback.append({"topic": self.topic, "_id": _id})
self._send_msg("create", content)
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
- def upload_content(self, session, _id, indata, kwargs, headers, force=False):
+ def upload_content(self, session, _id, indata, kwargs, headers):
"""
Only implemented for descriptor topics. Used for receiving content by chunks (with a transaction_id header
and/or gzip file. It will store and extract)
- :param session: session
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id : the database id of entry to be updated
:param indata: http body request
:param kwargs: user query string to override parameters. NOT USED
:param headers: http request headers
- :param force: to be more tolerant with validation
:return: True package has is completely uploaded or False if partial content has been uplodaed.
Raise exception on error
"""
def delete_list(self, session, filter_q=None):
"""
Delete a several entries of a topic. This is for internal usage and test only, not exposed to NBI API
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param filter_q: filter of data to be applied
:return: The deleted list, it can be empty if no one match the filter.
"""
# TODO add admin to filter, validate rights
if not filter_q:
filter_q = {}
- filter_q.update(self._get_project_filter(session, write=True, show_all=True))
+ filter_q.update(self._get_project_filter(session))
return self.db.del_list(self.topic, filter_q)
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete_extra(self, session, _id):
+ """
+ Delete other things apart from database entry of a item _id.
+ e.g.: other associated elements at database and other file system storage
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+ :param _id: server internal id
+ """
+ pass
+
+ def delete(self, session, _id, dry_run=False):
"""
Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
:param dry_run: make checking but do not delete
:return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
"""
# TODO add admin to filter, validate rights
# data = self.get_item(topic, _id)
- self.check_conflict_on_del(session, _id, force)
- filter_q = self._get_project_filter(session, write=True, show_all=True)
+ self.check_conflict_on_del(session, _id)
+ filter_q = self._get_project_filter(session)
# To allow project addressing by name AS WELL AS _id
filter_q[BaseTopic.id_field(self.topic, _id)] = _id
- if not dry_run:
+ if dry_run:
+ return None
+ if self.multiproject and session["project_id"]:
+ # remove reference from project_read. If not last delete
+ self.db.set_one(self.topic, filter_q, update_dict=None,
+ pull={"_admin.projects_read": {"$in": session["project_id"]}})
+ # try to delete if there is not any more reference from projects. Ignore if it is not deleted
+ filter_q = {'_id': _id, '_admin.projects_read': [[], ["ANY"]]}
+ v = self.db.del_one(self.topic, filter_q, fail_on_empty=False)
+ if not v or not v["deleted"]:
+ return v
+ else:
v = self.db.del_one(self.topic, filter_q)
- self._send_msg("deleted", {"_id": _id})
- return v
- return None
+ self.delete_extra(session, _id)
+ self._send_msg("deleted", {"_id": _id})
+ return v
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
+ """
+ Change the content of an item
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+ :param _id: server internal id
+ :param indata: contains the changes to apply
+ :param kwargs: modifies indata
+ :param content: original content of the item
+ :return:
+ """
indata = self._remove_envelop(indata)
# Override descriptor with query string kwargs
if kwargs:
self._update_input_with_kwargs(indata, kwargs)
try:
- indata = self._validate_input_edit(indata, force=force)
+ if indata and session.get("set_project"):
+ raise EngineException("Cannot edit content and set to project (query string SET_PROJECT) at same time",
+ HTTPStatus.UNPROCESSABLE_ENTITY)
+ indata = self._validate_input_edit(indata, force=session["force"])
# TODO self._check_edition(session, indata, _id, force)
if not content:
content = self.show(session, _id)
deep_update_rfc7396(content, indata)
- self.check_conflict_on_edit(session, content, indata, _id=_id, force=force)
+ self.check_conflict_on_edit(session, content, indata, _id=_id)
self.format_on_edit(content, indata)
# To allow project addressing by name AS WELL AS _id
# self.db.replace(self.topic, _id, content)
indata.pop("_admin", None)
indata["_id"] = _id
self._send_msg("edit", indata)
- return id
+ return _id
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
def __init__(self, db, fs, msg):
BaseTopic.__init__(self, db, fs, msg)
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
+ super().check_conflict_on_edit(session, final_content, edit_content, _id)
# 1. validate again with pyangbind
# 1.1. remove internal keys
internal_keys = {}
if k in final_content:
internal_keys[k] = final_content.pop(k)
storage_params = internal_keys["_admin"].get("storage")
- serialized = self._validate_input_new(final_content, storage_params, force)
+ serialized = self._validate_input_new(final_content, storage_params, session["force"])
# 1.2. modify final_content with a serialized version
final_content.clear()
final_content.update(serialized)
for k, v in internal_keys.items():
final_content[k] = v
- if force:
+ if session["force"]:
return
# 2. check that this id is not present
if "id" in edit_content:
- _filter = self._get_project_filter(session, write=False, show_all=False)
+ _filter = self._get_project_filter(session)
_filter["id"] = final_content["id"]
_filter["_id.neq"] = _id
if self.db.get_one(self.topic, _filter, fail_on_empty=False):
content["_admin"]["operationalState"] = "DISABLED"
content["_admin"]["usageState"] = "NOT_IN_USE"
- def delete(self, session, _id, force=False, dry_run=False):
- """
- Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
- :param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
- :param dry_run: make checking but do not delete
- :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
- """
- # TODO add admin to filter, validate rights
- v = BaseTopic.delete(self, session, _id, force, dry_run=True)
- if dry_run:
- return
- v = self.db.del_one(self.topic, {"_id": _id})
+ def delete_extra(self, session, _id):
self.fs.file_delete(_id, ignore_non_exist=True)
self.fs.file_delete(_id + "_", ignore_non_exist=True) # remove temp folder
- self._send_msg("delete", {"_id": _id})
- return v
@staticmethod
def get_one_by_id(db, session, topic, id):
# find owned by this project
- _filter = BaseTopic._get_project_filter(session, write=False, show_all=False)
+ _filter = BaseTopic._get_project_filter(session)
_filter["id"] = id
desc_list = db.get_list(topic, _filter)
if len(desc_list) == 1:
HTTPStatus.CONFLICT)
# not found any: try to find public
- _filter = BaseTopic._get_project_filter(session, write=False, show_all=True)
+ _filter = BaseTopic._get_project_filter(session)
_filter["id"] = id
desc_list = db.get_list(topic, _filter)
if not desc_list:
raise DbException("Found more than one public {} with id='{}'; and no one belonging to this project".format(
topic[:-1], id), HTTPStatus.CONFLICT)
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
"""
Creates a new almost empty DISABLED entry into database. Due to SOL005, it does not follow normal procedure.
Creating a VNFD or NSD is done in two steps: 1. Creates an empty descriptor (this step) and 2) upload content
(self.upload_content)
:param rollback: list to append created items at database in case a rollback may to be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created descriptor public to all projects
:return: _id: identity of the inserted data.
"""
self._update_input_with_kwargs(indata, kwargs)
# uncomment when this method is implemented.
# Avoid override in this case as the target is userDefinedData, but not vnfd,nsd descriptors
- # indata = DescriptorTopic._validate_input_new(self, indata, force=force)
+ # indata = DescriptorTopic._validate_input_new(self, indata, project_id=session["force"])
content = {"_admin": {"userDefinedData": indata}}
- self.format_on_new(content, session["project_id"], make_public=make_public)
+ self.format_on_new(content, session["project_id"], make_public=session["public"])
_id = self.db.create(self.topic, content)
rollback.append({"topic": self.topic, "_id": _id})
return _id
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
- def upload_content(self, session, _id, indata, kwargs, headers, force=False):
+ def upload_content(self, session, _id, indata, kwargs, headers):
"""
Used for receiving content by chunks (with a transaction_id header and/or gzip file. It will store and extract)
- :param session: session
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id : the nsd,vnfd is already created, this is the id
:param indata: http body request
:param kwargs: user query string to override parameters. NOT USED
:param headers: http request headers
- :param force: to be more tolerant with validation
:return: True if package is completely uploaded or False if partial content has been uploded
Raise exception on error
"""
if kwargs:
self._update_input_with_kwargs(indata, kwargs)
# it will call overrides method at VnfdTopic or NsdTopic
- # indata = self._validate_input_edit(indata, force=force)
+ # indata = self._validate_input_edit(indata, force=session["force"])
deep_update_rfc7396(current_desc, indata)
- self.check_conflict_on_edit(session, current_desc, indata, _id=_id, force=force)
+ self.check_conflict_on_edit(session, current_desc, indata, _id=_id)
self.db.replace(self.topic, _id, current_desc)
self.fs.dir_rename(temp_folder, _id)
def get_file(self, session, _id, path=None, accept_header=None):
"""
Return the file content of a vnfd or nsd
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: Identity of the vnfd, nsd
:param path: artifact path or "$DESCRIPTOR" or None
:param accept_header: Content of Accept header. Must contain applition/zip or/and text/plain
clean_indata = clean_indata['vnfd:vnfd'][0]
return clean_indata
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
- super().check_conflict_on_edit(session, final_content, edit_content, _id, force=force)
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
+ super().check_conflict_on_edit(session, final_content, edit_content, _id)
# set type of vnfd
contains_pdu = False
final_content["_admin"]["type"] = "vnfd"
# if neither vud nor pdu do not fill type
- def check_conflict_on_del(self, session, _id, force=False):
+ def check_conflict_on_del(self, session, _id):
"""
Check that there is not any NSD that uses this VNFD. Only NSDs belonging to this project are considered. Note
that VNFD can be public and be used by NSD of other projects. Also check there are not deployments, or vnfr
that uses this vnfd
- :param session:
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: vnfd inernal id
- :param force: Avoid this checking
:return: None or raises EngineException with the conflict
"""
- if force:
+ if session["force"]:
return
descriptor = self.db.get_one("vnfds", {"_id": _id})
descriptor_id = descriptor.get("id")
if not descriptor_id: # empty vnfd not uploaded
return
- _filter = self._get_project_filter(session, write=False, show_all=False)
+ _filter = self._get_project_filter(session)
# check vnfrs using this vnfd
_filter["vnfd-id"] = _id
if self.db.get_list("vnfrs", _filter):
# check NSD using this VNFD
_filter["constituent-vnfd.ANYINDEX.vnfd-id-ref"] = descriptor_id
if self.db.get_list("nsds", _filter):
- raise EngineException("There is soame NSD that depends on this VNFD", http_code=HTTPStatus.CONFLICT)
+ raise EngineException("There is at least a NSD that depends on this VNFD", http_code=HTTPStatus.CONFLICT)
def _validate_input_new(self, indata, storage_params, force=False):
indata = self.pyangbind_validation("vnfds", indata, force)
# not needed to validate with pyangbind becuase it will be validated at check_conflict_on_edit
return indata
- def _check_descriptor_dependencies(self, session, descriptor, force=False):
+ def _check_descriptor_dependencies(self, session, descriptor):
"""
Check that the dependent descriptors exist on a new descriptor or edition. Also checks references to vnfd
connection points are ok
- :param session: client session information
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param descriptor: descriptor to be inserted or edit
- :param force: if true skip dependencies checking
:return: None or raises exception
"""
- if force:
+ if session["force"]:
return
member_vnfd_index = {}
- if descriptor.get("constituent-vnfd") and not force:
+ if descriptor.get("constituent-vnfd") and not session["force"]:
for vnf in descriptor["constituent-vnfd"]:
vnfd_id = vnf["vnfd-id-ref"]
- filter_q = self._get_project_filter(session, write=False, show_all=True)
+ filter_q = self._get_project_filter(session)
filter_q["id"] = vnfd_id
vnf_list = self.db.get_list("vnfds", filter_q)
if not vnf_list:
referenced_vnfd_cp["vnfd-connection-point-ref"], vnfd["id"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
- super().check_conflict_on_edit(session, final_content, edit_content, _id, force=force)
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
+ super().check_conflict_on_edit(session, final_content, edit_content, _id)
- self._check_descriptor_dependencies(session, final_content, force)
+ self._check_descriptor_dependencies(session, final_content)
- def check_conflict_on_del(self, session, _id, force=False):
+ def check_conflict_on_del(self, session, _id):
"""
Check that there is not any NSR that uses this NSD. Only NSRs belonging to this project are considered. Note
that NSD can be public and be used by other projects.
- :param session:
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: vnfd inernal id
- :param force: Avoid this checking
:return: None or raises EngineException with the conflict
"""
- if force:
+ if session["force"]:
return
- _filter = self._get_project_filter(session, write=False, show_all=False)
+ _filter = self._get_project_filter(session)
_filter["nsdId"] = _id
if self.db.get_list("nsrs", _filter):
raise EngineException("There is some NSR that depends on this NSD", http_code=HTTPStatus.CONFLICT)
def _check_descriptor_dependencies(self, session, descriptor):
"""
Check that the dependent descriptors exist on a new descriptor or edition
- :param session: client session information
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param descriptor: descriptor to be inserted or edit
:return: None or raises exception
"""
return
for nsd in descriptor["netslice-subnet"]:
nsd_id = nsd["nsd-ref"]
- filter_q = self._get_project_filter(session, write=False, show_all=True)
+ filter_q = self._get_project_filter(session)
filter_q["id"] = nsd_id
if not self.db.get_list("nsds", filter_q):
raise EngineException("Descriptor error at 'netslice-subnet':'nsd-ref'='{}' references a non "
"existing nsd".format(nsd_id), http_code=HTTPStatus.CONFLICT)
- def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False):
- super().check_conflict_on_edit(session, final_content, edit_content, _id, force=force)
+ def check_conflict_on_edit(self, session, final_content, edit_content, _id):
+ super().check_conflict_on_edit(session, final_content, edit_content, _id)
self._check_descriptor_dependencies(session, final_content)
- def check_conflict_on_del(self, session, _id, force=False):
+ def check_conflict_on_del(self, session, _id):
"""
Check that there is not any NSIR that uses this NST. Only NSIRs belonging to this project are considered. Note
that NST can be public and be used by other projects.
- :param session:
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: nst internal id
- :param force: Avoid this checking
:return: None or raises EngineException with the conflict
"""
# TODO: Check this method
- if force:
+ if session["force"]:
return
# Get Network Slice Template from Database
- _filter = self._get_project_filter(session, write=False, show_all=False)
+ _filter = self._get_project_filter(session)
_filter["_id"] = _id
nst = self.db.get_one("nsts", _filter)
# Search NSIs using NST via nst-ref
- _filter = self._get_project_filter(session, write=False, show_all=False)
+ _filter = self._get_project_filter(session)
_filter["nst-ref"] = nst["id"]
nsis_list = self.db.get_list("nsis", _filter)
for nsi_item in nsis_list:
content["_admin"]["operationalState"] = "ENABLED"
content["_admin"]["usageState"] = "NOT_IN_USE"
- def check_conflict_on_del(self, session, _id, force=False):
- if force:
+ def check_conflict_on_del(self, session, _id):
+ if session["force"]:
return
# TODO Is it needed to check descriptors _admin.project_read/project_write??
_filter = {"vdur.pdu-id": _id}
except (DbException, FsException, MsgException) as e:
raise EngineException(str(e), http_code=e.http_code)
- def new_item(self, rollback, session, topic, indata=None, kwargs=None, headers=None, force=False):
+ def new_item(self, rollback, session, topic, indata=None, kwargs=None, headers=None):
"""
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 rollback: list to append created items at database in case a rollback must to be done
- :param session: contains the used login username and working project
+ :param session: contains the used login username and working project, force to avoid checkins, public
:param topic: it can be: users, projects, vim_accounts, sdns, nsrs, nsds, vnfds
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
:return: _id: identity of the inserted data.
"""
if topic not in self.map_topic:
raise EngineException("Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR)
with self.write_lock:
- return self.map_topic[topic].new(rollback, session, indata, kwargs, headers, force)
+ return self.map_topic[topic].new(rollback, session, indata, kwargs, headers)
- def upload_content(self, session, topic, _id, indata, kwargs, headers, force=False):
+ def upload_content(self, session, topic, _id, indata, kwargs, headers):
"""
Upload content for an already created entry (_id)
:param session: contains the used login username and working project
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
:return: _id: identity of the inserted data.
"""
if topic not in self.map_topic:
raise EngineException("Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR)
with self.write_lock:
- return self.map_topic[topic].upload_content(session, _id, indata, kwargs, headers, force)
+ return self.map_topic[topic].upload_content(session, _id, indata, kwargs, headers)
def get_item_list(self, session, topic, filter_q=None):
"""
with self.write_lock:
return self.map_topic[topic].delete_list(session, _filter)
- def del_item(self, session, topic, _id, force=False):
+ def del_item(self, session, topic, _id):
"""
Delete item by its internal id
:param session: contains the used login username and working project
:param topic: it can be: users, projects, vnfds, nsds, ...
:param _id: server id of the item
- :param force: indicates if deletion must be forced in case of conflict
:return: dictionary with deleted item _id. It raises exception if not found.
"""
if topic not in self.map_topic:
raise EngineException("Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR)
with self.write_lock:
- return self.map_topic[topic].delete(session, _id, force)
+ return self.map_topic[topic].delete(session, _id)
- def edit_item(self, session, topic, _id, indata=None, kwargs=None, force=False):
+ def edit_item(self, session, topic, _id, indata=None, kwargs=None):
"""
Update an existing entry at database
:param session: contains the used login username and working project
:param _id: identifier to be updated
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
- :param force: If True avoid some dependence checks
:return: dictionary, raise exception if not found.
"""
if topic not in self.map_topic:
raise EngineException("Unknown topic {}!!!".format(topic), HTTPStatus.INTERNAL_SERVER_ERROR)
with self.write_lock:
- return self.map_topic[topic].edit(session, _id, indata, kwargs, force)
+ return self.map_topic[topic].edit(session, _id, indata, kwargs)
def create_admin_project(self):
"""
if projects:
return None
project_desc = {"name": "admin"}
- fake_session = {"project_id": "admin", "username": "admin", "admin": True}
+ fake_session = {"project_id": "admin", "username": "admin", "admin": True, "force": True, "public": None}
rollback_list = []
- _id = self.map_topic["projects"].new(rollback_list, fake_session, project_desc, force=True)
+ _id = self.map_topic["projects"].new(rollback_list, fake_session, project_desc)
return _id
def create_admin_user(self):
return None
# raise EngineException("Unauthorized. Database users is not empty", HTTPStatus.UNAUTHORIZED)
user_desc = {"username": "admin", "password": "admin", "projects": ["admin"]}
- fake_session = {"project_id": "admin", "username": "admin", "admin": True}
+ fake_session = {"project_id": "admin", "username": "admin", "admin": True, "force": True, "public": None}
roolback_list = []
- _id = self.map_topic["users"].new(roolback_list, fake_session, user_desc, force=True)
+ _id = self.map_topic["users"].new(roolback_list, fake_session, user_desc)
return _id
def create_admin(self):
BaseTopic.format_on_new(content, project_id=project_id, make_public=make_public)
content["_admin"]["nsState"] = "NOT_INSTANTIATED"
- def check_conflict_on_del(self, session, _id, force=False):
- if force:
+ def check_conflict_on_del(self, session, _id):
+ if session["force"]:
return
nsr = self.db.get_one("nsrs", {"_id": _id})
if nsr["_admin"].get("nsState") == "INSTANTIATED":
"Launch 'terminate' operation first; or force deletion".format(_id),
http_code=HTTPStatus.CONFLICT)
- def delete(self, session, _id, force=False, dry_run=False):
- """
- Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
- :param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
- :param dry_run: make checking but do not delete
- :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
- """
- # TODO add admin to filter, validate rights
- BaseTopic.delete(self, session, _id, force, dry_run=True)
- if dry_run:
- return
-
+ def delete_extra(self, session, _id):
self.fs.file_delete(_id, ignore_non_exist=True)
- v = self.db.del_one("nsrs", {"_id": _id})
self.db.del_list("nslcmops", {"nsInstanceId": _id})
self.db.del_list("vnfrs", {"nsr-id-ref": _id})
# set all used pdus as free
self.db.set_list("pdus", {"_admin.usage.nsr_id": _id},
{"_admin.usageState": "NOT_IN_USE", "_admin.usage": None})
- self._send_msg("deleted", {"_id": _id})
- return v
@staticmethod
def _format_ns_request(ns_request):
return additional_params
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
"""
Creates a new nsr into database. It also creates needed vnfrs
:param rollback: list to append the created items at database in case a rollback must be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: params to be used for the nsr
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created item public to all projects
:return: the _id of nsr descriptor created at database
"""
ns_request = self._remove_envelop(indata)
# Override descriptor with query string kwargs
self._update_input_with_kwargs(ns_request, kwargs)
- self._validate_input_new(ns_request, force)
+ self._validate_input_new(ns_request, session["force"])
step = ""
# look for nsr
step = "getting nsd id='{}' from database".format(ns_request.get("nsdId"))
_filter = {"_id": ns_request["nsdId"]}
- _filter.update(BaseTopic._get_project_filter(session, write=False, show_all=True))
+ _filter.update(BaseTopic._get_project_filter(session))
nsd = self.db.get_one("nsds", _filter)
nsr_id = str(uuid4())
"orchestration-progress": {},
# {"networks": {"active": 0, "total": 0}, "vms": {"active": 0, "total": 0}},
- "crete-time": now,
+ "create-time": now,
"nsd-name-ref": nsd["name"],
"operational-events": [], # "id", "timestamp", "description", "event",
"nsd-ref": nsd["id"],
member_vnf["vnfd-id-ref"], member_vnf["member-vnf-index"])
# add at database
- BaseTopic.format_on_new(vnfr_descriptor, session["project_id"], make_public=make_public)
+ BaseTopic.format_on_new(vnfr_descriptor, session["project_id"], make_public=session["public"])
self.db.create("vnfrs", vnfr_descriptor)
rollback.append({"topic": "vnfrs", "_id": vnfr_id})
nsr_descriptor["constituent-vnfr-ref"].append(vnfr_id)
step = "creating nsr at database"
- self.format_on_new(nsr_descriptor, session["project_id"], make_public=make_public)
+ self.format_on_new(nsr_descriptor, session["project_id"], make_public=session["public"])
self.db.create("nsrs", nsr_descriptor)
rollback.append({"topic": "nsrs", "_id": nsr_id})
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
def __init__(self, db, fs, msg):
BaseTopic.__init__(self, db, fs, msg)
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
raise EngineException("Method delete called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
# Not used because vnfrs are created and deleted by NsrTopic class directly
raise EngineException("Method new called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
def _check_ns_operation(self, session, nsr, operation, indata):
"""
Check that user has enter right parameters for the operation
- :param session:
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param operation: it can be: instantiate, terminate, action, TODO: update, heal
:param indata: descriptor with the parameters of the operation
:return: None
if vim_account in vim_accounts:
return
try:
- db_filter = self._get_project_filter(session, write=False, show_all=True)
+ db_filter = self._get_project_filter(session)
db_filter["_id"] = vim_account
self.db.get_one("vim_accounts", db_filter)
except Exception:
(ip_address, ...) information.
Modifies PDU _admin.usageState to 'IN_USE'
- :param session: client session information
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param rollback: list with the database modifications to rollback if needed
:param vnfr: vnfr to be updated. It is modified with pdu interface info if pdu is found
:param vim_account: vim_account where this vnfr should be deployed
if not vdur.get("pdu-type"):
continue
pdu_type = vdur.get("pdu-type")
- pdu_filter = self._get_project_filter(session, write=True, show_all=True)
+ pdu_filter = self._get_project_filter(session)
pdu_filter["vim_accounts"] = vim_account
pdu_filter["type"] = pdu_type
pdu_filter["_admin.operationalState"] = "ENABLED"
}
return nslcmop
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False,
- slice_object=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None, slice_object=False):
"""
Performs a new operation over a ns
:param rollback: list to append created items at database in case a rollback must to be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: descriptor with the parameters of the operation. It must contains among others
nsInstanceId: _id of the nsr to perform the operation
operation: it can be: instantiate, terminate, action, TODO: update, heal
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created item public to all projects
:return: id of the nslcmops
"""
try:
validate_input(indata, self.operation_schema[operation])
# get ns from nsr_id
- _filter = BaseTopic._get_project_filter(session, write=True, show_all=False)
+ _filter = BaseTopic._get_project_filter(session)
_filter["_id"] = nsInstanceId
nsr = self.db.get_one("nsrs", _filter)
raise EngineException("ns_instance '{}' cannot be '{}' because it is not instantiated".format(
nsInstanceId, operation), HTTPStatus.CONFLICT)
else:
- if operation == "instantiate" and not indata.get("force"):
+ if operation == "instantiate" and not session["force"]:
raise EngineException("ns_instance '{}' cannot be '{}' because it is already instantiated".format(
nsInstanceId, operation), HTTPStatus.CONFLICT)
self._check_ns_operation(session, nsr, operation, indata)
self._update_vnfrs(session, rollback, nsr, indata)
nslcmop_desc = self._create_nslcmop(nsInstanceId, operation, indata)
- self.format_on_new(nslcmop_desc, session["project_id"], make_public=make_public)
+ self.format_on_new(nslcmop_desc, session["project_id"], make_public=session["public"])
_id = self.db.create("nslcmops", nslcmop_desc)
rollback.append({"topic": "nslcmops", "_id": _id})
if not slice_object:
# except DbException as e:
# raise EngineException("Cannot get ns_instance '{}': {}".format(e), HTTPStatus.NOT_FOUND)
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
raise EngineException("Method delete called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
def _check_descriptor_dependencies(self, session, descriptor):
"""
Check that the dependent descriptors exist on a new descriptor or edition
- :param session: client session information
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param descriptor: descriptor to be inserted or edit
:return: None or raises exception
"""
raise EngineException("Descriptor error at nst-ref='{}' references a non exist nstd".format(nstd_id),
http_code=HTTPStatus.CONFLICT)
- @staticmethod
- def format_on_new(content, project_id=None, make_public=False):
- BaseTopic.format_on_new(content, project_id=project_id, make_public=make_public)
-
- def check_conflict_on_del(self, session, _id, force=False):
- if force:
+ def check_conflict_on_del(self, session, _id, ):
+ if session["force"]:
return
nsi = self.db.get_one("nsis", {"_id": _id})
if nsi["_admin"].get("nsiState") == "INSTANTIATED":
"Launch 'terminate' operation first; or force deletion".format(_id),
http_code=HTTPStatus.CONFLICT)
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
"""
Delete item by its internal _id
- :param session: contains the used login username, working project, and admin rights
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: server internal id
- :param force: indicates if deletion must be forced in case of conflict
:param dry_run: make checking but do not delete
:return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
"""
# TODO add admin to filter, validate rights
- BaseTopic.delete(self, session, _id, force, dry_run=True)
+ BaseTopic.delete(self, session, _id, dry_run=True)
if dry_run:
return
if nsi: # last one using nsr
continue
try:
- self.nsrTopic.delete(session, nsr_id, force=force, dry_run=False)
+ self.nsrTopic.delete(session, nsr_id, dry_run=False)
except (DbException, EngineException) as e:
if e.http_code == HTTPStatus.NOT_FOUND:
pass
self.db.set_one("nsts", {"_id": nsir_admin["nst-id"]}, {"_admin.usageState": "NOT_IN_USE"})
return v
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
"""
Creates a new netslice instance record into database. It also creates needed nsrs and vnfrs
:param rollback: list to append the created items at database in case a rollback must be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: params to be used for the nsir
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created item public to all projects
:return: the _id of nsi descriptor created at database
"""
slice_request = self._remove_envelop(indata)
# Override descriptor with query string kwargs
self._update_input_with_kwargs(slice_request, kwargs)
- self._validate_input_new(slice_request, force)
+ self._validate_input_new(slice_request, session["force"])
step = ""
# look for nstd
step = "getting nstd id='{}' from database".format(slice_request.get("nstId"))
_filter = {"_id": slice_request["nstId"]}
- _filter.update(BaseTopic._get_project_filter(session, write=False, show_all=True))
+ _filter.update(BaseTopic._get_project_filter(session))
nstd = self.db.get_one("nsts", _filter)
nstd.pop("_admin", None)
nstd_id = nstd.pop("_id", None)
}
step = "creating nsi at database"
- self.format_on_new(nsi_descriptor, session["project_id"], make_public=make_public)
+ self.format_on_new(nsi_descriptor, session["project_id"], make_public=session["public"])
nsi_descriptor["_admin"]["nsiState"] = "NOT_INSTANTIATED"
nsi_descriptor["_admin"]["netslice-subnet"] = None
nsi_descriptor["_admin"]["deployed"] = {}
break
# Creates Nsr objects
- _id_nsr = self.nsrTopic.new(rollback, session, indata_ns, kwargs, headers, force)
+ _id_nsr = self.nsrTopic.new(rollback, session, indata_ns, kwargs, headers)
nsrs_item = {"nsrId": _id_nsr, "shared": service.get("is-shared-nss"), "nsd-id": service["nsd-ref"],
"nslcmop_instantiate": None}
indata_ns["nss-id"] = service["id"]
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
def _check_nsi_operation(self, session, nsir, operation, indata):
"""
Check that user has enter right parameters for the operation
- :param session:
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param operation: it can be: instantiate, terminate, action, TODO: update, heal
:param indata: descriptor with the parameters of the operation
:return: None
# self.db.set_one("nsis", {"_id": nsir["_id"]}, nsir)
self.db.set_one("nsis", {"_id": nsir["_id"]}, {"_admin.netslice-vld": nsir["_admin"].get("netslice-vld")})
- def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
+ def new(self, rollback, session, indata=None, kwargs=None, headers=None):
"""
Performs a new operation over a ns
:param rollback: list to append created items at database in case a rollback must to be done
- :param session: contains the used login username and working project
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: descriptor with the parameters of the operation. It must contains among others
nsiInstanceId: _id of the nsir to perform the operation
operation: it can be: instantiate, terminate, action, TODO: update, heal
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :param force: If True avoid some dependence checks
- :param make_public: Make the created item public to all projects
:return: id of the nslcmops
"""
try:
validate_input(indata, self.operation_schema[operation])
# get nsi from nsiInstanceId
- _filter = BaseTopic._get_project_filter(session, write=True, show_all=False)
+ _filter = BaseTopic._get_project_filter(session)
_filter["_id"] = nsiInstanceId
nsir = self.db.get_one("nsis", _filter)
raise EngineException("netslice_instance '{}' cannot be '{}' because it is not instantiated".format(
nsiInstanceId, operation), HTTPStatus.CONFLICT)
else:
- if operation == "instantiate" and not indata.get("force"):
+ if operation == "instantiate" and not session["force"]:
raise EngineException("netslice_instance '{}' cannot be '{}' because it is already instantiated".
format(nsiInstanceId, operation), HTTPStatus.CONFLICT)
del indata_ns["key-pair-ref"]
# Creating NS_LCM_OP with the flag slice_object=True to not trigger the service instantiation
# message via kafka bus
- nslcmop = self.nsi_NsLcmOpTopic.new(rollback, session, indata_ns, kwargs, headers, force,
+ nslcmop = self.nsi_NsLcmOpTopic.new(rollback, session, indata_ns, kwargs, headers,
slice_object=True)
nslcmops.append(nslcmop)
if operation == "terminate":
self._check_nsi_operation(session, nsir, operation, indata)
nsilcmop_desc = self._create_nsilcmop(session, nsiInstanceId, operation, indata)
- self.format_on_new(nsilcmop_desc, session["project_id"], make_public=make_public)
+ self.format_on_new(nsilcmop_desc, session["project_id"], make_public=session["public"])
_id = self.db.create("nsilcmops", nsilcmop_desc)
rollback.append({"topic": "nsilcmops", "_id": _id})
self.msg.write("nsi", operation, nsilcmop_desc)
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
- def delete(self, session, _id, force=False, dry_run=False):
+ def delete(self, session, _id, dry_run=False):
raise EngineException("Method delete called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
- def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
+ def edit(self, session, _id, indata=None, kwargs=None, content=None):
raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
present specification for the particular resource, but that are not part of <list>
+ Additionally it admits some administrator values:
+ FORCE: To force operations skipping dependency checkings
+ 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.
cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(main_topic, version, topic, id)
return
+ @staticmethod
+ def _manage_admin_query(session, kwargs, method, _id):
+ """
+ Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
+ Check that users has rights to use them and returs the admin_query
+ :param session: session rights obtained by token
+ :param kwargs: query string input.
+ :param method: http method: GET, POSST, PUT, ...
+ :param _id:
+ :return: admin_query dictionary with keys:
+ public: True, False or None
+ force: True or False
+ project_id: tuple with projects used for accessing an element
+ set_project: tuple with projects that a created element will belong to
+ method: show, list, delete, write
+ """
+ admin_query = {"force": False, "project_id": (session["project_id"], ), "username": session["username"],
+ "admin": session["admin"], "public": None}
+ if kwargs:
+ # FORCE
+ if "FORCE" in kwargs:
+ if kwargs["FORCE"].lower() != "false": # if None or True set force to True
+ admin_query["force"] = True
+ del kwargs["FORCE"]
+ # PUBLIC
+ if "PUBLIC" in kwargs:
+ if kwargs["PUBLIC"].lower() != "false": # if None or True set public to True
+ admin_query["public"] = True
+ else:
+ admin_query["public"] = False
+ del kwargs["PUBLIC"]
+ # ADMIN
+ if "ADMIN" in kwargs:
+ behave_as = kwargs.pop("ADMIN")
+ if behave_as.lower() != "false":
+ if not session["admin"]:
+ raise NbiException("Only admin projects can use 'ADMIN' query string", HTTPStatus.UNAUTHORIZED)
+ if not behave_as or behave_as.lower() == "true": # convert True, None to empty list
+ admin_query["project_id"] = ()
+ elif isinstance(behave_as, (list, tuple)):
+ admin_query["project_id"] = behave_as
+ else: # isinstance(behave_as, str)
+ admin_query["project_id"] = (behave_as, )
+ if "SET_PROJECT" in kwargs:
+ set_project = kwargs.pop("SET_PROJECT")
+ if not set_project:
+ admin_query["set_project"] = list(admin_query["project_id"])
+ else:
+ if isinstance(set_project, str):
+ set_project = (set_project, )
+ if admin_query["project_id"]:
+ for p in set_project:
+ if p not in admin_query["project_id"]:
+ raise NbiException("Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
+ "'ADMIN='{p}'".format(p=p), HTTPStatus.UNAUTHORIZED)
+ admin_query["set_project"] = set_project
+
+ # PROJECT_READ
+ # if "PROJECT_READ" in kwargs:
+ # admin_query["project"] = kwargs.pop("project")
+ # if admin_query["project"] == session["project_id"]:
+ if method == "GET":
+ if _id:
+ admin_query["method"] = "show"
+ else:
+ admin_query["method"] = "list"
+ elif method == "DELETE":
+ admin_query["method"] = "delete"
+ else:
+ admin_query["method"] = "write"
+ return admin_query
+
@cherrypy.expose
def default(self, main_topic=None, version=None, topic=None, _id=None, item=None, *args, **kwargs):
session = None
method = kwargs.pop("METHOD")
else:
method = cherrypy.request.method
- if kwargs and "FORCE" in kwargs:
- force = kwargs.pop("FORCE")
- else:
- force = False
+
self._check_valid_url_method(method, main_topic, version, topic, _id, item, *args)
+
if main_topic == "admin" and topic == "tokens":
return self.token(method, _id, kwargs)
# self.engine.load_dbase(cherrypy.request.app.config)
session = self.authenticator.authorize()
+ session = self._manage_admin_query(session, kwargs, method, _id)
indata = self._format_in(kwargs)
engine_topic = topic
if topic == "subscriptions":
engine_topic = "nsilcmops"
elif main_topic == "pdu":
engine_topic = "pdus"
- if engine_topic == "vims": # TODO this is for backward compatibility, it will remove in the future
+ if engine_topic == "vims": # TODO this is for backward compatibility, it will be removed in the future
engine_topic = "vim_accounts"
if method == "GET":
if topic in ("ns_descriptors_content", "vnf_packages_content", "netslice_templates_content"):
_id = cherrypy.request.headers.get("Transaction-Id")
if not _id:
- _id = self.engine.new_item(rollback, session, engine_topic, {}, None, cherrypy.request.headers,
- force=force)
+ _id = self.engine.new_item(rollback, session, engine_topic, {}, None, cherrypy.request.headers)
completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs,
- cherrypy.request.headers, force=force)
+ cherrypy.request.headers)
if completed:
self._set_location_header(main_topic, version, topic, _id)
else:
outdata = {"id": _id}
elif topic == "ns_instances_content":
# creates NSR
- _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs, force=force)
+ _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs)
# creates nslcmop
indata["lcmOperationType"] = "instantiate"
indata["nsInstanceId"] = _id
cherrypy.response.status = HTTPStatus.ACCEPTED.value
elif topic == "netslice_instances_content":
# creates NetSlice_Instance_record (NSIR)
- _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs, force=force)
+ _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs)
self._set_location_header(main_topic, version, topic, _id)
indata["lcmOperationType"] = "instantiate"
indata["nsiInstanceId"] = _id
cherrypy.response.status = HTTPStatus.ACCEPTED.value
else:
_id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs,
- cherrypy.request.headers, force=force)
+ cherrypy.request.headers)
self._set_location_header(main_topic, version, topic, _id)
outdata = {"id": _id}
# TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
cherrypy.response.status = HTTPStatus.OK.value
else: # len(args) > 1
delete_in_process = False
- if topic == "ns_instances_content" and not force:
+ if topic == "ns_instances_content" and not session["force"]:
nslcmop_desc = {
"lcmOperationType": "terminate",
"nsInstanceId": _id,
delete_in_process = True
outdata = {"_id": opp_id}
cherrypy.response.status = HTTPStatus.ACCEPTED.value
- elif topic == "netslice_instances_content" and not force:
+ elif topic == "netslice_instances_content" and not session["force"]:
nsilcmop_desc = {
"lcmOperationType": "terminate",
"nsiInstanceId": _id,
outdata = {"_id": opp_id}
cherrypy.response.status = HTTPStatus.ACCEPTED.value
if not delete_in_process:
- self.engine.del_item(session, engine_topic, _id, force)
+ self.engine.del_item(session, engine_topic, _id)
cherrypy.response.status = HTTPStatus.NO_CONTENT.value
if engine_topic in ("vim_accounts", "wim_accounts", "sdns"):
cherrypy.response.status = HTTPStatus.ACCEPTED.value
elif method in ("PUT", "PATCH"):
outdata = None
- if not indata and not kwargs:
+ if not indata and not kwargs and not session.get("set_project"):
raise NbiException("Nothing to update. Provide payload and/or query string",
HTTPStatus.BAD_REQUEST)
if item in ("nsd_content", "package_content", "nst_content") and method == "PUT":
completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs,
- cherrypy.request.headers, force=force)
+ cherrypy.request.headers)
if not completed:
cherrypy.response.headers["Transaction-Id"] = id
else:
- self.engine.edit_item(session, engine_topic, _id, indata, kwargs, force=force)
+ self.engine.edit_item(session, engine_topic, _id, indata, kwargs)
cherrypy.response.status = HTTPStatus.NO_CONTENT.value
else:
raise NbiException("Method {} not allowed".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
TODO: Ending database connections.
"""
global subscription_thread
- subscription_thread.terminate()
+ if subscription_thread:
+ subscription_thread.terminate()
subscription_thread = None
cherrypy.tree.apps['/osm'].root.engine.stop()
cherrypy.log.error("Stopping osm_nbi")
:param engine: an instance of Engine class, used for deleting instances
"""
threading.Thread.__init__(self)
-
+ self.to_terminate = False
self.config = config
self.db = None
self.msg = None
raise SubscriptionException(str(e), http_code=e.http_code)
self.logger.debug("Starting")
- while True:
+ while not self.to_terminate:
try:
self.aiomain_task = asyncio.ensure_future(self.msg.aioread(("ns", "nsi"), loop=self.loop,
callback=self._msg_callback),
loop=self.loop)
self.loop.run_until_complete(self.aiomain_task)
- except asyncio.CancelledError:
- break # if cancelled it should end, breaking loop
+ # except asyncio.CancelledError:
+ # break # if cancelled it should end, breaking loop
except Exception as e:
- self.logger.exception("Exception '{}' at messaging read loop".format(e), exc_info=True)
+ if not self.to_terminate:
+ self.logger.exception("Exception '{}' at messaging read loop".format(e), exc_info=True)
self.logger.debug("Finishing")
self._stop()
but not immediately.
:return: None
"""
+ self.to_terminate = True
self.loop.call_soon_threadsafe(self.aiomain_task.cancel)
engine.remove_authorization() # To finish
+class TestProjectsDescriptors:
+ description = "test descriptors visibility among projects"
+
+ @staticmethod
+ def run(engine, test_osm, manual_check, test_params=None):
+ vnfd_ids = []
+ engine.set_test_name("ProjectDescriptors")
+ engine.get_autorization()
+ engine.test("Create project Padmin", "POST", "/admin/v1/projects", headers_json,
+ {"name": "Padmin", "admin": True}, (201, 204),
+ {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
+ engine.test("Create project P2", "POST", "/admin/v1/projects", headers_json, {"name": "P2"},
+ (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
+ engine.test("Create project P3", "POST", "/admin/v1/projects", headers_json, {"name": "P3"},
+ (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
+
+ engine.test("Create user U1", "POST", "/admin/v1/users", headers_json,
+ {"username": "U1", "projects": ["Padmin", "P2", "P3"], "password": "pw1"}, 201,
+ {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
+
+ engine.test("Onboard VNFD id1", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id1", headers_yaml,
+ TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
+ vnfd_ids.append(engine.last_id)
+ engine.test("Onboard VNFD id2 PUBLIC", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id2&PUBLIC=TRUE",
+ headers_yaml, TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
+ vnfd_ids.append(engine.last_id)
+ engine.test("Onboard VNFD id3", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id3&PUBLIC=FALSE", headers_yaml,
+ TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
+ vnfd_ids.append(engine.last_id)
+
+ res = engine.test("Get VNFD descriptors", "GET", "/vnfpkgm/v1/vnf_packages?id=id1,id2,id3",
+ headers_json, None, 200, r_header_json, "json")
+ response = res.json()
+ if len(response) != 3:
+ logger.error("Only 3 vnfds should be present for project admin. {} listed".format(len(response)))
+ engine.failed_tests += 1
+
+ # Change to other project Padmin
+ res = engine.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json,
+ {"username": "U1", "password": "pw1", "project_id": "Padmin"}, (200, 201),
+ r_header_json, "json")
+ if res:
+ response = res.json()
+ engine.set_header({"Authorization": "Bearer {}".format(response["id"])})
+
+ # list vnfds
+ res = engine.test("List VNFD descriptors for Padmin", "GET", "/vnfpkgm/v1/vnf_packages",
+ headers_json, None, 200, r_header_json, "json")
+ response = res.json()
+ if len(response) != 0:
+ logger.error("Only 0 vnfds should be present for project Padmin. {} listed".format(len(response)))
+ engine.failed_tests += 1
+
+ # list Public vnfds
+ res = engine.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages?PUBLIC=True",
+ headers_json, None, 200, r_header_json, "json")
+ response = res.json()
+ if len(response) != 1:
+ logger.error("Only 1 vnfds should be present for project Padmin. {} listed".format(len(response)))
+ engine.failed_tests += 1
+
+ # list vnfds belonging to project "admin"
+ res = engine.test("List VNFD of admin project", "GET", "/vnfpkgm/v1/vnf_packages?ADMIN=admin",
+ headers_json, None, 200, r_header_json, "json")
+ response = res.json()
+ if len(response) != 3:
+ logger.error("Only 3 vnfds should be present for project Padmin. {} listed".format(len(response)))
+ engine.failed_tests += 1
+
+ # Get Public vnfds
+ engine.test("Get VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
+ headers_json, None, 200, r_header_json, "json")
+ # Edit not owned vnfd
+ engine.test("Edit VNFD ", "PATCH", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[0]),
+ headers_yaml, '{name: pepe}', 404, r_header_yaml, "yaml")
+
+ # Add to my catalog
+ engine.test("Add VNFD id2 to my catalog", "PATCH", "/vnfpkgm/v1/vnf_packages/{}?SET_PROJECT".
+ format(vnfd_ids[1]), headers_json, None, 204, None, 0)
+
+ # Add a new vnfd
+ engine.test("Onboard VNFD id4", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id4", headers_yaml,
+ TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
+ vnfd_ids.append(engine.last_id)
+
+ # list vnfds
+ res = engine.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages",
+ headers_json, None, 200, r_header_json, "json")
+ response = res.json()
+ if len(response) != 2:
+ logger.error("Only 2 vnfds should be present for project Padmin. {} listed".format(len(response)))
+ engine.failed_tests += 1
+
+ if manual_check:
+ input('VNFDs have been omboarded. Perform manual check and press enter to resume')
+
+ test_rest.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
+ headers_yaml, None, 204, None, 0)
+
+ # change to admin project
+ engine.remove_authorization() # To force get authorization
+ engine.get_autorization()
+ test_rest.test("Delete VNFD id1", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[0]),
+ headers_yaml, None, 204, None, 0)
+ test_rest.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
+ headers_yaml, None, 204, None, 0)
+ test_rest.test("Delete VNFD id3", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[2]),
+ headers_yaml, None, 204, None, 0)
+ test_rest.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[3]),
+ headers_yaml, None, 404, r_header_yaml, "yaml")
+ test_rest.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[3]),
+ headers_yaml, None, 204, None, 0)
+ # Get Public vnfds
+ engine.test("Get VNFD deleted id1", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[0]),
+ headers_json, None, 404, r_header_json, "json")
+ engine.test("Get VNFD deleted id2", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[1]),
+ headers_json, None, 404, r_header_json, "json")
+ engine.test("Get VNFD deleted id3", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[2]),
+ headers_json, None, 404, r_header_json, "json")
+ engine.test("Get VNFD deleted id4", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[3]),
+ headers_json, None, 404, r_header_json, "json")
+
+ engine.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json, None, 204, None, None)
+ engine.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json, None, 204, None, None)
+ engine.test("Delete project P2", "DELETE", "/admin/v1/projects/P2", headers_json, None, 204, None, None)
+ engine.test("Delete project P3", "DELETE", "/admin/v1/projects/P3", headers_json, None, 204, None, None)
+
+
class TestFakeVim:
description = "Creates/edit/delete fake VIMs and SDN controllers"
self.users = {'1': "ubuntu", '2': "ubuntu"}
self.passwords = {'1': "osm4u", '2': "osm4u"}
self.descriptor_edit = {
- "vnfd0": yaml.full_load(
+ "vnfd0": yaml.safe_load(
"""
vnf-configuration:
terminate-config-primitive:
class TestDescriptors:
description = "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
+ vnfd_empty = """vnfd:vnfd-catalog:
+ vnfd:
+ - name: prova
+ short-name: prova
+ id: prova
+ """
+ vnfd_prova = """vnfd:vnfd-catalog:
+ vnfd:
+ - connection-point:
+ - name: cp_0h8m
+ type: VPORT
+ id: prova
+ name: prova
+ short-name: prova
+ vdu:
+ - id: vdu_z4bm
+ image: ubuntu
+ interface:
+ - external-connection-point-ref: cp_0h8m
+ name: eth0
+ virtual-interface:
+ type: VIRTIO
+ name: vdu_z4bm
+ version: '1.0'
+ """
def __init__(self):
self.vnfd_filename = "hackfest_3charmed_vnfd.tar.gz"
self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
self.vnfd_id = None
self.nsd_id = None
- self.vnfd_empty = """vnfd:vnfd-catalog:
- vnfd:
- - name: prova
- short-name: prova
- id: prova
- """
- self.vnfd_prova = """vnfd:vnfd-catalog:
- vnfd:
- - connection-point:
- - name: cp_0h8m
- type: VPORT
- id: prova
- name: prova
- short-name: prova
- vdu:
- - id: vdu_z4bm
- image: ubuntu
- interface:
- - external-connection-point-ref: cp_0h8m
- name: eth0
- virtual-interface:
- type: VIRTIO
- name: vdu_z4bm
- version: '1.0'
- """
def run(self, engine, test_osm, manual_check, test_params=None):
engine.set_test_name("Descriptors")
test_classes = {
"NonAuthorized": TestNonAuthorized,
"FakeVIM": TestFakeVim,
- "TestUsersProjects": TestUsersProjects,
+ "Users-Projects": TestUsersProjects,
+ "Projects-Descriptors": TestProjectsDescriptors,
"VIM-SDN": TestVIMSDN,
"Deploy-Custom": TestDeploy,
"Deploy-Hackfest-Cirros": TestDeployHackfestCirros,
"Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3,
"Deploy-Hackfest-4": TestDeployHackfest4,
"Deploy-CirrosMacIp": TestDeployIpMac,
- "TestDescriptors": TestDescriptors,
- "TestDeployHackfest1": TestDeployHackfest1,
+ "Descriptors": TestDescriptors,
+ "Deploy-Hackfest1": TestDeployHackfest1,
# "Deploy-MultiVIM": TestDeployMultiVIM,
- "DeploySingleVdu": TestDeploySingleVdu,
- "DeployHnfd": TestDeployHnfd,
+ "Deploy-SingleVdu": TestDeploySingleVdu,
+ "Deploy-Hnfd": TestDeployHnfd,
"Upload-Slice-Template": TestNetSliceTemplates,
"Deploy-Slice-Instance": TestNetSliceInstances,
- "TestDeploySimpleCharm": TestDeploySimpleCharm,
- "TestDeploySimpleCharm2": TestDeploySimpleCharm2,
+ "Deploy-SimpleCharm": TestDeploySimpleCharm,
+ "Deploy-SimpleCharm2": TestDeploySimpleCharm2,
}
test_to_do = []
test_params = {}