diff options
Diffstat (limited to 'moon_wrapper/moon_wrapper')
-rw-r--r-- | moon_wrapper/moon_wrapper/__init__.py | 6 | ||||
-rw-r--r-- | moon_wrapper/moon_wrapper/__main__.py | 4 | ||||
-rw-r--r-- | moon_wrapper/moon_wrapper/api/__init__.py | 0 | ||||
-rw-r--r-- | moon_wrapper/moon_wrapper/api/generic.py | 131 | ||||
-rw-r--r-- | moon_wrapper/moon_wrapper/api/wrapper.py | 120 | ||||
-rw-r--r-- | moon_wrapper/moon_wrapper/http_server.py | 140 | ||||
-rw-r--r-- | moon_wrapper/moon_wrapper/server.py | 33 |
7 files changed, 434 insertions, 0 deletions
diff --git a/moon_wrapper/moon_wrapper/__init__.py b/moon_wrapper/moon_wrapper/__init__.py new file mode 100644 index 00000000..903c6518 --- /dev/null +++ b/moon_wrapper/moon_wrapper/__init__.py @@ -0,0 +1,6 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. + +__version__ = "0.1.0" diff --git a/moon_wrapper/moon_wrapper/__main__.py b/moon_wrapper/moon_wrapper/__main__.py new file mode 100644 index 00000000..46cafa76 --- /dev/null +++ b/moon_wrapper/moon_wrapper/__main__.py @@ -0,0 +1,4 @@ +from moon_wrapper.server import main + +server = main() +server.run() diff --git a/moon_wrapper/moon_wrapper/api/__init__.py b/moon_wrapper/moon_wrapper/api/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/moon_wrapper/moon_wrapper/api/__init__.py diff --git a/moon_wrapper/moon_wrapper/api/generic.py b/moon_wrapper/moon_wrapper/api/generic.py new file mode 100644 index 00000000..7dd44fb4 --- /dev/null +++ b/moon_wrapper/moon_wrapper/api/generic.py @@ -0,0 +1,131 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. +""" +Those API are helping API used to manage the Moon platform. +""" + +from flask_restful import Resource, request +from oslo_log import log as logging +import moon_wrapper.api +from python_moonutilities.security_functions import check_auth + +__version__ = "0.1.0" + +LOG = logging.getLogger("moon.manager.api." + __name__) + + +class Status(Resource): + """ + Endpoint for status requests + """ + + __urls__ = ("/status", "/status/", "/status/<string:component_id>") + + def get(self, component_id=None): + """Retrieve status of all components + + :return: { + "orchestrator": { + "status": "Running" + }, + "security_router": { + "status": "Running" + } + } + """ + raise NotImplemented + + +class Logs(Resource): + """ + Endpoint for logs requests + """ + + __urls__ = ("/logs", "/logs/", "/logs/<string:component_id>") + + def get(self, component_id=None): + """Get logs from the Moon platform + + :param component_id: the ID of the component your are looking for (optional) + :return: [ + "2015-04-15-13:45:20 + "2015-04-15-13:45:21 + "2015-04-15-13:45:22 + "2015-04-15-13:45:23 + ] + """ + filter_str = request.args.get('filter', '') + from_str = request.args.get('from', '') + to_str = request.args.get('to', '') + event_number = request.args.get('event_number', '') + try: + event_number = int(event_number) + except ValueError: + event_number = None + args = dict() + args["filter"] = filter_str + args["from"] = from_str + args["to"] = to_str + args["event_number"] = event_number + + raise NotImplemented + + +class API(Resource): + """ + Endpoint for API requests + """ + + __urls__ = ( + "/api", + "/api/", + "/api/<string:group_id>", + "/api/<string:group_id>/", + "/api/<string:group_id>/<string:endpoint_id>") + + @check_auth + def get(self, group_id="", endpoint_id="", user_id=""): + """Retrieve all API endpoints or a specific endpoint if endpoint_id is given + + :param group_id: the name of one existing group (ie generic, ...) + :param endpoint_id: the name of one existing component (ie Logs, Status, ...) + :return: { + "group_name": { + "endpoint_name": { + "description": "a description", + "methods": { + "get": "description of the HTTP method" + }, + "urls": ('/api', '/api/', '/api/<string:endpoint_id>') + } + } + """ + __methods = ("get", "post", "put", "delete", "options", "patch") + api_list = filter(lambda x: "__" not in x, dir(moon_wrapper.api)) + api_desc = dict() + for api_name in api_list: + api_desc[api_name] = {} + group_api_obj = eval("moon_interface.api.{}".format(api_name)) + api_desc[api_name]["description"] = group_api_obj.__doc__ + if "__version__" in dir(group_api_obj): + api_desc[api_name]["version"] = group_api_obj.__version__ + object_list = list(filter(lambda x: "__" not in x, dir(group_api_obj))) + for obj in map(lambda x: eval("moon_interface.api.{}.{}".format(api_name, x)), object_list): + if "__urls__" in dir(obj): + api_desc[api_name][obj.__name__] = dict() + api_desc[api_name][obj.__name__]["urls"] = obj.__urls__ + api_desc[api_name][obj.__name__]["methods"] = dict() + for _method in filter(lambda x: x in __methods, dir(obj)): + docstring = eval("moon_interface.api.{}.{}.{}.__doc__".format(api_name, obj.__name__, _method)) + api_desc[api_name][obj.__name__]["methods"][_method] = docstring + api_desc[api_name][obj.__name__]["description"] = str(obj.__doc__) + if group_id in api_desc: + if endpoint_id in api_desc[group_id]: + return {group_id: {endpoint_id: api_desc[group_id][endpoint_id]}} + elif len(endpoint_id) > 0: + LOG.error("Unknown endpoint_id {}".format(endpoint_id)) + return {"error": "Unknown endpoint_id {}".format(endpoint_id)} + return {group_id: api_desc[group_id]} + return api_desc diff --git a/moon_wrapper/moon_wrapper/api/wrapper.py b/moon_wrapper/moon_wrapper/api/wrapper.py new file mode 100644 index 00000000..e1ce783a --- /dev/null +++ b/moon_wrapper/moon_wrapper/api/wrapper.py @@ -0,0 +1,120 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. +""" +Authz is the endpoint to get authorization response +""" + +import flask +from flask import request +from flask_restful import Resource +import logging +import json +import requests +from python_moonutilities import exceptions + +__version__ = "0.1.0" + +LOG = logging.getLogger("moon.wrapper.api." + __name__) + + +class Wrapper(Resource): + """ + Endpoint for authz requests + """ + + __urls__ = ( + "/authz", + "/authz/", + ) + + def __init__(self, **kwargs): + self.port = kwargs.get("port") + self.CACHE = kwargs.get("cache", {}) + self.TIMEOUT = 5 + + # def get(self): + # LOG.info("GET") + # return self.manage_data() + + def post(self): + LOG.debug("POST {}".format(request.form)) + response = flask.make_response("False") + if self.manage_data(): + response = flask.make_response("True") + response.headers['content-type'] = 'application/octet-stream' + return response + + @staticmethod + def __get_subject(target, credentials): + _subject = target.get("user_id", "") + if not _subject: + _subject = credentials.get("user_id", "none") + return _subject + + @staticmethod + def __get_object(target, credentials): + try: + # note: case of Glance + return target['target']['name'] + except KeyError: + pass + + # note: default case + return target.get("project_id", "none") + + @staticmethod + def __get_project_id(target, credentials): + LOG.info("__get_project_id {}".format(target)) + return target.get("project_id", "none") + + def get_interface_url(self, project_id): + LOG.info("project_id {}".format(project_id)) + for containers in self.CACHE.containers.values(): + LOG.info("containers {}".format(containers)) + for container in containers: + if container.get("keystone_project_id") == project_id: + if "interface" in container['name']: + return "http://{}:{}".format( + container['name'], + container['port']) + self.CACHE.update() + # Note (asteroide): test an other time after the update + for containers in self.CACHE.containers.values(): + for container in containers: + if container.get("keystone_project_id") == project_id: + if "interface" in container['name']: + return "http://{}:{}".format( + container['name'], + container['port']) + raise exceptions.AuthzException("Keystone Project " + "ID ({}) is unknown or not mapped " + "to a PDP.".format(project_id)) + + def manage_data(self): + data = request.form + if not dict(request.form): + data = json.loads(request.data.decode("utf-8")) + target = json.loads(data.get('target', {})) + credentials = json.loads(data.get('credentials', {})) + rule = data.get('rule', "") + _subject = self.__get_subject(target, credentials) + _object = self.__get_object(target, credentials) + _project_id = self.__get_project_id(target, credentials) + LOG.debug("POST with args project={} / " + "subject={} - object={} - action={}".format( + _project_id, _subject, _object, rule)) + interface_url = self.get_interface_url(_project_id) + LOG.debug("interface_url={}".format(interface_url)) + req = requests.get("{}/authz/{}/{}/{}/{}".format( + interface_url, + _project_id, + _subject, + _object, + rule + )) + LOG.debug("Get interface {}".format(req.text)) + if req.status_code == 200: + if req.json().get("result", False): + return True diff --git a/moon_wrapper/moon_wrapper/http_server.py b/moon_wrapper/moon_wrapper/http_server.py new file mode 100644 index 00000000..1b429bc5 --- /dev/null +++ b/moon_wrapper/moon_wrapper/http_server.py @@ -0,0 +1,140 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. + +from flask import Flask, jsonify +from flask_restful import Resource, Api +import logging +from moon_wrapper import __version__ +from moon_wrapper.api.generic import Status, Logs, API +from moon_wrapper.api.wrapper import Wrapper +from python_moonutilities.cache import Cache +from python_moonutilities import configuration, exceptions + +logger = logging.getLogger("moon.wrapper.http") + + +CACHE = Cache() + + +class Server: + """Base class for HTTP server""" + + def __init__(self, host="localhost", port=80, api=None, **kwargs): + """Run a server + + :param host: hostname of the server + :param port: port for the running server + :param kwargs: optional parameters + :return: a running server + """ + self._host = host + self._port = port + self._api = api + self._extra = kwargs + + @property + def host(self): + return self._host + + @host.setter + def host(self, name): + self._host = name + + @host.deleter + def host(self): + self._host = "" + + @property + def port(self): + return self._port + + @port.setter + def port(self, number): + self._port = number + + @port.deleter + def port(self): + self._port = 80 + + def run(self): + raise NotImplementedError() + +__API__ = ( + Status, Logs, API + ) + + +class Root(Resource): + """ + The root of the web service + """ + __urls__ = ("/", ) + __methods = ("get", "post", "put", "delete", "options") + + def get(self): + tree = {"/": {"methods": ("get",), + "description": "List all methods for that service."}} + for item in __API__: + tree[item.__name__] = {"urls": item.__urls__} + _methods = [] + for _method in self.__methods: + if _method in dir(item): + _methods.append(_method) + tree[item.__name__]["methods"] = _methods + tree[item.__name__]["description"] = item.__doc__.strip() + return { + "version": __version__, + "tree": tree + } + + +class HTTPServer(Server): + + def __init__(self, host="localhost", port=80, **kwargs): + super(HTTPServer, self).__init__(host=host, port=port, **kwargs) + self.app = Flask(__name__) + self.port = port + conf = configuration.get_configuration("components/orchestrator") + _hostname = conf["components/orchestrator"].get("hostname", + "orchestrator") + _port = conf["components/orchestrator"].get("port", 80) + _protocol = conf["components/orchestrator"].get("protocol", "http") + self.orchestrator_url = "{}://{}:{}".format( + _protocol, _hostname, _port) + # Todo : specify only few urls instead of * + # CORS(self.app) + self.api = Api(self.app) + self.__set_route() + self.__hook_errors() + + def __hook_errors(self): + + def get_404_json(e): + return jsonify({"result": False, "code": 404, + "description": str(e)}), 404 + self.app.register_error_handler(404, get_404_json) + + def get_400_json(e): + return jsonify({"result": False, "code": 400, + "description": str(e)}), 400 + self.app.register_error_handler(400, lambda e: get_400_json) + self.app.register_error_handler(403, exceptions.AuthException) + + def __set_route(self): + self.api.add_resource(Root, '/') + + for api in __API__: + self.api.add_resource(api, *api.__urls__) + self.api.add_resource(Wrapper, *Wrapper.__urls__, + resource_class_kwargs={ + "orchestrator_url": self.orchestrator_url, + "cache": CACHE, + } + ) + + def run(self): + self.app.run(host=self._host, port=self._port) # nosec + # self.app.run(debug=True, host=self._host, port=self._port) # nosec + diff --git a/moon_wrapper/moon_wrapper/server.py b/moon_wrapper/moon_wrapper/server.py new file mode 100644 index 00000000..2f236c4f --- /dev/null +++ b/moon_wrapper/moon_wrapper/server.py @@ -0,0 +1,33 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. + +import logging +from python_moonutilities import configuration, exceptions +from moon_wrapper.http_server import HTTPServer + +LOG = logging.getLogger("moon.wrapper") + + +def main(): + configuration.init_logging() + try: + conf = configuration.get_configuration("components/wrapper") + LOG.debug("wrapper.conf={}".format(conf)) + hostname = conf["components/wrapper"].get("hostname", "wrapper") + port = conf["components/wrapper"].get("port", 80) + bind = conf["components/wrapper"].get("bind", "127.0.0.1") + except exceptions.ConsulComponentNotFound: + hostname = "wrapper" + bind = "127.0.0.1" + port = 80 + configuration.add_component(uuid="wrapper", name=hostname, port=port, bind=bind) + LOG.info("Starting server with IP {} on port {} bind to {}".format(hostname, port, bind)) + server = HTTPServer(host=bind, port=port) + return server + + +if __name__ == '__main__': + server = main() + server.run() |