2 * ============LICENSE_START=======================================================
\r
4 * ================================================================================
\r
5 * Copyright (C) 2017-2018 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 * ============LICENSE_END=========================================================
\r
24 package org.onap.appc.adapter.iaas.provider.operation.impl.base;
\r
26 import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
\r
27 import static org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants.MDC_ADAPTER;
\r
28 import static org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants.MDC_SERVICE;
\r
30 import com.att.cdp.exceptions.ZoneException;
\r
31 import com.att.cdp.zones.Context;
\r
32 import com.att.cdp.zones.model.ModelObject;
\r
33 import com.att.cdp.zones.model.Server;
\r
34 import com.att.eelf.configuration.EELFLogger;
\r
35 import com.att.eelf.configuration.EELFManager;
\r
36 import com.att.eelf.i18n.EELFResourceManager;
\r
37 import java.net.URI;
\r
38 import java.util.Map;
\r
39 import java.util.Map.Entry;
\r
40 import java.util.Set;
\r
41 import java.util.regex.Pattern;
\r
42 import org.glassfish.grizzly.http.util.HttpStatus;
\r
43 import org.onap.appc.adapter.iaas.ProviderAdapter;
\r
44 import org.onap.appc.adapter.iaas.impl.IdentityURL;
\r
45 import org.onap.appc.adapter.iaas.impl.ProviderCache;
\r
46 import org.onap.appc.adapter.iaas.impl.RequestContext;
\r
47 import org.onap.appc.adapter.iaas.impl.RequestFailedException;
\r
48 import org.onap.appc.adapter.iaas.impl.TenantCache;
\r
49 import org.onap.appc.adapter.iaas.impl.VMURL;
\r
50 import org.onap.appc.adapter.iaas.provider.operation.api.IProviderOperation;
\r
51 import org.onap.appc.adapter.iaas.provider.operation.common.constants.Constants;
\r
52 import org.onap.appc.adapter.iaas.provider.operation.common.enums.Outcome;
\r
53 import org.onap.appc.configuration.Configuration;
\r
54 import org.onap.appc.configuration.ConfigurationFactory;
\r
55 import org.onap.appc.exceptions.APPCException;
\r
56 import org.onap.appc.i18n.Msg;
\r
57 import org.onap.appc.pool.Pool;
\r
58 import org.onap.appc.pool.PoolExtensionException;
\r
59 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
\r
60 import org.slf4j.MDC;
\r
62 public abstract class ProviderOperation implements IProviderOperation {
\r
64 private static final EELFLogger logger = EELFManager.getInstance().getLogger(ProviderOperation.class);
\r
65 protected static final Configuration configuration = ConfigurationFactory.getConfiguration();
\r
69 * A cache of providers that are predefined.
\r
71 private Map<String /* provider name */, ProviderCache> providerCache;
\r
74 * The username and password to use for dynamically created connections
\r
76 private String defaultUser;
\r
77 private String defaultPassword;
\r
78 private String defaultDomain;
\r
81 public void setDefaultUser(String defaultUser) {
\r
82 this.defaultUser = defaultUser;
\r
86 public void setDefaultPassword(String defaultPassword) {
\r
87 this.defaultPassword = defaultPassword;
\r
91 public void setProviderCache(Map<String, ProviderCache> providerCache) {
\r
92 this.providerCache = providerCache;
\r
96 public void setDefaultDomain(String defaultDomain) {
\r
97 this.defaultDomain = defaultDomain;
\r
103 protected void setMDC(String service, String serviceName, String adapterName) {
\r
104 MDC.put(MDC_ADAPTER, adapterName);
\r
105 MDC.put(MDC_SERVICE, service);
\r
106 MDC.put(MDC_SERVICE_NAME, serviceName);
\r
110 * initial log of the operation
\r
112 protected void logOperation(Msg msg, Map<String, String> params, SvcLogicContext context) {
\r
114 String appName = configuration.getProperty(org.onap.appc.Constants.PROPERTY_APPLICATION_NAME);
\r
115 logger.info(msg, appName);
\r
117 debugParameters(params);
\r
118 debugContext(context);
\r
121 * This method is used to dump the value of the parameters to the log for debugging purposes.
\r
123 * @param parameters The parameters to be printed to the log
\r
125 private void debugParameters(Map<String, String> parameters) {
\r
126 for (Entry<String, String> entry : parameters.entrySet()) {
\r
127 logger.debug(Msg.PROPERTY_VALUE, entry.getKey(), entry.getValue());
\r
131 * This method is used to create a diagnostic dump of the context for the log
\r
133 * @param context The context to be dumped
\r
135 @SuppressWarnings({"nls", "static-method"})
\r
136 private void debugContext(SvcLogicContext context) {
\r
137 Set<String> keys = context.getAttributeKeySet();
\r
138 StringBuilder builder = new StringBuilder();
\r
139 builder.append("Service Logic Context: Status ");
\r
140 builder.append(Constants.LPAREN);
\r
141 builder.append(context.getStatus());
\r
142 builder.append(Constants.RPAREN);
\r
143 builder.append(", Attribute count ");
\r
144 builder.append(Constants.LPAREN);
\r
145 builder.append(keys == null ? "none" : Integer.toString(keys.size()));
\r
146 builder.append(Constants.RPAREN);
\r
147 if (keys != null && !keys.isEmpty()) {
\r
148 builder.append(Constants.NL);
\r
149 for (String key : keys) {
\r
150 String value = context.getAttribute(key);
\r
151 builder.append("Attribute ");
\r
152 builder.append(Constants.LPAREN);
\r
153 builder.append(key);
\r
154 builder.append(Constants.RPAREN);
\r
155 builder.append(", value ");
\r
156 builder.append(Constants.LPAREN);
\r
157 builder.append(value == null ? "" : value);
\r
158 builder.append(Constants.RPAREN);
\r
159 builder.append(Constants.NL);
\r
162 logger.debug(builder.toString());
\r
167 * This method is used to validate that the parameters contain all required property names, and that the values are
\r
168 * non-null and non-empty strings. We are still not ensured that the value is valid, but at least it exists.
\r
170 * @param parameters The parameters to be checked
\r
171 * @param propertyNames The list of property names that are required to be present.
\r
172 * @throws RequestFailedException If the parameters are not valid
\r
174 protected void validateParametersExist(Map<String, String> parameters, String... propertyNames)
\r
175 throws RequestFailedException {
\r
176 boolean success = true;
\r
177 StringBuilder msg =
\r
178 new StringBuilder(EELFResourceManager.format(Msg.MISSING_REQUIRED_PROPERTIES, MDC.get(MDC_SERVICE)));
\r
179 msg.append(Constants.NL);
\r
180 for (String propertyName : propertyNames) {
\r
181 String value = parameters.get(propertyName);
\r
182 if (value == null || value.trim().length() == 0) {
\r
184 msg.append(Constants.QUOTE);
\r
185 msg.append(propertyName);
\r
186 msg.append(Constants.QUOTE);
\r
187 msg.append(Constants.SPACE);
\r
192 logger.error(msg.toString());
\r
193 throw new RequestFailedException("Check Parameters", msg.toString(), HttpStatus.BAD_REQUEST_400,
\r
199 * @param rc The request context that manages the state and recovery of the request for the life of its processing.
\r
201 protected void doFailure(RequestContext rc, HttpStatus code, String message) {
\r
203 doFailure(rc, code, message, null);
\r
204 } catch (APPCException e) {
\r
205 logger.error("An APPC exception caught. Should never happen", e);
\r
209 protected void doFailure(RequestContext rc, HttpStatus code, String message, Throwable cause) throws APPCException {
\r
210 SvcLogicContext svcLogic = rc.getSvcLogicContext();
\r
211 String msg = (message == null) ? code.getReasonPhrase() : message;
\r
212 if ((msg.contains("PALOS"))) {
\r
213 msg = msg.substring(msg.indexOf("PALOS"), msg.length());
\r
214 msg = msg.substring(msg.indexOf("PALOS"), msg.indexOf("\n"));
\r
216 if (msg.contains("\n")) {
\r
217 msg = msg.substring(0, msg.indexOf('\n'));
\r
222 status = Integer.toString(code.getStatusCode());
\r
223 } catch (Exception e) {
\r
224 logger.error("Error when parsing status code", e);
\r
227 svcLogic.setStatus(Outcome.FAILURE.toString());
\r
228 svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_CODE, status);
\r
229 svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_MESSAGE, msg);
\r
230 if (null != cause) {
\r
231 throw new APPCException(cause);
\r
236 * @param rc The request context that manages the state and recovery of the request for the life of its processing.
\r
238 @SuppressWarnings("static-method")
\r
239 protected void doSuccess(RequestContext rc) {
\r
240 SvcLogicContext svcLogic = rc.getSvcLogicContext();
\r
241 svcLogic.setStatus(Outcome.SUCCESS.toString());
\r
242 svcLogic.setAttribute(org.onap.appc.Constants.ATTRIBUTE_ERROR_CODE,
\r
243 Integer.toString(HttpStatus.OK_200.getStatusCode()));
\r
246 protected boolean validateVM(RequestContext rc, String appName, String vmUrl, VMURL vm)
\r
247 throws RequestFailedException {
\r
250 msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, vmUrl);
\r
252 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
259 protected void validateVMURL(VMURL vm) throws RequestFailedException {
\r
260 String name = "vm-id";
\r
262 throw new RequestFailedException(String.format("The value %s cannot be null.", name));
\r
264 // Check that its a good uri
\r
265 // This will probably never get hit bc of an earlier check while parsing
\r
266 // the string to a VMURL
\r
268 // noinspection ResultOfMethodCallIgnored
\r
269 URI.create(vm.toString());
\r
270 } catch (Exception e) {
\r
271 logger.error("An error occurred when validating vm url", e);
\r
272 throw new RequestFailedException(
\r
273 String.format("The value %s is not well formed [%s].", name, vm.toString()));
\r
275 // Check the tenant and vmid segments
\r
276 String patternRegex = "([0-9a-f]{8}(-)?[0-9a-f]{4}(-)?[0-9a-f]{4}(-)?[0-9a-f]{4}(-)?[0-9a-f]{12})";
\r
277 Pattern pattern = Pattern.compile(patternRegex, Pattern.CASE_INSENSITIVE);
\r
278 if (!pattern.matcher(vm.getTenantId()).matches()) {
\r
279 throw new RequestFailedException(
\r
280 String.format("The value %s has an invalid tenantId [%s].", name, vm.getTenantId()));
\r
282 if (!pattern.matcher(vm.getServerId()).matches()) {
\r
283 throw new RequestFailedException(
\r
284 String.format("The value %s has an invalid serverId [%s].", name, vm.getServerId()));
\r
288 private ProviderCache createProviderCache(VMURL vm, IdentityURL ident) {
\r
289 if (vm != null && ident != null) {
\r
290 ProviderCache cache = new ProviderCache();
\r
291 cache.setIdentityURL(ident.toString());
\r
292 cache.setProviderName(ident.toString());
\r
293 TenantCache tenant = cache.addTenant(vm.getTenantId(), null, defaultUser, defaultPassword, defaultDomain);
\r
294 // Make sure we could initialize the the cache otherwise return null
\r
295 if (tenant != null && tenant.isInitialized()) {
\r
302 * This method is a general helper method used to locate a server given its fully-qualified self-link URL on a
\r
303 * supported provider, regardless of region(s), and to return an opened context that can be used to access that
\r
306 * @param rc The request context that wraps and manages the state of the request
\r
307 * @param selfLinkURL The fully-qualified self-link URL of the server
\r
308 * @param providerName The name of the provider to be searched
\r
309 * @return The context that can be used to access the server, or null if not found.
\r
311 @SuppressWarnings("nls")
\r
312 protected Context getContext(RequestContext rc, String selfLinkURL, String providerName) {
\r
313 VMURL vm = VMURL.parseURL(selfLinkURL);
\r
314 IdentityURL ident = IdentityURL.parseURL(providerName);
\r
315 String appName = configuration.getProperty(org.onap.appc.Constants.PROPERTY_APPLICATION_NAME);
\r
317 String msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, selfLinkURL);
\r
319 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
323 * Get the cache of tenants and contexts for the named provider, if one exists
\r
325 ProviderCache cache = providerCache.get(providerName);
\r
327 * If one doesn't exist, try and create it. If we have enough information to create it successfully, add it to
\r
328 * the cache and continue, otherwise fail the request.
\r
330 if (cache == null) {
\r
331 if (ident != null) {
\r
332 cache = createProviderCache(vm, ident);
\r
334 if (cache != null) {
\r
335 providerCache.put(cache.getProviderName(), cache);
\r
337 String msg = EELFResourceManager.format(Msg.UNKNOWN_PROVIDER, providerName,
\r
338 providerCache.keySet().toString());
\r
340 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
344 if (providerName == null) {
\r
346 String.format("Using the default provider cache [%s] since no valid identity url was passed in.",
\r
347 cache.getIdentityURL()));
\r
349 // get the tenant cache for the vm
\r
350 String identityURL = cache.getIdentityURL();
\r
351 TenantCache tenantCache = cache.getTenant(vm.getTenantId());
\r
352 if (tenantCache == null) {
\r
353 // no tenantCache matching tenant, add tenant to the provider cache
\r
354 tenantCache = cache.addTenant(vm.getTenantId(), null, defaultUser, defaultPassword, defaultDomain);
\r
355 if (tenantCache == null) {
\r
356 // tenant not found
\r
357 String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, selfLinkURL);
\r
359 doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
\r
363 // reserve the context
\r
364 String tenantName = tenantCache.getTenantName();
\r
365 String tenantId = tenantCache.getTenantId();
\r
366 String region = tenantCache.determineRegion(vm);
\r
368 if (region != null) {
\r
369 Pool<Context> pool = tenantCache.getPools().get(region);
\r
370 while (rc.attempt()) {
\r
372 Context context = pool.reserve();
\r
374 * Insert logic here to test the context for connectivity because we may have gotten one from the
\r
375 * pool that was previously created.
\r
377 reloginIfNeeded(context);
\r
379 } catch (PoolExtensionException e) {
\r
380 String msg = EELFResourceManager.format(Msg.CONNECTION_FAILED_RETRY, providerName, identityURL,
\r
381 tenantName, tenantId, e.getMessage(), Long.toString(rc.getRetryDelay()),
\r
382 Integer.toString(rc.getAttempts()), Integer.toString(rc.getRetryLimit()));
\r
383 logger.error(msg, e);
\r
385 } catch (Exception e) {
\r
386 String msg = EELFResourceManager.format(Msg.SERVER_OPERATION_EXCEPTION, e,
\r
387 e.getClass().getSimpleName(), "find", selfLinkURL, tenantCache.getTenantName());
\r
388 logger.error(msg, e);
\r
389 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
393 String msg = EELFResourceManager.format(Msg.CONNECTION_FAILED, providerName, identityURL);
\r
395 doFailure(rc, HttpStatus.BAD_GATEWAY_502, msg);
\r
398 String msg = EELFResourceManager.format(Msg.SERVER_NOT_FOUND, selfLinkURL);
\r
400 doFailure(rc, HttpStatus.NOT_FOUND_404, msg);
\r
404 private void reloginIfNeeded(Context context) throws ZoneException {
\r
405 if (context.isStale()) {
\r
410 protected Context resolveContext(RequestContext rc, Map<String, String> params, String appName, String vmUrl)
\r
411 throws RequestFailedException {
\r
412 VMURL vm = VMURL.parseURL(vmUrl);
\r
414 String msg = EELFResourceManager.format(Msg.INVALID_SELF_LINK_URL, appName, vmUrl);
\r
415 doFailure(rc, HttpStatus.INTERNAL_SERVER_ERROR_500, msg);
\r
420 IdentityURL ident = IdentityURL.parseURL(params.get(ProviderAdapter.PROPERTY_IDENTITY_URL));
\r
421 String identStr = (ident == null) ? null : ident.toString();
\r
422 return getContext(rc, vmUrl, identStr);
\r
425 protected abstract ModelObject executeProviderOperation(Map<String, String> params, SvcLogicContext context)
\r
426 throws APPCException;
\r
428 public ModelObject doOperation(Map<String, String> params, SvcLogicContext context) throws APPCException {
\r
429 return executeProviderOperation(params, context);
\r