package org.onap.policy.controlloop.actor.so;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.time.LocalDateTime;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import lombok.Getter;
import org.onap.aai.domain.yang.CloudRegion;
import org.onap.aai.domain.yang.GenericVnf;
import org.onap.aai.domain.yang.ServiceInstance;
import org.onap.aai.domain.yang.Tenant;
+import org.onap.policy.aai.AaiConstants;
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.impl.HttpOperation;
import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
import org.onap.policy.controlloop.policy.PolicyResult;
import org.onap.policy.controlloop.policy.Target;
+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.onap.policy.so.util.SoLocalDateTimeTypeAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*/
public abstract class SoOperation extends HttpOperation<SoResponse> {
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 PAYLOAD_KEY_VF_COUNT = "vfCount";
public static final String FAILED = "FAILED";
public static final String COMPLETE = "COMPLETE";
public static final int SO_RESPONSE_CODE = 999;
private final SoConfig config;
+ // values extracted from the parameter Target
+ private final String modelCustomizationId;
+ private final String modelInvariantId;
+ private final String modelVersionId;
+
+ private final String vfCountKey;
+
/**
* Number of "get" requests issued so far, on the current operation attempt.
*/
public SoOperation(ControlLoopOperationParams params, HttpConfig config) {
super(params, config, SoResponse.class);
this.config = (SoConfig) config;
+
+ verifyNotNull("Target information", params.getTarget());
+
+ this.modelCustomizationId = params.getTarget().getModelCustomizationId();
+ this.modelInvariantId = params.getTarget().getModelInvariantId();
+ this.modelVersionId = params.getTarget().getModelVersionId();
+
+ vfCountKey = SoConstants.VF_COUNT_PREFIX + "[" + modelCustomizationId + "][" + modelInvariantId + "]["
+ + modelVersionId + "]";
}
/**
*/
protected void resetGetCount() {
getCount = 0;
+ setSubRequestId(null);
}
/**
* the VF count from the custom query.
*/
protected void validateTarget() {
- verifyNotNull("Target information", params.getTarget());
- verifyNotNull("model-customization-id", params.getTarget().getModelCustomizationId());
- verifyNotNull("model-invariant-id", params.getTarget().getModelInvariantId());
- verifyNotNull("model-version-id", params.getTarget().getModelVersionId());
+ verifyNotNull("modelCustomizationId", modelCustomizationId);
+ verifyNotNull("modelInvariantId", modelInvariantId);
+ verifyNotNull("modelVersionId", modelVersionId);
}
private void verifyNotNull(String type, Object value) {
}
/**
- * Stores the VF count and then runs the guard.
+ * Gets the VF Count.
*
- * @return a future to cancel or await the guard response
+ * @return a future to cancel or await the VF Count
*/
- protected CompletableFuture<OperationOutcome> storeVfCountRunGuard() {
- String custId = params.getTarget().getModelCustomizationId();
- String invId = params.getTarget().getModelInvariantId();
- String verId = params.getTarget().getModelVersionId();
+ @SuppressWarnings("unchecked")
+ protected CompletableFuture<OperationOutcome> obtainVfCount() {
+ if (params.getContext().contains(vfCountKey)) {
+ // already have the VF count
+ return null;
+ }
- AaiCqResponse cq = params.getContext().getProperty(AaiCqResponse.CONTEXT_KEY);
- int vfcount = cq.getVfModuleCount(custId, invId, verId);
+ // need custom query from which to extract the VF count
+ ControlLoopOperationParams cqParams = params.toBuilder().actor(AaiConstants.ACTOR_NAME)
+ .operation(AaiCqResponse.OPERATION).payload(null).retry(null).timeoutSec(null).build();
- params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, vfcount);
+ // run Custom Query and then extract the VF count
+ return sequence(() -> params.getContext().obtain(AaiCqResponse.CONTEXT_KEY, cqParams), this::storeVfCount);
+ }
- return startGuardAsync();
+ /**
+ * Stores the VF count.
+ *
+ * @return {@code null}
+ */
+ private CompletableFuture<OperationOutcome> storeVfCount() {
+ if (!params.getContext().contains(vfCountKey)) {
+ AaiCqResponse cq = params.getContext().getProperty(AaiCqResponse.CONTEXT_KEY);
+ int vfcount = cq.getVfModuleCount(modelCustomizationId, modelInvariantId, modelVersionId);
+
+ params.getContext().setProperty(vfCountKey, vfcount);
+ }
+
+ return null;
+ }
+
+ protected int getVfCount() {
+ return params.getContext().getProperty(vfCountKey);
+ }
+
+ protected void setVfCount(int vfCount) {
+ params.getContext().setProperty(vfCountKey, vfCount);
}
/**
if (rawResponse.getStatus() == 200) {
String requestState = getRequestState(response);
if (COMPLETE.equalsIgnoreCase(requestState)) {
+ extractSubRequestId(response);
successfulCompletion();
return CompletableFuture
.completedFuture(setOutcome(outcome, PolicyResult.SUCCESS, rawResponse, response));
}
if (FAILED.equalsIgnoreCase(requestState)) {
+ extractSubRequestId(response);
return CompletableFuture
.completedFuture(setOutcome(outcome, PolicyResult.FAILURE, rawResponse, response));
}
// 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");
}
}
// sleep and then perform a "get" operation
- Function<Void, CompletableFuture<OperationOutcome>> doGet = unused -> issueGet(outcome, response);
+ Function<Void, CompletableFuture<OperationOutcome>> doGet = unused -> issueGet(outcome);
return sleep(getWaitMsGet(), TimeUnit.MILLISECONDS).thenComposeAsync(doGet);
}
+ @Override
+ public void generateSubRequestId(int attempt) {
+ setSubRequestId(null);
+ }
+
+ private boolean extractSubRequestId(SoResponse response) {
+ if (response == null || response.getRequestReferences() == null
+ || response.getRequestReferences().getRequestId() == null) {
+ return false;
+ }
+
+ setSubRequestId(response.getRequestReferences().getRequestId());
+ return true;
+ }
+
/**
* Invoked when a request completes successfully.
*/
* 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<OperationOutcome> issueGet(OperationOutcome outcome, SoResponse response) {
- String path = getPathGet() + response.getRequestReferences().getRequestId();
+ private CompletableFuture<OperationOutcome> issueGet(OperationOutcome outcome) {
+ String path = getPathGet() + getSubRequestId();
String url = getClient().getBaseUrl() + path;
logger.debug("{}: 'get' count {} for {}", getFullName(), getCount, params.getRequestId());
logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
- // TODO should this use "path" or the full "url"?
return handleResponse(outcome, url, callback -> getClient().get(callback, path, null));
}
/**
* Builds the request parameters from the policy payload.
*/
- protected SoRequestParameters buildRequestParameters() {
+ protected Optional<SoRequestParameters> buildRequestParameters() {
if (params.getPayload() == null) {
- return null;
+ return Optional.empty();
}
Object data = params.getPayload().get(REQ_PARAM_NM);
if (data == null) {
- return null;
+ return Optional.empty();
}
try {
- return coder.decode(data.toString(), SoRequestParameters.class);
+ return Optional.of(coder.decode(data.toString(), SoRequestParameters.class));
} catch (CoderException e) {
throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM);
}
/**
* Builds the configuration parameters from the policy payload.
*/
- protected List<Map<String, String>> buildConfigurationParameters() {
+ protected Optional<List<Map<String, String>>> buildConfigurationParameters() {
if (params.getPayload() == null) {
- return null;
+ return Optional.empty();
}
Object data = params.getPayload().get(CONFIG_PARAM_NM);
if (data == null) {
- return null;
+ return Optional.empty();
}
try {
@SuppressWarnings("unchecked")
List<Map<String, String>> result = coder.decode(data.toString(), ArrayList.class);
- return result;
+ return Optional.of(result);
} catch (CoderException | RuntimeException e) {
throw new IllegalArgumentException("invalid payload value: " + CONFIG_PARAM_NM);
}
}
+ /**
+ * Construct cloudConfiguration for the SO requestDetails. Overridden for custom
+ * query.
+ *
+ * @param tenantItem tenant item from A&AI named-query response
+ * @return SO cloud configuration
+ */
+ protected SoCloudConfiguration constructCloudConfigurationCq(Tenant tenantItem, CloudRegion cloudRegionItem) {
+ SoCloudConfiguration cloudConfiguration = new SoCloudConfiguration();
+ cloudConfiguration.setTenantId(tenantItem.getTenantId());
+ cloudConfiguration.setLcpCloudRegionId(cloudRegionItem.getCloudRegionId());
+ return cloudConfiguration;
+ }
+
+ /**
+ * Create simple HTTP headers for unauthenticated requests to SO.
+ *
+ * @return the HTTP headers
+ */
+ protected Map<String, Object> createSimpleHeaders() {
+ Map<String, Object> 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.
public int getWaitSecGet() {
return config.getWaitSecGet();
}
+
+ @Override
+ protected Coder makeCoder() {
+ return coder;
+ }
+
+ private static class SoCoder extends StandardCoder {
+
+ /**
+ * 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);
+ }
+ }
}