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