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