40ef11d3473c1b840eac2069c225f4ae216c7037
[so.git] / bpmn / MSOCoreBPMN / src / main / java / org / openecomp / mso / bpmn / core / HealthCheckHandler.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * OPENECOMP - MSO
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
21 package org.openecomp.mso.bpmn.core;
22
23 import java.io.BufferedReader;
24 import java.io.InputStreamReader;
25 import java.util.Base64;
26 import java.util.Map;
27 import java.util.UUID;
28
29 import javax.ws.rs.GET;
30 import javax.ws.rs.HEAD;
31 import javax.ws.rs.Path;
32 import javax.ws.rs.Produces;
33 import javax.ws.rs.QueryParam;
34 import javax.ws.rs.core.Response;
35
36 import org.apache.http.HttpEntity;
37 import org.apache.http.HttpResponse;
38 import org.apache.http.HttpStatus;
39 import org.apache.http.client.config.RequestConfig;
40 import org.apache.http.client.methods.HttpPost;
41 import org.apache.http.entity.StringEntity;
42 import org.apache.http.impl.client.CloseableHttpClient;
43 import org.apache.http.impl.client.HttpClientBuilder;
44 import org.camunda.bpm.engine.ProcessEngines;
45 import org.openecomp.mso.HealthCheckUtils;
46 import org.openecomp.mso.logger.MessageEnum;
47 import org.openecomp.mso.logger.MsoLogger;
48 import org.openecomp.mso.utils.CryptoUtils;
49 import org.openecomp.mso.utils.UUIDChecker;
50
51 @Path("/")
52 public class HealthCheckHandler  {
53
54     private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
55     private static final String SITENAME = "mso.sitename";
56     private static final String ADPTER_ENDPOINT = "mso.openecomp.adapters.db.endpoint";
57     private static final String OPENECOMP_ADAPTER_NAMESPACE = "mso.openecomp.adapter.namespace";
58     private static final String CONFIG = "mso.bpmn.urn.properties";
59     private static final String CREDENTIAL = "mso.adapters.db.auth";
60     private static final String MSOKEY = "mso.msoKey";
61     private String healthcheckDebugEnabled = "mso.healthcheck.log.debug";
62
63     private static final String CHECK_HTML = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Health Check</title></head><body>Application ready</body></html>";
64     private static final String NOT_FOUND = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Application Not Started</title></head><body>Application not started. Properties file missing or invalid or database Connection failed</body></html>";
65     private static final String NOT_HEALTHY = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Application Not Started</title></head><body>Application not available or at least one of the sub-modules is not available.</body></html>";
66     public static final Response HEALTH_CHECK_RESPONSE = Response.status (HttpStatus.SC_OK)
67             .entity (CHECK_HTML)
68             .build ();
69     public static final Response HEALTH_CHECK_NOK_RESPONSE = Response.status (HttpStatus.SC_SERVICE_UNAVAILABLE)
70             .entity (NOT_HEALTHY)
71             .  build ();
72     public static final Response NOT_STARTED_RESPONSE = Response.status (HttpStatus.SC_SERVICE_UNAVAILABLE)
73             .entity (NOT_FOUND)
74             .build ();
75
76     @HEAD
77     @GET
78     @Path("/nodehealthcheck")
79     @Produces("text/html")
80     public Response nodeHealthcheck () {
81         MsoLogger.setServiceName ("NodeHealthcheck");
82         // Generate a Request Id
83         String requestId = UUIDChecker.generateUUID(msoLogger);
84
85         PropertyConfiguration propertyConfiguration = PropertyConfiguration.getInstance();
86         Map<String,String> props = propertyConfiguration.getProperties(CONFIG);
87
88         if (props == null) {
89
90             msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(),  MsoLogger.ErrorCode.AvailabilityError, "Unable to load " + CONFIG);
91
92             return NOT_STARTED_RESPONSE;
93         }
94
95         String siteName = props.get(SITENAME);
96         String endpoint = props.get(ADPTER_ENDPOINT);
97
98         if (null == siteName || siteName.length () == 0 || null == endpoint || endpoint.length () == 0) {
99
100             msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError, "Unable to load key attributes (" + SITENAME + " or " + ADPTER_ENDPOINT + ") from the config file:" + CONFIG);
101
102             return NOT_STARTED_RESPONSE;
103         }
104
105         try {
106             if (!this.getSiteStatus (endpoint, siteName, props.get(CREDENTIAL), props.get(MSOKEY), props.get(OPENECOMP_ADAPTER_NAMESPACE))) {
107                 msoLogger.debug("This site is currently disabled for maintenance.");
108                 return HEALTH_CHECK_NOK_RESPONSE;
109             }
110         } catch (Exception e) {
111
112             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Exception while getting SiteStatus", e);
113
114             msoLogger.debug("Exception while getting SiteStatus");
115             return NOT_STARTED_RESPONSE;
116         }
117
118
119         HealthCheckUtils healthCheck = new HealthCheckUtils ();
120         if (healthCheck.verifyNodeHealthCheck(HealthCheckUtils.NodeType.BPMN, requestId)) {
121             msoLogger.debug("nodeHealthcheck - Successful");
122             return HealthCheckUtils.HEALTH_CHECK_RESPONSE;
123         } else {
124             msoLogger.debug("nodeHealthcheck - At leaset one of the sub-modules is not available.");
125             return  HealthCheckUtils.HEALTH_CHECK_NOK_RESPONSE;
126         }
127     }
128
129     @HEAD
130     @GET
131     @Path("/healthcheck")
132     @Produces("text/html")
133     public Response healthcheck (@QueryParam("requestId") String requestId) {
134         MsoLogger.setServiceName ("Healthcheck");
135         verifyOldUUID(requestId);
136
137         PropertyConfiguration propertyConfiguration = PropertyConfiguration.getInstance();
138         Map<String,String> props = propertyConfiguration.getProperties(CONFIG);
139
140         if (props == null) {
141
142             msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(),  MsoLogger.ErrorCode.AvailabilityError, "Unable to load " + CONFIG);
143
144             return NOT_STARTED_RESPONSE;
145         }
146
147         String siteName = props.get(SITENAME);
148         String endpoint = props.get(ADPTER_ENDPOINT);
149
150         if (null == siteName || siteName.length () == 0 || null == endpoint || endpoint.length () == 0) {
151
152             msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError, "Unable to load key attributes (" + SITENAME + " or " + ADPTER_ENDPOINT + ") from the config file:" + CONFIG);
153
154             return NOT_STARTED_RESPONSE;
155         }
156
157         try {
158             if (!this.getSiteStatus (endpoint, siteName, props.get(CREDENTIAL), props.get(MSOKEY), props.get(OPENECOMP_ADAPTER_NAMESPACE))) {
159                 msoLogger.debug("This site is currently disabled for maintenance.");
160                 return HEALTH_CHECK_NOK_RESPONSE;
161             }
162         } catch (Exception e) {
163
164             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Exception while getting SiteStatus", e);
165
166             msoLogger.debug("Exception while getting SiteStatus");
167             return NOT_STARTED_RESPONSE;
168         }
169
170         try {
171                 ProcessEngines.getDefaultProcessEngine().getIdentityService().createGroupQuery().list();
172         } catch (final Exception e) {
173
174             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Exception while verifying Camunda engine", e);
175
176             msoLogger.debug("Exception while verifying Camunda engine");
177             return NOT_STARTED_RESPONSE;
178         }
179
180         return HEALTH_CHECK_RESPONSE;
181     }
182
183
184     private String verifyOldUUID (String oldId) {
185         if (!isValidUUID(oldId)) {
186             String newId = UUID.randomUUID().toString();
187             MsoLogger.setLogContext(newId, null);
188             return newId;
189         }
190         MsoLogger.setLogContext(oldId, null);
191         return oldId;
192     }
193
194
195     private boolean isValidUUID (String id) {
196         try {
197             if (null == id) {
198                 return false;
199             }
200             UUID uuid = UUID.fromString(id);
201             return uuid.toString().equalsIgnoreCase(id);
202         } catch (IllegalArgumentException iae) {
203             return false;
204         }
205     }
206
207     private String decrypt(String encryptedString, String key){
208         try {
209             if (encryptedString != null && !encryptedString.isEmpty() && key != null && !key.isEmpty()) {
210                 return CryptoUtils.decrypt(encryptedString, key);
211             }
212         } catch (Exception e) {
213             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Failed to decrypt credentials", e);
214         }
215         return null;
216     }
217
218     private boolean getSiteStatus (String url, String site, String credential, String key, String adapterNamespace) throws Exception {
219         // set the connection timeout value to 30 seconds (30000 milliseconds)
220         RequestConfig.Builder requestBuilder = RequestConfig.custom();
221         requestBuilder = requestBuilder.setConnectTimeout(30000);
222         requestBuilder = requestBuilder.setConnectionRequestTimeout(30000);
223         HttpClientBuilder builder = HttpClientBuilder.create ();
224         builder.setDefaultRequestConfig (requestBuilder.build ());
225
226         HttpPost post = new HttpPost(url);
227
228         String cred = decrypt(credential, key);
229         if (cred != null && !cred.isEmpty()) {
230             post.setHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString(cred.getBytes()));
231         }
232         if(healthcheckDebugEnabled == null){
233                 healthcheckDebugEnabled = "false";
234         }
235         BPMNLogger.debug(healthcheckDebugEnabled, "Post url is: " + url);
236
237         //now create a soap request message as follows:
238         final StringBuffer payload = new StringBuffer();
239         payload.append("\n");
240         payload.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:req=\"" + adapterNamespace + "/requestsdb\">\n");
241         payload.append("<soapenv:Header/>\n");
242         payload.append("<soapenv:Body>\n");
243         payload.append("<req:getSiteStatus>\n");
244         payload.append("<siteName>" + site + "</siteName>\n");
245         payload.append("</req:getSiteStatus>\n");
246         payload.append("</soapenv:Body>\n");
247         payload.append("</soapenv:Envelope>\n");
248
249         BPMNLogger.debug(healthcheckDebugEnabled, "Initialize SOAP request to url:" + url);
250         BPMNLogger.debug(healthcheckDebugEnabled, "The payload of the request is:" + payload);
251         HttpEntity entity = new StringEntity(payload.toString(),"UTF-8");
252         post.setEntity(entity);
253
254         CloseableHttpClient client = builder.build ();
255         HttpResponse response = client.execute(post);
256         BPMNLogger.debug(healthcheckDebugEnabled, "Response received is:" + response);
257
258         int statusCode = response.getStatusLine().getStatusCode();
259         if (statusCode != 200) {
260
261             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError,
262                     "Communication with DB Adapter failed, The response received from DB Adapter is with failed status code:" + statusCode);
263
264             Exception e = new Exception("Communication with DB Adapter failed");
265             throw e;
266         }
267         BufferedReader rd = new BufferedReader(
268                 new InputStreamReader(response.getEntity().getContent()));
269
270         StringBuffer result = new StringBuffer();
271         String line = "";
272         while ((line = rd.readLine()) != null) {
273             result.append(line);
274         }
275         BPMNLogger.debug(healthcheckDebugEnabled, "Content of the response is:" + result);
276         String status = result.substring(result.indexOf("<return>") + 8, result.indexOf("</return>"));
277
278         client.close (); //shut down the connection
279         return Boolean.valueOf(status);
280     }
281 }