87d287271ea5cf8380ab1c07716d483f22a5fb0a
[appc.git] /
1 /*-\r
2  * ============LICENSE_START=======================================================\r
3  * ONAP : APPC\r
4  * ================================================================================\r
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.\r
6  * ================================================================================\r
7  * Copyright (C) 2017 Amdocs\r
8  * =============================================================================\r
9  * Licensed under the Apache License, Version 2.0 (the "License");\r
10  * you may not use this file except in compliance with the License.\r
11  * You may obtain a copy of the License at\r
12  * \r
13  *      http://www.apache.org/licenses/LICENSE-2.0\r
14  * \r
15  * Unless required by applicable law or agreed to in writing, software\r
16  * distributed under the License is distributed on an "AS IS" BASIS,\r
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
18  * See the License for the specific language governing permissions and\r
19  * limitations under the License.\r
20  * \r
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
22  * ============LICENSE_END=========================================================\r
23  */\r
24 \r
25 package org.onap.appc.adapter.iaas.provider.operation.impl.base;\r
26 \r
27 import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;\r
28 import static org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants.MDC_ADAPTER;\r
29 import static org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants.MDC_SERVICE;\r
30 \r
31 import com.att.cdp.exceptions.ZoneException;\r
32 import com.att.cdp.zones.Context;\r
33 import com.att.cdp.zones.model.ModelObject;\r
34 import com.att.cdp.zones.model.Server;\r
35 import com.att.eelf.configuration.EELFLogger;\r
36 import com.att.eelf.configuration.EELFManager;\r
37 import com.att.eelf.i18n.EELFResourceManager;\r
38 import java.net.URI;\r
39 import java.util.Map;\r
40 import java.util.Map.Entry;\r
41 import java.util.Set;\r
42 import java.util.regex.Pattern;\r
43 import org.glassfish.grizzly.http.util.HttpStatus;\r
44 import org.onap.appc.adapter.iaas.ProviderAdapter;\r
45 import org.onap.appc.adapter.iaas.impl.IdentityURL;\r
46 import org.onap.appc.adapter.iaas.impl.ProviderCache;\r
47 import org.onap.appc.adapter.iaas.impl.RequestContext;\r
48 import org.onap.appc.adapter.iaas.impl.RequestFailedException;\r
49 import org.onap.appc.adapter.iaas.impl.TenantCache;\r
50 import org.onap.appc.adapter.iaas.impl.VMURL;\r
51 import org.onap.appc.adapter.iaas.provider.operation.api.IProviderOperation;\r
52 import org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants;\r
53 import org.onap.appc.adapter.iaas.provider.operation.common.enums.Outcome;\r
54 import org.onap.appc.configuration.Configuration;\r
55 import org.onap.appc.configuration.ConfigurationFactory;\r
56 import org.onap.appc.exceptions.APPCException;\r
57 import org.onap.appc.i18n.Msg;\r
58 import org.onap.appc.pool.Pool;\r
59 import org.onap.appc.pool.PoolExtensionException;\r
60 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;\r
61 import org.slf4j.MDC;\r
62 \r
63 public abstract class ProviderOperation implements IProviderOperation {\r
64 \r
65     private static final EELFLogger logger = EELFManager.getInstance().getLogger(ProviderOperation.class);\r
66     protected static final Configuration configuration = ConfigurationFactory.getConfiguration();\r
67 \r
68 \r
69     /**\r
70      * A cache of providers that are predefined.\r
71      */\r
72     private Map<String /* provider name */, ProviderCache> providerCache;\r
73 \r
74     /**\r
75      * The username and password to use for dynamically created connections\r
76      */\r
77     private String defaultUser;\r
78     private String defaultPassword;\r
79     private String defaultDomain;\r
80 \r
81     @Override\r
82     public void setDefaultUser(String defaultUser) {\r
83         this.defaultUser = defaultUser;\r
84     }\r
85 \r
86     @Override\r
87     public void setDefaultPassword(String defaultPassword) {\r
88         this.defaultPassword = defaultPassword;\r
89     }\r
90 \r
91     @Override\r
92     public void setProviderCache(Map<String, ProviderCache> providerCache) {\r
93         this.providerCache = providerCache;\r
94     }\r
95 \r
96     @Override\r
97     public void setDefaultDomain(String defaultDomain) {\r
98         this.defaultDomain = defaultDomain;\r
99     }\r
100 \r
101     /**\r
102      * set MDC props\r
103      */\r
104     protected void setMDC(String service, String serviceName, String adapterName) {\r
105         MDC.put(MDC_ADAPTER, adapterName);\r
106         MDC.put(MDC_SERVICE, service);\r
107         MDC.put(MDC_SERVICE_NAME, serviceName);\r
108     }\r
109 \r
110     /**\r
111      * initial log of the operation\r
112      */\r
113     protected void logOperation(Msg msg, Map<String, String> params, SvcLogicContext context) {\r
114 \r
115         String appName = configuration.getProperty(org.onap.appc.Constants.PROPERTY_APPLICATION_NAME);\r
116         logger.info(msg, appName);\r
117 \r
118         debugParameters(params);\r
119         debugContext(context);\r
120     }\r
121     /**\r
122      * This method is used to dump the value of the parameters to the log for debugging purposes.\r
123      *\r
124      * @param parameters The parameters to be printed to the log\r
125      */\r
126     private void debugParameters(Map<String, String> parameters) {\r
127         for (Entry<String, String> entry : parameters.entrySet()) {\r
128             logger.debug(Msg.PROPERTY_VALUE, entry.getKey(), entry.getValue());\r
129         }\r
130     }\r
131     /**\r
132      * This method is used to create a diagnostic dump of the context for the log\r
133      *\r
134      * @param context The context to be dumped\r
135      */\r
136     @SuppressWarnings({"nls", "static-method"})\r
137     private void debugContext(SvcLogicContext context) {\r
138         Set<String> keys = context.getAttributeKeySet();\r
139         StringBuilder builder = new StringBuilder();\r
140         builder.append("Service Logic Context: Status ");\r
141         builder.append(Constants.LPAREN);\r
142         builder.append(context.getStatus());\r
143         builder.append(Constants.RPAREN);\r
144         builder.append(", Attribute count ");\r
145         builder.append(Constants.LPAREN);\r
146         builder.append(keys == null ? "none" : Integer.toString(keys.size()));\r
147         builder.append(Constants.RPAREN);\r
148         if (keys != null && !keys.isEmpty()) {\r
149             builder.append(Constants.NL);\r
150             for (String key : keys) {\r
151                 String value = context.getAttribute(key);\r
152                 builder.append("Attribute ");\r
153                 builder.append(Constants.LPAREN);\r
154                 builder.append(key);\r
155                 builder.append(Constants.RPAREN);\r
156                 builder.append(", value ");\r
157                 builder.append(Constants.LPAREN);\r
158                 builder.append(value == null ? "" : value);\r
159                 builder.append(Constants.RPAREN);\r
160                 builder.append(Constants.NL);\r
161             }\r
162         }\r
163         logger.debug(builder.toString());\r
164     }\r
165 \r
166 \r
167     /**\r
168      * This method is used to validate that the parameters contain all required property names, and that the values are\r
169      * non-null and non-empty strings. We are still not ensured that the value is valid, but at least it exists.\r
170      *\r
171      * @param parameters The parameters to be checked\r
172      * @param propertyNames The list of property names that are required to be present.\r
173      * @throws RequestFailedException If the parameters are not valid\r
174      */\r
175     protected void validateParametersExist(Map<String, String> parameters, String... propertyNames)\r
176         throws RequestFailedException {\r
177         boolean success = true;\r
178         StringBuilder msg =\r
179             new StringBuilder(EELFResourceManager.format(Msg.MISSING_REQUIRED_PROPERTIES, MDC.get(MDC_SERVICE)));\r
180         msg.append(Constants.NL);\r
181         for (String propertyName : propertyNames) {\r
182             String value = parameters.get(propertyName);\r
183             if (value == null || value.trim().length() == 0) {\r
184                 success = false;\r
185                 msg.append(Constants.QUOTE);\r
186                 msg.append(propertyName);\r
187                 msg.append(Constants.QUOTE);\r
188                 msg.append(Constants.SPACE);\r
189             }\r
190         }\r
191 \r
192         if (!success) {\r
193             logger.error(msg.toString());\r
194             throw new RequestFailedException("Check Parameters", msg.toString(), HttpStatus.BAD_REQUEST_400,\r
195                 (Server) null);\r
196         }\r
197     }\r
198 \r
199     /**\r
200      * @param rc The request context that manages the state and recovery of the request for the life of its processing.\r
201      */\r
202     protected void doFailure(RequestContext rc, HttpStatus code, String message) {\r
203         try {\r
204             doFailure(rc, code, message, null);\r
205         } catch (APPCException e) {\r
206             logger.error("An APPC exception caught. Should never happen", e);\r
207         }\r
208     }\r
209 \r
210     protected void doFailure(RequestContext rc, HttpStatus code, String message, Throwable cause) throws APPCException {\r
211         SvcLogicContext svcLogic = rc.getSvcLogicContext();\r
212         String msg = (message == null) ? code.getReasonPhrase() : message;\r
213         if ((msg.contains("PALOS"))) {\r
214              msg = msg.substring(msg.indexOf("PALOS"), msg.length());\r
215          msg = msg.substring(msg.indexOf("PALOS"), msg.indexOf("\n"));\r
216          } else {\r
217         if (msg.contains("\n")) {\r
218             msg = msg.substring(0, msg.indexOf('\n'));\r
219             }\r
220            }\r
221         String status;\r
222         try {\r
223             status = Integer.toString(code.getStatusCode());\r
224         } catch (Exception e) {\r
225             logger.error("Error when parsing status code", e);\r
226             status = "500";\r
227         }\r
228         svcLogic.setStatus(Outcome.FAILURE.toString());\r
229         svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_CODE, status);\r
230         svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_MESSAGE, msg);\r
231         if (null != cause) {\r
232             throw new APPCException(cause);\r
233         }\r
234     }\r
235 \r
236     /**\r
237      * @param rc The request context that manages the state and recovery of the request for the life of its processing.\r
238      */\r
239     @SuppressWarnings("static-method")\r
240     protected void doSuccess(RequestContext rc) {\r
241         SvcLogicContext svcLogic = rc.getSvcLogicContext();\r
242         svcLogic.setStatus(Outcome.SUCCESS.toString());\r
243         svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_CODE,\r
244             Integer.toString(HttpStatus.OK_200.getStatusCode()));\r
245     }\r
246 \r
247     protected boolean validateVM(RequestContext rc, String appName, String vmUrl, VMURL vm)\r
248         throws RequestFailedException {\r
249         String msg;\r
250         if (vm == null) {\r
251             msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, vmUrl);\r
252             logger.error(msg);\r
253             doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);\r
254             return true;\r
255         }\r
256         validateVMURL(vm);\r
257         return false;\r
258     }\r
259 \r
260     protected void validateVMURL(VMURL vm) throws RequestFailedException {\r
261         String name = "vm-id";\r
262         if (vm == null) {\r
263             throw new RequestFailedException(String.format("The value %s cannot be null.", name));\r
264         }\r
265         // Check that its a good uri\r
266         // This will probably never get hit bc of an earlier check while parsing\r
267         // the string to a VMURL\r
268         try {\r
269             // noinspection ResultOfMethodCallIgnored\r
270             URI.create(vm.toString());\r
271         } catch (Exception e) {\r
272             logger.error("An error occurred when validating vm url", e);\r
273             throw new RequestFailedException(\r
274                 String.format("The value %s is not well formed [%s].", name, vm.toString()));\r
275         }\r
276         // Check the tenant and vmid segments\r
277         String patternRegex = "([0-9a-f]{8}(-)?[0-9a-f]{4}(-)?[0-9a-f]{4}(-)?[0-9a-f]{4}(-)?[0-9a-f]{12})";\r
278         Pattern pattern = Pattern.compile(patternRegex, Pattern.CASE_INSENSITIVE);\r
279         if (!pattern.matcher(vm.getTenantId()).matches()) {\r
280             throw new RequestFailedException(\r
281                 String.format("The value %s has an invalid tenantId [%s].", name, vm.getTenantId()));\r
282         }\r
283         if (!pattern.matcher(vm.getServerId()).matches()) {\r
284             throw new RequestFailedException(\r
285                 String.format("The value %s has an invalid serverId [%s].", name, vm.getServerId()));\r
286         }\r
287     }\r
288 \r
289     private ProviderCache createProviderCache(VMURL vm, IdentityURL ident) {\r
290         if (vm != null && ident != null) {\r
291             ProviderCache cache = new ProviderCache();\r
292             cache.setIdentityURL(ident.toString());\r
293             cache.setProviderName(ident.toString());\r
294             TenantCache tenant = cache.addTenant(vm.getTenantId(), null, defaultUser, defaultPassword, defaultDomain);\r
295             // Make sure we could initialize the the cache otherwise return null\r
296             if (tenant != null && tenant.isInitialized()) {\r
297                 return cache;\r
298             }\r
299         }\r
300         return null;\r
301     }\r
302     /**\r
303      * This method is a general helper method used to locate a server given its fully-qualified self-link URL on a\r
304      * supported provider, regardless of region(s), and to return an opened context that can be used to access that\r
305      * server.\r
306      *\r
307      * @param rc The request context that wraps and manages the state of the request\r
308      * @param selfLinkURL The fully-qualified self-link URL of the server\r
309      * @param providerName The name of the provider to be searched\r
310      * @return The context that can be used to access the server, or null if not found.\r
311      */\r
312     @SuppressWarnings("nls")\r
313     protected Context getContext(RequestContext rc, String selfLinkURL, String providerName) {\r
314         VMURL vm = VMURL.parseURL(selfLinkURL);\r
315         IdentityURL ident = IdentityURL.parseURL(providerName);\r
316         String appName = configuration.getProperty(org.onap.appc.Constants.PROPERTY_APPLICATION_NAME);\r
317         if (vm == null) {\r
318             String msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, selfLinkURL);\r
319             logger.error(msg);\r
320             doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);\r
321             return null;\r
322         }\r
323         /*\r
324          * Get the cache of tenants and contexts for the named provider, if one exists\r
325          */\r
326         ProviderCache cache = providerCache.get(providerName);\r
327         /*\r
328          * If one doesn't exist, try and create it. If we have enough information to create it successfully, add it to\r
329          * the cache and continue, otherwise fail the request.\r
330          */\r
331         if (cache == null) {\r
332             if (ident != null) {\r
333                 cache = createProviderCache(vm, ident);\r
334             }\r
335             if (cache != null) {\r
336                 providerCache.put(cache.getProviderName(), cache);\r
337             } else {\r
338                 String msg = EELFResourceManager.format(Msg.UNKNOWN_PROVIDER, providerName,\r
339                     providerCache.keySet().toString());\r
340                 logger.error(msg);\r
341                 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);\r
342                 return null;\r
343             }\r
344         }\r
345         if (providerName == null) {\r
346             logger.debug(\r
347                 String.format("Using the default provider cache [%s] since no valid identity url was passed in.",\r
348                     cache.getIdentityURL()));\r
349         }\r
350         // get the tenant cache for the vm\r
351         String identityURL = cache.getIdentityURL();\r
352         TenantCache tenantCache = cache.getTenant(vm.getTenantId());\r
353         if (tenantCache == null) {\r
354             // no tenantCache matching tenant, add tenant to the provider cache\r
355             tenantCache = cache.addTenant(vm.getTenantId(), null, defaultUser, defaultPassword, defaultDomain);\r
356             if (tenantCache == null) {\r
357                 // tenant not found\r
358                 String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, selfLinkURL);\r
359                 logger.error(msg);\r
360                 doFailure(rc, HttpStatus.NOT_FOUND_404, msg);\r
361                 return null;\r
362             }\r
363         }\r
364         // reserve the context\r
365         String tenantName = tenantCache.getTenantName();\r
366         String tenantId = tenantCache.getTenantId();\r
367         String region = tenantCache.determineRegion(vm);\r
368 \r
369         if (region != null) {\r
370             Pool<Context> pool = tenantCache.getPools().get(region);\r
371             while (rc.attempt()) {\r
372                 try {\r
373                     Context context = pool.reserve();\r
374                     /*\r
375                      * Insert logic here to test the context for connectivity because we may have gotten one from the\r
376                      * pool that was previously created.\r
377                      */\r
378                     reloginIfNeeded(context);\r
379                     return context;\r
380                 } catch (PoolExtensionException e) {\r
381                     String msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, providerName, identityURL,\r
382                         tenantName, tenantId, e.getMessage(), Long.toString(rc.getRetryDelay()),\r
383                         Integer.toString(rc.getAttempts()), Integer.toString(rc.getRetryLimit()));\r
384                     logger.error(msg, e);\r
385                     rc.delay();\r
386                 } catch (Exception e) {\r
387                     String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, e,\r
388                         e.getClass().getSimpleName(), "find", selfLinkURL, tenantCache.getTenantName());\r
389                     logger.error(msg, e);\r
390                     doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);\r
391                     return null;\r
392                 }\r
393             }\r
394             String msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, providerName, identityURL);\r
395             logger.error(msg);\r
396             doFailure(rc, HttpStatus.BAD_GATEWAY_502, msg);\r
397             return null;\r
398         }\r
399         String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, selfLinkURL);\r
400         logger.error(msg);\r
401         doFailure(rc, HttpStatus.NOT_FOUND_404, msg);\r
402         return null;\r
403     }\r
404 \r
405     private void reloginIfNeeded(Context context) throws ZoneException {\r
406         if (context.isStale()) {\r
407             context.relogin();\r
408         }\r
409     }\r
410 \r
411     protected Context resolveContext(RequestContext rc, Map<String, String> params, String appName, String vmUrl)\r
412         throws RequestFailedException {\r
413         VMURL vm = VMURL.parseURL(vmUrl);\r
414         if (vm == null) {\r
415             String msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, vmUrl);\r
416             doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);\r
417             logger.error(msg);\r
418             return null;\r
419         }\r
420         validateVMURL(vm);\r
421         IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));\r
422         String identStr = (ident == null) ? null : ident.toString();\r
423         return getContext(rc, vmUrl, identStr);\r
424     }\r
425 \r
426     protected abstract ModelObject executeProviderOperation(Map<String, String> params, SvcLogicContext context)\r
427         throws APPCException;\r
428     @Override\r
429     public ModelObject doOperation(Map<String, String> params, SvcLogicContext context) throws APPCException {\r
430         return executeProviderOperation(params, context);\r
431     }\r
432 }\r