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