#!/usr/bin/python3
# -*- coding: utf-8 -*-
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
import cherrypy
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 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>"
-# TODO consider to remove and provide version using the static version file
-__version__ = "0.1.3"
-version_date = "Apr 2018"
-database_version = '1.0'
+__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
- /nsd/v1 O O
+ /nsd/v1
/ns_descriptors_content O O
/<nsdInfoId> O O O O
/ns_descriptors O5 O5
/<vnfInstanceId> O
/subscriptions 5 5
/<subscriptionId> 5 X
+
/pdu/v1
- /pdu_descriptor O O
+ /pdu_descriptors O O
/<id> O O O O
+
/admin/v1
/tokens O O
/<id> O O
/<id> O O O O
/projects O O
/<id> O O
- /vims_accounts (also vims for compatibility) O O
+ /vim_accounts (also vims for compatibility) O O
+ /<id> O O O
+ /wim_accounts O O
/<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
+ /<nstInfoId> O O O O
+ /netslice_templates O O
+ /<nstInfoId> O O O
+ /nst_content O O
+ /nst O
+ /artifacts[/<artifactPath>] O
+ /subscriptions X X
+ /<subscriptionId> X X
+
+ /nsilcm/v1
+ /netslice_instances_content O O
+ /<SliceInstanceId> O O
+ /netslice_instances O O
+ /<SliceInstanceId> O O
+ instantiate O
+ terminate O
+ action O
+ /nsi_lcm_op_occs O O
+ /<nsiLcmOpOccId> O O O
+ /subscriptions X X
+ /<subscriptionId> X X
query string:
Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
+ simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
+ filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
+ op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
+ attrName := string
For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
item of the array, that is, pass if any item of the array pass the filter.
It allows both ne and neq for not equal
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.
Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
"""
+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:"
+ }
+ },
+ "wim_accounts": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "wim_accounts:",
+ "<ID>": {"METHODS": ("GET", "DELETE", "PATCH"),
+ "ROLE_PERMISSION": "wim_accounts:id:"
+ }
+ },
+ "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:"
+ }
+ },
+ "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:"}
+ },
+ "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"),
+ "ROLE_PERMISSION": "ns_instances:id:"
+ }
+ },
+ "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:"
+ }
+ },
+ }
+ },
+ "nst": {
+ "v1": {
+ "netslice_templates_content": {"METHODS": ("GET", "POST"),
+ "ROLE_PERMISSION": "slice_templates:",
+ "<ID>": {"METHODS": ("GET", "PUT", "DELETE"),
+ "ROLE_PERMISSION": "slice_templates:id:", }
+ },
+ "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"),
+ "ROLE_PERMISSION": "slice_instances:id:"
+ }
+ },
+ "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):
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")}
- },
- "vims": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
- },
- "vim_accounts": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
- },
- "sdns": {"METHODS": ("GET", "POST"),
- "<ID>": {"METHODS": ("GET", "DELETE", "PATCH", "PUT")}
- },
- }
- },
- "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"), "TODO": "PATCH",
- "nsd_content": {"METHODS": ("GET", "PUT")},
- "nsd": {"METHODS": "GET"}, # descriptor inside package
- "artifacts": {"*": {"METHODS": "GET"}}
- }
- },
- "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"}}
- }
- },
- "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"),
- "<ID>": {"METHODS": ("GET", "DELETE"),
- "scale": {"METHODS": "POST"},
- "terminate": {"METHODS": "POST"},
- "instantiate": {"METHODS": "POST"},
- "action": {"METHODS": "POST"},
- }
- },
- "ns_lcm_op_occs": {"METHODS": "GET",
- "<ID>": {"METHODS": "GET"},
- },
- "vnfrs": {"METHODS": ("GET"),
- "<ID>": {"METHODS": ("GET")}
- },
- "vnf_instances": {"METHODS": ("GET"),
- "<ID>": {"METHODS": ("GET")}
- },
- }
- },
- }
+ self.authenticator = Authenticator(valid_url_methods, valid_query_string)
+ self.engine = Engine(self.authenticator)
def _format_in(self, kwargs):
try:
if "application/json" in cherrypy.request.headers["Content-Type"]:
error_text = "Invalid json format "
indata = json.load(self.reader(cherrypy.request.body))
+ 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 \
"application/zip" 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
- else:
+ # if there is not any valid accept, raise an error. But if response is already an error, format in yaml
+ elif cherrypy.response.status >= 400:
raise cherrypy.HTTPError(HTTPStatus.NOT_ACCEPTABLE.value,
"Only 'Accept' of type 'application/json' or 'application/yaml' "
"for output format are available")
@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\nprune\nlogin\nlogin2\n"\
+ return "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"\
"sleep/<time>\nmessage/topic\n</pre></html>"
elif args and args[0] == "init":
return f
elif len(args) == 2 and args[0] == "db-clear":
- return self.engine.db.del_list(args[1], kwargs)
- elif args and args[0] == "prune":
- return self.engine.prune()
+ deleted_info = self.engine.db.del_list(args[1], kwargs)
+ return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
+ elif len(args) and args[0] == "fs-clear":
+ if len(args) >= 2:
+ folders = (args[1],)
+ else:
+ folders = self.engine.fs.dir_ls(".")
+ for folder in folders:
+ self.engine.fs.file_delete(folder)
+ return ",".join(folders) + " folders deleted\n"
elif args and args[0] == "login":
if not cherrypy.request.headers.get("Authorization"):
cherrypy.response.headers["WWW-Authenticate"] = 'Basic realm="Access to OSM site", charset="UTF-8"'
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"
" session: {}\n".format(cherrypy.session) +
" cookie: {}\n".format(cherrypy.request.cookie) +
" method: {}\n".format(cherrypy.request.method) +
- " session: {}\n".format(cherrypy.session.get('fieldname')) +
+ " session: {}\n".format(cherrypy.session.get('fieldname')) +
" body:\n")
return_text += " length: {}\n".format(cherrypy.request.body.length)
if cherrypy.request.body.length:
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):
cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(main_topic, version, topic, id)
return
+ @staticmethod
+ 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 token_info: token_info 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": (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 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 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"] = ()
+ 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"] == token_info["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
+ 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'",
HTTPStatus.METHOD_NOT_ALLOWED)
- if main_topic not in ("admin", "vnfpkgm", "nsd", "nslcm"):
+ if main_topic not in ("admin", "vnfpkgm", "nsd", "nslcm", "pdu", "nst", "nsilcm", "nspm"):
raise NbiException("URL main_topic '{}' not supported".format(main_topic),
HTTPStatus.METHOD_NOT_ALLOWED)
if version != 'v1':
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)
+ 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()
+ 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 = main_topic + "_" + topic
- if item:
+ if item and topic != "pm_jobs":
engine_topic = item
if main_topic == "nsd":
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":
engine_topic = "nslcmops"
if topic == "vnfrs" or topic == "vnf_instances":
engine_topic = "vnfrs"
+ elif main_topic == "nst":
+ engine_topic = "nsts"
+ elif main_topic == "nsilcm":
+ engine_topic = "nsis"
+ if topic == "nsi_lcm_op_occs":
+ 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 item in ("nsd_content", "package_content", "artifacts", "vnfd", "nsd"):
- if item in ("vnfd", "nsd"):
+ if item in ("nsd_content", "package_content", "artifacts", "vnfd", "nsd", "nst", "nst_content"):
+ if item in ("vnfd", "nsd", "nst"):
path = "$DESCRIPTOR"
elif args:
path = args
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:
- outdata = self.engine.get_item(session, engine_topic, _id)
+ if item == "reports":
+ # TODO check that project_id (_id in this context) has permissions
+ _id = args[0]
+ outdata = self.engine.get_item(engine_session, engine_topic, _id)
+
elif method == "POST":
- if topic in ("ns_descriptors_content", "vnf_packages_content"):
+ 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,
- force=force)
- completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs,
- cherrypy.request.headers, force=force)
+ _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)
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, engine_session, engine_topic, indata, kwargs)
# creates nslcmop
indata["lcmOperationType"] = "instantiate"
indata["nsInstanceId"] = _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}
+ 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, 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, 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, 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, force=force)
+ _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
- if topic == "ns_instances_content" and not 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)
- outdata = {"_id": opp_id}
- cherrypy.response.status = HTTPStatus.ACCEPTED.value
- else:
- self.engine.del_item(session, engine_topic, _id, force)
- cherrypy.response.status = HTTPStatus.NO_CONTENT.value
- if engine_topic in ("vim_accounts", "sdns"):
- cherrypy.response.status = HTTPStatus.ACCEPTED.value
+ 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
+ }
+ 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:
+ 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") and method == "PUT":
- completed = self.engine.upload_content(session, engine_topic, _id, indata, kwargs,
- cherrypy.request.headers, force=force)
+ if item in ("nsd_content", "package_content", "nst_content") and method == "PUT":
+ 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, force=force)
- 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)):
+ if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
+ ValidationError, AuthconnException)):
http_code_value = cherrypy.response.status = e.http_code.value
http_code_name = e.http_code.name
cherrypy.log("Exception {}".format(e))
else:
http_code_value = cherrypy.response.status = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
- cherrypy.log("CRITICAL: Exception {}".format(e))
+ cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
http_code_name = HTTPStatus.BAD_REQUEST.name
if hasattr(outdata, "close"): # is an open file
outdata.close()
rollback.reverse()
for rollback_item in rollback:
try:
- self.engine.del_item(**rollback_item, session=session, force=True)
+ if rollback_item.get("operation") == "set":
+ self.engine.db.set_one(rollback_item["topic"], {"_id": rollback_item["_id"]},
+ rollback_item["content"], fail_on_empty=False)
+ else:
+ self.engine.db.del_one(rollback_item["topic"], {"_id": rollback_item["_id"]},
+ fail_on_empty=False)
except Exception as e2:
rollback_error_text = "Rollback Exception {}: {}".format(rollback_item, e2)
cherrypy.log(rollback_error_text)
"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))
-
-
-# def validate_password(realm, username, password):
-# cherrypy.log("realm "+ str(realm))
-# if username == "admin" and password == "admin":
-# return True
-# return False
+ 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():
Set database, storage, message configuration
Init database with admin/admin user password
"""
+ global nbi_server
+ global subscription_thread
cherrypy.log.error("Starting osm_nbi")
# update general cherrypy configuration
update_dict = {}
# TODO add more entries, e.g.: storage
cherrypy.tree.apps['/osm'].root.engine.start(engine_config)
cherrypy.tree.apps['/osm'].root.authenticator.start(engine_config)
- try:
- cherrypy.tree.apps['/osm'].root.engine.init_db(target_version=database_version)
- cherrypy.tree.apps['/osm'].root.authenticator.init_db(target_version=auth_database_version)
- except (EngineException, AuthException):
- pass
- # getenv('OSMOPENMANO_TENANT', None)
+ cherrypy.tree.apps['/osm'].root.engine.init_db(target_version=database_version)
+ cherrypy.tree.apps['/osm'].root.authenticator.init_db(target_version=auth_database_version)
+
+ # start subscriptions thread:
+ subscription_thread = SubscriptionThread(config=engine_config, engine=nbi_server.engine)
+ subscription_thread.start()
+ # Do not capture except SubscriptionException
+
+ 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():
Callback function called when cherrypy.engine stops
TODO: Ending database connections.
"""
+ global subscription_thread
+ if subscription_thread:
+ subscription_thread.terminate()
+ subscription_thread = None
cherrypy.tree.apps['/osm'].root.engine.stop()
cherrypy.log.error("Stopping osm_nbi")
def nbi(config_file):
+ global nbi_server
# conf = {
# '/': {
# #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
# cherrypy.config.update({'tools.auth_basic.on': True,
# 'tools.auth_basic.realm': 'localhost',
# 'tools.auth_basic.checkpassword': validate_password})
+ nbi_server = Server()
cherrypy.engine.subscribe('start', _start_service)
cherrypy.engine.subscribe('stop', _stop_service)
- cherrypy.quickstart(Server(), '/osm', config_file)
+ cherrypy.quickstart(nbi_server, '/osm', config_file)
def usage():