2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.sdc.be.components.health;
23 import com.fasterxml.jackson.core.type.TypeReference;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.google.common.annotations.VisibleForTesting;
26 import org.apache.commons.lang3.tuple.ImmutablePair;
27 import org.apache.commons.lang3.tuple.Pair;
28 import org.openecomp.sdc.be.catalog.impl.DmaapProducerHealth;
29 import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineClusterHealth;
30 import org.openecomp.sdc.be.components.distribution.engine.DmaapHealth;
31 import org.openecomp.sdc.be.components.impl.CADIHealthCheck;
32 import org.openecomp.sdc.be.components.impl.CassandraHealthCheck;
33 import org.openecomp.sdc.be.config.BeEcompErrorManager;
34 import org.openecomp.sdc.be.config.Configuration;
35 import org.openecomp.sdc.be.config.ConfigurationManager;
36 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
37 import org.openecomp.sdc.be.switchover.detector.SwitchoverDetector;
38 import org.openecomp.sdc.common.api.HealthCheckInfo;
39 import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
40 import org.openecomp.sdc.common.http.client.api.HttpRequest;
41 import org.openecomp.sdc.common.http.client.api.HttpResponse;
42 import org.openecomp.sdc.common.http.config.HttpClientConfig;
43 import org.openecomp.sdc.common.http.config.Timeouts;
44 import org.openecomp.sdc.common.log.elements.LogFieldsMdcHandler;
45 import org.openecomp.sdc.common.log.wrappers.Logger;
46 import org.openecomp.sdc.common.util.HealthCheckUtil;
47 import org.springframework.beans.factory.annotation.Autowired;
48 import org.springframework.stereotype.Component;
50 import javax.annotation.PostConstruct;
51 import javax.annotation.PreDestroy;
52 import javax.annotation.Resource;
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.List;
57 import java.util.Map.Entry;
58 import java.util.concurrent.ScheduledExecutorService;
59 import java.util.concurrent.ScheduledFuture;
60 import java.util.concurrent.TimeUnit;
61 import java.util.stream.Collectors;
63 import static java.lang.String.format;
64 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
65 import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
66 import static org.apache.http.HttpStatus.SC_OK;
67 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_BE;
68 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_CASSANDRA;
69 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_DCAE;
70 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_DMAAP_PRODUCER;
71 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_ECOMP_PORTAL;
72 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_JANUSGRAPH;
73 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_ON_BOARDING;
74 import static org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus.DOWN;
75 import static org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus.UP;
76 import static org.openecomp.sdc.common.impl.ExternalConfiguration.getAppVersion;
79 @Component("healthCheckBusinessLogic")
80 public class HealthCheckBusinessLogic {
82 private static String hcUrl = "%s://%s:%s%s";
83 private static final String BE_HEALTH_CHECK_STR = "beHealthCheck";
84 private static final String LOG_PARTNER_NAME = "SDC.BE";
85 private static final String COMPONENT_CHANGED_MESSAGE = "BE Component %s state changed from %s to %s";
86 private static final Logger log = Logger.getLogger(HealthCheckBusinessLogic.class.getName());
87 private static final HealthCheckUtil healthCheckUtil = new HealthCheckUtil();
88 private final ScheduledExecutorService healthCheckScheduler = newSingleThreadScheduledExecutor((Runnable r) -> new Thread(r, "BE-Health-Check-Task"));
89 private HealthCheckScheduledTask healthCheckScheduledTask = null;
90 private static LogFieldsMdcHandler mdcFieldsHandler = new LogFieldsMdcHandler();
93 private JanusGraphGenericDao janusGraphGenericDao;
95 private DistributionEngineClusterHealth distributionEngineClusterHealth;
97 private DmaapHealth dmaapHealth;
99 private DmaapProducerHealth dmaapProducerHealth;
101 private CassandraHealthCheck cassandraHealthCheck;
103 private PortalHealthCheckBuilder portalHealthCheck;
106 private SwitchoverDetector switchoverDetector;
107 private volatile List<HealthCheckInfo> prevBeHealthCheckInfos = null;
108 private ScheduledFuture<?> scheduledFuture = null;
113 prevBeHealthCheckInfos = getBeHealthCheckInfos();
115 log.debug("After initializing prevBeHealthCheckInfos: {}", prevBeHealthCheckInfos);
117 healthCheckScheduledTask = new HealthCheckScheduledTask();
119 if (this.scheduledFuture == null) {
120 this.scheduledFuture = this.healthCheckScheduler.scheduleAtFixedRate(healthCheckScheduledTask, 0, 3, TimeUnit.SECONDS);
125 public boolean isDistributionEngineUp() {
127 HealthCheckInfo healthCheckInfo = distributionEngineClusterHealth.getHealthCheckInfo();
128 return healthCheckInfo.getHealthCheckStatus() != DOWN;
131 public Pair<Boolean, List<HealthCheckInfo>> getBeHealthCheckInfosStatus() {
132 Configuration config = ConfigurationManager.getConfigurationManager().getConfiguration();
133 return new ImmutablePair<>(healthCheckUtil.getAggregateStatus(prevBeHealthCheckInfos, config.getHealthStatusExclude()), prevBeHealthCheckInfos);
136 private List<HealthCheckInfo> getBeHealthCheckInfos() {
138 log.trace("In getBeHealthCheckInfos");
140 List<HealthCheckInfo> healthCheckInfos = new ArrayList<>();
143 HealthCheckInfo info;
144 if ((info = getDmaapHealthCheck()) != null) {
145 healthCheckInfos.add(info);
149 healthCheckInfos.add(getDmaapProducerHealthCheck());
152 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_BE, UP, getAppVersion(), "OK"));
155 healthCheckInfos.add(getJanusGraphHealthCheck());
157 // Distribution Engine
158 healthCheckInfos.add(distributionEngineClusterHealth.getHealthCheckInfo());
161 healthCheckInfos.add(getCassandraHealthCheck());
164 healthCheckInfos.add(getHostedComponentsBeHealthCheck(HC_COMPONENT_ON_BOARDING, buildOnBoardingHealthCheckUrl()));
167 healthCheckInfos.add(getHostedComponentsBeHealthCheck(HC_COMPONENT_DCAE, buildDcaeHealthCheckUrl()));
170 healthCheckInfos.add(portalHealthCheck.getHealthCheckInfo());
173 healthCheckInfos.add(CADIHealthCheck.getCADIHealthCheckInstance().getCADIStatus());
175 return healthCheckInfos;
178 private HealthCheckInfo getDmaapHealthCheck() {
179 HealthCheckInfo healthCheckInfo = null;
180 if(ConfigurationManager.getConfigurationManager().getConfiguration().getDmaapConsumerConfiguration().isActive()){
181 String appVersion = getAppVersion();
182 dmaapHealth.getHealthCheckInfo().setVersion(appVersion);
183 healthCheckInfo = dmaapHealth.getHealthCheckInfo();
185 log.debug("Dmaap health check disabled");
187 return healthCheckInfo;
190 private HealthCheckInfo getDmaapProducerHealthCheck() {
191 HealthCheckInfo healthCheckInfo = null;
192 if(ConfigurationManager.getConfigurationManager().getConfiguration().getDmaapConsumerConfiguration().isActive()){
193 String appVersion = getAppVersion();
194 dmaapProducerHealth.getHealthCheckInfo().setVersion(appVersion);
195 healthCheckInfo = dmaapProducerHealth.getHealthCheckInfo();
197 log.debug("Dmaap health check disabled");
198 String description = ("Dmaap health check disabled");
199 healthCheckInfo = new HealthCheckInfo(HC_COMPONENT_DMAAP_PRODUCER, DOWN, null, description);
201 return healthCheckInfo;
204 public HealthCheckInfo getJanusGraphHealthCheck() {
205 // JanusGraph health check and version
207 boolean isJanusGraphUp;
208 HealthCheckInfo healthCheckInfo = new HealthCheckInfo(HC_COMPONENT_JANUSGRAPH, DOWN, null, null);
211 isJanusGraphUp = janusGraphGenericDao.isGraphOpen();
212 } catch (Exception e) {
213 description = "JanusGraph error: " + e.getMessage();
214 healthCheckInfo.setDescription(description);
215 log.error(description);
216 return healthCheckInfo;
218 if (isJanusGraphUp) {
220 healthCheckInfo.setDescription(description);
221 healthCheckInfo.setHealthCheckStatus(HealthCheckInfo.HealthCheckStatus.UP);
224 description = "JanusGraph graph is down";
225 healthCheckInfo.setDescription(description);
227 return healthCheckInfo;
230 private HealthCheckInfo getCassandraHealthCheck() {
233 boolean isCassandraUp = false;
234 HealthCheckInfo healthCheckInfo = new HealthCheckInfo(HC_COMPONENT_CASSANDRA, DOWN, null, null);
237 isCassandraUp = cassandraHealthCheck.getCassandraStatus();
238 } catch (Exception e) {
239 description = "Cassandra error: " + e.getMessage();
240 log.error(description, e);
244 // healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_CASSANDRA, UP, null, description));
245 healthCheckInfo.setHealthCheckStatus(HealthCheckStatus.UP);
246 healthCheckInfo.setDescription(description);
248 description = "Cassandra is down";
249 // healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_CASSANDRA, DOWN, null, description));
250 healthCheckInfo.setDescription(description);
252 return healthCheckInfo;
255 private HealthCheckInfo getHostedComponentsBeHealthCheck(String componentName, String healthCheckUrl) {
256 HealthCheckStatus healthCheckStatus;
258 String version = null;
259 List<HealthCheckInfo> componentsInfo = new ArrayList<>();
260 final int timeout = 3000;
262 if (healthCheckUrl != null) {
264 HttpResponse<String> httpResponse = HttpRequest.get(healthCheckUrl, new HttpClientConfig(new Timeouts(timeout, timeout)));
265 int statusCode = httpResponse.getStatusCode();
266 String aggDescription = "";
268 if ((statusCode == SC_OK || statusCode == SC_INTERNAL_SERVER_ERROR) && !componentName.equals(HC_COMPONENT_ECOMP_PORTAL)) {
269 String response = httpResponse.getResponse();
270 log.trace("{} Health Check response: {}", componentName, response);
271 ObjectMapper mapper = new ObjectMapper();
272 Map<String, Object> healthCheckMap = mapper.readValue(response, new TypeReference<Map<String, Object>>() {
274 version = getVersion(healthCheckMap);
275 if (healthCheckMap.containsKey("componentsInfo")) {
276 componentsInfo = mapper.convertValue(healthCheckMap.get("componentsInfo"), new TypeReference<List<HealthCheckInfo>>() {
279 aggDescription = getAggDescription(componentsInfo, aggDescription);
281 log.trace("{} Health Check Response code: {}", componentName, statusCode);
284 if (statusCode != SC_OK) {
285 healthCheckStatus = DOWN;
286 description = getDescription(componentName, aggDescription);
287 setDescriptionToObject(description, componentsInfo);
289 healthCheckStatus = UP;
293 } catch (Exception e) {
294 log.error("{} unexpected response: ", componentName, e);
295 healthCheckStatus = DOWN;
296 description = componentName + " unexpected response: " + e.getMessage();
297 addToHealthCheckInfoObject(description, componentsInfo);
300 healthCheckStatus = DOWN;
301 description = componentName + " health check Configuration is missing";
302 componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
304 return new HealthCheckInfo(componentName, healthCheckStatus, version, description, componentsInfo);
307 private void addToHealthCheckInfoObject(String description, List<HealthCheckInfo> componentsInfo) {
308 if (componentsInfo != null && componentsInfo.isEmpty()) {
309 componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
313 private void setDescriptionToObject(String description, List<HealthCheckInfo> componentsInfo) {
314 if (componentsInfo.isEmpty()) {
315 componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
319 private String getDescription(String componentName, String aggDescription) {
321 description = aggDescription.length() > 0
323 : componentName + " is Down, specific reason unknown";//No inner component returned DOWN, but the status of HC is still DOWN.
327 private String getVersion(Map<String, Object> healthCheckMap) {
328 return healthCheckMap.get("sdcVersion") != null ? healthCheckMap.get("sdcVersion").toString() : null;
331 private String getAggDescription(List<HealthCheckInfo> componentsInfo, String aggDescription) {
332 if (!componentsInfo.isEmpty()) {
333 aggDescription = healthCheckUtil.getAggregateDescription(componentsInfo);
335 componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, null));
337 return aggDescription;
341 protected void destroy() {
343 if (scheduledFuture != null) {
344 scheduledFuture.cancel(true);
345 scheduledFuture = null;
348 if (healthCheckScheduler != null) {
349 healthCheckScheduler.shutdown();
354 private void logAlarm(String componentChangedMsg) {
355 BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(componentChangedMsg);
358 public String getSiteMode() {
359 return switchoverDetector.getSiteMode();
362 public boolean anyStatusChanged(List<HealthCheckInfo> beHealthCheckInfos, List<HealthCheckInfo> prevBeHealthCheckInfos) {
364 boolean result = false;
366 if (beHealthCheckInfos != null && prevBeHealthCheckInfos != null) {
368 Map<String, HealthCheckStatus> currentValues = beHealthCheckInfos.stream().collect(Collectors.toMap(HealthCheckInfo::getHealthCheckComponent, HealthCheckInfo::getHealthCheckStatus));
369 Map<String, HealthCheckStatus> prevValues = prevBeHealthCheckInfos.stream().collect(Collectors.toMap(HealthCheckInfo::getHealthCheckComponent, HealthCheckInfo::getHealthCheckStatus));
371 if (currentValues != null && prevValues != null) {
372 int currentSize = currentValues.size();
373 int prevSize = prevValues.size();
375 if (currentSize != prevSize) {
376 result = true; //extra/missing component
377 updateHealthCheckStatusMap(currentValues, prevValues);
379 result = isHealthStatusChanged(result, currentValues, prevValues);
383 } else if (beHealthCheckInfos == null && prevBeHealthCheckInfos == null) {
386 writeLogAlarm(prevBeHealthCheckInfos);
393 private void writeLogAlarm(List<HealthCheckInfo> prevBeHealthCheckInfos) {
394 logAlarm(format(COMPONENT_CHANGED_MESSAGE, "", prevBeHealthCheckInfos == null ? "null" : "true", prevBeHealthCheckInfos == null ? "true" : "null"));
397 private boolean isHealthStatusChanged(boolean result, Map<String, HealthCheckStatus> currentValues, Map<String, HealthCheckStatus> prevValues) {
398 for (Entry<String, HealthCheckStatus> entry : currentValues.entrySet()) {
399 String key = entry.getKey();
400 HealthCheckStatus value = entry.getValue();
402 if (!prevValues.containsKey(key)) {
403 result = true; //component missing
404 logAlarm(format(COMPONENT_CHANGED_MESSAGE, key, prevValues.get(key), currentValues.get(key)));
408 HealthCheckStatus prevHealthCheckStatus = prevValues.get(key);
410 if (value != prevHealthCheckStatus) {
411 result = true; //component status changed
412 logAlarm(format(COMPONENT_CHANGED_MESSAGE, key, prevValues.get(key), currentValues.get(key)));
419 private void updateHealthCheckStatusMap(Map<String, HealthCheckStatus> currentValues, Map<String, HealthCheckStatus> prevValues) {
420 Map<String, HealthCheckStatus> notPresent;
421 if (currentValues.keySet().containsAll(prevValues.keySet())) {
422 notPresent = new HashMap<>(currentValues);
423 notPresent.keySet().removeAll(prevValues.keySet());
425 notPresent = new HashMap<>(prevValues);
426 notPresent.keySet().removeAll(currentValues.keySet());
429 for (String component : notPresent.keySet()) {
430 logAlarm(format(COMPONENT_CHANGED_MESSAGE, component, prevValues.get(component), currentValues.get(component)));
434 String buildOnBoardingHealthCheckUrl() {
436 Configuration.OnboardingConfig onboardingConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getOnboarding();
437 if (onboardingConfig != null) {
438 return String.format(hcUrl, onboardingConfig.getProtocol(), onboardingConfig.getHost(),
439 onboardingConfig.getPort(),onboardingConfig.getHealthCheckUri());
441 log.error("Onboarding health check configuration is missing.");
446 String buildDcaeHealthCheckUrl() {
448 Configuration.DcaeConfig dcaeConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getDcae();
450 if (dcaeConfig != null) {
451 return String.format(hcUrl, dcaeConfig.getProtocol(), dcaeConfig.getHost(),
452 dcaeConfig.getPort(),dcaeConfig.getHealthCheckUri());
455 log.error("DCAE health check configuration is missing.");
459 public class HealthCheckScheduledTask implements Runnable {
462 mdcFieldsHandler.addInfoForErrorAndDebugLogging(LOG_PARTNER_NAME);
463 Configuration config = ConfigurationManager.getConfigurationManager().getConfiguration();
464 log.trace("Executing BE Health Check Task");
466 List<HealthCheckInfo> currentBeHealthCheckInfos = getBeHealthCheckInfos();
467 boolean healthStatus = healthCheckUtil.getAggregateStatus(currentBeHealthCheckInfos, config.getHealthStatusExclude());
469 boolean prevHealthStatus = healthCheckUtil.getAggregateStatus(prevBeHealthCheckInfos, config.getHealthStatusExclude());
471 boolean anyStatusChanged = anyStatusChanged(currentBeHealthCheckInfos, prevBeHealthCheckInfos);
473 if (prevHealthStatus != healthStatus || anyStatusChanged) {
474 log.trace("BE Health State Changed to {}. Issuing alarm / recovery alarm...", healthStatus);
476 prevBeHealthCheckInfos = currentBeHealthCheckInfos;
477 logAlarm(healthStatus);
481 private void logAlarm(boolean prevHealthState) {
482 if (prevHealthState) {
483 BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(BE_HEALTH_CHECK_STR);
485 BeEcompErrorManager.getInstance().logBeHealthCheckError(BE_HEALTH_CHECK_STR);