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