2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.controlloop.actor.so;
23 import com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import java.time.LocalDateTime;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Optional;
31 import java.util.concurrent.CompletableFuture;
32 import javax.ws.rs.core.MediaType;
33 import javax.ws.rs.core.Response;
34 import org.onap.aai.domain.yang.CloudRegion;
35 import org.onap.aai.domain.yang.GenericVnf;
36 import org.onap.aai.domain.yang.ServiceInstance;
37 import org.onap.aai.domain.yang.Tenant;
38 import org.onap.policy.aai.AaiConstants;
39 import org.onap.policy.aai.AaiCqResponse;
40 import org.onap.policy.common.gson.GsonMessageBodyHandler;
41 import org.onap.policy.common.utils.coder.Coder;
42 import org.onap.policy.common.utils.coder.CoderException;
43 import org.onap.policy.common.utils.coder.StandardCoder;
44 import org.onap.policy.common.utils.coder.StandardCoderObject;
45 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
46 import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
47 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
48 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpPollingConfig;
49 import org.onap.policy.controlloop.policy.PolicyResult;
50 import org.onap.policy.controlloop.policy.Target;
51 import org.onap.policy.so.SoCloudConfiguration;
52 import org.onap.policy.so.SoModelInfo;
53 import org.onap.policy.so.SoRequest;
54 import org.onap.policy.so.SoRequestInfo;
55 import org.onap.policy.so.SoRequestParameters;
56 import org.onap.policy.so.SoRequestStatus;
57 import org.onap.policy.so.SoResponse;
58 import org.onap.policy.so.util.SoLocalDateTimeTypeAdapter;
61 * Superclass for SDNC Operators. Note: subclasses should invoke {@link #resetPollCount()}
62 * each time they issue an HTTP request.
64 public abstract class SoOperation extends HttpOperation<SoResponse> {
65 private static final Coder coder = new SoCoder();
67 public static final String PAYLOAD_KEY_VF_COUNT = "vfCount";
68 public static final String FAILED = "FAILED";
69 public static final String COMPLETE = "COMPLETE";
70 public static final int SO_RESPONSE_CODE = 999;
72 // fields within the policy payload
73 public static final String REQ_PARAM_NM = "requestParameters";
74 public static final String CONFIG_PARAM_NM = "configurationParameters";
76 // values extracted from the parameter Target
77 private final String modelCustomizationId;
78 private final String modelInvariantId;
79 private final String modelVersionId;
81 private final String vfCountKey;
85 * Constructs the object.
87 * @param params operation parameters
88 * @param config configuration for this operation
89 * @param propertyNames names of properties required by this operation
91 public SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List<String> propertyNames) {
92 super(params, config, SoResponse.class, propertyNames);
96 verifyNotNull("Target information", params.getTarget());
98 this.modelCustomizationId = params.getTarget().getModelCustomizationId();
99 this.modelInvariantId = params.getTarget().getModelInvariantId();
100 this.modelVersionId = params.getTarget().getModelVersionId();
102 vfCountKey = SoConstants.VF_COUNT_PREFIX + "[" + modelCustomizationId + "][" + modelInvariantId + "]["
103 + modelVersionId + "]";
107 protected void resetPollCount() {
108 super.resetPollCount();
109 setSubRequestId(null);
113 * Validates that the parameters contain the required target information to extract
114 * the VF count from the custom query.
116 protected void validateTarget() {
117 verifyNotNull("modelCustomizationId", modelCustomizationId);
118 verifyNotNull("modelInvariantId", modelInvariantId);
119 verifyNotNull("modelVersionId", modelVersionId);
122 private void verifyNotNull(String type, Object value) {
124 throw new IllegalArgumentException("missing " + type + " for guard payload");
132 protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
133 return startGuardAsync();
139 * @return a future to cancel or await the VF Count
141 @SuppressWarnings("unchecked")
142 protected CompletableFuture<OperationOutcome> obtainVfCount() {
143 if (params.getContext().contains(vfCountKey)) {
144 // already have the VF count
148 // need custom query from which to extract the VF count
149 ControlLoopOperationParams cqParams = params.toBuilder().actor(AaiConstants.ACTOR_NAME)
150 .operation(AaiCqResponse.OPERATION).payload(null).retry(null).timeoutSec(null).build();
152 // run Custom Query and then extract the VF count
153 return sequence(() -> params.getContext().obtain(AaiCqResponse.CONTEXT_KEY, cqParams), this::storeVfCount);
157 * Stores the VF count.
159 * @return {@code null}
161 private CompletableFuture<OperationOutcome> storeVfCount() {
162 if (!params.getContext().contains(vfCountKey)) {
163 AaiCqResponse cq = params.getContext().getProperty(AaiCqResponse.CONTEXT_KEY);
164 int vfcount = cq.getVfModuleCount(modelCustomizationId, modelInvariantId, modelVersionId);
166 params.getContext().setProperty(vfCountKey, vfcount);
172 protected int getVfCount() {
173 return params.getContext().getProperty(vfCountKey);
176 protected void setVfCount(int vfCount) {
177 params.getContext().setProperty(vfCountKey, vfCount);
181 protected Status detmStatus(Response rawResponse, SoResponse response) {
182 if (rawResponse.getStatus() == 200) {
183 String requestState = getRequestState(response);
184 if (COMPLETE.equalsIgnoreCase(requestState)) {
185 extractSubRequestId(response);
186 return Status.SUCCESS;
189 if (FAILED.equalsIgnoreCase(requestState)) {
190 extractSubRequestId(response);
191 return Status.FAILURE;
197 // need a request ID with which to query
198 if (getSubRequestId() == null && !extractSubRequestId(response)) {
199 throw new IllegalArgumentException("missing request ID in response");
202 return Status.STILL_WAITING;
206 protected String getPollingPath() {
207 return super.getPollingPath() + getSubRequestId();
211 public void generateSubRequestId(int attempt) {
212 setSubRequestId(null);
215 private boolean extractSubRequestId(SoResponse response) {
216 if (response == null || response.getRequestReferences() == null
217 || response.getRequestReferences().getRequestId() == null) {
221 setSubRequestId(response.getRequestReferences().getRequestId());
226 * Gets the request state of a response.
228 * @param response response from which to get the state
229 * @return the request state of the response, or {@code null} if it does not exist
231 protected String getRequestState(SoResponse response) {
232 SoRequest request = response.getRequest();
233 if (request == null) {
237 SoRequestStatus status = request.getRequestStatus();
238 if (status == null) {
242 return status.getRequestState();
246 * Treats everything as a success, so we always go into
247 * {@link #postProcessResponse(OperationOutcome, String, Response, SoResponse)}.
250 protected boolean isSuccess(Response rawResponse, SoResponse response) {
255 * Prepends the message with the http status code.
258 public OperationOutcome setOutcome(OperationOutcome outcome, PolicyResult result, Response rawResponse,
259 SoResponse response) {
261 // set default result and message
262 setOutcome(outcome, result);
264 int code = (result == PolicyResult.FAILURE_TIMEOUT ? SO_RESPONSE_CODE : rawResponse.getStatus());
266 outcome.setResponse(response);
267 outcome.setMessage(code + " " + outcome.getMessage());
271 protected SoModelInfo prepareSoModelInfo() {
272 Target target = params.getTarget();
273 if (target == null) {
274 throw new IllegalArgumentException("missing Target");
277 if (target.getModelCustomizationId() == null || target.getModelInvariantId() == null
278 || target.getModelName() == null || target.getModelVersion() == null
279 || target.getModelVersionId() == null) {
280 throw new IllegalArgumentException("missing VF Module model");
283 SoModelInfo soModelInfo = new SoModelInfo();
284 soModelInfo.setModelCustomizationId(target.getModelCustomizationId());
285 soModelInfo.setModelInvariantId(target.getModelInvariantId());
286 soModelInfo.setModelName(target.getModelName());
287 soModelInfo.setModelVersion(target.getModelVersion());
288 soModelInfo.setModelVersionId(target.getModelVersionId());
289 soModelInfo.setModelType("vfModule");
294 * Construct requestInfo for the SO requestDetails.
296 * @return SO request information
298 protected SoRequestInfo constructRequestInfo() {
299 SoRequestInfo soRequestInfo = new SoRequestInfo();
300 soRequestInfo.setSource("POLICY");
301 soRequestInfo.setSuppressRollback(false);
302 soRequestInfo.setRequestorId("policy");
303 return soRequestInfo;
307 * Builds the request parameters from the policy payload.
309 protected Optional<SoRequestParameters> buildRequestParameters() {
310 if (params.getPayload() == null) {
311 return Optional.empty();
314 Object data = params.getPayload().get(REQ_PARAM_NM);
316 return Optional.empty();
320 return Optional.of(coder.decode(data.toString(), SoRequestParameters.class));
321 } catch (CoderException e) {
322 throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM);
327 * Builds the configuration parameters from the policy payload.
329 protected Optional<List<Map<String, String>>> buildConfigurationParameters() {
330 if (params.getPayload() == null) {
331 return Optional.empty();
334 Object data = params.getPayload().get(CONFIG_PARAM_NM);
336 return Optional.empty();
340 @SuppressWarnings("unchecked")
341 List<Map<String, String>> result = coder.decode(data.toString(), ArrayList.class);
342 return Optional.of(result);
343 } catch (CoderException | RuntimeException e) {
344 throw new IllegalArgumentException("invalid payload value: " + CONFIG_PARAM_NM);
349 * Construct cloudConfiguration for the SO requestDetails. Overridden for custom
352 * @param tenantItem tenant item from A&AI named-query response
353 * @return SO cloud configuration
355 protected SoCloudConfiguration constructCloudConfigurationCq(Tenant tenantItem, CloudRegion cloudRegionItem) {
356 SoCloudConfiguration cloudConfiguration = new SoCloudConfiguration();
357 cloudConfiguration.setTenantId(tenantItem.getTenantId());
358 cloudConfiguration.setLcpCloudRegionId(cloudRegionItem.getCloudRegionId());
359 return cloudConfiguration;
363 * Create simple HTTP headers for unauthenticated requests to SO.
365 * @return the HTTP headers
367 protected Map<String, Object> createSimpleHeaders() {
368 Map<String, Object> headers = new HashMap<>();
369 headers.put("Accept", MediaType.APPLICATION_JSON);
374 * These methods extract data from the Custom Query and throw an
375 * IllegalArgumentException if the desired data item is not found.
378 protected GenericVnf getVnfItem(AaiCqResponse aaiCqResponse, SoModelInfo soModelInfo) {
379 GenericVnf vnf = aaiCqResponse.getGenericVnfByVfModuleModelInvariantId(soModelInfo.getModelInvariantId());
381 throw new IllegalArgumentException("missing generic VNF");
387 protected ServiceInstance getServiceInstance(AaiCqResponse aaiCqResponse) {
388 ServiceInstance vnfService = aaiCqResponse.getServiceInstance();
389 if (vnfService == null) {
390 throw new IllegalArgumentException("missing VNF Service Item");
396 protected Tenant getDefaultTenant(AaiCqResponse aaiCqResponse) {
397 Tenant tenant = aaiCqResponse.getDefaultTenant();
398 if (tenant == null) {
399 throw new IllegalArgumentException("missing Tenant Item");
405 protected CloudRegion getDefaultCloudRegion(AaiCqResponse aaiCqResponse) {
406 CloudRegion cloudRegion = aaiCqResponse.getDefaultCloudRegion();
407 if (cloudRegion == null) {
408 throw new IllegalArgumentException("missing Cloud Region");
414 // these may be overridden by junit tests
417 protected Coder getCoder() {
421 private static class SoCoder extends StandardCoder {
424 * Gson object used to encode and decode messages.
426 private static final Gson SO_GSON;
429 * Gson object used to encode messages in "pretty" format.
431 private static final Gson SO_GSON_PRETTY;
434 GsonBuilder builder = GsonMessageBodyHandler
435 .configBuilder(new GsonBuilder().registerTypeAdapter(StandardCoderObject.class,
436 new StandardTypeAdapter()))
437 .registerTypeAdapter(LocalDateTime.class, new SoLocalDateTimeTypeAdapter());
439 SO_GSON = builder.create();
440 SO_GSON_PRETTY = builder.setPrettyPrinting().create();
444 super(SO_GSON, SO_GSON_PRETTY);