aboutsummaryrefslogtreecommitdiffstats
path: root/config_binding_service/client.py
diff options
context:
space:
mode:
Diffstat (limited to 'config_binding_service/client.py')
-rw-r--r--config_binding_service/client.py117
1 files changed, 77 insertions, 40 deletions
diff --git a/config_binding_service/client.py b/config_binding_service/client.py
index 957b4be..8facf35 100644
--- a/config_binding_service/client.py
+++ b/config_binding_service/client.py
@@ -17,13 +17,13 @@
# ECOMP is a trademark and service mark of AT&T Intellectual Property.
import re
-import requests
-import copy
+from functools import partial, reduce
import base64
+import copy
import json
+import requests
import six
from config_binding_service import get_consul_uri, get_logger
-from functools import partial, reduce
_logger = get_logger(__name__)
CONSUL = get_consul_uri()
@@ -34,13 +34,31 @@ template_match_dmaap = re.compile("<{2}([^><]*)>{2}")
###
# Cusom Exception
###
+
+
class CantGetConfig(Exception):
+ """
+ Represents an exception where a required key in consul isn't there
+ """
def __init__(self, code, response):
self.code = code
self.response = response
+
+
+class BadRequest(Exception):
+ """
+ Exception to be raised when the user tried to do something they shouldn't
+ """
+ def __init__(self, response):
+ self.code = 400
+ self.response = response
+
+
###
# Private Functions
###
+
+
def _consul_get_all_as_transaction(service_component_name):
"""
Use Consul's transaction API to get all keys of the form service_component_name:*
@@ -54,27 +72,26 @@ def _consul_get_all_as_transaction(service_component_name):
}
}]
- response = requests.put("{0}/v1/txn".format(CONSUL),
- json = payload)
+ response = requests.put("{0}/v1/txn".format(CONSUL), json=payload)
try:
response.raise_for_status()
- except requests.exceptions.HTTPError as e:
- raise CantGetConfig(e.response.status_code, e.response.text)
+ except requests.exceptions.HTTPError as exc:
+ raise CantGetConfig(exc.response.status_code, exc.response.text)
- res = json.loads(response.text)['Results']
+ result = json.loads(response.text)['Results']
new_res = {}
- for r in res:
- key = r["KV"]["Key"]
- val = r["KV"]["Value"]
- new_res[key] = json.loads(base64.b64decode(r["KV"]["Value"]).decode("utf-8"))
+ for res in result:
+ key = res["KV"]["Key"]
+ new_res[key] = json.loads(base64.b64decode(res["KV"]["Value"]).decode("utf-8"))
if service_component_name not in new_res:
raise CantGetConfig(404, "")
return new_res
+
def _get_config_rels_dmaap(service_component_name):
allk = _consul_get_all_as_transaction(service_component_name)
config = allk[service_component_name]
@@ -82,6 +99,7 @@ def _get_config_rels_dmaap(service_component_name):
dmaap = allk.get(service_component_name + ":dmaap", {})
return config, rels, dmaap
+
def _get_connection_info_from_consul(service_component_name):
"""
Call consul's catalog
@@ -97,19 +115,18 @@ 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:
- ip = services[0]["ServiceAddress"]
- port = services[0]["ServicePort"]
- if "cdap_app" in service_component_name:
- redirectish_url = "http://{0}:{1}/application/{2}".format(ip, port, service_component_name)
- _logger.info("component is a CDAP application; trying the broker redirect on {0}".format(redirectish_url))
- r = requests.get(redirectish_url)
- r.raise_for_status()
- details = r.json()
- # Pick out the details to expose to the component developers. These keys come from the broker API
- return { key: details[key] for key in ["connectionurl", "serviceendpoints"] }
- else:
- return "{0}:{1}".format(ip, port)
+ ip_addr = services[0]["ServiceAddress"]
+ port = services[0]["ServicePort"]
+ if "cdap_app" in service_component_name:
+ redirectish_url = "http://{0}:{1}/application/{2}".format(ip_addr, port, service_component_name)
+ _logger.info("component is a CDAP application; trying the broker redirect on {0}".format(redirectish_url))
+ res = requests.get(redirectish_url)
+ res.raise_for_status()
+ details = res.json()
+ # Pick out the details to expose to the component developers. These keys come from the broker API
+ return {key: details[key] for key in ["connectionurl", "serviceendpoints"]}
+ return "{0}:{1}".format(ip_addr, port)
+
def _replace_rels_template(rels, template_identifier):
"""
@@ -118,11 +135,12 @@ def _replace_rels_template(rels, template_identifier):
it resolve to the empty list. So, it does resolve it to empty list.
"""
returnl = []
- for r in rels:
- if template_identifier in r and template_identifier is not "":
- returnl.append(r)
+ for rel in rels:
+ if template_identifier in rel and template_identifier is not "":
+ returnl.append(rel)
#returnl now contains a list of DNS names (possible empty), now resolve them (or not if they are not regustered)
- return list(filter(lambda x: x is not None, map(_get_connection_info_from_consul, returnl)))
+ return list(filter(lambda x: x is not None, map(_get_connection_info_from_consul, returnl)))
+
def _replace_dmaap_template(dmaap, template_identifier):
"""
@@ -131,6 +149,7 @@ def _replace_dmaap_template(dmaap, template_identifier):
"""
return {} if (template_identifier not in dmaap or template_identifier == "<<>>") else dmaap[template_identifier]
+
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.
@@ -145,7 +164,7 @@ def _replace_value(v, rels, dmaap):
if match_on_rels:
template_identifier = match_on_rels.groups()[0].strip() #now holds just x,.. of {{x,...}}
rtpartial = partial(_replace_rels_template, rels)
- return reduce(lambda a,b: a+b, map(rtpartial, template_identifier.split(",")), [])
+ return reduce(lambda a, b: a + b, map(rtpartial, template_identifier.split(",")), [])
match_on_dmaap = re.match(template_match_dmaap, v)
if match_on_dmaap:
template_identifier = match_on_dmaap.groups()[0].strip()
@@ -159,6 +178,7 @@ def _replace_value(v, rels, dmaap):
return _replace_dmaap_template(dmaap, template_identifier)
return v #was not a match or was not a string, return value as is
+
def _recurse(config, rels, dmaap):
"""
Recurse throug a configuration, or recursively a sub elemebt of it.
@@ -169,19 +189,21 @@ def _recurse(config, rels, dmaap):
"""
if isinstance(config, list):
return [_recurse(item, rels, dmaap) for item in config]
- elif isinstance(config,dict):
+ if isinstance(config, dict):
for key in config:
config[key] = _recurse(config[key], rels, dmaap)
return config
- elif isinstance(config, six.string_types):
+ if 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
+ #not a dict, not a list, not a string, nothing to do.
+ return config
+
#########
# PUBLIC API
#########
+
+
def resolve(service_component_name):
"""
Return the bound config of service_component_name
@@ -189,6 +211,7 @@ def resolve(service_component_name):
config, rels, dmaap = _get_config_rels_dmaap(service_component_name)
return _recurse(config, rels, dmaap)
+
def resolve_override(config, rels=[], dmaap={}):
"""
Explicitly take in a config, rels, dmaap and try to resolve it.
@@ -197,9 +220,10 @@ 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_all(service_component_name):
"""
- Return config, DTI, and policies, and any other key (other than :dmaap and :rels)
+ Return config, policies, and any other k such that service_component_name:k exists (other than :dmaap and :rels)
"""
allk = _consul_get_all_as_transaction(service_component_name)
returnk = {}
@@ -219,11 +243,24 @@ def resolve_all(service_component_name):
returnk["policies"]["event"] = allk[k]
elif ":policies/items" in k:
returnk["policies"]["items"].append(allk[k])
- elif k == service_component_name or k.endswith(":rels") or k.endswith(":dmaap"):
- pass
else:
- suffix = k.split(":")[1] #this would blow up if you had a key in consul without a : but this shouldnt happen
- returnk[suffix] = allk[k]
+ if not(k == service_component_name or k.endswith(":rels") or k.endswith(":dmaap")):
+ suffix = k.split(":")[1] #this would blow up if you had a key in consul without a : but this shouldnt happen
+ returnk[suffix] = allk[k]
return returnk
+
+def get_key(key, service_component_name):
+ """
+ Try to fetch a key k from Consul of the form service_component_name:k
+ """
+ if key == "policies":
+ raise BadRequest(":policies is a complex folder and should be retrieved using the service_component_all API")
+ response = requests.get("{0}/v1/kv/{1}:{2}".format(CONSUL, service_component_name, key))
+ try:
+ response.raise_for_status()
+ except requests.exceptions.HTTPError as exc:
+ raise CantGetConfig(exc.response.status_code, exc.response.text)
+ rest = json.loads(response.text)[0]
+ return json.loads(base64.b64decode(rest["Value"]).decode("utf-8"))