22013c1c04ccef7df0678a660648159956dc32c1
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / health / HealthCheckBusinessLogic.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20 package org.openecomp.sdc.be.components.health;
21
22 import static java.lang.String.format;
23 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
24 import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
25 import static org.apache.http.HttpStatus.SC_OK;
26 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_BE;
27 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_CASSANDRA;
28 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_DMAAP_PRODUCER;
29 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_ECOMP_PORTAL;
30 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_JANUSGRAPH;
31 import static org.openecomp.sdc.common.api.Constants.HC_COMPONENT_ON_BOARDING;
32 import static org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus.DOWN;
33 import static org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus.UP;
34 import static org.openecomp.sdc.common.impl.ExternalConfiguration.getAppVersion;
35
36 import com.fasterxml.jackson.core.type.TypeReference;
37 import com.fasterxml.jackson.databind.ObjectMapper;
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Map.Entry;
43 import java.util.concurrent.ScheduledExecutorService;
44 import java.util.concurrent.ScheduledFuture;
45 import java.util.concurrent.TimeUnit;
46 import java.util.stream.Collectors;
47 import javax.annotation.PostConstruct;
48 import javax.annotation.PreDestroy;
49 import javax.annotation.Resource;
50 import org.apache.commons.lang3.tuple.ImmutablePair;
51 import org.apache.commons.lang3.tuple.Pair;
52 import org.openecomp.sdc.be.catalog.impl.DmaapProducerHealth;
53 import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineClusterHealth;
54 import org.openecomp.sdc.be.components.distribution.engine.DmaapHealth;
55 import org.openecomp.sdc.be.components.impl.CADIHealthCheck;
56 import org.openecomp.sdc.be.components.impl.CassandraHealthCheck;
57 import org.openecomp.sdc.be.config.BeEcompErrorManager;
58 import org.openecomp.sdc.be.config.Configuration;
59 import org.openecomp.sdc.be.config.ConfigurationManager;
60 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
61 import org.openecomp.sdc.be.switchover.detector.SwitchoverDetector;
62 import org.openecomp.sdc.common.api.HealthCheckInfo;
63 import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
64 import org.openecomp.sdc.common.http.client.api.HttpRequest;
65 import org.openecomp.sdc.common.http.client.api.HttpResponse;
66 import org.openecomp.sdc.common.http.config.HttpClientConfig;
67 import org.openecomp.sdc.common.http.config.Timeouts;
68 import org.openecomp.sdc.common.log.elements.LogFieldsMdcHandler;
69 import org.openecomp.sdc.common.log.wrappers.Logger;
70 import org.openecomp.sdc.common.util.HealthCheckUtil;
71 import org.springframework.beans.factory.annotation.Autowired;
72 import org.springframework.stereotype.Component;
73
74 @Component("healthCheckBusinessLogic")
75 public class HealthCheckBusinessLogic {
76
77     private static String hcUrl = "%s://%s:%s%s";
78     private static final String BE_HEALTH_CHECK_STR = "beHealthCheck";
79     private static final String LOG_PARTNER_NAME = "SDC.BE";
80     private static final String COMPONENT_CHANGED_MESSAGE = "BE Component %s state changed from %s to %s";
81     private static final Logger log = Logger.getLogger(HealthCheckBusinessLogic.class.getName());
82     private static final HealthCheckUtil healthCheckUtil = new HealthCheckUtil();
83     private final ScheduledExecutorService healthCheckScheduler = newSingleThreadScheduledExecutor(
84         (Runnable r) -> new Thread(r, "BE-Health-Check-Task"));
85     private HealthCheckScheduledTask healthCheckScheduledTask = null;
86     private static LogFieldsMdcHandler mdcFieldsHandler = new LogFieldsMdcHandler();
87     @Resource
88     private JanusGraphGenericDao janusGraphGenericDao;
89     @Resource
90     private DistributionEngineClusterHealth distributionEngineClusterHealth;
91     @Resource
92     private DmaapHealth dmaapHealth;
93     @Resource
94     private DmaapProducerHealth dmaapProducerHealth;
95     @Resource
96     private CassandraHealthCheck cassandraHealthCheck;
97     @Resource
98     private PortalHealthCheckBuilder portalHealthCheck;
99     @Autowired
100     private SwitchoverDetector switchoverDetector;
101     private volatile List<HealthCheckInfo> prevBeHealthCheckInfos = null;
102     private ScheduledFuture<?> scheduledFuture = null;
103
104     @PostConstruct
105     public void init() {
106         prevBeHealthCheckInfos = getBeHealthCheckInfos();
107         log.debug("After initializing prevBeHealthCheckInfos: {}", prevBeHealthCheckInfos);
108         healthCheckScheduledTask = new HealthCheckScheduledTask();
109         if (this.scheduledFuture == null) {
110             this.scheduledFuture = this.healthCheckScheduler.scheduleAtFixedRate(healthCheckScheduledTask, 0, 3, TimeUnit.SECONDS);
111         }
112     }
113
114     public boolean isDistributionEngineUp() {
115         HealthCheckInfo healthCheckInfo = distributionEngineClusterHealth.getHealthCheckInfo();
116         return healthCheckInfo.getHealthCheckStatus() != DOWN;
117     }
118
119     public Pair<Boolean, List<HealthCheckInfo>> getBeHealthCheckInfosStatus() {
120         Configuration config = ConfigurationManager.getConfigurationManager().getConfiguration();
121         return new ImmutablePair<>(healthCheckUtil.getAggregateStatus(prevBeHealthCheckInfos, config.getHealthStatusExclude()),
122             prevBeHealthCheckInfos);
123     }
124
125     private List<HealthCheckInfo> getBeHealthCheckInfos() {
126         log.trace("In getBeHealthCheckInfos");
127         List<HealthCheckInfo> healthCheckInfos = new ArrayList<>();
128         //Dmaap
129         HealthCheckInfo info;
130         if ((info = getDmaapHealthCheck()) != null) {
131             healthCheckInfos.add(info);
132         }
133         //DmaapProducer
134         healthCheckInfos.add(getDmaapProducerHealthCheck());
135         // BE
136         healthCheckInfos.add(new HealthCheckInfo(HC_COMPONENT_BE, UP, getAppVersion(), "OK"));
137         // JanusGraph
138         healthCheckInfos.add(getJanusGraphHealthCheck());
139         // Distribution Engine
140         healthCheckInfos.add(distributionEngineClusterHealth.getHealthCheckInfo());
141         //Cassandra
142         healthCheckInfos.add(getCassandraHealthCheck());
143         // Amdocs
144         healthCheckInfos.add(getHostedComponentsBeHealthCheck(HC_COMPONENT_ON_BOARDING, buildOnBoardingHealthCheckUrl()));
145         //ECOMP Portal
146         healthCheckInfos.add(portalHealthCheck.getHealthCheckInfo());
147         //CADI
148         healthCheckInfos.add(CADIHealthCheck.getCADIHealthCheckInstance().getCADIStatus());
149         return healthCheckInfos;
150     }
151
152     private HealthCheckInfo getDmaapHealthCheck() {
153         HealthCheckInfo healthCheckInfo = null;
154         if (ConfigurationManager.getConfigurationManager().getConfiguration().getDmaapConsumerConfiguration().isActive()) {
155             String appVersion = getAppVersion();
156             dmaapHealth.getHealthCheckInfo().setVersion(appVersion);
157             healthCheckInfo = dmaapHealth.getHealthCheckInfo();
158         } else {
159             log.debug("Dmaap health check disabled");
160         }
161         return healthCheckInfo;
162     }
163
164     private HealthCheckInfo getDmaapProducerHealthCheck() {
165         if (ConfigurationManager.getConfigurationManager().getConfiguration().getDmaapConsumerConfiguration().isActive()) {
166             String appVersion = getAppVersion();
167             dmaapProducerHealth.getHealthCheckInfo().setVersion(appVersion);
168             return dmaapProducerHealth.getHealthCheckInfo();
169         } else {
170             String description = "Dmaap health check disabled";
171             log.debug(description);
172             return new HealthCheckInfo(HC_COMPONENT_DMAAP_PRODUCER, DOWN, null, description);
173         }
174     }
175
176     public HealthCheckInfo getJanusGraphHealthCheck() {
177         // JanusGraph health check and version
178         String description;
179         boolean isJanusGraphUp;
180         HealthCheckInfo healthCheckInfo = new HealthCheckInfo(HC_COMPONENT_JANUSGRAPH, DOWN, null, null);
181         try {
182             isJanusGraphUp = janusGraphGenericDao.isGraphOpen();
183         } catch (Exception e) {
184             description = "JanusGraph error: " + e.getMessage();
185             healthCheckInfo.setDescription(description);
186             log.error(description);
187             return healthCheckInfo;
188         }
189         if (isJanusGraphUp) {
190             description = "OK";
191             healthCheckInfo.setDescription(description);
192             healthCheckInfo.setHealthCheckStatus(HealthCheckInfo.HealthCheckStatus.UP);
193         } else {
194             description = "JanusGraph graph is down";
195             healthCheckInfo.setDescription(description);
196         }
197         return healthCheckInfo;
198     }
199
200     private HealthCheckInfo getCassandraHealthCheck() {
201         String description;
202         boolean isCassandraUp = false;
203         HealthCheckInfo healthCheckInfo = new HealthCheckInfo(HC_COMPONENT_CASSANDRA, DOWN, null, null);
204         try {
205             isCassandraUp = cassandraHealthCheck.getCassandraStatus();
206         } catch (Exception e) {
207             description = "Cassandra error: " + e.getMessage();
208             log.error(description, e);
209         }
210         if (isCassandraUp) {
211             description = "OK";
212             healthCheckInfo.setHealthCheckStatus(HealthCheckStatus.UP);
213             healthCheckInfo.setDescription(description);
214         } else {
215             description = "Cassandra is down";
216             healthCheckInfo.setDescription(description);
217         }
218         return healthCheckInfo;
219     }
220
221     private HealthCheckInfo getHostedComponentsBeHealthCheck(String componentName, String healthCheckUrl) {
222         HealthCheckStatus healthCheckStatus;
223         String description;
224         String version = null;
225         List<HealthCheckInfo> componentsInfo = new ArrayList<>();
226         final int timeout = 3000;
227         if (healthCheckUrl != null) {
228             try {
229                 HttpResponse<String> httpResponse = HttpRequest.get(healthCheckUrl, new HttpClientConfig(new Timeouts(timeout, timeout)));
230                 int statusCode = httpResponse.getStatusCode();
231                 String aggDescription = "";
232                 if ((statusCode == SC_OK || statusCode == SC_INTERNAL_SERVER_ERROR) && !componentName.equals(HC_COMPONENT_ECOMP_PORTAL)) {
233                     String response = httpResponse.getResponse();
234                     log.trace("{} Health Check response: {}", componentName, response);
235                     ObjectMapper mapper = new ObjectMapper();
236                     Map<String, Object> healthCheckMap = mapper.readValue(response, new TypeReference<Map<String, Object>>() {
237                     });
238                     version = getVersion(healthCheckMap);
239                     if (healthCheckMap.containsKey("componentsInfo")) {
240                         componentsInfo = mapper.convertValue(healthCheckMap.get("componentsInfo"), new TypeReference<List<HealthCheckInfo>>() {
241                         });
242                     }
243                     aggDescription = getAggDescription(componentsInfo, aggDescription);
244                 } else {
245                     log.trace("{} Health Check Response code: {}", componentName, statusCode);
246                 }
247                 if (statusCode != SC_OK) {
248                     healthCheckStatus = DOWN;
249                     description = getDescription(componentName, aggDescription);
250                     setDescriptionToObject(description, componentsInfo);
251                 } else {
252                     healthCheckStatus = UP;
253                     description = "OK";
254                 }
255             } catch (Exception e) {
256                 log.error("{} unexpected response: ", componentName, e);
257                 healthCheckStatus = DOWN;
258                 description = componentName + " unexpected response: " + e.getMessage();
259                 addToHealthCheckInfoObject(description, componentsInfo);
260             }
261         } else {
262             healthCheckStatus = DOWN;
263             description = componentName + " health check Configuration is missing";
264             componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
265         }
266         return new HealthCheckInfo(componentName, healthCheckStatus, version, description, componentsInfo);
267     }
268
269     private void addToHealthCheckInfoObject(String description, List<HealthCheckInfo> componentsInfo) {
270         if (componentsInfo != null && componentsInfo.isEmpty()) {
271             componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
272         }
273     }
274
275     private void setDescriptionToObject(String description, List<HealthCheckInfo> componentsInfo) {
276         if (componentsInfo.isEmpty()) {
277             componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, description));
278         }
279     }
280
281     private String getDescription(String componentName, String aggDescription) {
282         String description;
283         description = aggDescription.length() > 0 ? aggDescription
284             : componentName + " is Down, specific reason unknown";//No inner component returned DOWN, but the status of HC is still DOWN.
285         return description;
286     }
287
288     private String getVersion(Map<String, Object> healthCheckMap) {
289         return healthCheckMap.get("sdcVersion") != null ? healthCheckMap.get("sdcVersion").toString() : null;
290     }
291
292     private String getAggDescription(List<HealthCheckInfo> componentsInfo, String aggDescription) {
293         if (!componentsInfo.isEmpty()) {
294             aggDescription = healthCheckUtil.getAggregateDescription(componentsInfo);
295         } else {
296             componentsInfo.add(new HealthCheckInfo(HC_COMPONENT_BE, DOWN, null, null));
297         }
298         return aggDescription;
299     }
300
301     @PreDestroy
302     protected void destroy() {
303         if (scheduledFuture != null) {
304             scheduledFuture.cancel(true);
305             scheduledFuture = null;
306         }
307         if (healthCheckScheduler != null) {
308             healthCheckScheduler.shutdown();
309         }
310     }
311
312     private void logAlarm(String componentChangedMsg) {
313         BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(componentChangedMsg);
314     }
315
316     public String getSiteMode() {
317         return switchoverDetector.getSiteMode();
318     }
319
320     public boolean anyStatusChanged(List<HealthCheckInfo> beHealthCheckInfos, List<HealthCheckInfo> prevBeHealthCheckInfos) {
321         boolean result = false;
322         if (beHealthCheckInfos != null && prevBeHealthCheckInfos != null) {
323             Map<String, HealthCheckStatus> currentValues = beHealthCheckInfos.stream()
324                 .collect(Collectors.toMap(HealthCheckInfo::getHealthCheckComponent, HealthCheckInfo::getHealthCheckStatus));
325             Map<String, HealthCheckStatus> prevValues = prevBeHealthCheckInfos.stream()
326                 .collect(Collectors.toMap(HealthCheckInfo::getHealthCheckComponent, HealthCheckInfo::getHealthCheckStatus));
327             if (currentValues != null && prevValues != null) {
328                 int currentSize = currentValues.size();
329                 int prevSize = prevValues.size();
330                 if (currentSize != prevSize) {
331                     result = true; //extra/missing component
332                     updateHealthCheckStatusMap(currentValues, prevValues);
333                 } else {
334                     result = isHealthStatusChanged(result, currentValues, prevValues);
335                 }
336             }
337         } else if (beHealthCheckInfos == null && prevBeHealthCheckInfos == null) {
338             result = false;
339         } else {
340             writeLogAlarm(prevBeHealthCheckInfos);
341             result = true;
342         }
343         return result;
344     }
345
346     private void writeLogAlarm(List<HealthCheckInfo> prevBeHealthCheckInfos) {
347         logAlarm(format(COMPONENT_CHANGED_MESSAGE, "", prevBeHealthCheckInfos == null ? "null" : "true",
348             prevBeHealthCheckInfos == null ? "true" : "null"));
349     }
350
351     private boolean isHealthStatusChanged(boolean result, Map<String, HealthCheckStatus> currentValues, Map<String, HealthCheckStatus> prevValues) {
352         for (Entry<String, HealthCheckStatus> entry : currentValues.entrySet()) {
353             String key = entry.getKey();
354             HealthCheckStatus value = entry.getValue();
355             if (!prevValues.containsKey(key)) {
356                 result = true; //component missing
357                 logAlarm(format(COMPONENT_CHANGED_MESSAGE, key, prevValues.get(key), currentValues.get(key)));
358                 break;
359             }
360             HealthCheckStatus prevHealthCheckStatus = prevValues.get(key);
361             if (value != prevHealthCheckStatus) {
362                 result = true; //component status changed
363                 logAlarm(format(COMPONENT_CHANGED_MESSAGE, key, prevValues.get(key), currentValues.get(key)));
364                 break;
365             }
366         }
367         return result;
368     }
369
370     private void updateHealthCheckStatusMap(Map<String, HealthCheckStatus> currentValues, Map<String, HealthCheckStatus> prevValues) {
371         Map<String, HealthCheckStatus> notPresent;
372         if (currentValues.keySet().containsAll(prevValues.keySet())) {
373             notPresent = new HashMap<>(currentValues);
374             notPresent.keySet().removeAll(prevValues.keySet());
375         } else {
376             notPresent = new HashMap<>(prevValues);
377             notPresent.keySet().removeAll(currentValues.keySet());
378         }
379         for (String component : notPresent.keySet()) {
380             logAlarm(format(COMPONENT_CHANGED_MESSAGE, component, prevValues.get(component), currentValues.get(component)));
381         }
382     }
383
384     private String buildOnBoardingHealthCheckUrl() {
385         Configuration.OnboardingConfig onboardingConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getOnboarding();
386         if (onboardingConfig != null) {
387             return String.format(hcUrl, onboardingConfig.getProtocol(), onboardingConfig.getHost(), onboardingConfig.getPort(),
388                 onboardingConfig.getHealthCheckUri());
389         }
390         log.error("Onboarding health check configuration is missing.");
391         return null;
392     }
393
394     public class HealthCheckScheduledTask implements Runnable {
395
396         @Override
397         public void run() {
398             mdcFieldsHandler.addInfoForErrorAndDebugLogging(LOG_PARTNER_NAME);
399             Configuration config = ConfigurationManager.getConfigurationManager().getConfiguration();
400             log.trace("Executing BE Health Check Task");
401             List<HealthCheckInfo> currentBeHealthCheckInfos = getBeHealthCheckInfos();
402             boolean healthStatus = healthCheckUtil.getAggregateStatus(currentBeHealthCheckInfos, config.getHealthStatusExclude());
403             boolean prevHealthStatus = healthCheckUtil.getAggregateStatus(prevBeHealthCheckInfos, config.getHealthStatusExclude());
404             boolean anyStatusChanged = anyStatusChanged(currentBeHealthCheckInfos, prevBeHealthCheckInfos);
405             if (prevHealthStatus != healthStatus || anyStatusChanged) {
406                 log.trace("BE Health State Changed to {}. Issuing alarm / recovery alarm...", healthStatus);
407                 prevBeHealthCheckInfos = currentBeHealthCheckInfos;
408                 logAlarm(healthStatus);
409             }
410         }
411
412         private void logAlarm(boolean prevHealthState) {
413             if (prevHealthState) {
414                 BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(BE_HEALTH_CHECK_STR);
415             } else {
416                 BeEcompErrorManager.getInstance().logBeHealthCheckError(BE_HEALTH_CHECK_STR);
417             }
418         }
419     }
420 }