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