X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=models-interactions%2Fmodel-actors%2Factor.so%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Fpolicy%2Fcontrolloop%2Factor%2Fso%2FSoOperation.java;h=d8404572a4fa9f797db253241e7cc32bbd6a3676;hb=938005505883cf7a636a8840e20e3dc8a0ad9176;hp=510a737a6948ba35f8e88b230190448abd41ff4f;hpb=f6da7772d9dc01ce4ddd21a55b0f1c5fb7ad814f;p=policy%2Fmodels.git diff --git a/models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoOperation.java b/models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoOperation.java index 510a737a6..d8404572a 100644 --- a/models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoOperation.java +++ b/models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoOperation.java @@ -2,7 +2,9 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2020 Wipro Limited. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,45 +22,47 @@ package org.onap.policy.controlloop.actor.so; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import javax.ws.rs.core.Response; -import lombok.Getter; +import java.util.Optional; import org.onap.aai.domain.yang.CloudRegion; import org.onap.aai.domain.yang.GenericVnf; +import org.onap.aai.domain.yang.ModelVer; import org.onap.aai.domain.yang.ServiceInstance; import org.onap.aai.domain.yang.Tenant; -import org.onap.policy.aai.AaiCqResponse; -import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; -import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType; +import org.onap.policy.common.gson.GsonMessageBodyHandler; import org.onap.policy.common.utils.coder.Coder; import org.onap.policy.common.utils.coder.CoderException; import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.coder.StandardCoderObject; import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; +import org.onap.policy.controlloop.actorserviceprovider.OperationProperties; +import org.onap.policy.controlloop.actorserviceprovider.OperationResult; import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation; import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; -import org.onap.policy.controlloop.policy.PolicyResult; -import org.onap.policy.controlloop.policy.Target; +import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpPollingConfig; +import org.onap.policy.so.SoCloudConfiguration; import org.onap.policy.so.SoModelInfo; import org.onap.policy.so.SoRequest; import org.onap.policy.so.SoRequestInfo; import org.onap.policy.so.SoRequestParameters; import org.onap.policy.so.SoRequestStatus; import org.onap.policy.so.SoResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.onap.policy.so.util.SoLocalDateTimeTypeAdapter; /** - * Superclass for SDNC Operators. Note: subclasses should invoke {@link #resetGetCount()} + * Superclass for SDNC Operators. Note: subclasses should invoke {@link #resetPollCount()} * each time they issue an HTTP request. */ public abstract class SoOperation extends HttpOperation { - private static final Logger logger = LoggerFactory.getLogger(SoOperation.class); - private static final Coder coder = new StandardCoder(); + private static final Coder coder = new SoCoder(); public static final String FAILED = "FAILED"; public static final String COMPLETE = "COMPLETE"; @@ -68,102 +72,137 @@ public abstract class SoOperation extends HttpOperation { public static final String REQ_PARAM_NM = "requestParameters"; public static final String CONFIG_PARAM_NM = "configurationParameters"; - @Getter - private final SoOperator operator; + /* Values extracted from the parameter Target. These fields are required by any + subclasses that make use of prepareSoModelInfo(). + */ + private final String modelCustomizationId; + private final String modelInvariantId; + private final String modelVersionId; + private final String modelName; + private final String modelVersion; - /** - * Number of "get" requests issued so far, on the current operation attempt. - */ - @Getter - private int getCount; /** * Constructs the object. * * @param params operation parameters - * @param operator operator that created this operation + * @param config configuration for this operation + * @param propertyNames names of properties required by this operation */ - public SoOperation(ControlLoopOperationParams params, SoOperator operator) { - super(params, operator, SoResponse.class); - this.operator = operator; + protected SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List propertyNames) { + super(params, config, SoResponse.class, propertyNames); + + this.modelCustomizationId = null; + this.modelInvariantId = null; + this.modelVersionId = null; + this.modelVersion = null; + this.modelName = null; + + verifyNotNull("Target information", params.getTargetType()); } /** - * Subclasses should invoke this before issuing their first HTTP request. + * Constructs the object. + * + * @param params operation parameters + * @param config configuration for this operation + * @param propertyNames names of properties required by this operation + * @param targetEntityIds Target Entity information */ - protected void resetGetCount() { - getCount = 0; + protected SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List propertyNames, + Map targetEntityIds) { + super(params, config, SoResponse.class, propertyNames); + + verifyNotNull("Target entity Ids information", targetEntityIds); + + this.modelCustomizationId = targetEntityIds + .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_CUSTOMIZATION_ID); + this.modelInvariantId = targetEntityIds + .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_INVARIANT_ID); + this.modelVersionId = targetEntityIds + .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION_ID); + this.modelVersion = targetEntityIds + .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION); + this.modelName = targetEntityIds + .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_NAME); + + verifyNotNull("Target information", params.getTargetType()); } - /** - * Starts the GUARD. - */ @Override - protected CompletableFuture startPreprocessorAsync() { - return startGuardAsync(); + protected void resetPollCount() { + super.resetPollCount(); + setSubRequestId(null); } /** - * If the response does not indicate that the request has been completed, then sleep a - * bit and issue a "get". + * Validates that the parameters contain the required target information to construct + * the request. */ - @Override - protected CompletableFuture postProcessResponse(OperationOutcome outcome, String url, - Response rawResponse, SoResponse response) { + protected void validateTarget() { + verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_CUSTOMIZATION_ID, modelCustomizationId); + verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_INVARIANT_ID, modelInvariantId); + verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION_ID, modelVersionId); + } - // see if the request has "completed", whether or not it was successful + private void verifyNotNull(String type, Object value) { + if (value == null) { + throw new IllegalArgumentException("missing Target." + type); + } + } + + protected int getVfCount() { + return getRequiredProperty(OperationProperties.DATA_VF_COUNT, "VF Count"); + } + + protected void setVfCount(int vfCount) { + setProperty(OperationProperties.DATA_VF_COUNT, vfCount); + } + + @Override + protected Status detmStatus(Response rawResponse, SoResponse response) { if (rawResponse.getStatus() == 200) { String requestState = getRequestState(response); if (COMPLETE.equalsIgnoreCase(requestState)) { - return CompletableFuture - .completedFuture(setOutcome(outcome, PolicyResult.SUCCESS, rawResponse, response)); + extractSubRequestId(response); + return Status.SUCCESS; } if (FAILED.equalsIgnoreCase(requestState)) { - return CompletableFuture - .completedFuture(setOutcome(outcome, PolicyResult.FAILURE, rawResponse, response)); + extractSubRequestId(response); + return Status.FAILURE; } } // still incomplete // need a request ID with which to query - if (response.getRequestReferences() == null || response.getRequestReferences().getRequestId() == null) { + if (getSubRequestId() == null && !extractSubRequestId(response)) { throw new IllegalArgumentException("missing request ID in response"); } - // see if the limit for the number of "gets" has been reached - if (getCount++ >= operator.getMaxGets()) { - logger.warn("{}: execeeded 'get' limit {} for {}", getFullName(), operator.getMaxGets(), - params.getRequestId()); - setOutcome(outcome, PolicyResult.FAILURE_TIMEOUT); - outcome.setMessage(SO_RESPONSE_CODE + " " + outcome.getMessage()); - return CompletableFuture.completedFuture(outcome); - } - - // sleep and then perform a "get" operation - Function> doGet = unused -> issueGet(outcome, response); - return sleep(getWaitMsGet(), TimeUnit.MILLISECONDS).thenComposeAsync(doGet); + return Status.STILL_WAITING; } - /** - * Issues a "get" request to see if the original request is complete yet. - * - * @param outcome outcome to be populated with the response - * @param response previous response - * @return a future that can be used to cancel the "get" request or await its response - */ - private CompletableFuture issueGet(OperationOutcome outcome, SoResponse response) { - String path = operator.getPathGet() + response.getRequestReferences().getRequestId(); - String url = operator.getClient().getBaseUrl() + path; + @Override + protected String getPollingPath() { + return super.getPollingPath() + getSubRequestId(); + } - logger.debug("{}: 'get' count {} for {}", getFullName(), getCount, params.getRequestId()); + @Override + public void generateSubRequestId(int attempt) { + setSubRequestId(null); + } - logMessage(EventType.OUT, CommInfrastructure.REST, url, null); + private boolean extractSubRequestId(SoResponse response) { + if (response == null || response.getRequestReferences() == null + || response.getRequestReferences().getRequestId() == null) { + return false; + } - // TODO should this use "path" or the full "url"? - return handleResponse(outcome, url, callback -> operator.getClient().get(callback, path, null)); + setSubRequestId(response.getRequestReferences().getRequestId()); + return true; } /** @@ -199,34 +238,26 @@ public abstract class SoOperation extends HttpOperation { * Prepends the message with the http status code. */ @Override - public OperationOutcome setOutcome(OperationOutcome outcome, PolicyResult result, Response rawResponse, + public OperationOutcome setOutcome(OperationOutcome outcome, OperationResult result, Response rawResponse, SoResponse response) { // set default result and message setOutcome(outcome, result); - outcome.setMessage(rawResponse.getStatus() + " " + outcome.getMessage()); + int code = (result == OperationResult.FAILURE_TIMEOUT ? SO_RESPONSE_CODE : rawResponse.getStatus()); + + outcome.setResponse(response); + outcome.setMessage(code + " " + outcome.getMessage()); return outcome; } protected SoModelInfo prepareSoModelInfo() { - Target target = params.getTarget(); - if (target == null) { - throw new IllegalArgumentException("missing Target"); - } - - if (target.getModelCustomizationId() == null || target.getModelInvariantId() == null - || target.getModelName() == null || target.getModelVersion() == null - || target.getModelVersionId() == null) { - throw new IllegalArgumentException("missing VF Module model"); - } - - SoModelInfo soModelInfo = new SoModelInfo(); - soModelInfo.setModelCustomizationId(target.getModelCustomizationId()); - soModelInfo.setModelInvariantId(target.getModelInvariantId()); - soModelInfo.setModelName(target.getModelName()); - soModelInfo.setModelVersion(target.getModelVersion()); - soModelInfo.setModelVersionId(target.getModelVersionId()); + var soModelInfo = new SoModelInfo(); + soModelInfo.setModelCustomizationId(modelCustomizationId); + soModelInfo.setModelInvariantId(modelInvariantId); + soModelInfo.setModelName(modelName); + soModelInfo.setModelVersion(modelVersion); + soModelInfo.setModelVersionId(modelVersionId); soModelInfo.setModelType("vfModule"); return soModelInfo; } @@ -237,7 +268,7 @@ public abstract class SoOperation extends HttpOperation { * @return SO request information */ protected SoRequestInfo constructRequestInfo() { - SoRequestInfo soRequestInfo = new SoRequestInfo(); + var soRequestInfo = new SoRequestInfo(); soRequestInfo.setSource("POLICY"); soRequestInfo.setSuppressRollback(false); soRequestInfo.setRequestorId("policy"); @@ -247,18 +278,18 @@ public abstract class SoOperation extends HttpOperation { /** * Builds the request parameters from the policy payload. */ - protected SoRequestParameters buildRequestParameters() { + protected Optional buildRequestParameters() { if (params.getPayload() == null) { - return null; + return Optional.empty(); } - String json = params.getPayload().get(REQ_PARAM_NM); - if (json == null) { - return null; + Object data = params.getPayload().get(REQ_PARAM_NM); + if (data == null) { + return Optional.empty(); } try { - return coder.decode(json, SoRequestParameters.class); + return Optional.of(coder.decode(data.toString(), SoRequestParameters.class)); } catch (CoderException e) { throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM); } @@ -267,74 +298,124 @@ public abstract class SoOperation extends HttpOperation { /** * Builds the configuration parameters from the policy payload. */ - protected List> buildConfigurationParameters() { + protected Optional>> buildConfigurationParameters() { if (params.getPayload() == null) { - return null; + return Optional.empty(); } - String json = params.getPayload().get(CONFIG_PARAM_NM); - if (json == null) { - return null; + Object data = params.getPayload().get(CONFIG_PARAM_NM); + if (data == null) { + return Optional.empty(); } try { @SuppressWarnings("unchecked") - List> result = coder.decode(json, ArrayList.class); - return result; + List> result = coder.decode(data.toString(), ArrayList.class); + return Optional.of(result); } catch (CoderException | RuntimeException e) { throw new IllegalArgumentException("invalid payload value: " + CONFIG_PARAM_NM); } } + /** + * Construct cloudConfiguration for the SO requestDetails. + * + * @param tenantItem tenant item from A&AI named-query response + * @return SO cloud configuration + */ + protected SoCloudConfiguration constructCloudConfiguration(Tenant tenantItem, CloudRegion cloudRegionItem) { + var cloudConfiguration = new SoCloudConfiguration(); + cloudConfiguration.setTenantId(getRequiredText("tenant ID", tenantItem.getTenantId())); + cloudConfiguration.setLcpCloudRegionId(getRequiredText("cloud region ID", cloudRegionItem.getCloudRegionId())); + return cloudConfiguration; + } + + /** + * Verifies that a value is not {@code null}. + * + * @param name value name + * @param value value to check + * @return the value + */ + protected String getRequiredText(String name, String value) { + if (value == null) { + throw new IllegalArgumentException("missing " + name); + } + + return value; + } + + /** + * Create simple HTTP headers for unauthenticated requests to SO. + * + * @return the HTTP headers + */ + protected Map createSimpleHeaders() { + Map headers = new HashMap<>(); + headers.put("Accept", MediaType.APPLICATION_JSON); + return headers; + } + /* * These methods extract data from the Custom Query and throw an * IllegalArgumentException if the desired data item is not found. */ - protected GenericVnf getVnfItem(AaiCqResponse aaiCqResponse, SoModelInfo soModelInfo) { - GenericVnf vnf = aaiCqResponse.getGenericVnfByVfModuleModelInvariantId(soModelInfo.getModelInvariantId()); - if (vnf == null) { - throw new IllegalArgumentException("missing generic VNF"); - } + protected GenericVnf getVnfItem() { + return getRequiredProperty(OperationProperties.AAI_VNF, "generic VNF"); + } - return vnf; + protected ServiceInstance getServiceInstance() { + return getRequiredProperty(OperationProperties.AAI_SERVICE, "VNF Service Item"); } - protected ServiceInstance getServiceInstance(AaiCqResponse aaiCqResponse) { - ServiceInstance vnfService = aaiCqResponse.getServiceInstance(); - if (vnfService == null) { - throw new IllegalArgumentException("missing VNF Service Item"); - } + protected Tenant getDefaultTenant() { + return getRequiredProperty(OperationProperties.AAI_DEFAULT_TENANT, "Default Tenant Item"); + } - return vnfService; + protected CloudRegion getDefaultCloudRegion() { + return getRequiredProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, "Default Cloud Region"); } - protected Tenant getDefaultTenant(AaiCqResponse aaiCqResponse) { - Tenant tenant = aaiCqResponse.getDefaultTenant(); - if (tenant == null) { - throw new IllegalArgumentException("missing Tenant Item"); - } + protected ModelVer getVnfModel() { + return getRequiredProperty(OperationProperties.AAI_VNF_MODEL, "generic VNF Model"); + } - return tenant; + protected ModelVer getServiceModel() { + return getRequiredProperty(OperationProperties.AAI_SERVICE_MODEL, "Service Model"); } - protected CloudRegion getDefaultCloudRegion(AaiCqResponse aaiCqResponse) { - CloudRegion cloudRegion = aaiCqResponse.getDefaultCloudRegion(); - if (cloudRegion == null) { - throw new IllegalArgumentException("missing Cloud Region"); - } + // these may be overridden by junit tests - return cloudRegion; + @Override + protected Coder getCoder() { + return coder; } - // these may be overridden by junit tests + private static class SoCoder extends StandardCoder { - /** - * Gets the wait time, in milliseconds, between "get" requests. - * - * @return the wait time, in milliseconds, between "get" requests - */ - public long getWaitMsGet() { - return TimeUnit.MILLISECONDS.convert(operator.getWaitSecGet(), TimeUnit.SECONDS); + /** + * Gson object used to encode and decode messages. + */ + private static final Gson SO_GSON; + + /** + * Gson object used to encode messages in "pretty" format. + */ + private static final Gson SO_GSON_PRETTY; + + static { + GsonBuilder builder = GsonMessageBodyHandler + .configBuilder(new GsonBuilder().registerTypeAdapter(StandardCoderObject.class, + new StandardTypeAdapter())) + .registerTypeAdapter(LocalDateTime.class, new SoLocalDateTimeTypeAdapter()); + + SO_GSON = builder.create(); + SO_GSON_PRETTY = builder.setPrettyPrinting().create(); + } + + public SoCoder() { + super(SO_GSON, SO_GSON_PRETTY); + } } }