blob: 1fab5c65df305eff24a566cb29ae6ae4505910a2 [file] [log] [blame]
Alex Shatovebc1a062019-01-31 16:07:48 -05001# ================================================================================
Alex Shatov78ff88f2020-02-27 12:45:54 -05002# Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
Alex Shatovebc1a062019-01-31 16:07:48 -05003# ================================================================================
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#
Alex Shatovebc1a062019-01-31 16:07:48 -050017
18"""
19 ask service_activator for the mode_of_operation
20 that is whether the current site/cluster is active versus passive
21
22 active is the default and expects the polisy-handler
23 to receive the push notifications from policy-engine
24 as well as to periodically run the catch_up process
25
26 passive expects the polisy-handler
27 to stop listening for the policy-updates from the policy-engine
28 and to stop doing the periodic catch_up
29"""
30
31import json
Alex Shatovebc1a062019-01-31 16:07:48 -050032from copy import deepcopy
33from urllib.parse import urljoin
34
35import requests
36
37from .config import Config, Settings
Schmalzried, Terry (ts862m)05f475f2019-11-13 16:47:45 -050038from .discovery import DiscoveryClient
Alex Shatov78ff88f2020-02-27 12:45:54 -050039from .onap.audit import Audit, AuditHttpCode, Metrics
Alex Shatovebc1a062019-01-31 16:07:48 -050040from .policy_consts import TARGET_ENTITY
Alex Shatov9a4d3c52019-04-01 11:32:06 -040041from .utils import Utils
Alex Shatovebc1a062019-01-31 16:07:48 -050042
Alex Shatov9a4d3c52019-04-01 11:32:06 -040043_LOGGER = Utils.get_logger(__file__)
Alex Shatovebc1a062019-01-31 16:07:48 -050044
45class ServiceActivator(object):
46 """calling the service_activator web api to determine the mode_of_operation"""
Alex Shatovebc1a062019-01-31 16:07:48 -050047 DEFAULT_TARGET_ENTITY = "service_activator"
48 DEFAULT_TIMEOUT_IN_SECS = 10
49 MODE_OF_OPERATION_ACTIVE = "active"
Alex Shatov9a4d3c52019-04-01 11:32:06 -040050 SERVICE_MODE = "service_mode"
Alex Shatovebc1a062019-01-31 16:07:48 -050051
52 _lazy_inited = False
53 _settings = Settings(Config.MODE_OF_OPERATION, Config.SERVICE_ACTIVATOR)
54
55 _mode_of_operation = None
56 _url = None
57 _url_register = None
58 _post_register = {}
59 _target_entity = None
60 _custom_kwargs = {}
61 _timeout_in_secs = DEFAULT_TIMEOUT_IN_SECS
62
63
64 @staticmethod
65 def _init(audit):
66 """
67 initialize service-activator client config based on discovered config:
68
69 "mode_of_operation" : "active",
70 "service_activator" : {
71 "target_entity" : "service_activator",
72 "url" : "https://service-activator-service:123",
73 "path_register" : "/register",
74 "tls_ca_mode" : "cert_directory",
75 "timeout_in_secs": 20,
76 "post_register" : {
77 "component_name" : "policy_handler",
78 "reconfigure_path" : "/reconfigure",
79 "http_protocol" : "http"
80 }
81 }
82 """
83 ServiceActivator._custom_kwargs = {}
84 ServiceActivator._url = ServiceActivator._url_register = ""
Alex Shatov9a4d3c52019-04-01 11:32:06 -040085 Audit.register_item_health(ServiceActivator.SERVICE_MODE, ServiceActivator._get_service_mode)
Alex Shatovebc1a062019-01-31 16:07:48 -050086
87 try:
88 _, ServiceActivator._mode_of_operation = ServiceActivator._settings.get_by_key(
89 Config.MODE_OF_OPERATION, ServiceActivator._mode_of_operation)
90
91 _, config_sa = ServiceActivator._settings.get_by_key(Config.SERVICE_ACTIVATOR)
92 if config_sa and isinstance(config_sa, dict):
93 ServiceActivator._target_entity = config_sa.get(
94 TARGET_ENTITY, ServiceActivator.DEFAULT_TARGET_ENTITY)
95 ServiceActivator._url = config_sa.get("url", "")
Schmalzried, Terry (ts862m)05f475f2019-11-13 16:47:45 -050096 if not ServiceActivator._url:
97 ServiceActivator._url = DiscoveryClient.get_service_url(audit,
98 ServiceActivator._target_entity)
Alex Shatovebc1a062019-01-31 16:07:48 -050099 if ServiceActivator._url:
100 ServiceActivator._url_register = urljoin(ServiceActivator._url,
101 config_sa.get("path_register", ""))
102 ServiceActivator._post_register = deepcopy(config_sa.get("post_register", {}))
103 tls_ca_mode = config_sa.get(Config.TLS_CA_MODE)
104 ServiceActivator._custom_kwargs = Config.get_requests_kwargs(tls_ca_mode)
105
Alex Shatov9a4d3c52019-04-01 11:32:06 -0400106 _LOGGER.info(audit.info(
Alex Shatovebc1a062019-01-31 16:07:48 -0500107 "dns based routing to %s: url(%s) tls_ca_mode(%s) custom_kwargs(%s)",
108 ServiceActivator._target_entity, ServiceActivator._url_register,
109 tls_ca_mode, json.dumps(ServiceActivator._custom_kwargs)))
110
111 ServiceActivator._timeout_in_secs = config_sa.get(Config.TIMEOUT_IN_SECS)
112 if not ServiceActivator._timeout_in_secs or ServiceActivator._timeout_in_secs < 1:
113 ServiceActivator._timeout_in_secs = ServiceActivator.DEFAULT_TIMEOUT_IN_SECS
114
115 ServiceActivator._settings.commit_change()
116 except Exception:
117 pass
118 ServiceActivator._lazy_inited = True
119
120 @staticmethod
121 def reconfigure(audit):
122 """reconfigure"""
123 ServiceActivator._settings.set_config(Config.discovered_config)
124 if not ServiceActivator._settings.is_changed():
125 ServiceActivator._settings.commit_change()
126 return False
127
128 ServiceActivator._lazy_inited = False
129 ServiceActivator._init(audit)
130 return True
131
132 @staticmethod
133 def _lazy_init(audit):
134 """set config"""
135 if ServiceActivator._lazy_inited:
136 return
137
138 ServiceActivator._settings.set_config(Config.discovered_config)
139 ServiceActivator._init(audit)
140
141 @staticmethod
Alex Shatov9a4d3c52019-04-01 11:32:06 -0400142 def _get_service_mode():
143 """returns the service_mode as json to be reported by the healthcheck"""
144 return {
145 "is_active_mode_of_operation": ServiceActivator.is_active_mode_of_operation(),
146 "is_pdp_api_default": Config.is_pdp_api_default(log_status=False)
147 }
148
149 @staticmethod
150 def is_active_mode_of_operation(audit=None):
Alex Shatovebc1a062019-01-31 16:07:48 -0500151 """
152 mode_of_operation - whether the service is
153 active == True or passive == False
154 based on the current value of the mode_of_operation
155 """
156 active = (ServiceActivator._mode_of_operation is None
157 or ServiceActivator._mode_of_operation
158 == ServiceActivator.MODE_OF_OPERATION_ACTIVE)
Alex Shatov9a4d3c52019-04-01 11:32:06 -0400159
Alex Shatov9a4d3c52019-04-01 11:32:06 -0400160 if audit:
161 _LOGGER.info(audit.info("mode_of_operation = {} active = {}".format(
Alex Shatovebc1a062019-01-31 16:07:48 -0500162 ServiceActivator._mode_of_operation, active)))
163 return active
164
165 @staticmethod
166 def determine_mode_of_operation(audit):
167 """retrieves the mode_of_operation from service_activator"""
168 try:
169 ServiceActivator._lazy_init(audit)
170
171 target_entity = ServiceActivator._target_entity
172
173 if not ServiceActivator._url:
Alex Shatov9a4d3c52019-04-01 11:32:06 -0400174 _LOGGER.info(audit.info("no url found for {}".format(target_entity)))
Alex Shatovebc1a062019-01-31 16:07:48 -0500175 return ServiceActivator.is_active_mode_of_operation(audit)
176
177 url = ServiceActivator._url_register
178 json_body = deepcopy(ServiceActivator._post_register)
179 timeout_in_secs = ServiceActivator._timeout_in_secs
180 custom_kwargs = deepcopy(ServiceActivator._custom_kwargs)
181
182 metrics = Metrics(aud_parent=audit,
183 targetEntity="{} determine_mode_of_operation".format(target_entity),
184 targetServiceName=url)
Alex Shatov78ff88f2020-02-27 12:45:54 -0500185 headers = metrics.put_request_id_into_headers()
Alex Shatovebc1a062019-01-31 16:07:48 -0500186
187 log_action = "post to {} at {}".format(target_entity, url)
188 log_data = "headers={}, json_body={}, timeout_in_secs={}, custom_kwargs({})".format(
189 json.dumps(headers), json.dumps(json_body), timeout_in_secs,
190 json.dumps(custom_kwargs))
191 log_line = log_action + " " + log_data
192
Alex Shatov9a4d3c52019-04-01 11:32:06 -0400193 _LOGGER.info(log_line)
Alex Shatovebc1a062019-01-31 16:07:48 -0500194 metrics.metrics_start(log_line)
195
196 res = None
197 try:
198 res = requests.post(url, json=json_body, headers=headers, timeout=timeout_in_secs,
199 **custom_kwargs)
200 except Exception as ex:
201 error_code = (AuditHttpCode.SERVICE_UNAVAILABLE_ERROR.value
202 if isinstance(ex, requests.exceptions.RequestException)
203 else AuditHttpCode.SERVER_INTERNAL_ERROR.value)
204 error_msg = "failed to {} {}: {} {}".format(
205 log_action, type(ex).__name__, str(ex), log_data)
Alex Shatov78ff88f2020-02-27 12:45:54 -0500206 _LOGGER.exception(metrics.fatal(error_msg))
Alex Shatovebc1a062019-01-31 16:07:48 -0500207 metrics.set_http_status_code(error_code)
208 audit.set_http_status_code(error_code)
209 metrics.metrics(error_msg)
210 return ServiceActivator.is_active_mode_of_operation(audit)
211
212 metrics.set_http_status_code(res.status_code)
213 audit.set_http_status_code(res.status_code)
214
215 log_line = "response {} from {}: text={} {}".format(
216 res.status_code, log_action, res.text, log_data)
217 metrics.metrics(log_line)
218
219 if res.status_code != requests.codes.ok:
Alex Shatov9a4d3c52019-04-01 11:32:06 -0400220 _LOGGER.error(log_line)
Alex Shatovebc1a062019-01-31 16:07:48 -0500221 return ServiceActivator.is_active_mode_of_operation(audit)
222
223 result = res.json() or {}
224
225 ServiceActivator._mode_of_operation = (result.get(Config.MODE_OF_OPERATION)
226 or ServiceActivator._mode_of_operation)
227 return ServiceActivator.is_active_mode_of_operation(audit)
228
229 except Exception as ex:
230 return ServiceActivator.is_active_mode_of_operation(audit)