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 org.apache.commons.lang3.tuple.ImmutablePair;
26 import org.apache.commons.lang3.tuple.Pair;
27 import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineClusterHealth;
28 import org.openecomp.sdc.be.components.distribution.engine.DmaapHealth;
29 import org.openecomp.sdc.be.components.impl.CassandraHealthCheck;
30 import org.openecomp.sdc.be.config.BeEcompErrorManager;
31 import org.openecomp.sdc.be.config.Configuration;
32 import org.openecomp.sdc.be.config.ConfigurationManager;
33 import org.openecomp.sdc.be.dao.impl.EsHealthCheckDao;
34 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
35 import org.openecomp.sdc.be.switchover.detector.SwitchoverDetector;
36 import org.openecomp.sdc.common.api.HealthCheckInfo;
37 import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
38 import org.openecomp.sdc.common.http.client.api.HttpRequest;
39 import org.openecomp.sdc.common.http.client.api.HttpResponse;
40 import org.openecomp.sdc.common.http.config.HttpClientConfig;
41 import org.openecomp.sdc.common.http.config.Timeouts;
42 import org.openecomp.sdc.common.log.wrappers.Logger;
43 import org.openecomp.sdc.common.util.HealthCheckUtil;
44 import org.springframework.beans.factory.annotation.Autowired;
45 import org.springframework.stereotype.Component;
47 import javax.annotation.PostConstruct;
48 import javax.annotation.PreDestroy;
49 import javax.annotation.Resource;
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.List;
54 import java.util.Map.Entry;
55 import java.util.concurrent.ScheduledExecutorService;
56 import java.util.concurrent.ScheduledFuture;
57 import java.util.concurrent.TimeUnit;
58 import java.util.stream.Collectors;
60 import static java.lang.String.format;
61 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
62 import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
63 import static org.apache.http.HttpStatus.SC_OK;
64 import static org.openecomp.sdc.common.api.Constants.*;
65 import static org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus.DOWN;
66 import static org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus.UP;
67 import static org.openecomp.sdc.common.impl.ExternalConfiguration.getAppVersion;
70 @Component("healthCheckBusinessLogic")
71 public class HealthCheckBusinessLogic {
73 protected static final String BE_HEALTH_LOG_CONTEXT = "be.healthcheck";
74 private static final String BE_HEALTH_CHECK_STR = "beHealthCheck";
75 private static final String COMPONENT_CHANGED_MESSAGE = "BE Component %s state changed from %s to %s";
76 private static final Logger log = Logger.getLogger(HealthCheckBusinessLogic.class.getName());
77 private static final HealthCheckUtil healthCheckUtil = new HealthCheckUtil();
78 private final ScheduledExecutorService healthCheckScheduler = newSingleThreadScheduledExecutor((Runnable r) -> new Thread(r, "BE-Health-Check-Task"));
79 private HealthCheckScheduledTask healthCheckScheduledTask = null;
81 private JanusGraphGenericDao janusGraphGenericDao;
83 private EsHealthCheckDao esHealthCheckDao;
85 private DistributionEngineClusterHealth distributionEngineClusterHealth;
87 private DmaapHealth dmaapHealth;
89 private CassandraHealthCheck cassandraHealthCheck;
91 private SwitchoverDetector switchoverDetector;
92 private volatile List<HealthCheckInfo> prevBeHealthCheckInfos = null;
93 private ScheduledFuture<?> scheduledFuture = null;
98 prevBeHealthCheckInfos = getBeHealthCheckInfos();
100 log.debug("After initializing prevBeHealthCheckInfos: {}", prevBeHealthCheckInfos);
102 healthCheckScheduledTask = new HealthCheckScheduledTask();
104 if (this.scheduledFuture == null) {
105 this.scheduledFuture = this.healthCheckScheduler.scheduleAtFixedRate(healthCheckScheduledTask, 0, 3, TimeUnit.SECONDS);
110 public boolean isDistributionEngineUp() {
112 HealthCheckInfo healthCheckInfo = distributionEngineClusterHealth.getHealthCheckInfo();
113 return !healthCheckInfo.getHealthCheckStatus().equals(DOWN);
116 public Pair<Boolean, List<HealthCheckInfo>> getBeHealthCheckInfosStatus() {
117 Configuration config = ConfigurationManager.getConfigurationManager().getConfiguration();
118 return new ImmutablePair<>(healthCheckUtil.getAggregateStatus(prevBeHealthCheckInfos, config.getHealthStatusExclude()), prevBeHealthCheckInfos);
121 private List<HealthCheckInfo> getBeHealthCheckInfos() {
123 log.trace("In getBeHealthCheckInfos");
125 List<HealthCheckInfo> healthCheckInfos = new ArrayList<>();
128 getDmaapHealthCheck(healthCheckInfos);
131 getBeHealthCheck(healthCheckInfos);
134 getJanusGraphHealthCheck(healthCheckInfos);
136 getEsHealthCheck(healthCheckInfos);
138 // Distribution Engine
139 getDistributionEngineCheck(healthCheckInfos);
142 getCassandraHealthCheck(healthCheckInfos);
145 getAmdocsHealthCheck(healthCheckInfos);
148 getDcaeHealthCheck(healthCheckInfos);
150 return healthCheckInfos;
153 private List<HealthCheckInfo> getEsHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
155 // ES health check and version
156 String appVersion = getAppVersion();
157 HealthCheckStatus healthCheckStatus;
161 healthCheckStatus = esHealthCheckDao.getClusterHealthStatus();
162 } catch (Exception e) {
163 healthCheckStatus = DOWN;
164 description = "ES cluster error: " + e.getMessage();
165 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_ES, healthCheckStatus, appVersion, description));
166 log.error(description, e);
167 return healthCheckInfos;
169 if (healthCheckStatus.equals(DOWN)) {
170 description = "ES cluster is down";
174 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_ES, healthCheckStatus, appVersion, description));
175 return healthCheckInfos;
178 private List<HealthCheckInfo> getBeHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
179 String appVersion = getAppVersion();
180 String description = "OK";
181 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_BE, UP, appVersion, description));
182 return healthCheckInfos;
185 private List<HealthCheckInfo> getDmaapHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
186 if(ConfigurationManager.getConfigurationManager().getConfiguration().getDmaapConsumerConfiguration().isActive()){
187 String appVersion = getAppVersion();
188 dmaapHealth.getHealthCheckInfo().setVersion(appVersion);
189 healthCheckInfos.add(dmaapHealth.getHealthCheckInfo());
191 log.debug("Dmaap health check disabled");
194 return healthCheckInfos;
198 public List<HealthCheckInfo> getJanusGraphHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
199 // JanusGraph health check and version
201 boolean isJanusGraphUp;
204 isJanusGraphUp = janusGraphGenericDao.isGraphOpen();
205 } catch (Exception e) {
206 description = "JanusGraph error: ";
207 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_JANUSGRAPH, DOWN, null, description));
208 log.error(description, e);
209 return healthCheckInfos;
211 if (isJanusGraphUp) {
213 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_JANUSGRAPH, UP, null, description));
215 description = "JanusGraph graph is down";
216 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_JANUSGRAPH, DOWN, null, description));
218 return healthCheckInfos;
221 private List<HealthCheckInfo> getCassandraHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
224 boolean isCassandraUp = false;
227 isCassandraUp = cassandraHealthCheck.getCassandraStatus();
228 } catch (Exception e) {
229 description = "Cassandra error: " + e.getMessage();
230 log.error(description, e);
234 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_CASSANDRA, UP, null, description));
236 description = "Cassandra is down";
237 healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_CASSANDRA, DOWN, null, description));
239 return healthCheckInfos;
243 private void getDistributionEngineCheck(List<HealthCheckInfo> healthCheckInfos) {
245 HealthCheckInfo healthCheckInfo = distributionEngineClusterHealth.getHealthCheckInfo();
247 healthCheckInfos.add(healthCheckInfo);
251 private List<HealthCheckInfo> getAmdocsHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
252 HealthCheckInfo beHealthCheckInfo = getHostedComponentsBeHealthCheck(HC_COMPONENT_ON_BOARDING, buildOnBoardingHealthCheckUrl());
253 healthCheckInfos.add(beHealthCheckInfo);
254 return healthCheckInfos;
257 private List<HealthCheckInfo> getDcaeHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
258 HealthCheckInfo beHealthCheckInfo = getHostedComponentsBeHealthCheck(HC_COMPONENT_DCAE, buildDcaeHealthCheckUrl());
259 healthCheckInfos.add(beHealthCheckInfo);
260 return healthCheckInfos;
263 private HealthCheckInfo getHostedComponentsBeHealthCheck(String componentName, String healthCheckUrl) {
264 HealthCheckStatus healthCheckStatus;
266 String version = null;
267 List<HealthCheckInfo> componentsInfo = new ArrayList<>();
268 final int timeout = 3000;
270 if (healthCheckUrl != null) {
272 HttpResponse<String> httpResponse = HttpRequest.get(healthCheckUrl, new HttpClientConfig(new Timeouts(timeout, timeout)));
273 int statusCode = httpResponse.getStatusCode();
274 String aggDescription = "";
276 if (statusCode == SC_OK || statusCode == SC_INTERNAL_SERVER_ERROR) {
277 String response = httpResponse.getResponse();
278 log.trace("{} Health Check response: {}", componentName, response);
279 ObjectMapper mapper = new ObjectMapper();
280 Map<String, Object> healthCheckMap = mapper.readValue(response, new TypeReference<Map<String, Object>>() {
282 version = healthCheckMap.get("sdcVersion") != null ? healthCheckMap.get("sdcVersion").toString() : null;
283 if (healthCheckMap.containsKey("componentsInfo")) {
284 componentsInfo = mapper.convertValue(healthCheckMap.get("componentsInfo"), new TypeReference<List<HealthCheckInfo>>() {
288 if (!componentsInfo.isEmpty()) {
289 aggDescription = healthCheckUtil.getAggregateDescription(componentsInfo, null);
291 componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, null));
294 log.trace("{} Health Check Response code: {}", componentName, statusCode);
297 if (statusCode != SC_OK) {
298 healthCheckStatus = DOWN;
299 description = aggDescription.length() > 0
301 : componentName + " is Down, specific reason unknown";//No inner component returned DOWN, but the status of HC is still DOWN.
302 if (componentsInfo.isEmpty()) {
303 componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
306 healthCheckStatus = UP;
310 } catch (Exception e) {
311 log.error("{} unexpected response: ", componentName, e);
312 healthCheckStatus = DOWN;
313 description = componentName + " unexpected response: " + e.getMessage();
314 if (componentsInfo != null && componentsInfo.isEmpty()) {
315 componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
319 healthCheckStatus = DOWN;
320 description = componentName + " health check Configuration is missing";
321 componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
324 return new HealthCheckInfo(componentName, healthCheckStatus, version, description, componentsInfo);
328 protected void destroy() {
330 if (scheduledFuture != null) {
331 scheduledFuture.cancel(true);
332 scheduledFuture = null;
335 if (healthCheckScheduler != null) {
336 healthCheckScheduler.shutdown();
341 private void logAlarm(String componentChangedMsg) {
342 BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(componentChangedMsg);
345 public String getSiteMode() {
346 return switchoverDetector.getSiteMode();
349 public boolean anyStatusChanged(List<HealthCheckInfo> beHealthCheckInfos, List<HealthCheckInfo> prevBeHealthCheckInfos) {
351 boolean result = false;
353 if (beHealthCheckInfos != null && prevBeHealthCheckInfos != null) {
355 Map<String, HealthCheckStatus> currentValues = beHealthCheckInfos.stream().collect(Collectors.toMap(HealthCheckInfo::getHealthCheckComponent, HealthCheckInfo::getHealthCheckStatus));
356 Map<String, HealthCheckStatus> prevValues = prevBeHealthCheckInfos.stream().collect(Collectors.toMap(HealthCheckInfo::getHealthCheckComponent, HealthCheckInfo::getHealthCheckStatus));
358 if (currentValues != null && prevValues != null) {
359 int currentSize = currentValues.size();
360 int prevSize = prevValues.size();
362 if (currentSize != prevSize) {
364 result = true; //extra/missing component
366 Map<String, HealthCheckStatus> notPresent = null;
367 if (currentValues.keySet().containsAll(prevValues.keySet())) {
368 notPresent = new HashMap<>(currentValues);
369 notPresent.keySet().removeAll(prevValues.keySet());
371 notPresent = new HashMap<>(prevValues);
372 notPresent.keySet().removeAll(currentValues.keySet());
375 for (String component : notPresent.keySet()) {
376 logAlarm(format(COMPONENT_CHANGED_MESSAGE, component, prevValues.get(component), currentValues.get(component)));
381 for (Entry<String, HealthCheckStatus> entry : currentValues.entrySet()) {
382 String key = entry.getKey();
383 HealthCheckStatus value = entry.getValue();
385 if (!prevValues.containsKey(key)) {
386 result = true; //component missing
387 logAlarm(format(COMPONENT_CHANGED_MESSAGE, key, prevValues.get(key), currentValues.get(key)));
391 HealthCheckStatus prevHealthCheckStatus = prevValues.get(key);
393 if (value != prevHealthCheckStatus) {
394 result = true; //component status changed
395 logAlarm(format(COMPONENT_CHANGED_MESSAGE, key, prevValues.get(key), currentValues.get(key)));
402 } else if (beHealthCheckInfos == null && prevBeHealthCheckInfos == null) {
405 logAlarm(format(COMPONENT_CHANGED_MESSAGE, "", prevBeHealthCheckInfos == null ? "null" : "true", prevBeHealthCheckInfos == null ? "true" : "null"));
412 private String buildOnBoardingHealthCheckUrl() {
414 Configuration.OnboardingConfig onboardingConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getOnboarding();
416 if (onboardingConfig != null) {
417 String protocol = onboardingConfig.getProtocol();
418 String host = onboardingConfig.getHost();
419 Integer port = onboardingConfig.getPort();
420 String uri = onboardingConfig.getHealthCheckUri();
422 return protocol + "://" + host + ":" + port + uri;
425 log.error("onboarding health check configuration is missing.");
429 private String buildDcaeHealthCheckUrl() {
431 Configuration.DcaeConfig dcaeConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getDcae();
433 if (dcaeConfig != null) {
434 String protocol = dcaeConfig.getProtocol();
435 String host = dcaeConfig.getHost();
436 Integer port = dcaeConfig.getPort();
437 String uri = dcaeConfig.getHealthCheckUri();
439 return protocol + "://" + host + ":" + port + uri;
442 log.error("dcae health check configuration is missing.");
446 public class HealthCheckScheduledTask implements Runnable {
449 Configuration config = ConfigurationManager.getConfigurationManager().getConfiguration();
450 log.trace("Executing BE Health Check Task");
452 List<HealthCheckInfo> currentBeHealthCheckInfos = getBeHealthCheckInfos();
453 boolean healthStatus = healthCheckUtil.getAggregateStatus(currentBeHealthCheckInfos, config.getHealthStatusExclude());
455 boolean prevHealthStatus = healthCheckUtil.getAggregateStatus(prevBeHealthCheckInfos, config.getHealthStatusExclude());
457 boolean anyStatusChanged = anyStatusChanged(currentBeHealthCheckInfos, prevBeHealthCheckInfos);
459 if (prevHealthStatus != healthStatus || anyStatusChanged) {
460 log.trace("BE Health State Changed to {}. Issuing alarm / recovery alarm...", healthStatus);
462 prevBeHealthCheckInfos = currentBeHealthCheckInfos;
463 logAlarm(healthStatus);
467 private void logAlarm(boolean prevHealthState) {
468 if (prevHealthState) {
469 BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(BE_HEALTH_CHECK_STR);
471 BeEcompErrorManager.getInstance().logBeHealthCheckError(BE_HEALTH_CHECK_STR);