diff options
author | Thomas Duval <thomas.duval@orange.com> | 2018-01-15 21:12:32 +0100 |
---|---|---|
committer | Thomas Duval <thomas.duval@orange.com> | 2018-01-15 21:12:32 +0100 |
commit | ba90e446c6d5a51c9ed392416f90b7bb56d11603 (patch) | |
tree | d4f2f02ace9af594fbe6abce3824cfb494ece998 /moon_orchestrator | |
parent | 1da8f2e5f28813a90ecc329462dcc647ce757494 (diff) |
Add th ability to (un)load wrapper components through API
Change-Id: I58a25dbc0479e416d471115885dab7ccfb27e18a
Diffstat (limited to 'moon_orchestrator')
-rw-r--r-- | moon_orchestrator/moon_orchestrator/api/pods.py | 100 | ||||
-rw-r--r-- | moon_orchestrator/moon_orchestrator/api/slaves.py | 46 | ||||
-rw-r--r-- | moon_orchestrator/moon_orchestrator/drivers.py | 124 | ||||
-rw-r--r-- | moon_orchestrator/moon_orchestrator/http_server.py | 7 | ||||
-rw-r--r-- | moon_orchestrator/tests/unit_python/test_pods.py | 60 | ||||
-rw-r--r-- | moon_orchestrator/tests/unit_python/test_slaves.py | 17 |
6 files changed, 300 insertions, 54 deletions
diff --git a/moon_orchestrator/moon_orchestrator/api/pods.py b/moon_orchestrator/moon_orchestrator/api/pods.py index 31ae31de..3a01c3a9 100644 --- a/moon_orchestrator/moon_orchestrator/api/pods.py +++ b/moon_orchestrator/moon_orchestrator/api/pods.py @@ -6,6 +6,7 @@ from flask import request from flask_restful import Resource from python_moonutilities.security_functions import check_auth +from python_moonutilities import exceptions import logging logger = logging.getLogger("moon.orchestrator.api.pods") @@ -17,6 +18,7 @@ class Pods(Resource): """ __version__ = "4.3.1" + POD_TYPES = ("authz", "wrapper") __urls__ = ( "/pods", @@ -57,6 +59,21 @@ class Pods(Resource): except Exception as e: return {"result": False, "message": str(e)}, 500 + def __get_pod_with_keystone_pid(self, keystone_pid): + for pod_key, pod_values in self.driver.get_pods().items(): + if pod_values[0]['keystone_project_id'] == keystone_pid: + return True + + def __get_wrapper(self, slave_name): + for slave in self.driver.get_slaves(): + if slave_name == slave["name"] \ + and slave["configured"]: + return True + + def __get_slave_names(self): + for slave in self.driver.get_slaves(): + yield slave["name"] + @check_auth def post(self, uuid=None, user_id=None): """Create a new pod. @@ -64,9 +81,14 @@ class Pods(Resource): :param uuid: uuid of the pod (not used here) :param user_id: user ID who do the request :request body: { - "name": "...", - "description": "...", - "type": "plugin_name" + "pdp_id": "fa2323f7055d4a88b1b85d31fe5e8369", + "name": "pdp_rbac3", + "keystone_project_id": "ceacbb5564cc48ad929dd4f00e52bf63", + "models": {...}, + "policies": {...}, + "description": "test", + "security_pipeline": [...], + "slave_name": "" } :return: { "pdp_id1": { @@ -76,25 +98,35 @@ class Pods(Resource): } } """ - logger.debug("POST param={}".format(request.json)) - try: + pods = {} + if "security_pipeline" in request.json: + if self.__get_pod_with_keystone_pid(request.json.get("keystone_project_id")): + raise exceptions.PipelineConflict self.driver.create_pipeline( request.json.get("keystone_project_id"), request.json.get("pdp_id"), request.json.get("security_pipeline"), manager_data=request.json, - active_context=None, - active_context_name=None) - pods = {} + slave_name=request.json.get("slave_name")) for _pod_key, _pod_values in self.driver.get_pods().items(): pods[_pod_key] = [] for _pod_value in _pod_values: if _pod_value['namespace'] != "moon": continue pods[_pod_key].append(_pod_value) - return {"pods": pods} - except Exception as e: - return {"result": False, "message": str(e)}, 500 + else: + logger.info("------------------------------------") + logger.info(list(self.__get_slave_names())) + logger.info("------------------------------------") + if self.__get_wrapper(request.json.get("slave_name")): + raise exceptions.WrapperConflict + if request.json.get("slave_name") not in self.__get_slave_names(): + raise exceptions.SlaveNameUnknown + slave_name = request.json.get("slave_name") + if not slave_name: + slave_name = self.driver.get_slaves(active=True) + self.driver.create_wrappers(slave_name) + return {"pods": self.driver.get_pods()} @check_auth def delete(self, uuid=None, user_id=None): @@ -110,27 +142,31 @@ class Pods(Resource): try: self.driver.delete_pipeline(uuid) return {'result': True} + except exceptions.PipelineUnknown: + for slave in self.driver.get_slaves(): + if uuid in (slave['name'], slave["wrapper_name"]): + self.driver.delete_wrapper(name=slave["wrapper_name"]) except Exception as e: return {"result": False, "message": str(e)}, 500 - @check_auth - def patch(self, uuid=None, user_id=None): - """Update a pod - - :param uuid: uuid of the pdp to update - :param user_id: user ID who do the request - :request body: { - "name": "...", - "replicas": "...", - "description": "...", - } - :return: { - "pod_id1": { - "name": "...", - "replicas": "...", - "description": "...", - } - } - :internal_api: update_pdp - """ - return {"pods": None} + # @check_auth + # def patch(self, uuid=None, user_id=None): + # """Update a pod + # + # :param uuid: uuid of the pdp to update + # :param user_id: user ID who do the request + # :request body: { + # "name": "...", + # "replicas": "...", + # "description": "...", + # } + # :return: { + # "pod_id1": { + # "name": "...", + # "replicas": "...", + # "description": "...", + # } + # } + # :internal_api: update_pdp + # """ + # return {"pods": None} diff --git a/moon_orchestrator/moon_orchestrator/api/slaves.py b/moon_orchestrator/moon_orchestrator/api/slaves.py new file mode 100644 index 00000000..7453d305 --- /dev/null +++ b/moon_orchestrator/moon_orchestrator/api/slaves.py @@ -0,0 +1,46 @@ +# 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 request +from flask_restful import Resource +from python_moonutilities.security_functions import check_auth +import logging + +logger = logging.getLogger("moon.orchestrator.api.slaves") + + +class Slaves(Resource): + """ + Endpoint for slaves requests + """ + + __version__ = "4.3.1" + + __urls__ = ( + "/slaves", + "/slaves/", + "/slaves/<string:uuid>", + "/slaves/<string:uuid>/", + ) + + def __init__(self, **kwargs): + self.driver = kwargs.get("driver") + + @check_auth + def get(self, uuid=None, user_id=None): + """Retrieve all pods + + :param uuid: uuid of the pod + :param user_id: user ID who do the request + :return: { + "pod_id1": { + "name": "...", + "replicas": "...", + "description": "...", + } + } + """ + slaves = self.driver.get_slaves() + return {"slaves": slaves} diff --git a/moon_orchestrator/moon_orchestrator/drivers.py b/moon_orchestrator/moon_orchestrator/drivers.py index 9c230cce..bb0d0c2c 100644 --- a/moon_orchestrator/moon_orchestrator/drivers.py +++ b/moon_orchestrator/moon_orchestrator/drivers.py @@ -159,17 +159,17 @@ class K8S(Driver): resp = client.create_namespaced_service(namespace="moon", body=service_manifest) logger.info("Service {} created!".format(data.get('name'))) - return resp + return service_manifest def load_deployment_and_service(self, data, api_client=None, ext_client=None, expose=False): _client = api_client if api_client else self.client + manifest = self.__create_service(client=_client, data=data[0], + expose=expose) + data[0]["external_port"] = manifest['spec']['ports'][0].get('nodePort') pod = self.__create_deployment(client=ext_client, data=data) - self.__create_service(client=_client, data=data[0], - expose=expose) self.cache[pod.metadata.uid] = data - @staticmethod - def delete_deployment(name=None, namespace="moon", ext_client=None): + def delete_deployment(self, name=None, namespace="moon", ext_client=None): logger.info("Deleting deployment {}".format(name)) body = client.V1DeleteOptions(propagation_policy='Foreground') ret = ext_client.delete_namespaced_deployment( @@ -177,7 +177,16 @@ class K8S(Driver): namespace=namespace, body=body ) - logger.info(ret) + logger.debug(ret) + _uid = None + for uid, value in self.cache.items(): + if value[0]['name'] == name: + _uid = uid + break + if _uid: + self.cache.pop(_uid) + else: + raise exceptions.DockerError("Cannot find and delete pod named {}".format(name)) def delete_service(self, name, namespace="moon", api_client=None): if not api_client: @@ -185,12 +194,45 @@ class K8S(Driver): ret = api_client.delete_namespaced_service(name=name, namespace=namespace) logger.debug("delete_service {}".format(ret)) - def get_slaves(self): + def get_slaves(self, active=False): + contexts, active_context = self.get_contexts() + pods = self.get_pods() + # logger.info("pods = {}".format(pods)) + slaves = [] + if active: + for key, value in pods.items(): + # logger.info("ctx={}".format(active_context)) + # logger.info("value={}".format(value)) + if active_context["name"] == value[0].get('slave_name'): + data = dict(active_context) + data["wrapper_name"] = value[0]['name'] + data["ip"] = value[0].get("ip", "NC") + data["port"] = value[0].get("external_port", "NC") + slaves.append(data) + break + return slaves + for ctx in contexts: + data = dict(ctx) + data["configured"] = False + for key, value in pods.items(): + # logger.info("ctx={}".format(ctx)) + # logger.info("value={}".format(value)) + if ctx["name"] == value[0].get('slave_name'): + data["wrapper_name"] = value[0]['name'] + data["ip"] = value[0].get("ip", "NC") + data["port"] = value[0].get("external_port", "NC") + data["configured"] = True + break + slaves.append(data) + return slaves + + @staticmethod + def get_contexts(): contexts, active_context = config.list_kube_config_contexts() return contexts, active_context - def create_wrappers(self): - contexts, active_context = self.get_slaves() + def create_wrappers(self, slave_name=None): + contexts, active_context = self.get_contexts() logger.debug("contexts: {}".format(contexts)) logger.debug("active_context: {}".format(active_context)) conf = configuration.get_configuration("components/wrapper") @@ -201,6 +243,8 @@ class K8S(Driver): "container", "wukongsun/moon_wrapper:v4.3") for _ctx in contexts: + if slave_name and slave_name != _ctx['name']: + continue _config = config.new_client_from_config(context=_ctx['name']) logger.debug("_config={}".format(_config)) api_client = client.CoreV1Api(_config) @@ -209,14 +253,56 @@ class K8S(Driver): "name": hostname + "-" + get_random_name(), "container": container, "port": port, - "namespace": "moon" + "namespace": "moon", + "slave_name": _ctx['name'] }, ] self.load_deployment_and_service(data, api_client, ext_client, expose=True) + def delete_wrapper(self, uuid=None, name=None, namespace="moon", + active_context=None, + active_context_name=None): + name_to_delete = None + if uuid and uuid in self.get_pods(): + name_to_delete = self.get_pods()[uuid][0]['name'] + elif name: + for pod_key, pod_list in self.get_pods().items(): + for pod_value in pod_list: + if pod_value.get("name") == name: + name_to_delete = pod_value.get("name") + break + if not name_to_delete: + raise exceptions.WrapperUnknown + contexts, _active_context = self.get_contexts() + if active_context_name: + for _context in contexts: + if _context["name"] == active_context_name: + active_context = _context + break + if active_context: + active_context = _active_context + _config = config.new_client_from_config( + context=active_context['name']) + logger.debug("_config={}".format(_config)) + api_client = client.CoreV1Api(_config) + ext_client = client.ExtensionsV1beta1Api(_config) + self.delete_deployment(name=name_to_delete, namespace=namespace, + ext_client=ext_client) + self.delete_service(name=name_to_delete, api_client=api_client) + return + logger.debug("contexts={}".format(contexts)) + for _ctx in contexts: + _config = config.new_client_from_config(context=_ctx['name']) + logger.debug("_config={}".format(_config)) + api_client = client.CoreV1Api(_config) + ext_client = client.ExtensionsV1beta1Api(_config) + self.delete_deployment(name=name_to_delete, namespace=namespace, + ext_client=ext_client) + self.delete_service(name=name_to_delete, api_client=api_client) + def create_pipeline(self, keystone_project_id, pdp_id, policy_ids, manager_data=None, active_context=None, - active_context_name=None): + slave_name=None): """ Create security functions :param keystone_project_id: the Keystone project id @@ -225,7 +311,7 @@ class K8S(Driver): :param manager_data: data needed to create pods :param active_context: if present, add the security function in this context - :param active_context_name: if present, add the security function in + :param slave_name: if present, add the security function in this context name if active_context_name and active_context are not present, add the security function in all context (ie, in all slaves) @@ -295,12 +381,12 @@ class K8S(Driver): "namespace": "moon" }) logger.debug("data={}".format(data)) - contexts, _active_context = self.get_slaves() - logger.debug("active_context_name={}".format(active_context_name)) + contexts, _active_context = self.get_contexts() + logger.debug("active_context_name={}".format(slave_name)) logger.debug("active_context={}".format(active_context)) - if active_context_name: + if slave_name: for _context in contexts: - if _context["name"] == active_context_name: + if _context["name"] == slave_name: active_context = _context break if active_context: @@ -314,6 +400,8 @@ class K8S(Driver): return logger.debug("contexts={}".format(contexts)) for _ctx in contexts: + if slave_name and slave_name != _ctx['name']: + continue _config = config.new_client_from_config(context=_ctx['name']) logger.debug("_config={}".format(_config)) api_client = client.CoreV1Api(_config) @@ -342,9 +430,9 @@ class K8S(Driver): name_to_delete = pod_value.get("name") break if not name_to_delete: - raise exceptions.MoonError("Cannot find pipeline") + raise exceptions.PipelineUnknown logger.info("Will delete deployment and service named {}".format(name_to_delete)) - contexts, _active_context = self.get_slaves() + contexts, _active_context = self.get_contexts() if active_context_name: for _context in contexts: if _context["name"] == active_context_name: diff --git a/moon_orchestrator/moon_orchestrator/http_server.py b/moon_orchestrator/moon_orchestrator/http_server.py index fa5308d0..85e29cd0 100644 --- a/moon_orchestrator/moon_orchestrator/http_server.py +++ b/moon_orchestrator/moon_orchestrator/http_server.py @@ -10,6 +10,7 @@ import requests import time from moon_orchestrator import __version__ from moon_orchestrator.api.pods import Pods +from moon_orchestrator.api.slaves import Slaves from moon_orchestrator.api.generic import Status from moon_orchestrator.drivers import get_driver from python_moonutilities import configuration, exceptions @@ -122,7 +123,7 @@ class HTTPServer(Server): if "pdps" in pdp.json(): break logger.debug("pdp={}".format(pdp)) - self.driver.create_wrappers() + # self.driver.create_wrappers() for _pdp_key, _pdp_value in pdp.json()['pdps'].items(): if _pdp_value.get('keystone_project_id'): # TODO: select context to add security function @@ -151,6 +152,10 @@ class HTTPServer(Server): resource_class_kwargs={ "driver": self.driver }) + self.api.add_resource(Slaves, *Slaves.__urls__, + resource_class_kwargs={ + "driver": self.driver + }) def run(self): self.app.run(host=self._host, port=self._port) # nosec diff --git a/moon_orchestrator/tests/unit_python/test_pods.py b/moon_orchestrator/tests/unit_python/test_pods.py index f760aa62..678645be 100644 --- a/moon_orchestrator/tests/unit_python/test_pods.py +++ b/moon_orchestrator/tests/unit_python/test_pods.py @@ -31,7 +31,7 @@ def test_get_pods_failure(context, monkeypatch): assert not data["pods"] -def test_add_pods(context, monkeypatch): +def test_add_pods_with_pipeline(context, monkeypatch): patch_k8s(monkeypatch) import moon_orchestrator.server @@ -52,6 +52,60 @@ def test_add_pods(context, monkeypatch): assert data["pods"] +def test_add_pods_without_pipeline_with_bad_slave_name(context, monkeypatch): + patch_k8s(monkeypatch) + + import moon_orchestrator.server + server = moon_orchestrator.server.create_server() + _client = server.app.test_client() + data = { + "slave_name": "test", + } + req = _client.post("/pods", data=json.dumps(data), + headers={'Content-Type': 'application/json'}) + assert req.status_code == 400 + assert req.data + data = get_json(req.data) + assert isinstance(data, dict) + assert 'The slave is unknown.' in data['message'] + + +def test_add_pods_without_pipeline_with_good_slave_name(context, monkeypatch): + patch_k8s(monkeypatch) + + import moon_orchestrator.server + server = moon_orchestrator.server.create_server() + _client = server.app.test_client() + data = { + "slave_name": "active_context", + } + req = _client.post("/pods", data=json.dumps(data), + headers={'Content-Type': 'application/json'}) + assert req.status_code == 200 + assert req.data + data = get_json(req.data) + assert isinstance(data, dict) + assert "pods" in data + assert data["pods"] + + +def test_add_pods_without_pipeline_without_slave_name(context, monkeypatch): + patch_k8s(monkeypatch) + + import moon_orchestrator.server + server = moon_orchestrator.server.create_server() + _client = server.app.test_client() + data = { + } + req = _client.post("/pods", data=json.dumps(data), + headers={'Content-Type': 'application/json'}) + assert req.status_code == 400 + assert req.data + data = get_json(req.data) + assert isinstance(data, dict) + assert 'The slave is unknown.' in data['message'] + + def test_add_pods_with_no_data(context, monkeypatch): patch_k8s(monkeypatch) import moon_orchestrator.server @@ -59,10 +113,10 @@ def test_add_pods_with_no_data(context, monkeypatch): _client = server.app.test_client() req = _client.post("/pods", data=json.dumps({}), headers={'Content-Type': 'application/json'}) - assert req.status_code == 500 + assert req.status_code == 400 assert req.data data = get_json(req.data) - assert '400: Policy Unknown' in data['message'] + assert 'The slave is unknown.' in data['message'] def test_add_pods_with_no_policies_no_models(context, monkeypatch, no_requests): diff --git a/moon_orchestrator/tests/unit_python/test_slaves.py b/moon_orchestrator/tests/unit_python/test_slaves.py new file mode 100644 index 00000000..88ff7e55 --- /dev/null +++ b/moon_orchestrator/tests/unit_python/test_slaves.py @@ -0,0 +1,17 @@ +import json +from mock_pods import patch_k8s +from utilities import get_json + + +def test_get_slaves(context, monkeypatch): + patch_k8s(monkeypatch) + + import moon_orchestrator.server + server = moon_orchestrator.server.create_server() + _client = server.app.test_client() + req = _client.get("/slaves") + assert req.status_code == 200 + assert req.data + data = get_json(req.data) + assert isinstance(data, dict) + assert "slaves" in data |