#!/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
from authconn import AuthException
from auth import Authenticator
from engine import Engine, EngineException
+from subscriptions import SubscriptionThread
from validation import ValidationError
from osm_common.dbbase import DbException
from osm_common.fsbase import FsException
__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"
+version_date = "Jan 2019"
database_version = '1.0'
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)
/<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
"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")}
},
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")
def test(self, *args, **kwargs):
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"'
" 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:
outdata = self.engine.del_item_list(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 force:
nslcmop_desc = {
"lcmOperationType": "terminate",
"autoremove": True
}
opp_id = self.engine.new_item(rollback, session, "nslcmops", nslcmop_desc, None)
- outdata = {"_id": opp_id}
- cherrypy.response.status = HTTPStatus.ACCEPTED.value
+ if opp_id:
+ delete_in_process = True
+ outdata = {"_id": opp_id}
+ cherrypy.response.status = HTTPStatus.ACCEPTED.value
elif topic == "netslice_instances_content" and not force:
nsilcmop_desc = {
"lcmOperationType": "terminate",
"autoremove": True
}
opp_id = self.engine.new_item(rollback, session, "nsilcmops", nsilcmop_desc, None)
- outdata = {"_id": opp_id}
- cherrypy.response.status = HTTPStatus.ACCEPTED.value
- else:
+ 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, force)
cherrypy.response.status = HTTPStatus.NO_CONTENT.value
- if engine_topic in ("vim_accounts", "sdns"):
+ if engine_topic in ("vim_accounts", "wim_accounts", "sdns"):
cherrypy.response.status = HTTPStatus.ACCEPTED.value
elif method in ("PUT", "PATCH"):
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()
self.engine.db.set_one(rollback_item["topic"], {"_id": rollback_item["_id"]},
rollback_item["content"], fail_on_empty=False)
else:
- self.engine.del_item(**rollback_item, session=session, force=True)
+ 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)
# 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
-
-
def _start_service():
"""
Callback function called when cherrypy.engine starts
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 = {}
cherrypy.tree.apps['/osm'].root.authenticator.start(engine_config)
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)
- # getenv('OSMOPENMANO_TENANT', None)
+
+ # start subscriptions thread:
+ subscription_thread = SubscriptionThread(config=engine_config, engine=nbi_server.engine)
+ 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
def _stop_service():
Callback function called when cherrypy.engine stops
TODO: Ending database connections.
"""
+ global 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():