import time
import json
import yaml
-import html_out as html
+import osm_nbi.html_out as html
import logging
import logging.handlers
import getopt
import sys
-from authconn import AuthException
-from auth import Authenticator
-from engine import Engine, EngineException
-from subscriptions import SubscriptionThread
-from validation import ValidationError
+from osm_nbi.authconn import AuthException, AuthconnException
+from osm_nbi.auth import Authenticator
+from osm_nbi.engine import Engine, EngineException
+from osm_nbi.subscriptions import SubscriptionThread
+from osm_nbi.validation import ValidationError
from osm_common.dbbase import DbException
from osm_common.fsbase import FsException
from osm_common.msgbase import MsgException
from http import HTTPStatus
from codecs import getreader
from os import environ, path
+from osm_nbi import version as nbi_version, version_date as nbi_version_date
__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
-__version__ = "0.1.3"
-version_date = "Jan 2019"
+__version__ = "0.1.3" # file version, not NBI version
+version_date = "Aug 2019"
+
database_version = '1.2'
auth_database_version = '1.0'
nbi_server = None # instance of Server class
subscription_thread = None # instance of SubscriptionThread class
-
"""
North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented)
URL: /osm GET POST PUT DELETE PATCH
/<id> O O O
/sdns O O
/<id> O O O
+ /k8sclusters O O
+ /<id> O O O
+ /k8srepos O O
+ /<id> O O
+ /osmrepos O O
+ /<id> O O
/nst/v1 O O
/netslice_templates_content O O
Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
"""
-
-class NbiException(Exception):
-
- def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
- Exception.__init__(self, message)
- self.http_code = http_code
-
-
-class Server(object):
- instance = 0
- # to decode bytes to str
- reader = getreader("utf-8")
-
- def __init__(self):
- self.instance += 1
- self.engine = Engine()
- self.authenticator = Authenticator()
- self.valid_methods = { # contains allowed URL and methods
- "admin": {
- "v1": {
- "tokens": {"METHODS": ("GET", "POST", "DELETE"),
- "<ID>": {"METHODS": ("GET", "DELETE")}
- },
- "users": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT")}
- },
- "projects": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PUT")}
- },
- "roles": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PUT")}
- },
- "vims": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
+valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
+# ^ Contains possible administrative query string words:
+# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
+# (not owned by my session project).
+# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
+# FORCE=True(by default)|False: Force edition/deletion operations
+# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
+
+valid_url_methods = {
+ # contains allowed URL and methods, and the role_permission name
+ "admin": {
+ "v1": {
+ "tokens": {"METHODS": ("GET", "POST", "DELETE"),
+ "ROLE_PERMISSION": "tokens:",
+ "<ID>": {"METHODS": ("GET", "DELETE"),
+ "ROLE_PERMISSION": "tokens:id:"
+ }
+ },
+ "users": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "users:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "users:id:"
+ }
+ },
+ "projects": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "projects:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "projects:id:"}
+ },
+ "roles": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "roles:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "roles:id:"
+ }
+ },
+ "vims": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "vims:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "vims:id:"
+ }
+ },
+ "vim_accounts": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "vim_accounts:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "vim_accounts:id:"
+ }
},
- "vim_accounts": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
- },
- "wim_accounts": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
- },
- "sdns": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
+ "wim_accounts": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "wim_accounts:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "wim_accounts:id:"
+ }
},
- }
- },
- "pdu": {
- "v1": {
- "pdu_descriptors": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT")}
- },
- }
- },
- "nsd": {
- "v1": {
- "ns_descriptors_content": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "PUT", "DELETE")}
- },
- "ns_descriptors": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
- "nsd_content": {"METHODS": ("GET", "PUT")},
- "nsd": {"METHODS": "GET"}, # descriptor inside package
- "artifacts": {"*": {"METHODS": "GET"}}
+ "sdns": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "sdn_controllers:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "sdn_controllers:id:"
+ }
+ },
+ "k8sclusters": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "k8sclusters:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "k8sclusters:id:"
+ }
+ },
+ "k8srepos": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "k8srepos:",
+ "<ID>": {"METHODS": ("GET", "DELETE"),
+ "ROLE_PERMISSION": "k8srepos:id:"
+ }
+ },
+ "osmrepos": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "osmrepos:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "osmrepos:id:"
+ }
+ },
+ "domains": {"METHODS": ("GET", ),
+ "ROLE_PERMISSION": "domains:",
+ },
+ }
+ },
+ "pdu": {
+ "v1": {
+ "pdu_descriptors": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "pduds:",
+ "<ID>": {"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
+ "ROLE_PERMISSION": "pduds:id:"
+ }
+ },
+ }
+ },
+ "nsd": {
+ "v1": {
+ "ns_descriptors_content": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "nsds:",
+ "<ID>": {"METHODS": ("GET", "PUT", "DELETE"),
+ "ROLE_PERMISSION": "nsds:id:"
}
},
- "pnf_descriptors": {"TODO": ("GET", "POST"),
- "<ID>": {"TODO": ("GET", "DELETE", "PATCH"),
- "pnfd_content": {"TODO": ("GET", "PUT")}
- }
- },
- "subscriptions": {"TODO": ("GET", "POST"),
- "<ID>": {"TODO": ("GET", "DELETE")}
- },
- }
- },
- "vnfpkgm": {
- "v1": {
- "vnf_packages_content": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "PUT", "DELETE")}
- },
- "vnf_packages": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
- "package_content": {"METHODS": ("GET", "PUT"), # package
- "upload_from_uri": {"TODO": "POST"}
- },
- "vnfd": {"METHODS": "GET"}, # descriptor inside package
- "artifacts": {"*": {"METHODS": "GET"}}
- }
+ "ns_descriptors": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "nsds:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "nsds:id:",
+ "nsd_content": {"METHODS": ("GET", "PUT"),
+ "ROLE_PERMISSION": "nsds:id:content:",
+ },
+ "nsd": {"METHODS": ("GET",), # descriptor inside package
+ "ROLE_PERMISSION": "nsds:id:content:"
+ },
+ "artifacts": {"*": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "nsds:id:nsd_artifact:"
+ }
+ }
+ }
+ },
+ "pnf_descriptors": {"TODO": ("GET", "POST"),
+ "<ID>": {"TODO": ("GET", "DELETE", "PATCH"),
+ "pnfd_content": {"TODO": ("GET", "PUT")}
+ }
+ },
+ "subscriptions": {"TODO": ("GET", "POST"),
+ "<ID>": {"TODO": ("GET", "DELETE")}
+ },
+ }
+ },
+ "vnfpkgm": {
+ "v1": {
+ "vnf_packages_content": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "vnfds:",
+ "<ID>": {"METHODS": ("GET", "PUT", "DELETE"),
+ "ROLE_PERMISSION": "vnfds:id:"}
},
- "subscriptions": {"TODO": ("GET", "POST"),
- "<ID>": {"TODO": ("GET", "DELETE")}
- },
- }
- },
- "nslcm": {
- "v1": {
- "ns_instances_content": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE")}
- },
- "ns_instances": {"METHODS": ("GET", "POST"),
+ "vnf_packages": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "vnfds:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
+ "ROLE_PERMISSION": "vnfds:id:",
+ "package_content": {"METHODS": ("GET", "PUT"), # package
+ "ROLE_PERMISSION": "vnfds:id:",
+ "upload_from_uri": {"METHODS": (),
+ "TODO": ("POST", ),
+ "ROLE_PERMISSION": "vnfds:id:upload:"
+ }
+ },
+ "vnfd": {"METHODS": ("GET", ), # descriptor inside package
+ "ROLE_PERMISSION": "vnfds:id:content:"
+ },
+ "artifacts": {"*": {"METHODS": ("GET", ),
+ "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:"
+ }
+ },
+ "action": {"METHODS": ("POST", ),
+ "ROLE_PERMISSION": "vnfds:id:action:"
+ },
+ }
+ },
+ "subscriptions": {"TODO": ("GET", "POST"),
+ "<ID>": {"TODO": ("GET", "DELETE")}
+ },
+ "vnfpkg_op_occs": {"METHODS": ("GET", ),
+ "ROLE_PERMISSION": "vnfds:vnfpkgops:",
+ "<ID>": {"METHODS": ("GET", ),
+ "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"
+ }
+ },
+ }
+ },
+ "nslcm": {
+ "v1": {
+ "ns_instances_content": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "ns_instances:",
"<ID>": {"METHODS": ("GET", "DELETE"),
- "scale": {"METHODS": "POST"},
- "terminate": {"METHODS": "POST"},
- "instantiate": {"METHODS": "POST"},
- "action": {"METHODS": "POST"},
+ "ROLE_PERMISSION": "ns_instances:id:"
}
},
- "ns_lcm_op_occs": {"METHODS": "GET",
- "<ID>": {"METHODS": "GET"},
- },
- "vnfrs": {"METHODS": ("GET"),
- "<ID>": {"METHODS": ("GET")}
+ "ns_instances": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "ns_instances:",
+ "<ID>": {"METHODS": ("GET", "DELETE"),
+ "ROLE_PERMISSION": "ns_instances:id:",
+ "scale": {"METHODS": ("POST",),
+ "ROLE_PERMISSION": "ns_instances:id:scale:"
+ },
+ "terminate": {"METHODS": ("POST",),
+ "ROLE_PERMISSION": "ns_instances:id:terminate:"
+ },
+ "instantiate": {"METHODS": ("POST",),
+ "ROLE_PERMISSION": "ns_instances:id:instantiate:"
+ },
+ "action": {"METHODS": ("POST",),
+ "ROLE_PERMISSION": "ns_instances:id:action:"
+ },
+ }
+ },
+ "ns_lcm_op_occs": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "ns_instances:opps:",
+ "<ID>": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "ns_instances:opps:id:"
+ },
+ },
+ "vnfrs": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "vnf_instances:",
+ "<ID>": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "vnf_instances:id:"
+ }
+ },
+ "vnf_instances": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "vnf_instances:",
+ "<ID>": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "vnf_instances:id:"
+ }
},
- "vnf_instances": {"METHODS": ("GET"),
- "<ID>": {"METHODS": ("GET")}
- },
- }
- },
- "nst": {
- "v1": {
- "netslice_templates_content": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "PUT", "DELETE")}
- },
- "netslice_templates": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE"), "TODO": "PATCH",
- "nst_content": {"METHODS": ("GET", "PUT")},
- "nst": {"METHODS": "GET"}, # descriptor inside package
- "artifacts": {"*": {"METHODS": "GET"}}
- }
+ }
+ },
+ "nst": {
+ "v1": {
+ "netslice_templates_content": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "slice_templates:",
+ "<ID>": {"METHODS": ("GET", "PUT", "DELETE"),
+ "ROLE_PERMISSION": "slice_templates:id:", }
},
- "subscriptions": {"TODO": ("GET", "POST"),
- "<ID>": {"TODO": ("GET", "DELETE")}
- },
- }
- },
- "nsilcm": {
- "v1": {
- "netslice_instances_content": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE")}
- },
- "netslice_instances": {"METHODS": ("GET", "POST"),
+ "netslice_templates": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "slice_templates:",
+ "<ID>": {"METHODS": ("GET", "DELETE"),
+ "TODO": ("PATCH",),
+ "ROLE_PERMISSION": "slice_templates:id:",
+ "nst_content": {"METHODS": ("GET", "PUT"),
+ "ROLE_PERMISSION": "slice_templates:id:content:"
+ },
+ "nst": {"METHODS": ("GET",), # descriptor inside package
+ "ROLE_PERMISSION": "slice_templates:id:content:"
+ },
+ "artifacts": {"*": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "slice_templates:id:content:"
+ }
+ }
+ }
+ },
+ "subscriptions": {"TODO": ("GET", "POST"),
+ "<ID>": {"TODO": ("GET", "DELETE")}
+ },
+ }
+ },
+ "nsilcm": {
+ "v1": {
+ "netslice_instances_content": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "slice_instances:",
"<ID>": {"METHODS": ("GET", "DELETE"),
- "terminate": {"METHODS": "POST"},
- "instantiate": {"METHODS": "POST"},
- "action": {"METHODS": "POST"},
+ "ROLE_PERMISSION": "slice_instances:id:"
}
},
- "nsi_lcm_op_occs": {"METHODS": "GET",
- "<ID>": {"METHODS": "GET"},
- },
- }
- },
- "nspm": {
- "v1": {
- "pm_jobs": {
- "<ID>": {
- "reports": {
- "<ID>": {"METHODS": ("GET")}
- }
- },
- },
+ "netslice_instances": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "slice_instances:",
+ "<ID>": {"METHODS": ("GET", "DELETE"),
+ "ROLE_PERMISSION": "slice_instances:id:",
+ "terminate": {"METHODS": ("POST",),
+ "ROLE_PERMISSION": "slice_instances:id:terminate:"
+ },
+ "instantiate": {"METHODS": ("POST",),
+ "ROLE_PERMISSION": "slice_instances:id:instantiate:"
+ },
+ "action": {"METHODS": ("POST",),
+ "ROLE_PERMISSION": "slice_instances:id:action:"
+ },
+ }
+ },
+ "nsi_lcm_op_occs": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "slice_instances:opps:",
+ "<ID>": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "slice_instances:opps:id:",
+ },
+ },
+ }
+ },
+ "nspm": {
+ "v1": {
+ "pm_jobs": {
+ "<ID>": {
+ "reports": {
+ "<ID>": {"METHODS": ("GET",),
+ "ROLE_PERMISSION": "reports:id:",
+ }
+ }
},
},
- }
+ },
+ },
+}
+
+
+class NbiException(Exception):
+
+ def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
+ Exception.__init__(self, message)
+ self.http_code = http_code
+
+
+class Server(object):
+ instance = 0
+ # to decode bytes to str
+ reader = getreader("utf-8")
+
+ def __init__(self):
+ self.instance += 1
+ self.authenticator = Authenticator(valid_url_methods, valid_query_string)
+ self.engine = Engine(self.authenticator)
def _format_in(self, kwargs):
try:
cherrypy.request.headers.pop("Content-File-MD5", None)
elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
error_text = "Invalid yaml format "
- indata = yaml.load(cherrypy.request.body)
+ indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
cherrypy.request.headers.pop("Content-File-MD5", None)
elif "application/binary" in cherrypy.request.headers["Content-Type"] or \
"application/gzip" in cherrypy.request.headers["Content-Type"] or \
# "Only 'Content-Type' of type 'application/json' or
# 'application/yaml' for input format are available")
error_text = "Invalid yaml format "
- indata = yaml.load(cherrypy.request.body)
+ indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
cherrypy.request.headers.pop("Content-File-MD5", None)
else:
error_text = "Invalid yaml format "
- indata = yaml.load(cherrypy.request.body)
+ indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
cherrypy.request.headers.pop("Content-File-MD5", None)
if not indata:
indata = {}
kwargs[k] = None
elif format_yaml:
try:
- kwargs[k] = yaml.load(v)
+ kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader)
except Exception:
pass
elif k.endswith(".gt") or k.endswith(".lt") or k.endswith(".gte") or k.endswith(".lte"):
v[index] = None
elif format_yaml:
try:
- v[index] = yaml.load(v[index])
+ v[index] = yaml.load(v[index], Loader=yaml.SafeLoader)
except Exception:
pass
raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
@staticmethod
- def _format_out(data, session=None, _format=None):
+ def _format_out(data, token_info=None, _format=None):
"""
return string of dictionary data according to requested json, yaml, xml. By default json
:param data: response to be sent. Can be a dict, text or file
- :param session:
- :param _format: The format to be set as Content-Type ir data is a file
+ :param token_info: Contains among other username and project
+ :param _format: The format to be set as Content-Type if data is a file
:return: None
"""
accept = cherrypy.request.headers.get("Accept")
if data is None:
if accept and "text/html" in accept:
- return html.format(data, cherrypy.request, cherrypy.response, session)
+ return html.format(data, cherrypy.request, cherrypy.response, token_info)
# cherrypy.response.status = HTTPStatus.NO_CONTENT.value
return
elif hasattr(data, "read"): # file object
a = json.dumps(data, indent=4) + "\n"
return a.encode("utf8")
elif "text/html" in accept:
- return html.format(data, cherrypy.request, cherrypy.response, session)
+ return html.format(data, cherrypy.request, cherrypy.response, token_info)
elif "application/yaml" in accept or "*/*" in accept or "text/plain" in accept:
pass
@cherrypy.expose
def index(self, *args, **kwargs):
- session = None
+ token_info = None
try:
if cherrypy.request.method == "GET":
- session = self.authenticator.authorize()
- outdata = "Index page"
+ token_info = self.authenticator.authorize()
+ outdata = token_info # Home page
else:
raise cherrypy.HTTPError(HTTPStatus.METHOD_NOT_ALLOWED.value,
"Method {} not allowed for tokens".format(cherrypy.request.method))
- return self._format_out(outdata, session)
+ return self._format_out(outdata, token_info)
except (EngineException, AuthException) as e:
- cherrypy.log("index Exception {}".format(e))
+ # cherrypy.log("index Exception {}".format(e))
cherrypy.response.status = e.http_code.value
- return self._format_out("Welcome to OSM!", session)
+ return self._format_out("Welcome to OSM!", token_info)
@cherrypy.expose
def version(self, *args, **kwargs):
# TODO consider to remove and provide version using the static version file
- global __version__, version_date
try:
if cherrypy.request.method != "GET":
raise NbiException("Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED)
elif args or kwargs:
raise NbiException("Invalid URL or query string for version", HTTPStatus.METHOD_NOT_ALLOWED)
- return __version__ + " " + version_date
+ # TODO include version of other modules, pick up from some kafka admin message
+ osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
+ return self._format_out(osm_nbi_version)
except NbiException as e:
cherrypy.response.status = e.http_code.value
problem_details = {
}
return self._format_out(problem_details, None)
- @cherrypy.expose
- def token(self, method, token_id=None, kwargs=None):
- session = None
- # self.engine.load_dbase(cherrypy.request.app.config)
- indata = self._format_in(kwargs)
- if not isinstance(indata, dict):
- raise NbiException("Expected application/yaml or application/json Content-Type", HTTPStatus.BAD_REQUEST)
+ def domain(self):
try:
- if method == "GET":
- session = self.authenticator.authorize()
- if token_id:
- outdata = self.authenticator.get_token(session, token_id)
- else:
- outdata = self.authenticator.get_token_list(session)
- elif method == "POST":
- try:
- session = self.authenticator.authorize()
- except Exception:
- session = None
- if kwargs:
- indata.update(kwargs)
- outdata = self.authenticator.new_token(session, indata, cherrypy.request.remote)
- session = outdata
- cherrypy.session['Authorization'] = outdata["_id"]
- self._set_location_header("admin", "v1", "tokens", outdata["_id"])
- # cherrypy.response.cookie["Authorization"] = outdata["id"]
- # cherrypy.response.cookie["Authorization"]['expires'] = 3600
- elif method == "DELETE":
- if not token_id and "id" in kwargs:
- token_id = kwargs["id"]
- elif not token_id:
- session = self.authenticator.authorize()
- token_id = session["_id"]
- outdata = self.authenticator.del_token(token_id)
- session = None
- cherrypy.session['Authorization'] = "logout"
- # cherrypy.response.cookie["Authorization"] = token_id
- # cherrypy.response.cookie["Authorization"]['expires'] = 0
- else:
- raise NbiException("Method {} not allowed for token".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
- return self._format_out(outdata, session)
- except (NbiException, EngineException, DbException, AuthException) as e:
- cherrypy.log("tokens Exception {}".format(e))
+ domains = {
+ "user_domain_name": cherrypy.tree.apps['/osm'].config["authentication"].get("user_domain_name"),
+ "project_domain_name": cherrypy.tree.apps['/osm'].config["authentication"].get("project_domain_name")}
+ return self._format_out(domains)
+ except NbiException as e:
cherrypy.response.status = e.http_code.value
problem_details = {
"code": e.http_code.name,
"status": e.http_code.value,
"detail": str(e),
}
- return self._format_out(problem_details, session)
+ return self._format_out(problem_details, None)
+
+ @staticmethod
+ def _format_login(token_info):
+ """
+ Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
+ log this information
+ :param token_info: Dictionary with token content
+ :return: None
+ """
+ cherrypy.request.login = token_info.get("username", "-")
+ if token_info.get("project_name"):
+ cherrypy.request.login += "/" + token_info["project_name"]
+ if token_info.get("id"):
+ cherrypy.request.login += ";session=" + token_info["id"][0:12]
+
+ @cherrypy.expose
+ def token(self, method, token_id=None, kwargs=None):
+ token_info = None
+ # self.engine.load_dbase(cherrypy.request.app.config)
+ indata = self._format_in(kwargs)
+ if not isinstance(indata, dict):
+ raise NbiException("Expected application/yaml or application/json Content-Type", HTTPStatus.BAD_REQUEST)
+
+ if method == "GET":
+ token_info = self.authenticator.authorize()
+ # for logging
+ self._format_login(token_info)
+ if token_id:
+ outdata = self.authenticator.get_token(token_info, token_id)
+ else:
+ outdata = self.authenticator.get_token_list(token_info)
+ elif method == "POST":
+ try:
+ token_info = self.authenticator.authorize()
+ except Exception:
+ token_info = None
+ if kwargs:
+ indata.update(kwargs)
+ # This is needed to log the user when authentication fails
+ cherrypy.request.login = "{}".format(indata.get("username", "-"))
+ outdata = token_info = self.authenticator.new_token(token_info, indata, cherrypy.request.remote)
+ cherrypy.session['Authorization'] = outdata["_id"]
+ self._set_location_header("admin", "v1", "tokens", outdata["_id"])
+ # for logging
+ self._format_login(token_info)
+
+ # cherrypy.response.cookie["Authorization"] = outdata["id"]
+ # cherrypy.response.cookie["Authorization"]['expires'] = 3600
+ elif method == "DELETE":
+ if not token_id and "id" in kwargs:
+ token_id = kwargs["id"]
+ elif not token_id:
+ token_info = self.authenticator.authorize()
+ # for logging
+ self._format_login(token_info)
+ token_id = token_info["_id"]
+ outdata = self.authenticator.del_token(token_id)
+ token_info = None
+ cherrypy.session['Authorization'] = "logout"
+ # cherrypy.response.cookie["Authorization"] = token_id
+ # cherrypy.response.cookie["Authorization"]['expires'] = 0
+ else:
+ raise NbiException("Method {} not allowed for token".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
+ return self._format_out(outdata, token_info)
@cherrypy.expose
def test(self, *args, **kwargs):
+ if not cherrypy.config.get("server.enable_test") or (isinstance(cherrypy.config["server.enable_test"], str) and
+ cherrypy.config["server.enable_test"].lower() == "false"):
+ cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
+ return "test URL is disabled"
thread_info = None
if args and args[0] == "help":
return "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"\
return_text = "<html><pre>{} ->\n".format(main_topic)
try:
if cherrypy.request.method == 'POST':
- to_send = yaml.load(cherrypy.request.body)
+ to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
for k, v in to_send.items():
self.engine.msg.write(main_topic, k, v)
return_text += " {}: {}\n".format(k, v)
elif cherrypy.request.method == 'GET':
for k, v in kwargs.items():
- self.engine.msg.write(main_topic, k, yaml.load(v))
- return_text += " {}: {}\n".format(k, yaml.load(v))
+ v_dict = yaml.load(v, Loader=yaml.SafeLoader)
+ self.engine.msg.write(main_topic, k, v_dict)
+ return_text += " {}: {}\n".format(k, v_dict)
except Exception as e:
return_text += "Error: " + str(e)
return_text += "</pre></html>\n"
return_text += "</pre></html>"
return return_text
- def _check_valid_url_method(self, method, *args):
+ @staticmethod
+ def _check_valid_url_method(method, *args):
if len(args) < 3:
raise NbiException("URL must contain at least 'main_topic/version/topic'", HTTPStatus.METHOD_NOT_ALLOWED)
- reference = self.valid_methods
+ reference = valid_url_methods
for arg in args:
if arg is None:
break
raise NbiException("Method {} not supported yet for this URL".format(method), HTTPStatus.NOT_IMPLEMENTED)
elif "METHODS" in reference and method not in reference["METHODS"]:
raise NbiException("Method {} not supported for this URL".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
- return
+ return reference["ROLE_PERMISSION"] + method.lower()
@staticmethod
def _set_location_header(main_topic, version, topic, id):
return
@staticmethod
- def _manage_admin_query(session, kwargs, method, _id):
+ def _extract_query_string_operations(kwargs, method):
+ """
+
+ :param kwargs:
+ :return:
+ """
+ query_string_operations = []
+ if kwargs:
+ for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
+ if qs in kwargs and kwargs[qs].lower() != "false":
+ query_string_operations.append(qs.lower() + ":" + method.lower())
+ return query_string_operations
+
+ @staticmethod
+ def _manage_admin_query(token_info, 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 token_info: token_info rights obtained by token
:param kwargs: query string input.
:param method: http method: GET, POSST, PUT, ...
:param _id:
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}
+ admin_query = {"force": False, "project_id": (token_info["project_id"], ), "username": token_info["username"],
+ "admin": token_info["admin"], "public": None,
+ "allow_show_user_project_role": token_info["allow_show_user_project_role"]}
if kwargs:
# FORCE
if "FORCE" in kwargs:
if "ADMIN" in kwargs:
behave_as = kwargs.pop("ADMIN")
if behave_as.lower() != "false":
- if not session["admin"]:
+ if not token_info["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"] = ()
# PROJECT_READ
# if "PROJECT_READ" in kwargs:
# admin_query["project"] = kwargs.pop("project")
- # if admin_query["project"] == session["project_id"]:
+ # if admin_query["project"] == token_info["project_id"]:
if method == "GET":
if _id:
admin_query["method"] = "show"
@cherrypy.expose
def default(self, main_topic=None, version=None, topic=None, _id=None, item=None, *args, **kwargs):
- session = None
+ token_info = None
outdata = None
_format = None
method = "DONE"
engine_topic = None
rollback = []
- session = None
+ engine_session = None
try:
if not main_topic or not version or not topic:
raise NbiException("URL must contain at least 'main_topic/version/topic'",
else:
method = cherrypy.request.method
- self._check_valid_url_method(method, main_topic, version, topic, _id, item, *args)
-
+ role_permission = self._check_valid_url_method(method, main_topic, version, topic, _id, item, *args)
+ query_string_operations = self._extract_query_string_operations(kwargs, method)
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)
+ token_info = self.authenticator.authorize(role_permission, query_string_operations, _id)
+ if main_topic == "admin" and topic == "domains":
+ return self.domain()
+ engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
indata = self._format_in(kwargs)
engine_topic = topic
if topic == "subscriptions":
engine_topic = "nsds"
elif main_topic == "vnfpkgm":
engine_topic = "vnfds"
+ if topic == "vnfpkg_op_occs":
+ engine_topic = "vnfpkgops"
+ if topic == "vnf_packages" and item == "action":
+ engine_topic = "vnfpkgops"
elif main_topic == "nslcm":
engine_topic = "nsrs"
if topic == "ns_lcm_op_occs":
path = ()
else:
path = None
- file, _format = self.engine.get_file(session, engine_topic, _id, path,
+ file, _format = self.engine.get_file(engine_session, engine_topic, _id, path,
cherrypy.request.headers.get("Accept"))
outdata = file
elif not _id:
- outdata = self.engine.get_item_list(session, engine_topic, kwargs)
+ outdata = self.engine.get_item_list(engine_session, engine_topic, kwargs)
else:
if item == "reports":
# TODO check that project_id (_id in this context) has permissions
_id = args[0]
- outdata = self.engine.get_item(session, engine_topic, _id)
+ outdata = self.engine.get_item(engine_session, engine_topic, _id)
+
elif method == "POST":
+ cherrypy.response.status = HTTPStatus.CREATED.value
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)
- completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs,
+ _id, _ = self.engine.new_item(rollback, engine_session, engine_topic, {}, None,
+ cherrypy.request.headers)
+ completed = self.engine.upload_content(engine_session, engine_topic, _id, indata, kwargs,
cherrypy.request.headers)
if completed:
self._set_location_header(main_topic, version, topic, _id)
outdata = {"id": _id}
elif topic == "ns_instances_content":
# creates NSR
- _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs)
+ _id, _ = self.engine.new_item(rollback, engine_session, engine_topic, indata, kwargs)
# creates nslcmop
indata["lcmOperationType"] = "instantiate"
indata["nsInstanceId"] = _id
- nslcmop_id = self.engine.new_item(rollback, session, "nslcmops", indata, None)
+ nslcmop_id, _ = self.engine.new_item(rollback, engine_session, "nslcmops", indata, None)
self._set_location_header(main_topic, version, topic, _id)
outdata = {"id": _id, "nslcmop_id": nslcmop_id}
elif topic == "ns_instances" and item:
indata["lcmOperationType"] = item
indata["nsInstanceId"] = _id
- _id = self.engine.new_item(rollback, session, "nslcmops", indata, kwargs)
+ _id, _ = self.engine.new_item(rollback, engine_session, "nslcmops", indata, kwargs)
self._set_location_header(main_topic, version, "ns_lcm_op_occs", _id)
outdata = {"id": _id}
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)
+ _id, _ = self.engine.new_item(rollback, engine_session, engine_topic, indata, kwargs)
self._set_location_header(main_topic, version, topic, _id)
indata["lcmOperationType"] = "instantiate"
indata["netsliceInstanceId"] = _id
- nsilcmop_id = self.engine.new_item(rollback, session, "nsilcmops", indata, kwargs)
+ nsilcmop_id, _ = self.engine.new_item(rollback, engine_session, "nsilcmops", indata, kwargs)
outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
-
elif topic == "netslice_instances" and item:
indata["lcmOperationType"] = item
indata["netsliceInstanceId"] = _id
- _id = self.engine.new_item(rollback, session, "nsilcmops", indata, kwargs)
+ _id, _ = self.engine.new_item(rollback, engine_session, "nsilcmops", indata, kwargs)
self._set_location_header(main_topic, version, "nsi_lcm_op_occs", _id)
outdata = {"id": _id}
cherrypy.response.status = HTTPStatus.ACCEPTED.value
+ elif topic == "vnf_packages" and item == "action":
+ indata["lcmOperationType"] = item
+ indata["vnfPkgId"] = _id
+ _id, _ = self.engine.new_item(rollback, engine_session, "vnfpkgops", indata, kwargs)
+ self._set_location_header(main_topic, version, "vnfpkg_op_occs", _id)
+ outdata = {"id": _id}
+ cherrypy.response.status = HTTPStatus.ACCEPTED.value
else:
- _id = self.engine.new_item(rollback, session, engine_topic, indata, kwargs,
- cherrypy.request.headers)
+ _id, op_id = self.engine.new_item(rollback, engine_session, engine_topic, indata, kwargs,
+ cherrypy.request.headers)
self._set_location_header(main_topic, version, topic, _id)
outdata = {"id": _id}
+ if op_id:
+ outdata["op_id"] = op_id
+ cherrypy.response.status = HTTPStatus.ACCEPTED.value
# TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
- cherrypy.response.status = HTTPStatus.CREATED.value
elif method == "DELETE":
if not _id:
- outdata = self.engine.del_item_list(session, engine_topic, kwargs)
+ outdata = self.engine.del_item_list(engine_session, engine_topic, kwargs)
cherrypy.response.status = HTTPStatus.OK.value
else: # len(args) > 1
- delete_in_process = False
- if topic == "ns_instances_content" and not session["force"]:
+ # for NS NSI generate an operation
+ op_id = None
+ if topic == "ns_instances_content" and not engine_session["force"]:
nslcmop_desc = {
"lcmOperationType": "terminate",
"nsInstanceId": _id,
"autoremove": True
}
- opp_id = self.engine.new_item(rollback, session, "nslcmops", nslcmop_desc, None)
- if opp_id:
- delete_in_process = True
- outdata = {"_id": opp_id}
- cherrypy.response.status = HTTPStatus.ACCEPTED.value
- elif topic == "netslice_instances_content" and not session["force"]:
+ op_id, _ = self.engine.new_item(rollback, engine_session, "nslcmops", nslcmop_desc, kwargs)
+ if op_id:
+ outdata = {"_id": op_id}
+ elif topic == "netslice_instances_content" and not engine_session["force"]:
nsilcmop_desc = {
"lcmOperationType": "terminate",
"netsliceInstanceId": _id,
"autoremove": True
}
- opp_id = self.engine.new_item(rollback, session, "nsilcmops", nsilcmop_desc, None)
- if opp_id:
- delete_in_process = True
- outdata = {"_id": opp_id}
- cherrypy.response.status = HTTPStatus.ACCEPTED.value
- if not delete_in_process:
- 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
+ op_id, _ = self.engine.new_item(rollback, engine_session, "nsilcmops", nsilcmop_desc, None)
+ if op_id:
+ outdata = {"_id": op_id}
+ # if there is not any deletion in process, delete
+ if not op_id:
+ op_id = self.engine.del_item(engine_session, engine_topic, _id)
+ if op_id:
+ outdata = {"op_id": op_id}
+ cherrypy.response.status = HTTPStatus.ACCEPTED.value if op_id else HTTPStatus.NO_CONTENT.value
elif method in ("PUT", "PATCH"):
- outdata = None
- if not indata and not kwargs and not session.get("set_project"):
+ op_id = None
+ if not indata and not kwargs and not engine_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,
+ completed = self.engine.upload_content(engine_session, engine_topic, _id, indata, kwargs,
cherrypy.request.headers)
if not completed:
cherrypy.response.headers["Transaction-Id"] = id
else:
- self.engine.edit_item(session, engine_topic, _id, indata, kwargs)
- cherrypy.response.status = HTTPStatus.NO_CONTENT.value
+ op_id = self.engine.edit_item(engine_session, engine_topic, _id, indata, kwargs)
+
+ if op_id:
+ cherrypy.response.status = HTTPStatus.ACCEPTED.value
+ outdata = {"op_id": op_id}
+ else:
+ cherrypy.response.status = HTTPStatus.NO_CONTENT.value
+ outdata = None
else:
raise NbiException("Method {} not allowed".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
- return self._format_out(outdata, session, _format)
+
+ # if Role information changes, it is needed to reload the information of roles
+ if topic == "roles" and method != "GET":
+ self.authenticator.load_operation_to_allowed_roles()
+
+ if topic == "projects" and method == "DELETE" \
+ or topic in ["users", "roles"] and method in ["PUT", "PATCH", "DELETE"]:
+ self.authenticator.remove_token_from_cache()
+
+ return self._format_out(outdata, token_info, _format)
except Exception as e:
if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
- ValidationError)):
+ ValidationError, AuthconnException)):
http_code_value = cherrypy.response.status = e.http_code.value
http_code_name = e.http_code.name
cherrypy.log("Exception {}".format(e))
"status": http_code_value,
"detail": error_text,
}
- return self._format_out(problem_details, session)
+ return self._format_out(problem_details, token_info)
# raise cherrypy.HTTPError(e.http_code.value, str(e))
+ finally:
+ if token_info:
+ self._format_login(token_info)
+ if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
+ for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
+ if outdata.get(logging_id):
+ cherrypy.request.login += ";{}={}".format(logging_id, outdata[logging_id][:36])
def _start_service():
subscription_thread.start()
# Do not capture except SubscriptionException
- # load and print version. Ignore possible errors, e.g. file not found
- try:
- with open("{}/version".format(engine_config["/static"]['tools.staticdir.dir'])) as version_file:
- version_data = version_file.read()
- cherrypy.log.error("Starting OSM NBI Version: {}".format(version_data.replace("\n", " ")))
- except Exception:
- pass
+ backend = engine_config["authentication"]["backend"]
+ cherrypy.log.error("Starting OSM NBI Version '{} {}' with '{}' authentication backend"
+ .format(nbi_version, nbi_version_date, backend))
def _stop_service():