Change the header to SO
[so.git] / adapters / mso-adapter-utils / src / main / java / org / openecomp / mso / openstack / utils / MsoCommonUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
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.openstack.utils;
22
23
24 import java.lang.StackTraceElement;
25 import org.openecomp.mso.logger.MsoAlarmLogger;
26 import org.openecomp.mso.logger.MsoLogger;
27 import org.openecomp.mso.logger.MessageEnum;
28 import org.openecomp.mso.openstack.exceptions.MsoAdapterException;
29 import org.openecomp.mso.openstack.exceptions.MsoException;
30 import org.openecomp.mso.openstack.exceptions.MsoExceptionCategory;
31 import org.openecomp.mso.openstack.exceptions.MsoIOException;
32 import org.openecomp.mso.openstack.exceptions.MsoOpenstackException;
33 import org.openecomp.mso.properties.MsoJavaProperties;
34 import com.woorea.openstack.base.client.OpenStackBaseException;
35 import com.woorea.openstack.base.client.OpenStackConnectException;
36 import com.woorea.openstack.base.client.OpenStackRequest;
37 import com.woorea.openstack.base.client.OpenStackResponseException;
38 import com.woorea.openstack.heat.model.Explanation;
39 import com.woorea.openstack.keystone.model.Error;
40 import com.woorea.openstack.quantum.model.NeutronError;
41
42 public class MsoCommonUtils {
43
44         private static MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.RA);
45         protected static MsoAlarmLogger alarmLogger = new MsoAlarmLogger();
46         
47         protected static String retryDelayProp = "ecomp.mso.adapters.po.retryDelay";
48     protected static String retryCountProp = "ecomp.mso.adapters.po.retryCount";
49     protected static String retryCodesProp = "ecomp.mso.adapters.po.retryCodes";
50         
51         protected static int retryDelayDefault = 5;
52     protected static int retryCountDefault = 3;
53     protected static String retryCodesDefault = "504";
54   
55     /*
56      * Method to execute an Openstack command and track its execution time.
57      * For the metrics log, a category of "Openstack" is used along with a
58      * sub-category that identifies the specific call (using the real
59      * openstack-java-sdk classname of the OpenStackRequest<T> parameter).
60      */
61     
62     protected static <T> T executeAndRecordOpenstackRequest (OpenStackRequest <T> request)
63     {
64         return executeAndRecordOpenstackRequest (request, null);
65     }
66     protected static <T> T executeAndRecordOpenstackRequest (OpenStackRequest <T> request, MsoJavaProperties msoProps) {
67         
68         int limit;
69         // Get the name and method name of the parent class, which triggered this method
70         StackTraceElement[] classArr = new Exception ().getStackTrace ();
71         if (classArr.length >=2) {
72                 limit = 3;
73         } else {
74                 limit = classArr.length;
75         }
76         String parentServiceMethodName = classArr[0].getClassName () + "." + classArr[0].getMethodName ();
77         for (int i = 1; i < limit; i++) {
78             String className = classArr[i].getClassName ();
79             if (!className.equals (MsoCommonUtils.class.getName ())) {
80                 parentServiceMethodName = className + "." + classArr[i].getMethodName ();
81                 break;
82             }
83         }
84
85         long start = System.currentTimeMillis ();
86         String requestType;
87         if (request.getClass ().getEnclosingClass () != null) {
88             requestType = request.getClass ().getEnclosingClass ().getSimpleName () + "."
89                           + request.getClass ().getSimpleName ();
90         } else {
91             requestType = request.getClass ().getSimpleName ();
92         }
93         
94         int retryDelay = retryDelayDefault;
95         int retryCount = retryCountDefault;
96         String retryCodes  = retryCodesDefault;
97         if (msoProps != null) //extra check to avoid NPE
98         {
99                 retryDelay = msoProps.getIntProperty (retryDelayProp, retryDelayDefault);
100                 retryCount = msoProps.getIntProperty (retryCountProp, retryCountDefault);
101                 retryCodes = msoProps.getProperty (retryCodesProp, retryCodesDefault);
102         }
103         
104         // Run the actual command. All exceptions will be propagated
105         while (true)
106         {
107                 try {
108                 return request.execute ();
109                 } 
110                 catch (OpenStackResponseException e) {
111                         boolean retry = false;
112                         if (retryCodes != null ) {
113                                 int code = e.getStatus();
114                                 LOGGER.debug ("Config values RetryDelay:" + retryDelay + " RetryCount:" + retryCount + " RetryCodes:" + retryCodes + " ResponseCode:" + code);
115                                 for (String rCode : retryCodes.split (",")) {
116                                         try {
117                                                 if (retryCount > 0 && code == Integer.parseInt (rCode))
118                                                 {
119                                                         retryCount--;
120                                                         retry = true;
121                                                         LOGGER.debug ("OpenStackResponseException ResponseCode:" + code +  " at:" + parentServiceMethodName + " request:" + requestType +  " Retry indicated. Attempts remaining:" + retryCount);
122                                                         break;
123                                                 }
124                                         } catch (NumberFormatException e1) {
125                                                 LOGGER.error (MessageEnum.RA_CONFIG_EXC, "No retries. Exception in parsing retry code in config:" + rCode, "", "", MsoLogger.ErrorCode.SchemaError, "Exception in parsing retry code in config");
126                                                 throw e;
127                                         }
128                                 }
129                         }
130                         if (retry)
131                         {
132                                 try {
133                                         Thread.sleep (retryDelay * 1000L);
134                                 } catch (InterruptedException e1) {
135                                         LOGGER.debug ("Thread interrupted while sleeping", e1);
136                                 }
137                         }
138                         else
139                                 throw e; // exceeded retryCount or code is not retryable
140                 }
141                 catch (OpenStackConnectException e) {
142                         // Connection to Openstack failed
143                         if (retryCount > 0)
144                         {
145                                 retryCount--;
146                                 LOGGER.debug ("OpenstackConnectException at:" + parentServiceMethodName + " request:" + requestType + " Retry indicated. Attempts remaining:" + retryCount);
147                                 try {
148                                         Thread.sleep (retryDelay * 1000L);
149                                 } catch (InterruptedException e1) {
150                                         LOGGER.debug ("Thread interrupted while sleeping", e1);
151                                 }
152                         }
153                         else
154                                 throw e;
155                                 
156                 }
157         }
158     }
159   
160     /*
161      * Convert an Openstack Exception on a Keystone call to an MsoException.
162      * This method supports both OpenstackResponseException and OpenStackConnectException.
163      */
164     protected static MsoException keystoneErrorToMsoException (OpenStackBaseException e, String context) {
165         MsoException me = null;
166
167         if (e instanceof OpenStackResponseException) {
168             OpenStackResponseException re = (OpenStackResponseException) e;
169
170             try {
171                 // Failed Keystone calls return an Error entity body.
172                 Error error = re.getResponse ().getErrorEntity (Error.class);
173                 LOGGER.error (MessageEnum.RA_CONNECTION_EXCEPTION, "Openstack Keystone Error on " + context + ": " + error, "Openstack", "", MsoLogger.ErrorCode.DataError, "Openstack Keystone Error on " + context);
174                                 me = new MsoOpenstackException (error.getCode (), error.getTitle (), error.getMessage ());
175             } catch (Exception e2) {
176                 // Can't parse the body as an "Error". Report the HTTP error
177                 LOGGER.error (MessageEnum.RA_CONNECTION_EXCEPTION, "HTTP Error on " + context + ": " + re.getStatus() + "," + re.getMessage(), "Openstack", "", MsoLogger.ErrorCode.DataError, "HTTP Error on " + context, e2);
178                                 me = new MsoOpenstackException (re.getStatus (), re.getMessage (), "");
179             }
180
181             // Add the context of the error
182             me.addContext (context);
183
184             // Generate an alarm for 5XX and higher errors.
185             if (re.getStatus () >= 500) {
186                 alarmLogger.sendAlarm ("KeystoneError", MsoAlarmLogger.CRITICAL, me.getContextMessage ());
187             }
188         } else if (e instanceof OpenStackConnectException) {
189             OpenStackConnectException ce = (OpenStackConnectException) e;
190
191             me = new MsoIOException (ce.getMessage ());
192             me.addContext (context);
193
194             // Generate an alarm for all connection errors.
195             LOGGER.error(MessageEnum.RA_GENERAL_EXCEPTION_ARG, "Openstack Keystone connection error on " + context + ": " + e, "Openstack", "", MsoLogger.ErrorCode.DataError, "Openstack Keystone connection error on " + context);
196                         alarmLogger.sendAlarm ("KeystoneIOError", MsoAlarmLogger.CRITICAL, me.getContextMessage ());
197         }
198
199         return me;
200     }
201
202     /*
203      * Convert an Openstack Exception on a Heat call to an MsoOpenstackException.
204      * This method supports both OpenstackResponseException and OpenStackConnectException.
205      */
206     protected MsoException heatExceptionToMsoException (OpenStackBaseException e, String context) {
207         MsoException me = null;
208
209         if (e instanceof OpenStackResponseException) {
210             OpenStackResponseException re = (OpenStackResponseException) e;
211
212             try {
213                 // Failed Heat calls return an Explanation entity body.
214                 Explanation explanation = re.getResponse ().getErrorEntity (Explanation.class);
215                 LOGGER.error (MessageEnum.RA_CONNECTION_EXCEPTION, "OpenStack", "Openstack Error on " + context + ": " + explanation.toString(), "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception - Openstack Error on " + context);
216                 String fullError = explanation.getExplanation() + ", error.type=" + explanation.getError().getType() + ", error.message=" + explanation.getError().getMessage();
217                 LOGGER.debug(fullError);
218                                 me = new MsoOpenstackException (explanation.getCode (),
219                                                 explanation.getTitle (),
220                                                 //explanation.getExplanation ());
221                                                 fullError);
222             } catch (Exception e2) {
223                 // Couldn't parse the body as an "Explanation". Report the original HTTP error.
224                 LOGGER.error (MessageEnum.RA_CONNECTION_EXCEPTION, "OpenStack", "HTTP Error on " + context + ": " + re.getStatus() + "," + e.getMessage(), "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception - HTTP Error on " + context, e2);
225                                 me = new MsoOpenstackException (re.getStatus (), re.getMessage (), "");
226             }
227
228             // Add the context of the error
229             me.addContext (context);
230
231             // Generate an alarm for 5XX and higher errors.
232             if (re.getStatus () >= 500) {
233                 alarmLogger.sendAlarm ("HeatError", MsoAlarmLogger.CRITICAL, me.getContextMessage ());
234             }
235         } else if (e instanceof OpenStackConnectException) {
236             OpenStackConnectException ce = (OpenStackConnectException) e;
237
238             me = new MsoIOException (ce.getMessage ());
239             me.addContext (context);
240
241             // Generate an alarm for all connection errors.
242             alarmLogger.sendAlarm ("HeatIOError", MsoAlarmLogger.CRITICAL, me.getContextMessage ());
243             LOGGER.error(MessageEnum.RA_CONNECTION_EXCEPTION, "OpenStack", "Openstack Heat connection error on " + context + ": " + e, "Openstack", "", MsoLogger.ErrorCode.DataError, "Openstack Heat connection error on " + context);
244         }
245
246         return me;
247     }
248
249     /*
250      * Convert an Openstack Exception on a Neutron call to an MsoOpenstackException.
251      * This method supports both OpenstackResponseException and OpenStackConnectException.
252      */
253     protected MsoException neutronExceptionToMsoException (OpenStackBaseException e, String context) {
254         MsoException me = null;
255
256         if (e instanceof OpenStackResponseException) {
257             OpenStackResponseException re = (OpenStackResponseException) e;
258
259             try {
260                 // Failed Neutron calls return an NeutronError entity body
261                 NeutronError error = re.getResponse ().getErrorEntity (NeutronError.class);
262                 LOGGER.error (MessageEnum.RA_CONNECTION_EXCEPTION, "OpenStack", "Openstack Neutron Error on " + context + ": " + error, "Openstack", "", MsoLogger.ErrorCode.DataError, "Openstack Neutron Error on " + context);
263                                 me = new MsoOpenstackException (re.getStatus (), error.getType (), error.getMessage ());
264             } catch (Exception e2) {
265                 // Couldn't parse body as a NeutronError. Report the HTTP error.
266                 LOGGER.error (MessageEnum.RA_CONNECTION_EXCEPTION, "OpenStack", "HTTP Error on " + context + ": " + re.getStatus() + "," + e.getMessage(), "Openstack", "", MsoLogger.ErrorCode.DataError, "Openstack HTTP Error on " + context, e2);
267                                 me = new MsoOpenstackException (re.getStatus (), re.getMessage (), null);
268             }
269
270             // Add the context of the error
271             me.addContext (context);
272
273             // Generate an alarm for 5XX and higher errors.
274             if (re.getStatus () >= 500) {
275                 alarmLogger.sendAlarm ("NeutronError", MsoAlarmLogger.CRITICAL, me.getContextMessage ());
276             }
277         } else if (e instanceof OpenStackConnectException) {
278             OpenStackConnectException ce = (OpenStackConnectException) e;
279
280             me = new MsoIOException (ce.getMessage ());
281             me.addContext (context);
282
283             // Generate an alarm for all connection errors.
284             alarmLogger.sendAlarm ("NeutronIOError", MsoAlarmLogger.CRITICAL, me.getContextMessage ());
285             LOGGER.error(MessageEnum.RA_CONNECTION_EXCEPTION, "OpenStack", "Openstack Neutron Connection error on "+ context + ": " + e, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Openstack Neutron Connection error on "+ context);
286         }
287
288         return me;
289     }
290
291     /*
292      * Convert a Java Runtime Exception to an MsoException.
293      * All Runtime exceptions will be translated into an MsoAdapterException,
294      * which captures internal errors.
295      * Alarms will be generated on all such exceptions.
296      */
297     protected MsoException runtimeExceptionToMsoException (RuntimeException e, String context) {
298         MsoAdapterException me = new MsoAdapterException (e.getMessage (), e);
299         me.addContext (context);
300         me.setCategory (MsoExceptionCategory.INTERNAL);
301
302         // Always generate an alarm for internal exceptions
303         LOGGER.error(MessageEnum.RA_GENERAL_EXCEPTION_ARG, "An exception occured on  "+ context + ": " + e, "OpenStack", "", MsoLogger.ErrorCode.DataError, "An exception occured on  "+ context);
304                 alarmLogger.sendAlarm ("AdapterInternalError", MsoAlarmLogger.CRITICAL, me.getContextMessage ());
305
306         return me;
307     }
308
309     public static boolean isNullOrEmpty (String s) {
310         return s == null || s.isEmpty();
311     }
312     
313     
314
315 }