From 857c41af07fd88b50c2e960071611f425268e486 Mon Sep 17 00:00:00 2001 From: Tommy Carpenter Date: Thu, 15 Feb 2018 13:14:33 -0500 Subject: Add DTI and Policy to ONAP CBS Issue-ID: DCAEGEN2-341 Change-Id: Iead3b6568ec379988b840b5a01c7744c29b5fcf4 Signed-off-by: Tommy Carpenter --- config_binding_service/__init__.py | 12 +++--- config_binding_service/client.py | 65 ++++++++++++++++++++--------- config_binding_service/controller.py | 40 +++++++++++++++--- config_binding_service/swagger/swagger.yaml | 44 +++++++++++++++++-- 4 files changed, 128 insertions(+), 33 deletions(-) (limited to 'config_binding_service') diff --git a/config_binding_service/__init__.py b/config_binding_service/__init__.py index 51d3246..c50f47f 100644 --- a/config_binding_service/__init__.py +++ b/config_binding_service/__init__.py @@ -1,14 +1,14 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # 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 -# +# +# 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. @@ -17,6 +17,7 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + import os import logging @@ -45,8 +46,9 @@ def get_consul_uri(): if "CONSUL_HOST" in os.environ: # WARNING! TODO! Currently the env file does not include the port. # But some other people think that the port should be a part of that. - # For now, I'm hardcoding 8500 until this gets resolved. + # For now, I'm hardcoding 8500 until this gets resolved. return "http://{0}:{1}".format(os.environ["CONSUL_HOST"], 8500) else: raise BadEnviornmentENVNotFound("CONSUL_HOST") + diff --git a/config_binding_service/client.py b/config_binding_service/client.py index 02354ee..6b53996 100644 --- a/config_binding_service/client.py +++ b/config_binding_service/client.py @@ -1,14 +1,14 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # 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 -# +# +# 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. @@ -17,6 +17,7 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + import re import requests import copy @@ -44,7 +45,7 @@ class CantGetConfig(Exception): ### def _consul_get_key(key): """ - Try to fetch a key from Consul. + Try to fetch a key from Consul. No error checking here, let caller deal with it """ _logger.info("Fetching {0}".format(key)) @@ -78,7 +79,7 @@ def _get_connection_info_from_consul(service_component_name): TODO: currently assumes there is only one service TODO: WARNING: FIXTHIS: CALLINTHENATIONALARMY: - This tries to determine that a service_component_name is a cdap application by inspecting service_component_name and name munging. However, this would force all CDAP applications to have cdap_app in their name. A much better way to do this is to do some kind of catalog_lookup here, OR MAYBE change this API so that the component_type is passed in somehow. THis is a gaping TODO. + This tries to determine that a service_component_name is a cdap application by inspecting service_component_name and name munging. However, this would force all CDAP applications to have cdap_app in their name. A much better way to do this is to do some kind of catalog_lookup here, OR MAYBE change this API so that the component_type is passed in somehow. THis is a gaping TODO. """ _logger.info("Retrieving connection information for {0}".format(service_component_name)) res = requests.get("{0}/v1/catalog/service/{1}".format(CONSUL, service_component_name)) @@ -87,7 +88,7 @@ def _get_connection_info_from_consul(service_component_name): if services == []: _logger.info("Warning: config and rels keys were both valid, but there is no component named {0} registered in Consul!".format(service_component_name)) return None #later will get filtered out - else: + else: ip = services[0]["ServiceAddress"] port = services[0]["ServicePort"] if "cdap_app" in service_component_name: @@ -100,11 +101,11 @@ def _get_connection_info_from_consul(service_component_name): return { key: details[key] for key in ["connectionurl", "serviceendpoints"] } else: return "{0}:{1}".format(ip, port) - + def _replace_rels_template(rels, template_identifier): """ The magic. Replaces a template identifier {{...}} with the entrie(s) from the rels keys - NOTE: There was a discussion over whether the CBS should treat {{}} as invalid. Mike asked that + NOTE: There was a discussion over whether the CBS should treat {{}} as invalid. Mike asked that it resolve to the empty list. So, it does resolve it to empty list. """ returnl = [] @@ -125,7 +126,7 @@ def _replace_value(v, rels, dmaap): """ Takes a value v that was some value in the templatized configuration, determines whether it needs replacement (either {{}} or <<>>), and if so, replaces it. Otherwise just returns v - + implementation notes: - the split below sees if we have v = x,y,z... so we can support {{x,y,z,....}} - the lambda is because we can't fold operators in Python, wanted fold(+, L) where + when applied to lists in python is list concatenation @@ -150,16 +151,24 @@ def _replace_value(v, rels, dmaap): return v #was not a match or was not a string, return value as is def _recurse(config, rels, dmaap): - for key in config: - v = config[key] - if isinstance(v, list): - replacement = [_recurse(item, rels, dmaap) for item in v] - elif isinstance(v,dict): - replacement = _recurse(v, rels, dmaap) - else: - replacement = _replace_value(config[key], rels, dmaap) - config[key] = replacement - return config + """ + Recurse throug a configuration, or recursively a sub elemebt of it. + If it's a dict: recurse over all the values + If it's a list: recurse over all the values + If it's a string: return the replacement + If none of the above, just return the item. + """ + if isinstance(config, list): + return [_recurse(item, rels, dmaap) for item in config] + elif isinstance(config,dict): + for key in config: + config[key] = _recurse(config[key], rels, dmaap) + return config + elif isinstance(config, six.string_types): + return _replace_value(config, rels, dmaap) + else: + #not a dict, not a list, not a string, nothing to do. + return config ######### # PUBLIC API @@ -179,3 +188,19 @@ def resolve_override(config, rels=[], dmaap={}): """ #use deepcopy to make sure that config is not touched return _recurse(copy.deepcopy(config), rels, dmaap) + +def resolve_DTI(service_component_name): + try: + config = _consul_get_key("{}:dti".format(service_component_name)) + except requests.exceptions.HTTPError as e: + #might be a 404, or could be not even able to reach consul (503?), bubble up the requests error + raise CantGetConfig(e.response.status_code, e.response.text) + return config + +def resolve_policies(service_component_name): + try: + config = _consul_get_key("{}:policies".format(service_component_name)) + except requests.exceptions.HTTPError as e: + #might be a 404, or could be not even able to reach consul (503?), bubble up the requests error + raise CantGetConfig(e.response.status_code, e.response.text) + return config diff --git a/config_binding_service/controller.py b/config_binding_service/controller.py index a74d60f..ec6f05e 100644 --- a/config_binding_service/controller.py +++ b/config_binding_service/controller.py @@ -1,14 +1,14 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # 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 -# +# +# 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. @@ -17,16 +17,46 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + from config_binding_service import client, get_consul_uri, get_logger import requests from flask import request, Response import json +_logger = get_logger(__name__) + +def dtievents(service_component_name): + try: + dti = client.resolve_DTI(service_component_name) + return Response(response=json.dumps(dti), + status=200, + mimetype="application/json") + except client.CantGetConfig as e: + return Response(status=e.code, + response=e.response) + except Exception as e: + _logger.error(e) + return Response(response="Unknown error: please report", + status=500) + +def policies(service_component_name): + try: + dti = client.resolve_policies(service_component_name) + return Response(response=json.dumps(dti), + status=200, + mimetype="application/json") + except client.CantGetConfig as e: + return Response(status=e.code, + response=e.response) + except Exception as e: + _logger.error(e) + return Response(response="Unknown error: please report", + status=500) def bind_config_for_scn(service_component_name): try: bound = client.resolve(service_component_name) return Response(response=json.dumps(bound), - status=200, + status=200, mimetype="application/json") except client.CantGetConfig as e: return Response(status=e.code, diff --git a/config_binding_service/swagger/swagger.yaml b/config_binding_service/swagger/swagger.yaml index ecedb75..31fc42a 100644 --- a/config_binding_service/swagger/swagger.yaml +++ b/config_binding_service/swagger/swagger.yaml @@ -1,5 +1,7 @@ +# ============LICENSE_START======================================================= +# org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,10 +17,12 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + + --- swagger: "2.0" info: - version: "1.0.0" + version: "1.3.0" title: "Config Binding Service" paths: /service_component/{service_component_name}: @@ -34,10 +38,44 @@ paths: responses: 200: description: OK; the bound config is returned as an object - schema: + schema: type: object 404: description: there is no configuration in Consul for this component + /dti/{service_component_name}: + parameters: + - name: "service_component_name" + in: "path" + description: "Service Component Name. service_component_name:dti must be a key in consul." + required: true + type: "string" + get: + description: "Returns as JSON the value for service_component_name:dti" + operationId: "config_binding_service.controller.dtievents" + responses: + 200: + description: OK; the KV value is returned as an object + schema: + type: object + 404: + description: there is no configuration in Consul for this component's DTI events + /policies/{service_component_name}: + parameters: + - name: "service_component_name" + in: "path" + description: "Service Component Name. service_component_name:policies must be a key in consul." + required: true + type: "string" + get: + description: "Returns as JSON the value for service_component_name:policies" + operationId: "config_binding_service.controller.policies" + responses: + 200: + description: OK; the KV value is returned as an object + schema: + type: object + 404: + description: there is no configuration in Consul for this component's policies /healthcheck: get: description: "This is the health check endpoint. If this returns a 200, the server is alive and consul can be reached. If not a 200, either dead, or no connection to consul" -- cgit 1.2.3-korg