Replaced all tabs with spaces in java and pom.xml
[so.git] / adapters / mso-adapter-utils / src / main / java / org / onap / so / openstack / utils / MsoCommonUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2018 Intel Corp. All rights reserved.
6  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
7  * ================================================================================
8  * Modifications Copyright (c) 2019 Samsung
9  * ================================================================================
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.so.openstack.utils;
25
26
27 import com.fasterxml.jackson.core.type.TypeReference;
28 import com.fasterxml.jackson.databind.ObjectMapper;
29 import com.woorea.openstack.base.client.OpenStackBaseException;
30 import com.woorea.openstack.base.client.OpenStackConnectException;
31 import com.woorea.openstack.base.client.OpenStackRequest;
32 import com.woorea.openstack.base.client.OpenStackResponseException;
33 import com.woorea.openstack.heat.model.CreateStackParam;
34 import com.woorea.openstack.heat.model.Explanation;
35 import com.woorea.openstack.keystone.model.Error;
36 import com.woorea.openstack.quantum.model.NeutronError;
37 import java.io.IOException;
38 import java.util.HashMap;
39 import java.util.Map;
40 import java.util.Map.Entry;
41 import org.onap.so.config.beans.PoConfig;
42 import org.onap.so.logger.ErrorCode;
43 import org.onap.so.logger.MessageEnum;
44 import org.onap.so.openstack.exceptions.MsoAdapterException;
45 import org.onap.so.openstack.exceptions.MsoException;
46 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
47 import org.onap.so.openstack.exceptions.MsoIOException;
48 import org.onap.so.openstack.exceptions.MsoOpenstackException;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.springframework.beans.factory.annotation.Autowired;
52 import org.springframework.stereotype.Component;
53
54 @Component("CommonUtils")
55 public class MsoCommonUtils {
56
57     private static Logger logger = LoggerFactory.getLogger(MsoCommonUtils.class);
58
59
60     @Autowired
61     private PoConfig poConfig;
62     /*
63      * Method to execute an Openstack command and track its execution time. For the metrics log, a category of
64      * "Openstack" is used along with a sub-category that identifies the specific call (using the real
65      * openstack-java-sdk classname of the OpenStackRequest<T> parameter).
66      */
67
68     protected <T> T executeAndRecordOpenstackRequest(OpenStackRequest<T> request) {
69
70         int limit;
71
72         long start = System.currentTimeMillis();
73         String requestType;
74         if (request.getClass().getEnclosingClass() != null) {
75             requestType =
76                     request.getClass().getEnclosingClass().getSimpleName() + "." + request.getClass().getSimpleName();
77         } else {
78             requestType = request.getClass().getSimpleName();
79         }
80
81         int retryDelay = poConfig.getRetryDelay();
82         int retryCount = poConfig.getRetryCount();
83         String retryCodes = poConfig.getRetryCodes();
84
85         // Run the actual command. All exceptions will be propagated
86         while (true) {
87             try {
88                 return request.execute();
89             } catch (OpenStackResponseException e) {
90                 boolean retry = false;
91                 if (retryCodes != null) {
92                     int code = e.getStatus();
93                     logger.debug("Config values RetryDelay:{} RetryCount:{}  RetryCodes:{} ResponseCode:{}", retryDelay,
94                             retryCount, retryCodes, code);
95                     for (String rCode : retryCodes.split(",")) {
96                         try {
97                             if (retryCount > 0 && code == Integer.parseInt(rCode)) {
98                                 retryCount--;
99                                 retry = true;
100                                 logger.debug(
101                                         "OpenStackResponseException ResponseCode: {} request:{} Retry indicated. Attempts remaining:{}",
102                                         code, requestType, retryCount);
103                                 break;
104                             }
105                         } catch (NumberFormatException e1) {
106                             logger.error("{} No retries. Exception in parsing retry code in config:{} {} {}",
107                                     MessageEnum.RA_CONFIG_EXC, rCode, ErrorCode.SchemaError.getValue(),
108                                     "Exception in parsing retry code in config");
109                             throw e;
110                         }
111                     }
112                 }
113                 if (retry) {
114                     try {
115                         Thread.sleep(retryDelay * 1000L);
116                     } catch (InterruptedException e1) {
117                         logger.debug("Thread interrupted while sleeping", e1);
118                         Thread.currentThread().interrupt();
119                     }
120                 } else
121                     throw e; // exceeded retryCount or code is not retryable
122             } catch (OpenStackConnectException e) {
123                 // Connection to Openstack failed
124                 if (retryCount > 0) {
125                     retryCount--;
126                     logger.debug(" request: {} Retry indicated. Attempts remaining:{}", requestType, retryCount);
127                     try {
128                         Thread.sleep(retryDelay * 1000L);
129                     } catch (InterruptedException e1) {
130                         logger.debug("Thread interrupted while sleeping", e1);
131                         Thread.currentThread().interrupt();
132                     }
133                 } else
134                     throw e;
135
136             }
137         }
138     }
139
140     /*
141      * Convert an Openstack Exception on a Keystone call to an MsoException. This method supports both
142      * OpenstackResponseException and OpenStackConnectException.
143      */
144     protected MsoException keystoneErrorToMsoException(OpenStackBaseException e, String context) {
145         MsoException me = null;
146
147         if (e instanceof OpenStackResponseException) {
148             OpenStackResponseException re = (OpenStackResponseException) e;
149
150             try {
151                 // Failed Keystone calls return an Error entity body.
152                 Error error = re.getResponse().getErrorEntity(Error.class);
153                 logger.error("{} {} Openstack Keystone Error on {}: {}", MessageEnum.RA_CONNECTION_EXCEPTION,
154                         ErrorCode.DataError.getValue(), context, error);
155                 me = new MsoOpenstackException(error.getCode(), error.getTitle(), error.getMessage());
156             } catch (Exception e2) {
157                 // Can't parse the body as an "Error". Report the HTTP error
158                 logger.error("{} {} HTTP Error on {}: {}, {}", MessageEnum.RA_CONNECTION_EXCEPTION,
159                         ErrorCode.DataError.getValue(), context, re.getStatus(), re.getMessage(), e2);
160                 me = new MsoOpenstackException(re.getStatus(), re.getMessage(), "");
161             }
162
163             // Add the context of the error
164             me.addContext(context);
165
166             // Generate an alarm for 5XX and higher errors.
167             if (re.getStatus() >= 500) {
168
169             }
170         } else if (e instanceof OpenStackConnectException) {
171             OpenStackConnectException ce = (OpenStackConnectException) e;
172
173             me = new MsoIOException(ce.getMessage());
174             me.addContext(context);
175
176             // Generate an alarm for all connection errors.
177             logger.error("{} {} Openstack Keystone connection error on {}: ", MessageEnum.RA_GENERAL_EXCEPTION_ARG,
178                     ErrorCode.DataError.getValue(), context, e);
179         }
180
181         return me;
182     }
183
184     /*
185      * Convert an Openstack Exception on a Heat call to an MsoOpenstackException. This method supports both
186      * OpenstackResponseException and OpenStackConnectException.
187      */
188     protected MsoException heatExceptionToMsoException(OpenStackBaseException e, String context) {
189         MsoException me = null;
190
191         if (e instanceof OpenStackResponseException) {
192             OpenStackResponseException re = (OpenStackResponseException) e;
193
194             try {
195                 // Failed Heat calls return an Explanation entity body.
196                 Explanation explanation = re.getResponse().getErrorEntity(Explanation.class);
197                 logger.error("{} {} Exception - Openstack Error on {} : {}", MessageEnum.RA_CONNECTION_EXCEPTION,
198                         ErrorCode.DataError.getValue(), context, explanation.toString());
199                 String fullError = explanation.getExplanation() + ", error.type=" + explanation.getError().getType()
200                         + ", error.message=" + explanation.getError().getMessage();
201                 logger.debug(fullError);
202                 me = new MsoOpenstackException(explanation.getCode(), explanation.getTitle(),
203                         // explanation.getExplanation ());
204                         fullError);
205             } catch (Exception e2) {
206                 // Couldn't parse the body as an "Explanation". Report the original HTTP error.
207                 logger.error("{} {} Exception - HTTP Error on {}: {}, ", MessageEnum.RA_CONNECTION_EXCEPTION,
208                         ErrorCode.DataError.getValue(), context, re.getStatus(), e.getMessage(), e2);
209                 me = new MsoOpenstackException(re.getStatus(), re.getMessage(), "");
210             }
211
212             // Add the context of the error
213             me.addContext(context);
214
215             // Generate an alarm for 5XX and higher errors.
216             if (re.getStatus() >= 500) {
217
218             }
219         } else if (e instanceof OpenStackConnectException) {
220             OpenStackConnectException ce = (OpenStackConnectException) e;
221
222             me = new MsoIOException(ce.getMessage());
223             me.addContext(context);
224
225             // Generate an alarm for all connection errors.
226
227             logger.error("{} {} Openstack Heat connection error on {}: ", MessageEnum.RA_CONNECTION_EXCEPTION,
228                     ErrorCode.DataError.getValue(), context, e);
229         }
230
231         return me;
232     }
233
234     /*
235      * Convert an Openstack Exception on a Neutron call to an MsoOpenstackException. This method supports both
236      * OpenstackResponseException and OpenStackConnectException.
237      */
238     protected MsoException neutronExceptionToMsoException(OpenStackBaseException e, String context) {
239         MsoException me = null;
240
241         if (e instanceof OpenStackResponseException) {
242             OpenStackResponseException re = (OpenStackResponseException) e;
243
244             try {
245                 // Failed Neutron calls return an NeutronError entity body
246                 NeutronError error = re.getResponse().getErrorEntity(NeutronError.class);
247                 logger.error("{} {} Openstack Neutron Error on {} {}", MessageEnum.RA_CONNECTION_EXCEPTION,
248                         ErrorCode.DataError.getValue(), context, error);
249                 me = new MsoOpenstackException(re.getStatus(), error.getType(), error.getMessage());
250             } catch (Exception e2) {
251                 // Couldn't parse body as a NeutronError. Report the HTTP error.
252                 logger.error("{} {} Openstack HTTP Error on {}: {}, {}", MessageEnum.RA_CONNECTION_EXCEPTION,
253                         ErrorCode.DataError.getValue(), context, re.getStatus(), e.getMessage(), e2);
254                 me = new MsoOpenstackException(re.getStatus(), re.getMessage(), null);
255             }
256
257             // Add the context of the error
258             me.addContext(context);
259
260             // Generate an alarm for 5XX and higher errors.
261             if (re.getStatus() >= 500) {
262
263             }
264         } else if (e instanceof OpenStackConnectException) {
265             OpenStackConnectException ce = (OpenStackConnectException) e;
266
267             me = new MsoIOException(ce.getMessage());
268             me.addContext(context);
269
270             // Generate an alarm for all connection errors.
271
272             logger.error("{} {} Openstack Neutron Connection error on {}: ", MessageEnum.RA_CONNECTION_EXCEPTION,
273                     ErrorCode.DataError.getValue(), context, e);
274         }
275
276         return me;
277     }
278
279     /*
280      * Convert a Java Runtime Exception to an MsoException. All Runtime exceptions will be translated into an
281      * MsoAdapterException, which captures internal errors. Alarms will be generated on all such exceptions.
282      */
283     protected MsoException runtimeExceptionToMsoException(RuntimeException e, String context) {
284         MsoAdapterException me = new MsoAdapterException(e.getMessage(), e);
285         me.addContext(context);
286         me.setCategory(MsoExceptionCategory.INTERNAL);
287
288         // Always generate an alarm for internal exceptions
289         logger.error("{} {} An exception occured on {}: ", MessageEnum.RA_GENERAL_EXCEPTION_ARG,
290                 ErrorCode.DataError.getValue(), context, e);
291
292         return me;
293     }
294
295     protected MsoException ioExceptionToMsoException(IOException e, String context) {
296         MsoAdapterException me = new MsoAdapterException(e.getMessage(), e);
297         me.addContext(context);
298         me.setCategory(MsoExceptionCategory.INTERNAL);
299
300         // Always generate an alarm for internal exceptions
301         logger.error("{} {} An exception occured on {}: ", MessageEnum.RA_GENERAL_EXCEPTION_ARG,
302                 ErrorCode.DataError.getValue(), context, e);
303
304         return me;
305     }
306
307     public boolean isNullOrEmpty(String s) {
308         return s == null || s.isEmpty();
309     }
310
311
312     protected CreateStackParam createStackParam(String stackName, String heatTemplate, Map<String, ?> stackInputs,
313             int timeoutMinutes, String environment, Map<String, Object> files, Map<String, Object> heatFiles) {
314
315         // Create local variables checking to see if we have an environment, nested, get_files
316         // Could later add some checks to see if it's valid.
317         boolean haveEnvtVariable = true;
318         if (environment == null || "".equalsIgnoreCase(environment.trim())) {
319             haveEnvtVariable = false;
320             logger.debug("createStackParam called with no environment variable");
321         } else {
322             logger.debug("createStackParam called with an environment variable: {}", environment);
323         }
324
325         boolean haveFiles = true;
326         if (files == null || files.isEmpty()) {
327             haveFiles = false;
328             logger.debug("createStackParam called with no files / child template ids");
329         } else {
330             logger.debug("createStackParam called with {} files / child template ids", files.size());
331         }
332
333         boolean haveHeatFiles = true;
334         if (heatFiles == null || heatFiles.isEmpty()) {
335             haveHeatFiles = false;
336             logger.debug("createStackParam called with no heatFiles");
337         } else {
338             logger.debug("createStackParam called with {} heatFiles", heatFiles.size());
339         }
340
341         // force entire stackInput object to generic Map<String, Object> for openstack compatibility
342         ObjectMapper mapper = new ObjectMapper();
343         Map<String, Object> normalized = new HashMap<>();
344         try {
345             normalized = mapper.readValue(mapper.writeValueAsString(stackInputs),
346                     new TypeReference<HashMap<String, Object>>() {});
347         } catch (IOException e1) {
348             logger.debug("could not map json", e1);
349         }
350
351         // Build up the stack to create
352         // Disable auto-rollback, because error reason is lost. Always rollback in the code.
353         CreateStackParam stack = new CreateStackParam();
354         stack.setStackName(stackName);
355         stack.setTimeoutMinutes(timeoutMinutes);
356         stack.setParameters(normalized);
357         stack.setTemplate(heatTemplate);
358         stack.setDisableRollback(true);
359         // TJM New for PO Adapter - add envt variable
360         if (haveEnvtVariable) {
361             logger.debug("Found an environment variable - value: {}", environment);
362             stack.setEnvironment(environment);
363         }
364         // Now handle nested templates or get_files - have to combine if we have both
365         // as they're both treated as "files:" on the stack.
366         if (haveFiles && haveHeatFiles) {
367             // Let's do this here - not in the bean
368             logger.debug("Found files AND heatFiles - combine and add!");
369             Map<String, Object> combinedFiles = new HashMap<>();
370             for (Entry<String, Object> entry : files.entrySet()) {
371                 combinedFiles.put(entry.getKey(), entry.getValue());
372             }
373             for (Entry<String, Object> entry : heatFiles.entrySet()) {
374                 combinedFiles.put(entry.getKey(), entry.getValue());
375             }
376             stack.setFiles(combinedFiles);
377         } else {
378             // Handle if we only have one or neither:
379             if (haveFiles) {
380                 logger.debug("Found files - adding to stack");
381                 stack.setFiles(files);
382             }
383             if (haveHeatFiles) {
384                 logger.debug("Found heatFiles - adding to stack");
385                 // the setFiles was modified to handle adding the entries
386                 stack.setFiles(heatFiles);
387             }
388         }
389
390         // 1802 - attempt to add better formatted printout of request to openstack
391         try {
392             Map<String, Object> inputs = new HashMap<>();
393             for (Entry<String, ?> entry : stackInputs.entrySet()) {
394                 if (entry.getValue() != null) {
395                     inputs.put(entry.getKey(), entry.getValue());
396                 }
397             }
398             logger.debug("stack request: {}", stack.toString());
399         } catch (Exception e) {
400             // that's okay - this is a nice-to-have
401             logger.debug("(had an issue printing nicely formatted request to debuglog) {}", e.getMessage());
402         }
403
404         return stack;
405     }
406
407 }