blob: 4c5b64eaa4fa2759c9c3b6262654adbcdcfc6a19 [file] [log] [blame]
alex_sh9d980ce2017-08-23 17:30:56 -04001# ================================================================================
Lusheng Ji967cc9a2018-02-12 10:45:42 -05002# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved.
alex_sh9d980ce2017-08-23 17:30:56 -04003# ================================================================================
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15# ============LICENSE_END=========================================================
16#
17# ECOMP is a trademark and service mark of AT&T Intellectual Property.
18
Alex Shatov209f8232018-09-20 15:15:45 -040019"""client to talk to consul services and kv"""
Alex Shatovf53e5e72018-01-11 11:15:56 -050020
alex_sh9d980ce2017-08-23 17:30:56 -040021import base64
Alex Shatovb9b955c2018-03-08 13:12:23 -050022import json
23import logging
24
alex_sh9d980ce2017-08-23 17:30:56 -040025import requests
26
Alex Shatov209f8232018-09-20 15:15:45 -040027from .config import Config
Alex Shatovb9b955c2018-03-08 13:12:23 -050028from .customize import CustomizerUser
Alex Shatov1d693372018-08-24 13:15:04 -040029from .onap.audit import AuditHttpCode, Metrics
30
Alex Shatovb9b955c2018-03-08 13:12:23 -050031
alex_sh9d980ce2017-08-23 17:30:56 -040032class DiscoveryClient(object):
Alex Shatov209f8232018-09-20 15:15:45 -040033 """talking to consul at Config.consul_url
34
35 Config.consul_url is populated
36 from env var $CONSUL_URL
37 if not provided, then from consul_url in etc/config.json
38 if not provided, then from hardcoded value of http://consul:8500
alex_sh9d980ce2017-08-23 17:30:56 -040039
40 relies on proper --add-host "consul:<consul-agent ip>" in
41 docker run command that runs along the consul-agent:
42
43 docker run --name ${APPNAME} -d
Alex Shatov209f8232018-09-20 15:15:45 -040044 -e HOSTNAME -e CONSUL_URL
alex_sh9d980ce2017-08-23 17:30:56 -040045 --add-host "consul:<consul-agent ip>"
46 -v ${BASEDIR}/logs:${TARGETDIR}/logs
47 -v ${BASEDIR}/etc:${TARGETDIR}/etc
48 -p <outport>:<innerport>
49 ${APPNAME}:latest
50 """
Alex Shatov1d693372018-08-24 13:15:04 -040051 CONSUL_ENTITY = "consul"
Alex Shatov209f8232018-09-20 15:15:45 -040052 CONSUL_SERVICE_MASK = "{}/v1/catalog/service/{}"
53 CONSUL_KV_MASK = "{}/v1/kv/{}"
alex_sh9d980ce2017-08-23 17:30:56 -040054 _logger = logging.getLogger("policy_handler.discovery")
55
alex_sh9d980ce2017-08-23 17:30:56 -040056 @staticmethod
Alex Shatov1d693372018-08-24 13:15:04 -040057 def _discover_service(audit, service_name, service_path):
58 """find the service record in consul"""
Alex Shatova39f4e82018-12-05 15:23:50 -050059 response = requests.get(service_path, timeout=Config.consul_timeout_in_secs)
Alex Shatov1d693372018-08-24 13:15:04 -040060 DiscoveryClient._logger.info(audit.info("response {} from {}: {}".format(
61 response.status_code, service_path, response.text)))
62
63 response.raise_for_status()
64 status_code = response.status_code
65 service = response.json()[0]
66 return (status_code,
67 CustomizerUser.get_customizer().get_service_url(audit, service_name, service))
68
69 @staticmethod
Alex Shatovb9b955c2018-03-08 13:12:23 -050070 def get_service_url(audit, service_name):
alex_sh9d980ce2017-08-23 17:30:56 -040071 """find the service record in consul"""
Alex Shatov209f8232018-09-20 15:15:45 -040072 service_path = DiscoveryClient.CONSUL_SERVICE_MASK.format(Config.consul_url, service_name)
Alex Shatov1d693372018-08-24 13:15:04 -040073 metrics = Metrics(aud_parent=audit, targetEntity=DiscoveryClient.CONSUL_ENTITY,
74 targetServiceName=service_path)
Alex Shatovb9b955c2018-03-08 13:12:23 -050075
Alex Shatov1d693372018-08-24 13:15:04 -040076 log_line = "get from {} at {}".format(DiscoveryClient.CONSUL_ENTITY, service_path)
Alex Shatovb9b955c2018-03-08 13:12:23 -050077
Alex Shatov1d693372018-08-24 13:15:04 -040078 DiscoveryClient._logger.info(metrics.metrics_start(log_line))
79 status_code = None
80 try:
81 (status_code,
82 service_url) = DiscoveryClient._discover_service(audit, service_name, service_path)
83 except Exception as ex:
84 error_code = (AuditHttpCode.SERVICE_UNAVAILABLE_ERROR.value
85 if isinstance(ex, requests.exceptions.RequestException)
86 else AuditHttpCode.SERVER_INTERNAL_ERROR.value)
87 error_msg = ("failed {}/{} to {} {}: {}".format(status_code, error_code, log_line,
88 type(ex).__name__, str(ex)))
89 DiscoveryClient._logger.exception(error_msg)
90 metrics.set_http_status_code(error_code)
91 audit.set_http_status_code(error_code)
92 metrics.metrics(error_msg)
93 return None
Alex Shatovb9b955c2018-03-08 13:12:23 -050094
Alex Shatovb9b955c2018-03-08 13:12:23 -050095 if not service_url:
Alex Shatov1d693372018-08-24 13:15:04 -040096 error_code = AuditHttpCode.DATA_ERROR.value
97 error_msg = "failed {}/{} to {}".format(status_code, error_code, log_line)
98 DiscoveryClient._logger.error(audit.error(error_msg))
99 metrics.set_http_status_code(error_code)
100 audit.set_http_status_code(error_code)
101 metrics.metrics(error_msg)
102 return None
Alex Shatovb9b955c2018-03-08 13:12:23 -0500103
Alex Shatov1d693372018-08-24 13:15:04 -0400104 log_line = "response {} {}".format(status_code, log_line)
105 DiscoveryClient._logger.info(audit.info("got service_url: {} after {}"
106 .format(service_url, log_line)))
107
108 metrics.set_http_status_code(status_code)
109 audit.set_http_status_code(status_code)
110 metrics.metrics(log_line)
Alex Shatovb9b955c2018-03-08 13:12:23 -0500111 return service_url
alex_sh9d980ce2017-08-23 17:30:56 -0400112
113 @staticmethod
Alex Shatov1d693372018-08-24 13:15:04 -0400114 def _get_value_from_kv(url):
115 """get the value from consul-kv at discovery url"""
Alex Shatova39f4e82018-12-05 15:23:50 -0500116 response = requests.get(url, timeout=Config.consul_timeout_in_secs)
alex_sh9d980ce2017-08-23 17:30:56 -0400117 response.raise_for_status()
Alex Shatov1369bea2018-01-10 11:00:50 -0500118 data = response.json()
Alex Shatov1369bea2018-01-10 11:00:50 -0500119 value = base64.b64decode(data[0]["Value"]).decode("utf-8")
Alex Shatov1d693372018-08-24 13:15:04 -0400120 return response.status_code, json.loads(value)
121
122 @staticmethod
123 def get_value(audit, key):
124 """get the value for the key from consul-kv"""
Alex Shatov209f8232018-09-20 15:15:45 -0400125 discovery_url = DiscoveryClient.CONSUL_KV_MASK.format(Config.consul_url, key)
Alex Shatov1d693372018-08-24 13:15:04 -0400126 metrics = Metrics(aud_parent=audit, targetEntity=DiscoveryClient.CONSUL_ENTITY,
127 targetServiceName=discovery_url)
128
129 log_line = "get from {} at {}".format(DiscoveryClient.CONSUL_ENTITY, discovery_url)
130
131 DiscoveryClient._logger.info(metrics.metrics_start(log_line))
Alex Shatova39f4e82018-12-05 15:23:50 -0500132 status_code = None
Alex Shatov1d693372018-08-24 13:15:04 -0400133 try:
134 status_code, value = DiscoveryClient._get_value_from_kv(discovery_url)
135 except Exception as ex:
136 error_code = (AuditHttpCode.SERVICE_UNAVAILABLE_ERROR.value
137 if isinstance(ex, requests.exceptions.RequestException)
138 else AuditHttpCode.SERVER_INTERNAL_ERROR.value)
139 error_msg = ("failed {}/{} to {} {}: {}".format(status_code, error_code, log_line,
140 type(ex).__name__, str(ex)))
141 DiscoveryClient._logger.exception(error_msg)
142 metrics.set_http_status_code(error_code)
143 audit.set_http_status_code(error_code)
144 metrics.metrics(error_msg)
145 return None
146
147 log_line = "response {} {}".format(status_code, log_line)
148 metrics.set_http_status_code(status_code)
149 audit.set_http_status_code(status_code)
150 metrics.metrics(log_line)
151 return value