Add test cases for consolidated policy health check
[policy/pap.git] / main / src / main / java / org / onap / policy / pap / main / rest / PolicyComponentsHealthCheckProvider.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019-2020 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.pap.main.rest;
22
23 import java.net.HttpURLConnection;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30 import javax.ws.rs.core.Response;
31 import javax.ws.rs.core.Response.Status;
32 import org.apache.commons.lang3.tuple.Pair;
33 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
34 import org.onap.policy.common.endpoints.http.client.HttpClient;
35 import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
36 import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
37 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
38 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
39 import org.onap.policy.common.endpoints.report.HealthCheckReport;
40 import org.onap.policy.common.parameters.ParameterService;
41 import org.onap.policy.common.utils.services.Registry;
42 import org.onap.policy.models.base.PfModelException;
43 import org.onap.policy.models.pdp.concepts.Pdp;
44 import org.onap.policy.models.pdp.concepts.PdpGroup;
45 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
46 import org.onap.policy.models.pdp.enums.PdpHealthStatus;
47 import org.onap.policy.models.provider.PolicyModelsProvider;
48 import org.onap.policy.pap.main.PapConstants;
49 import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
50 import org.onap.policy.pap.main.parameters.PapParameterGroup;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * Provider for PAP to fetch health status of all Policy components, including PAP, API, Distribution, and PDPs.
56  *
57  * @author Yehui Wang (yehui.wang@est.tech)
58  */
59 public class PolicyComponentsHealthCheckProvider {
60
61     private static final Logger LOGGER = LoggerFactory.getLogger(PolicyComponentsHealthCheckProvider.class);
62     private static final String PAP_GROUP_PARAMS_NAME = "PapGroup";
63     private static final String HEALTH_STATUS = "healthy";
64     private static final Pattern IP_REPLACEMENT_PATTERN = Pattern.compile("//(\\S+):");
65     private static final String POLICY_PAP_HEALTHCHECK_URI = "/policy/pap/v1/healthcheck";
66     private PapParameterGroup papParameterGroup = ParameterService.get(PAP_GROUP_PARAMS_NAME);
67     private List<HttpClient> clients = new ArrayList<>();
68
69     /**
70      * Constructs the object.
71      *
72      * @throws HttpClientConfigException if creating http client failed
73      */
74     public PolicyComponentsHealthCheckProvider() throws HttpClientConfigException {
75         this(HttpClientFactoryInstance.getClientFactory());
76     }
77
78     /**
79      * Constructs the object with provided http client factory.
80      *
81      * <p>This constructor is for unit test to use a mock {@link HttpClientFactory}.
82      *
83      * @param clientFactory factory used to construct http client
84      * @throws HttpClientConfigException if creating http client failed
85      */
86     PolicyComponentsHealthCheckProvider(HttpClientFactory clientFactory) throws HttpClientConfigException {
87         for (BusTopicParams params : papParameterGroup.getHealthCheckRestClientParameters()) {
88             params.setManaged(false);
89             clients.add(clientFactory.build(params));
90         }
91     }
92
93     /**
94      * Returns health status of all Policy components.
95      *
96      * @return a pair containing the status and the response
97      */
98     public Pair<Status, Map<String, Object>> fetchPolicyComponentsHealthStatus() {
99         boolean isHealthy = true;
100         Map<String, Object> result = new HashMap<>();
101
102         // Check remote components
103         for (HttpClient client : clients) {
104             HealthCheckReport report = fetchPolicyComponentHealthStatus(client);
105             if (!report.isHealthy()) {
106                 isHealthy = false;
107             }
108             result.put(client.getName(), report);
109         }
110
111         // Check PAP itself
112         HealthCheckReport papReport = new HealthCheckProvider().performHealthCheck();
113         RestServerParameters restServerParameters = papParameterGroup.getRestServerParameters();
114         papReport.setUrl((restServerParameters.isHttps() ? "https://" : "http://") + papReport.getUrl() + ":"
115             + restServerParameters.getPort() + POLICY_PAP_HEALTHCHECK_URI);
116         if (!papReport.isHealthy()) {
117             isHealthy = false;
118         }
119         result.put(PapConstants.POLICY_PAP, papReport);
120
121         // Check PDPs, read status from DB
122         try {
123             Map<String, List<Pdp>> pdpListWithType = fetchPdpsHealthStatus();
124             if (isHealthy && (pdpListWithType.isEmpty() || pdpListWithType.values().stream().flatMap(List::stream)
125                 .anyMatch(pdp -> !PdpHealthStatus.HEALTHY.equals(pdp.getHealthy())))) {
126                 isHealthy = false;
127             }
128             result.put(PapConstants.POLICY_PDPS, pdpListWithType);
129         } catch (final PfModelException exp) {
130             result.put(PapConstants.POLICY_PDPS, exp.getErrorResponse());
131             isHealthy = false;
132         }
133
134         result.put(HEALTH_STATUS, isHealthy);
135         LOGGER.debug("Policy Components HealthCheck Response - {}", result);
136         return Pair.of(Response.Status.OK, result);
137     }
138
139     private Map<String, List<Pdp>> fetchPdpsHealthStatus() throws PfModelException {
140         Map<String, List<Pdp>> pdpListWithType = new HashMap<>();
141         final PolicyModelsProviderFactoryWrapper modelProviderWrapper =
142             Registry.get(PapConstants.REG_PAP_DAO_FACTORY, PolicyModelsProviderFactoryWrapper.class);
143         try (PolicyModelsProvider databaseProvider = modelProviderWrapper.create()) {
144             final List<PdpGroup> groups = databaseProvider.getPdpGroups(null);
145             for (final PdpGroup group : groups) {
146                 for (final PdpSubGroup subGroup : group.getPdpSubgroups()) {
147                     List<Pdp> pdpList = new ArrayList<>(subGroup.getPdpInstances());
148                     pdpListWithType.computeIfAbsent(subGroup.getPdpType(), k -> new ArrayList<>()).addAll(pdpList);
149                 }
150             }
151         }
152         return pdpListWithType;
153     }
154
155     private HealthCheckReport fetchPolicyComponentHealthStatus(HttpClient httpClient) {
156         HealthCheckReport clientReport;
157         try {
158             Response resp = httpClient.get();
159             clientReport = replaceIpWithHostname(
160                 resp.readEntity(HealthCheckReport.class), httpClient.getBaseUrl());
161
162             // A health report is read successfully when HTTP status is not OK, it is also not healthy
163             // even in the report it says healthy.
164             if (resp.getStatus() != HttpURLConnection.HTTP_OK) {
165                 clientReport.setHealthy(false);
166             }
167         } catch (RuntimeException e) {
168             LOGGER.warn("{} connection error", httpClient.getName());
169             clientReport = createUnHealthCheckReport(httpClient.getName(), httpClient.getBaseUrl(),
170                 HttpURLConnection.HTTP_INTERNAL_ERROR, e.getMessage());
171         }
172         return clientReport;
173     }
174
175     private HealthCheckReport createUnHealthCheckReport(String name, String url, int code, String message) {
176         HealthCheckReport report = new HealthCheckReport();
177         report.setName(name);
178         report.setUrl(url);
179         report.setHealthy(false);
180         report.setCode(code);
181         report.setMessage(message);
182         return report;
183     }
184
185     private HealthCheckReport replaceIpWithHostname(HealthCheckReport report, String baseUrl) {
186         Matcher matcher = IP_REPLACEMENT_PATTERN.matcher(baseUrl);
187         String ip = "";
188         if (matcher.find()) {
189             ip = matcher.group(1);
190             report.setUrl(baseUrl.replace(ip, report.getUrl()));
191         }
192         return report;
193     }
194 }