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.impl;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
29 import java.util.concurrent.Executors;
30 import java.util.concurrent.ScheduledExecutorService;
31 import java.util.concurrent.ScheduledFuture;
32 import java.util.concurrent.ThreadFactory;
33 import java.util.concurrent.TimeUnit;
34 import java.util.stream.Collectors;
36 import javax.annotation.PostConstruct;
37 import javax.annotation.PreDestroy;
38 import javax.annotation.Resource;
40 import org.apache.commons.lang3.tuple.ImmutablePair;
41 import org.apache.commons.lang3.tuple.Pair;
42 import org.apache.http.HttpEntity;
43 import org.apache.http.HttpStatus;
44 import org.apache.http.client.config.RequestConfig;
45 import org.apache.http.client.methods.CloseableHttpResponse;
46 import org.apache.http.client.methods.HttpGet;
47 import org.apache.http.impl.client.CloseableHttpClient;
48 import org.apache.http.impl.client.HttpClientBuilder;
49 import org.apache.http.util.EntityUtils;
50 import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineClusterHealth;
51 import org.openecomp.sdc.be.components.distribution.engine.UebHealthCheckCall;
52 import org.openecomp.sdc.be.config.BeEcompErrorManager;
53 import org.openecomp.sdc.be.config.Configuration;
54 import org.openecomp.sdc.be.config.ConfigurationManager;
55 import org.openecomp.sdc.be.dao.api.IEsHealthCheckDao;
56 import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
57 import org.openecomp.sdc.be.switchover.detector.SwitchoverDetector;
58 import org.openecomp.sdc.common.api.Constants;
59 import org.openecomp.sdc.common.api.HealthCheckInfo;
60 import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
61 import org.openecomp.sdc.common.impl.ExternalConfiguration;
62 import org.openecomp.sdc.common.util.HealthCheckUtil;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65 import org.springframework.beans.factory.annotation.Autowired;
66 import org.springframework.stereotype.Component;
68 import com.fasterxml.jackson.core.type.TypeReference;
69 import com.fasterxml.jackson.databind.ObjectMapper;
71 @Component("healthCheckBusinessLogic")
72 public class HealthCheckBusinessLogic {
74 protected static String BE_HEALTH_LOG_CONTEXT = "be.healthcheck";
76 private static Logger healthLogger = LoggerFactory.getLogger(BE_HEALTH_LOG_CONTEXT);
78 private static final String BE_HEALTH_CHECK_STR = "beHealthCheck";
79 private static final String COMPONENT_CHANGED_MESSAGE = "BE Component %s state changed from %s to %s";
82 private TitanGenericDao titanGenericDao;
85 private IEsHealthCheckDao esHealthCheckDao;
88 private DistributionEngineClusterHealth distributionEngineClusterHealth;
91 private CassandraHealthCheck cassandraHealthCheck;
94 private SwitchoverDetector switchoverDetector;
96 private static Logger log = LoggerFactory.getLogger(HealthCheckBusinessLogic.class.getName());
98 private volatile List<HealthCheckInfo> prevBeHealthCheckInfos = null;
100 public HealthCheckBusinessLogic() {
104 private ScheduledFuture<?> scheduledFuture = null;
106 ScheduledExecutorService healthCheckScheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
108 public Thread newThread(Runnable r) {
109 return new Thread(r, "BE-Health-Check-Task");
113 HealthCheckScheduledTask healthCheckScheduledTask = null;
118 prevBeHealthCheckInfos = getBeHealthCheckInfos();
120 log.debug("After initializing prevBeHealthCheckInfos: {}", prevBeHealthCheckInfos);
122 healthCheckScheduledTask = new HealthCheckScheduledTask();
124 if (this.scheduledFuture == null) {
125 this.scheduledFuture = this.healthCheckScheduler.scheduleAtFixedRate(healthCheckScheduledTask, 0, 3, TimeUnit.SECONDS);
130 public boolean isDistributionEngineUp() {
132 HealthCheckInfo healthCheckInfo = distributionEngineClusterHealth.getHealthCheckInfo();
133 if (healthCheckInfo.getHealthCheckStatus().equals(HealthCheckStatus.DOWN)) {
139 public Pair<Boolean, List<HealthCheckInfo>> getBeHealthCheckInfosStatus() {
141 return new ImmutablePair<Boolean, List<HealthCheckInfo>>(getAggregateBeStatus(prevBeHealthCheckInfos), prevBeHealthCheckInfos);
145 private Boolean getAggregateBeStatus(List<HealthCheckInfo> beHealthCheckInfos) {
147 Boolean status = true;
149 for (HealthCheckInfo healthCheckInfo : beHealthCheckInfos) {
150 if (healthCheckInfo.getHealthCheckStatus().equals(HealthCheckStatus.DOWN) && !healthCheckInfo.getHealthCheckComponent().equals(Constants.HC_COMPONENT_DISTRIBUTION_ENGINE)) {
159 private List<HealthCheckInfo> getBeHealthCheckInfos() {
161 log.trace("In getBeHealthCheckInfos");
163 List<HealthCheckInfo> healthCheckInfos = new ArrayList<HealthCheckInfo>();
166 getBeHealthCheck(healthCheckInfos);
169 getTitanHealthCheck(healthCheckInfos);
171 // Distribution Engine
172 getDistributionEngineCheck(healthCheckInfos);
175 getCassandraHealthCheck(healthCheckInfos);
178 getAmdocsHealthCheck(healthCheckInfos);
181 getDcaeHealthCheck(healthCheckInfos);
183 return healthCheckInfos;
186 private List<HealthCheckInfo> getBeHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
187 String appVersion = ExternalConfiguration.getAppVersion();
188 String description = "OK";
189 healthCheckInfos.add(new HealthCheckInfo(Constants.HC_COMPONENT_BE, HealthCheckStatus.UP, appVersion, description));
190 return healthCheckInfos;
193 //Removed from aggregate HC - TDP 293490
194 /* private List<HealthCheckInfo> getEsHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
196 // ES health check and version
197 HealthCheckStatus healthCheckStatus;
201 healthCheckStatus = esHealthCheckDao.getClusterHealthStatus();
202 } catch (Exception e) {
203 healthCheckStatus = HealthCheckStatus.DOWN;
204 description = "ES cluster error: " + e.getMessage();
205 healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.ES, healthCheckStatus, null, description));
206 return healthCheckInfos;
208 if (healthCheckStatus.equals(HealthCheckStatus.DOWN)) {
209 description = "ES cluster is down";
213 healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.ES, healthCheckStatus, null, description));
214 return healthCheckInfos;
217 public List<HealthCheckInfo> getTitanHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
218 // Titan health check and version
223 isTitanUp = titanGenericDao.isGraphOpen();
224 } catch (Exception e) {
225 description = "Titan error: " + e.getMessage();
226 healthCheckInfos.add(new HealthCheckInfo(Constants.HC_COMPONENT_TITAN, HealthCheckStatus.DOWN, null, description));
227 return healthCheckInfos;
231 healthCheckInfos.add(new HealthCheckInfo(Constants.HC_COMPONENT_TITAN, HealthCheckStatus.UP, null, description));
233 description = "Titan graph is down";
234 healthCheckInfos.add(new HealthCheckInfo(Constants.HC_COMPONENT_TITAN, HealthCheckStatus.DOWN, null, description));
236 return healthCheckInfos;
239 private List<HealthCheckInfo> getCassandraHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
242 boolean isCassandraUp;
245 isCassandraUp = cassandraHealthCheck.getCassandraStatus();
246 } catch (Exception e) {
247 isCassandraUp = false;
248 description = "Cassandra error: " + e.getMessage();
252 healthCheckInfos.add(new HealthCheckInfo(Constants.HC_COMPONENT_CASSANDRA, HealthCheckStatus.UP, null, description));
254 description = "Cassandra is down";
255 healthCheckInfos.add(new HealthCheckInfo(Constants.HC_COMPONENT_CASSANDRA, HealthCheckStatus.DOWN, null, description));
257 return healthCheckInfos;
261 private void getDistributionEngineCheck(List<HealthCheckInfo> healthCheckInfos) {
263 HealthCheckInfo healthCheckInfo = distributionEngineClusterHealth.getHealthCheckInfo();
265 healthCheckInfos.add(healthCheckInfo);
269 private List<HealthCheckInfo> getAmdocsHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
270 HealthCheckInfo beHealthCheckInfo = getHostedComponentsBeHealthCheck(Constants.HC_COMPONENT_ON_BOARDING, buildOnBoardingHealthCheckUrl());
271 healthCheckInfos.add(beHealthCheckInfo);
272 return healthCheckInfos;
275 private List<HealthCheckInfo> getDcaeHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
276 HealthCheckInfo beHealthCheckInfo = getHostedComponentsBeHealthCheck(Constants.HC_COMPONENT_DCAE, buildDcaeHealthCheckUrl());
277 healthCheckInfos.add(beHealthCheckInfo);
278 return healthCheckInfos;
281 private HealthCheckInfo getHostedComponentsBeHealthCheck(String componentName, String healthCheckUrl) {
282 HealthCheckStatus healthCheckStatus;
284 String version = null;
285 List<HealthCheckInfo> componentsInfo = new ArrayList<>();
287 CloseableHttpClient httpClient = getHttpClient();
289 if (healthCheckUrl != null) {
290 HttpGet httpGet = new HttpGet(healthCheckUrl);
291 CloseableHttpResponse beResponse;
294 beResponse = httpClient.execute(httpGet);
295 beStatus = beResponse.getStatusLine().getStatusCode();
297 String aggDescription = "";
299 if (beStatus == HttpStatus.SC_OK || beStatus == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
300 HttpEntity entity = beResponse.getEntity();
301 String beJsonResponse = EntityUtils.toString(entity);
302 log.trace("{} Health Check response: {}", componentName, beJsonResponse);
304 ObjectMapper mapper = new ObjectMapper();
305 Map<String, Object> healthCheckMap = mapper.readValue(beJsonResponse, new TypeReference<Map<String, Object>>(){});
306 version = healthCheckMap.get("sdcVersion") != null ? healthCheckMap.get("sdcVersion").toString() : null;
307 if (healthCheckMap.containsKey("componentsInfo")) {
308 componentsInfo = mapper.convertValue(healthCheckMap.get("componentsInfo"), new TypeReference<List<HealthCheckInfo>>() {});
311 if (componentsInfo.size() > 0) {
312 aggDescription = HealthCheckUtil.getAggregateDescription(componentsInfo, null);
314 componentsInfo.add(new HealthCheckInfo(Constants.HC_COMPONENT_BE, HealthCheckStatus.DOWN, null, null));
317 log.trace("{} Health Check Response code: {}", componentName, beStatus);
320 if (beStatus != HttpStatus.SC_OK) {
321 healthCheckStatus = HealthCheckStatus.DOWN;
322 description = aggDescription.length() > 0
324 : componentName + " is Down, specific reason unknown";//No inner component returned DOWN, but the status of HC is still DOWN.
325 if (componentsInfo.size() == 0) {
326 componentsInfo.add(new HealthCheckInfo(Constants.HC_COMPONENT_BE, HealthCheckStatus.DOWN, null, description));
329 healthCheckStatus = HealthCheckStatus.UP;
333 } catch (Exception e) {
334 log.error("{} unexpected response: ", componentName, e);
335 healthCheckStatus = HealthCheckStatus.DOWN;
336 description = componentName + " unexpected response: " + e.getMessage();
337 if (componentsInfo != null && componentsInfo.size() == 0) {
338 componentsInfo.add(new HealthCheckInfo(Constants.HC_COMPONENT_BE, HealthCheckStatus.DOWN, null, description));
341 if (httpClient != null) {
344 } catch (IOException e) {
345 log.error("closing http client has failed" , e);
350 healthCheckStatus = HealthCheckStatus.DOWN;
351 description = componentName + " health check Configuration is missing";
352 componentsInfo.add(new HealthCheckInfo(Constants.HC_COMPONENT_BE, HealthCheckStatus.DOWN, null, description));
355 return new HealthCheckInfo(componentName, healthCheckStatus, version, description, componentsInfo);
358 private CloseableHttpClient getHttpClient() {
360 RequestConfig.Builder requestBuilder = RequestConfig.custom();
361 requestBuilder.setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout);
363 HttpClientBuilder builder = HttpClientBuilder.create();
364 builder.setDefaultRequestConfig(requestBuilder.build());
365 return builder.build();
369 private void destroy() {
371 if (scheduledFuture != null) {
372 scheduledFuture.cancel(true);
373 scheduledFuture = null;
376 if (healthCheckScheduler != null) {
377 healthCheckScheduler.shutdown();
382 public class HealthCheckScheduledTask implements Runnable {
384 List<UebHealthCheckCall> healthCheckCalls = new ArrayList<>();
386 public HealthCheckScheduledTask() {
393 healthLogger.trace("Executing BE Health Check Task");
395 List<HealthCheckInfo> currentBeHealthCheckInfos = getBeHealthCheckInfos();
396 boolean healthStatus = getAggregateBeStatus(currentBeHealthCheckInfos);
398 boolean prevHealthStatus = getAggregateBeStatus(prevBeHealthCheckInfos);
400 boolean anyStatusChanged = anyStatusChanged(currentBeHealthCheckInfos, prevBeHealthCheckInfos);
402 if (prevHealthStatus != healthStatus || anyStatusChanged) {
403 log.trace("BE Health State Changed to {}. Issuing alarm / recovery alarm...", healthStatus);
405 prevBeHealthCheckInfos = currentBeHealthCheckInfos;
406 logAlarm(healthStatus);
412 private void logAlarm(boolean prevHealthState) {
413 if (prevHealthState) {
414 BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(BE_HEALTH_CHECK_STR);
416 BeEcompErrorManager.getInstance().logBeHealthCheckError(BE_HEALTH_CHECK_STR);
420 private void logAlarm(String componentChangedMsg) {
421 BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(componentChangedMsg);
425 public String getSiteMode() {
426 return switchoverDetector.getSiteMode();
429 public boolean anyStatusChanged(List<HealthCheckInfo> beHealthCheckInfos, List<HealthCheckInfo> prevBeHealthCheckInfos) {
431 boolean result = false;
433 if (beHealthCheckInfos != null && prevBeHealthCheckInfos != null) {
435 Map<String, HealthCheckStatus> currentValues = beHealthCheckInfos.stream().collect(Collectors.toMap(p -> p.getHealthCheckComponent(), p -> p.getHealthCheckStatus()));
436 Map<String, HealthCheckStatus> prevValues = prevBeHealthCheckInfos.stream().collect(Collectors.toMap(p -> p.getHealthCheckComponent(), p -> p.getHealthCheckStatus()));
438 if (currentValues != null && prevValues != null) {
439 int currentSize = currentValues.size();
440 int prevSize = prevValues.size();
442 if (currentSize != prevSize) {
444 result = true; //extra/missing component
446 Map<String, HealthCheckStatus> notPresent = null;
447 if (currentValues.keySet().containsAll(prevValues.keySet())) {
448 notPresent = new HashMap<>(currentValues);
449 notPresent.keySet().removeAll(prevValues.keySet());
451 notPresent = new HashMap<>(prevValues);
452 notPresent.keySet().removeAll(currentValues.keySet());
455 for (String component : notPresent.keySet()) {
456 logAlarm(String.format(COMPONENT_CHANGED_MESSAGE, component, prevValues.get(component), currentValues.get(component)));
458 // HealthCheckComponent changedComponent = notPresent.keySet().iterator().next();
462 for (Entry<String, HealthCheckStatus> entry : currentValues.entrySet()) {
463 String key = entry.getKey();
464 HealthCheckStatus value = entry.getValue();
466 if (!prevValues.containsKey(key)) {
467 result = true; //component missing
468 logAlarm(String.format(COMPONENT_CHANGED_MESSAGE, key, prevValues.get(key), currentValues.get(key)));
472 HealthCheckStatus prevHealthCheckStatus = prevValues.get(key);
474 if (value != prevHealthCheckStatus) {
475 result = true; //component status changed
476 logAlarm(String.format(COMPONENT_CHANGED_MESSAGE, key, prevValues.get(key), currentValues.get(key)));
483 } else if (beHealthCheckInfos == null && prevBeHealthCheckInfos == null) {
486 logAlarm(String.format(COMPONENT_CHANGED_MESSAGE, "", prevBeHealthCheckInfos == null ? "null" : "true", prevBeHealthCheckInfos == null ? "true" : "null"));
493 private String buildOnBoardingHealthCheckUrl() {
495 Configuration.OnboardingConfig onboardingConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getOnboarding();
497 if (onboardingConfig != null) {
498 String protocol = onboardingConfig.getProtocol();
499 String host = onboardingConfig.getHost();
500 Integer port = onboardingConfig.getPort();
501 String uri = onboardingConfig.getHealthCheckUri();
503 return protocol + "://" + host + ":" + port + uri;
506 log.error("onboarding health check configuration is missing.");
510 private String buildDcaeHealthCheckUrl() {
512 Configuration.DcaeConfig dcaeConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getDcae();
514 if (dcaeConfig != null) {
515 String protocol = dcaeConfig.getProtocol();
516 String host = dcaeConfig.getHost();
517 Integer port = dcaeConfig.getPort();
518 String uri = dcaeConfig.getHealthCheckUri();
520 return protocol + "://" + host + ":" + port + uri;
523 log.error("dcae health check configuration is missing.");