Removed MsoLogger class
[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.
64      * For the metrics log, a category of "Openstack" is used along with a
65      * sub-category that identifies the specific call (using the real
66      * openstack-java-sdk classname of the OpenStackRequest<T> parameter).
67      */
68
69     protected <T> T executeAndRecordOpenstackRequest (OpenStackRequest <T> request) {
70
71         int limit;
72
73         long start = System.currentTimeMillis ();
74         String requestType;
75         if (request.getClass ().getEnclosingClass () != null) {
76             requestType = request.getClass ().getEnclosingClass ().getSimpleName () + "."
77                           + request.getClass ().getSimpleName ();
78         } else {
79             requestType = request.getClass ().getSimpleName ();
80         }
81
82         int retryDelay = poConfig.getRetryDelay();
83         int retryCount = poConfig.getRetryCount();
84         String retryCodes  = poConfig.getRetryCodes();
85
86         // Run the actual command. All exceptions will be propagated
87         while (true)
88         {
89                 try {
90                 return request.execute ();
91                 }
92                 catch (OpenStackResponseException e) {
93                         boolean retry = false;
94                         if (retryCodes != null ) {
95                                 int code = e.getStatus();
96                 logger.debug("Config values RetryDelay:{} RetryCount:{}  RetryCodes:{} ResponseCode:{}", retryDelay,
97                     retryCount, retryCodes, code);
98                 for (String rCode : retryCodes.split (",")) {
99                                         try {
100                                                 if (retryCount > 0 && code == Integer.parseInt (rCode))
101                                                 {
102                                                         retryCount--;
103                                                         retry = true;
104                       logger.debug(
105                           "OpenStackResponseException ResponseCode: {} request:{} Retry indicated. Attempts remaining:{}",
106                           code, requestType, retryCount);
107                       break;
108                                                 }
109                                         } catch (NumberFormatException e1) {
110                     logger.error("{} No retries. Exception in parsing retry code in config:{} {} {}",
111                         MessageEnum.RA_CONFIG_EXC, rCode, ErrorCode.SchemaError.getValue(),
112                         "Exception in parsing retry code in config");
113                     throw e;
114                                         }
115                                 }
116                         }
117                         if (retry)
118                         {
119                                 try {
120                                         Thread.sleep (retryDelay * 1000L);
121                                 } catch (InterruptedException e1) {
122                         logger.debug ("Thread interrupted while sleeping", e1);
123                                                 Thread.currentThread().interrupt();
124                                 }
125                         }
126                         else
127                                 throw e; // exceeded retryCount or code is not retryable
128                 }
129                 catch (OpenStackConnectException e) {
130                         // Connection to Openstack failed
131                         if (retryCount > 0)
132                         {
133                                 retryCount--;
134                     logger.debug (" request: {} Retry indicated. Attempts remaining:{}", requestType, retryCount);
135                                 try {
136                                         Thread.sleep (retryDelay * 1000L);
137                                 } catch (InterruptedException e1) {
138                         logger.debug ("Thread interrupted while sleeping", e1);
139                                                 Thread.currentThread().interrupt();
140                                 }
141                         }
142                         else
143                                 throw e;
144
145                 }
146         }
147     }
148
149     /*
150      * Convert an Openstack Exception on a Keystone call to an MsoException.
151      * This method supports both OpenstackResponseException and OpenStackConnectException.
152      */
153     protected MsoException keystoneErrorToMsoException (OpenStackBaseException e, String context) {
154         MsoException me = null;
155
156         if (e instanceof OpenStackResponseException) {
157             OpenStackResponseException re = (OpenStackResponseException) e;
158
159             try {
160                 // Failed Keystone calls return an Error entity body.
161                 Error error = re.getResponse ().getErrorEntity (Error.class);
162                 logger.error("{} {} Openstack Keystone Error on {}: {}",
163                     MessageEnum.RA_CONNECTION_EXCEPTION, ErrorCode.DataError.getValue(), context, error);
164                 me = new MsoOpenstackException (error.getCode (), error.getTitle (), error.getMessage ());
165             } catch (Exception e2) {
166                 // Can't parse the body as an "Error". Report the HTTP error
167                 logger.error("{} {} HTTP Error on {}: {}, {}", MessageEnum.RA_CONNECTION_EXCEPTION,
168                     ErrorCode.DataError.getValue(), context, re.getStatus(), re.getMessage(), e2);
169                 me = new MsoOpenstackException (re.getStatus (), re.getMessage (), "");
170             }
171
172             // Add the context of the error
173             me.addContext (context);
174
175             // Generate an alarm for 5XX and higher errors.
176             if (re.getStatus () >= 500) {
177
178             }
179         } else if (e instanceof OpenStackConnectException) {
180             OpenStackConnectException ce = (OpenStackConnectException) e;
181
182             me = new MsoIOException (ce.getMessage ());
183             me.addContext (context);
184
185             // Generate an alarm for all connection errors.
186             logger.error("{} {} Openstack Keystone connection error on {}: ", MessageEnum.RA_GENERAL_EXCEPTION_ARG,
187                 ErrorCode.DataError.getValue(), context, e);
188         }
189
190         return me;
191     }
192
193     /*
194      * Convert an Openstack Exception on a Heat call to an MsoOpenstackException.
195      * This method supports both OpenstackResponseException and OpenStackConnectException.
196      */
197     protected MsoException heatExceptionToMsoException (OpenStackBaseException e, String context) {
198         MsoException me = null;
199
200         if (e instanceof OpenStackResponseException) {
201             OpenStackResponseException re = (OpenStackResponseException) e;
202
203             try {
204                 // Failed Heat calls return an Explanation entity body.
205                 Explanation explanation = re.getResponse ().getErrorEntity (Explanation.class);
206                 logger.error("{} {} Exception - Openstack Error on {} : {}", MessageEnum.RA_CONNECTION_EXCEPTION,
207                     ErrorCode.DataError.getValue(), context, explanation.toString());
208                 String fullError = explanation.getExplanation() + ", error.type=" + explanation.getError().getType() + ", error.message=" + explanation.getError().getMessage();
209                 logger.debug(fullError);
210                                 me = new MsoOpenstackException (explanation.getCode (),
211                                                 explanation.getTitle (),
212                                                 //explanation.getExplanation ());
213                                                 fullError);
214             } catch (Exception e2) {
215                 // Couldn't parse the body as an "Explanation". Report the original HTTP error.
216                 logger.error("{} {} Exception - HTTP Error on {}: {}, ", MessageEnum.RA_CONNECTION_EXCEPTION,
217                     ErrorCode.DataError.getValue(), context, re.getStatus(), e.getMessage(), e2);
218                 me = new MsoOpenstackException (re.getStatus (), re.getMessage (), "");
219             }
220
221             // Add the context of the error
222             me.addContext (context);
223
224             // Generate an alarm for 5XX and higher errors.
225             if (re.getStatus () >= 500) {
226
227             }
228         } else if (e instanceof OpenStackConnectException) {
229             OpenStackConnectException ce = (OpenStackConnectException) e;
230
231             me = new MsoIOException (ce.getMessage ());
232             me.addContext (context);
233
234             // Generate an alarm for all connection errors.
235
236             logger.error("{} {} Openstack Heat connection error on {}: ", MessageEnum.RA_CONNECTION_EXCEPTION,
237                 ErrorCode.DataError.getValue(), context, e);
238         }
239
240         return me;
241     }
242
243     /*
244      * Convert an Openstack Exception on a Neutron call to an MsoOpenstackException.
245      * This method supports both OpenstackResponseException and OpenStackConnectException.
246      */
247     protected MsoException neutronExceptionToMsoException (OpenStackBaseException e, String context) {
248         MsoException me = null;
249
250         if (e instanceof OpenStackResponseException) {
251             OpenStackResponseException re = (OpenStackResponseException) e;
252
253             try {
254                 // Failed Neutron calls return an NeutronError entity body
255                 NeutronError error = re.getResponse ().getErrorEntity (NeutronError.class);
256                 logger.error("{} {} Openstack Neutron Error on {} {}", MessageEnum.RA_CONNECTION_EXCEPTION,
257                     ErrorCode.DataError.getValue(), context, error);
258                 me = new MsoOpenstackException (re.getStatus (), error.getType (), error.getMessage ());
259             } catch (Exception e2) {
260                 // Couldn't parse body as a NeutronError. Report the HTTP error.
261                 logger.error("{} {} Openstack HTTP Error on {}: {}, {}", MessageEnum.RA_CONNECTION_EXCEPTION,
262                     ErrorCode.DataError.getValue(), context, re.getStatus(), e.getMessage(), e2);
263                 me = new MsoOpenstackException (re.getStatus (), re.getMessage (), null);
264             }
265
266             // Add the context of the error
267             me.addContext (context);
268
269             // Generate an alarm for 5XX and higher errors.
270             if (re.getStatus () >= 500) {
271
272             }
273         } else if (e instanceof OpenStackConnectException) {
274             OpenStackConnectException ce = (OpenStackConnectException) e;
275
276             me = new MsoIOException (ce.getMessage ());
277             me.addContext (context);
278
279             // Generate an alarm for all connection errors.
280
281             logger.error("{} {} Openstack Neutron Connection error on {}: ", MessageEnum.RA_CONNECTION_EXCEPTION,
282                 ErrorCode.DataError.getValue(), context, e);
283         }
284
285         return me;
286     }
287
288     /*
289      * Convert a Java Runtime Exception to an MsoException.
290      * All Runtime exceptions will be translated into an MsoAdapterException,
291      * which captures internal errors.
292      * Alarms will be generated on all such exceptions.
293      */
294     protected MsoException runtimeExceptionToMsoException (RuntimeException e, String context) {
295         MsoAdapterException me = new MsoAdapterException (e.getMessage (), e);
296         me.addContext (context);
297         me.setCategory (MsoExceptionCategory.INTERNAL);
298
299         // Always generate an alarm for internal exceptions
300         logger.error("{} {} An exception occured on {}: ", MessageEnum.RA_GENERAL_EXCEPTION_ARG,
301             ErrorCode.DataError.getValue(), context, e);
302
303         return me;
304     }
305
306     protected MsoException ioExceptionToMsoException(IOException e, String context) {
307         MsoAdapterException me = new MsoAdapterException (e.getMessage (), e);
308         me.addContext (context);
309         me.setCategory (MsoExceptionCategory.INTERNAL);
310
311         // Always generate an alarm for internal exceptions
312         logger.error("{} {} An exception occured on {}: ", MessageEnum.RA_GENERAL_EXCEPTION_ARG,
313             ErrorCode.DataError.getValue(), context, e);
314
315         return me;
316     }
317
318     public boolean isNullOrEmpty (String s) {
319         return s == null || s.isEmpty();
320     }
321
322
323     protected CreateStackParam createStackParam(String stackName,
324             String heatTemplate,
325             Map <String, ?> stackInputs,
326             int timeoutMinutes,
327             String environment,
328             Map <String, Object> files,
329             Map <String, Object> heatFiles) {
330
331         // Create local variables checking to see if we have an environment, nested, get_files
332         // Could later add some checks to see if it's valid.
333         boolean haveEnvtVariable = true;
334         if (environment == null || "".equalsIgnoreCase (environment.trim ())) {
335             haveEnvtVariable = false;
336             logger.debug ("createStackParam called with no environment variable");
337         } else {
338             logger.debug("createStackParam called with an environment variable: {}", environment);
339         }
340
341         boolean haveFiles = true;
342         if (files == null || files.isEmpty ()) {
343             haveFiles = false;
344             logger.debug ("createStackParam called with no files / child template ids");
345         } else {
346             logger.debug("createStackParam called with {} files / child template ids", files.size());
347         }
348
349         boolean haveHeatFiles = true;
350         if (heatFiles == null || heatFiles.isEmpty ()) {
351             haveHeatFiles = false;
352             logger.debug ("createStackParam called with no heatFiles");
353         } else {
354             logger.debug("createStackParam called with {} heatFiles", heatFiles.size());
355         }
356
357             //force entire stackInput object to generic Map<String, Object> for openstack compatibility
358                 ObjectMapper mapper = new ObjectMapper();
359                 Map<String, Object> normalized = new HashMap<>();
360                 try {
361                         normalized = mapper.readValue(mapper.writeValueAsString(stackInputs), new TypeReference<HashMap<String,Object>>() {});
362                 } catch (IOException e1) {
363                         logger.debug("could not map json", e1);
364                 }
365
366             // Build up the stack to create
367             // Disable auto-rollback, because error reason is lost. Always rollback in the code.
368             CreateStackParam stack = new CreateStackParam ();
369             stack.setStackName (stackName);
370             stack.setTimeoutMinutes (timeoutMinutes);
371             stack.setParameters (normalized);
372             stack.setTemplate (heatTemplate);
373             stack.setDisableRollback (true);
374             // TJM New for PO Adapter - add envt variable
375             if (haveEnvtVariable) {
376           logger.debug("Found an environment variable - value: {}", environment);
377           stack.setEnvironment (environment);
378             }
379             // Now handle nested templates or get_files - have to combine if we have both
380             // as they're both treated as "files:" on the stack.
381             if (haveFiles && haveHeatFiles) {
382                 // Let's do this here - not in the bean
383                 logger.debug ("Found files AND heatFiles - combine and add!");
384                 Map <String, Object> combinedFiles = new HashMap <> ();
385                 for (Entry<String, Object> entry : files.entrySet()) {
386                         combinedFiles.put(entry.getKey(), entry.getValue());
387                 }
388                 for (Entry<String, Object> entry : heatFiles.entrySet()) {
389                         combinedFiles.put(entry.getKey(), entry.getValue());
390                 }
391                 stack.setFiles (combinedFiles);
392             } else {
393                 // Handle if we only have one or neither:
394                 if (haveFiles) {
395                         logger.debug ("Found files - adding to stack");
396                     stack.setFiles (files);
397                 }
398                 if (haveHeatFiles) {
399                         logger.debug ("Found heatFiles - adding to stack");
400                     // the setFiles was modified to handle adding the entries
401                     stack.setFiles (heatFiles);
402                 }
403             }
404
405             // 1802 - attempt to add better formatted printout of request to openstack
406             try {
407                 Map<String, Object> inputs = new HashMap<>();
408                 for (Entry<String, ?> entry : stackInputs.entrySet()) {
409                         if (entry.getValue() != null) {
410                                 inputs.put(entry.getKey(), entry.getValue());
411                         }
412                 }
413           logger.debug("stack request: {}", stack.toString());
414       } catch (Exception e) {
415                 // that's okay - this is a nice-to-have
416           logger.debug("(had an issue printing nicely formatted request to debuglog) {}", e.getMessage());
417       }
418
419             return stack;
420     }
421
422 }