2 * ============LICENSE_START=======================================================
\r
4 * ================================================================================
\r
5 * Copyright (C) 2017 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
13 * http://www.apache.org/licenses/LICENSE-2.0
\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
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 * ============LICENSE_END=========================================================
\r
25 package org.onap.appc.adapter.iaas.provider.operation.impl.base;
\r
27 import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
\r
28 import static org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants.MDC_ADAPTER;
\r
29 import static org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants.MDC_SERVICE;
\r
31 import com.att.cdp.exceptions.ZoneException;
\r
32 import com.att.cdp.zones.Context;
\r
33 import com.att.cdp.zones.model.ModelObject;
\r
34 import com.att.cdp.zones.model.Server;
\r
35 import com.att.eelf.configuration.EELFLogger;
\r
36 import com.att.eelf.configuration.EELFManager;
\r
37 import com.att.eelf.i18n.EELFResourceManager;
\r
38 import java.net.URI;
\r
39 import java.util.Map;
\r
40 import java.util.Map.Entry;
\r
41 import java.util.Set;
\r
42 import java.util.regex.Pattern;
\r
43 import org.glassfish.grizzly.http.util.HttpStatus;
\r
44 import org.onap.appc.adapter.iaas.ProviderAdapter;
\r
45 import org.onap.appc.adapter.iaas.impl.IdentityURL;
\r
46 import org.onap.appc.adapter.iaas.impl.ProviderCache;
\r
47 import org.onap.appc.adapter.iaas.impl.RequestContext;
\r
48 import org.onap.appc.adapter.iaas.impl.RequestFailedException;
\r
49 import org.onap.appc.adapter.iaas.impl.TenantCache;
\r
50 import org.onap.appc.adapter.iaas.impl.VMURL;
\r
51 import org.onap.appc.adapter.iaas.provider.operation.api.IProviderOperation;
\r
52 import org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants;
\r
53 import org.onap.appc.adapter.iaas.provider.operation.common.enums.Outcome;
\r
54 import org.onap.appc.configuration.Configuration;
\r
55 import org.onap.appc.configuration.ConfigurationFactory;
\r
56 import org.onap.appc.exceptions.APPCException;
\r
57 import org.onap.appc.i18n.Msg;
\r
58 import org.onap.appc.pool.Pool;
\r
59 import org.onap.appc.pool.PoolExtensionException;
\r
60 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
\r
61 import org.slf4j.MDC;
\r
63 public abstract class ProviderOperation implements IProviderOperation {
\r
65 private static final EELFLogger logger = EELFManager.getInstance().getLogger(ProviderOperation.class);
\r
66 protected static final Configuration configuration = ConfigurationFactory.getConfiguration();
\r
70 * A cache of providers that are predefined.
\r
72 private Map<String /* provider name */, ProviderCache> providerCache;
\r
75 * The username and password to use for dynamically created connections
\r
77 private String defaultUser;
\r
78 private String defaultPassword;
\r
79 private String defaultDomain;
\r
82 public void setDefaultUser(String defaultUser) {
\r
83 this.defaultUser = defaultUser;
\r
87 public void setDefaultPassword(String defaultPassword) {
\r
88 this.defaultPassword = defaultPassword;
\r
92 public void setProviderCache(Map<String, ProviderCache> providerCache) {
\r
93 this.providerCache = providerCache;
\r
97 public void setDefaultDomain(String defaultDomain) {
\r
98 this.defaultDomain = defaultDomain;
\r
104 protected void setMDC(String service, String serviceName, String adapterName) {
\r
105 MDC.put(MDC_ADAPTER, adapterName);
\r
106 MDC.put(MDC_SERVICE, service);
\r
107 MDC.put(MDC_SERVICE_NAME, serviceName);
\r
111 * initial log of the operation
\r
113 protected void logOperation(Msg msg, Map<String, String> params, SvcLogicContext context) {
\r
115 String appName = configuration.getProperty(org.onap.appc.Constants.PROPERTY_APPLICATION_NAME);
\r
116 logger.info(msg, appName);
\r
118 debugParameters(params);
\r
119 debugContext(context);
\r
122 * This method is used to dump the value of the parameters to the log for debugging purposes.
\r
124 * @param parameters The parameters to be printed to the log
\r
126 private void debugParameters(Map<String, String> parameters) {
\r
127 for (Entry<String, String> entry : parameters.entrySet()) {
\r
128 logger.debug(Msg.PROPERTY_VALUE, entry.getKey(), entry.getValue());
\r
132 * This method is used to create a diagnostic dump of the context for the log
\r
134 * @param context The context to be dumped
\r
136 @SuppressWarnings({"nls", "static-method"})
\r
137 private void debugContext(SvcLogicContext context) {
\r
138 Set<String> keys = context.getAttributeKeySet();
\r
139 StringBuilder builder = new StringBuilder();
\r
140 builder.append("Service Logic Context: Status ");
\r
141 builder.append(Constants.LPAREN);
\r
142 builder.append(context.getStatus());
\r
143 builder.append(Constants.RPAREN);
\r
144 builder.append(", Attribute count ");
\r
145 builder.append(Constants.LPAREN);
\r
146 builder.append(keys == null ? "none" : Integer.toString(keys.size()));
\r
147 builder.append(Constants.RPAREN);
\r
148 if (keys != null && !keys.isEmpty()) {
\r
149 builder.append(Constants.NL);
\r
150 for (String key : keys) {
\r
151 String value = context.getAttribute(key);
\r
152 builder.append("Attribute ");
\r
153 builder.append(Constants.LPAREN);
\r
154 builder.append(key);
\r
155 builder.append(Constants.RPAREN);
\r
156 builder.append(", value ");
\r
157 builder.append(Constants.LPAREN);
\r
158 builder.append(value == null ? "" : value);
\r
159 builder.append(Constants.RPAREN);
\r
160 builder.append(Constants.NL);
\r
163 logger.debug(builder.toString());
\r
168 * This method is used to validate that the parameters contain all required property names, and that the values are
\r
169 * non-null and non-empty strings. We are still not ensured that the value is valid, but at least it exists.
\r
171 * @param parameters The parameters to be checked
\r
172 * @param propertyNames The list of property names that are required to be present.
\r
173 * @throws RequestFailedException If the parameters are not valid
\r
175 protected void validateParametersExist(Map<String, String> parameters, String... propertyNames)
\r
176 throws RequestFailedException {
\r
177 boolean success = true;
\r
178 StringBuilder msg =
\r
179 new StringBuilder(EELFResourceManager.format(Msg.MISSING_REQUIRED_PROPERTIES, MDC.get(MDC_SERVICE)));
\r
180 msg.append(Constants.NL);
\r
181 for (String propertyName : propertyNames) {
\r
182 String value = parameters.get(propertyName);
\r
183 if (value == null || value.trim().length() == 0) {
\r
185 msg.append(Constants.QUOTE);
\r
186 msg.append(propertyName);
\r
187 msg.append(Constants.QUOTE);
\r
188 msg.append(Constants.SPACE);
\r
193 logger.error(msg.toString());
\r
194 throw new RequestFailedException("Check Parameters", msg.toString(), HttpStatus.BAD_REQUEST_400,
\r
200 * @param rc The request context that manages the state and recovery of the request for the life of its processing.
\r
202 protected void doFailure(RequestContext rc, HttpStatus code, String message) {
\r
204 doFailure(rc, code, message, null);
\r
205 } catch (APPCException e) {
\r
206 logger.error("An APPC exception caught. Should never happen", e);
\r
210 protected void doFailure(RequestContext rc, HttpStatus code, String message, Throwable cause) throws APPCException {
\r
211 SvcLogicContext svcLogic = rc.getSvcLogicContext();
\r
212 String msg = (message == null) ? code.getReasonPhrase() : message;
\r
213 if ((msg.contains("PALOS"))) {
\r
214 msg = msg.substring(msg.indexOf("PALOS"), msg.length());
\r
215 msg = msg.substring(msg.indexOf("PALOS"), msg.indexOf("\n"));
\r
217 if (msg.contains("\n")) {
\r
218 msg = msg.substring(0, msg.indexOf('\n'));
\r
223 status = Integer.toString(code.getStatusCode());
\r
224 } catch (Exception e) {
\r
225 logger.error("Error when parsing status code", e);
\r
228 svcLogic.setStatus(Outcome.FAILURE.toString());
\r
229 svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_CODE, status);
\r
230 svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_MESSAGE, msg);
\r
231 if (null != cause) {
\r
232 throw new APPCException(cause);
\r
237 * @param rc The request context that manages the state and recovery of the request for the life of its processing.
\r
239 @SuppressWarnings("static-method")
\r
240 protected void doSuccess(RequestContext rc) {
\r
241 SvcLogicContext svcLogic = rc.getSvcLogicContext();
\r
242 svcLogic.setStatus(Outcome.SUCCESS.toString());
\r
243 svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_CODE,
\r
244 Integer.toString(HttpStatus.OK_200.getStatusCode()));
\r
247 protected boolean validateVM(RequestContext rc, String appName, String vmUrl, VMURL vm)
\r
248 throws RequestFailedException {
\r
251 msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, vmUrl);
\r
253 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
260 protected void validateVMURL(VMURL vm) throws RequestFailedException {
\r
261 String name = "vm-id";
\r
263 throw new RequestFailedException(String.format("The value %s cannot be null.", name));
\r
265 // Check that its a good uri
\r
266 // This will probably never get hit bc of an earlier check while parsing
\r
267 // the string to a VMURL
\r
269 // noinspection ResultOfMethodCallIgnored
\r
270 URI.create(vm.toString());
\r
271 } catch (Exception e) {
\r
272 logger.error("An error occurred when validating vm url", e);
\r
273 throw new RequestFailedException(
\r
274 String.format("The value %s is not well formed [%s].", name, vm.toString()));
\r
276 // Check the tenant and vmid segments
\r
277 String patternRegex = "([0-9a-f]{8}(-)?[0-9a-f]{4}(-)?[0-9a-f]{4}(-)?[0-9a-f]{4}(-)?[0-9a-f]{12})";
\r
278 Pattern pattern = Pattern.compile(patternRegex, Pattern.CASE_INSENSITIVE);
\r
279 if (!pattern.matcher(vm.getTenantId()).matches()) {
\r
280 throw new RequestFailedException(
\r
281 String.format("The value %s has an invalid tenantId [%s].", name, vm.getTenantId()));
\r
283 if (!pattern.matcher(vm.getServerId()).matches()) {
\r
284 throw new RequestFailedException(
\r
285 String.format("The value %s has an invalid serverId [%s].", name, vm.getServerId()));
\r
289 private ProviderCache createProviderCache(VMURL vm, IdentityURL ident) {
\r
290 if (vm != null && ident != null) {
\r
291 ProviderCache cache = new ProviderCache();
\r
292 cache.setIdentityURL(ident.toString());
\r
293 cache.setProviderName(ident.toString());
\r
294 TenantCache tenant = cache.addTenant(vm.getTenantId(), null, defaultUser, defaultPassword, defaultDomain);
\r
295 // Make sure we could initialize the the cache otherwise return null
\r
296 if (tenant != null && tenant.isInitialized()) {
\r
303 * This method is a general helper method used to locate a server given its fully-qualified self-link URL on a
\r
304 * supported provider, regardless of region(s), and to return an opened context that can be used to access that
\r
307 * @param rc The request context that wraps and manages the state of the request
\r
308 * @param selfLinkURL The fully-qualified self-link URL of the server
\r
309 * @param providerName The name of the provider to be searched
\r
310 * @return The context that can be used to access the server, or null if not found.
\r
312 @SuppressWarnings("nls")
\r
313 protected Context getContext(RequestContext rc, String selfLinkURL, String providerName) {
\r
314 VMURL vm = VMURL.parseURL(selfLinkURL);
\r
315 IdentityURL ident = IdentityURL.parseURL(providerName);
\r
316 String appName = configuration.getProperty(org.onap.appc.Constants.PROPERTY_APPLICATION_NAME);
\r
318 String msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, selfLinkURL);
\r
320 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
324 * Get the cache of tenants and contexts for the named provider, if one exists
\r
326 ProviderCache cache = providerCache.get(providerName);
\r
328 * If one doesn't exist, try and create it. If we have enough information to create it successfully, add it to
\r
329 * the cache and continue, otherwise fail the request.
\r
331 if (cache == null) {
\r
332 if (ident != null) {
\r
333 cache = createProviderCache(vm, ident);
\r
335 if (cache != null) {
\r
336 providerCache.put(cache.getProviderName(), cache);
\r
338 String msg = EELFResourceManager.format(Msg.UNKNOWN_PROVIDER, providerName,
\r
339 providerCache.keySet().toString());
\r
341 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
345 if (providerName == null) {
\r
347 String.format("Using the default provider cache [%s] since no valid identity url was passed in.",
\r
348 cache.getIdentityURL()));
\r
350 // get the tenant cache for the vm
\r
351 String identityURL = cache.getIdentityURL();
\r
352 TenantCache tenantCache = cache.getTenant(vm.getTenantId());
\r
353 if (tenantCache == null) {
\r
354 // no tenantCache matching tenant, add tenant to the provider cache
\r
355 tenantCache = cache.addTenant(vm.getTenantId(), null, defaultUser, defaultPassword, defaultDomain);
\r
356 if (tenantCache == null) {
\r
357 // tenant not found
\r
358 String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, selfLinkURL);
\r
360 doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
\r
364 // reserve the context
\r
365 String tenantName = tenantCache.getTenantName();
\r
366 String tenantId = tenantCache.getTenantId();
\r
367 String region = tenantCache.determineRegion(vm);
\r
369 if (region != null) {
\r
370 Pool<Context> pool = tenantCache.getPools().get(region);
\r
371 while (rc.attempt()) {
\r
373 Context context = pool.reserve();
\r
375 * Insert logic here to test the context for connectivity because we may have gotten one from the
\r
376 * pool that was previously created.
\r
378 reloginIfNeeded(context);
\r
380 } catch (PoolExtensionException e) {
\r
381 String msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, providerName, identityURL,
\r
382 tenantName, tenantId, e.getMessage(), Long.toString(rc.getRetryDelay()),
\r
383 Integer.toString(rc.getAttempts()), Integer.toString(rc.getRetryLimit()));
\r
384 logger.error(msg, e);
\r
386 } catch (Exception e) {
\r
387 String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, e,
\r
388 e.getClass().getSimpleName(), "find", selfLinkURL, tenantCache.getTenantName());
\r
389 logger.error(msg, e);
\r
390 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
394 String msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, providerName, identityURL);
\r
396 doFailure(rc, HttpStatus.BAD_GATEWAY_502, msg);
\r
399 String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, selfLinkURL);
\r
401 doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
\r
405 private void reloginIfNeeded(Context context) throws ZoneException {
\r
406 if (context.isStale()) {
\r
411 protected Context resolveContext(RequestContext rc, Map<String, String> params, String appName, String vmUrl)
\r
412 throws RequestFailedException {
\r
413 VMURL vm = VMURL.parseURL(vmUrl);
\r
415 String msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, vmUrl);
\r
416 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
421 IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
\r
422 String identStr = (ident == null) ? null : ident.toString();
\r
423 return getContext(rc, vmUrl, identStr);
\r
426 protected abstract ModelObject executeProviderOperation(Map<String, String> params, SvcLogicContext context)
\r
427 throws APPCException;
\r
429 public ModelObject doOperation(Map<String, String> params, SvcLogicContext context) throws APPCException {
\r
430 return executeProviderOperation(params, context);
\r