2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2020 Wipro Limited.
7 * Modifications Copyright (C) 2023 Nordix Foundation.
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.controlloop.actor.so;
25 import com.google.gson.Gson;
26 import com.google.gson.GsonBuilder;
27 import jakarta.ws.rs.core.MediaType;
28 import jakarta.ws.rs.core.Response;
29 import java.time.LocalDateTime;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
34 import java.util.Optional;
35 import org.onap.aai.domain.yang.CloudRegion;
36 import org.onap.aai.domain.yang.GenericVnf;
37 import org.onap.aai.domain.yang.ModelVer;
38 import org.onap.aai.domain.yang.ServiceInstance;
39 import org.onap.aai.domain.yang.Tenant;
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.OperationProperties;
47 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
48 import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
49 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
50 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpPollingConfig;
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 FAILED = "FAILED";
68 public static final String COMPLETE = "COMPLETE";
69 public static final int SO_RESPONSE_CODE = 999;
71 // fields within the policy payload
72 public static final String REQ_PARAM_NM = "requestParameters";
73 public static final String CONFIG_PARAM_NM = "configurationParameters";
75 /* Values extracted from the parameter Target. These fields are required by any
76 subclasses that make use of prepareSoModelInfo().
78 private final String modelCustomizationId;
79 private final String modelInvariantId;
80 private final String modelVersionId;
81 private final String modelName;
82 private final String modelVersion;
87 * Constructs the object.
89 * @param params operation parameters
90 * @param config configuration for this operation
91 * @param propertyNames names of properties required by this operation
93 protected SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List<String> propertyNames) {
94 super(params, config, SoResponse.class, propertyNames);
96 this.modelCustomizationId = null;
97 this.modelInvariantId = null;
98 this.modelVersionId = null;
99 this.modelVersion = null;
100 this.modelName = null;
102 verifyNotNull("Target information", params.getTargetType());
106 * Constructs the object.
108 * @param params operation parameters
109 * @param config configuration for this operation
110 * @param propertyNames names of properties required by this operation
111 * @param targetEntityIds Target Entity information
113 protected SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List<String> propertyNames,
114 Map<String, String> targetEntityIds) {
115 super(params, config, SoResponse.class, propertyNames);
117 verifyNotNull("Target entity Ids information", targetEntityIds);
119 this.modelCustomizationId = targetEntityIds
120 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_CUSTOMIZATION_ID);
121 this.modelInvariantId = targetEntityIds
122 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_INVARIANT_ID);
123 this.modelVersionId = targetEntityIds
124 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION_ID);
125 this.modelVersion = targetEntityIds
126 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION);
127 this.modelName = targetEntityIds
128 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_NAME);
130 verifyNotNull("Target information", params.getTargetType());
134 protected void resetPollCount() {
135 super.resetPollCount();
136 setSubRequestId(null);
140 * Validates that the parameters contain the required target information to construct
143 protected void validateTarget() {
144 verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_CUSTOMIZATION_ID, modelCustomizationId);
145 verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_INVARIANT_ID, modelInvariantId);
146 verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION_ID, modelVersionId);
149 private void verifyNotNull(String type, Object value) {
151 throw new IllegalArgumentException("missing Target." + type);
155 protected int getVfCount() {
156 return getRequiredProperty(OperationProperties.DATA_VF_COUNT, "VF Count");
159 protected void setVfCount(int vfCount) {
160 setProperty(OperationProperties.DATA_VF_COUNT, vfCount);
164 protected Status detmStatus(Response rawResponse, SoResponse response) {
165 if (rawResponse.getStatus() == 200) {
166 String requestState = getRequestState(response);
167 if (COMPLETE.equalsIgnoreCase(requestState)) {
168 extractSubRequestId(response);
169 return Status.SUCCESS;
172 if (FAILED.equalsIgnoreCase(requestState)) {
173 extractSubRequestId(response);
174 return Status.FAILURE;
180 // need a request ID with which to query
181 if (getSubRequestId() == null && !extractSubRequestId(response)) {
182 throw new IllegalArgumentException("missing request ID in response");
185 return Status.STILL_WAITING;
189 protected String getPollingPath() {
190 return super.getPollingPath() + getSubRequestId();
194 public void generateSubRequestId(int attempt) {
195 setSubRequestId(null);
198 private boolean extractSubRequestId(SoResponse response) {
199 if (response == null || response.getRequestReferences() == null
200 || response.getRequestReferences().getRequestId() == null) {
204 setSubRequestId(response.getRequestReferences().getRequestId());
209 * Gets the request state of a response.
211 * @param response response from which to get the state
212 * @return the request state of the response, or {@code null} if it does not exist
214 protected String getRequestState(SoResponse response) {
215 SoRequest request = response.getRequest();
216 if (request == null) {
220 SoRequestStatus status = request.getRequestStatus();
221 if (status == null) {
225 return status.getRequestState();
229 * Treats everything as a success, so we always go into
230 * {@link #postProcessResponse(OperationOutcome, String, Response, SoResponse)}.
233 protected boolean isSuccess(Response rawResponse, SoResponse response) {
238 * Prepends the message with the http status code.
241 public OperationOutcome setOutcome(OperationOutcome outcome, OperationResult result, Response rawResponse,
242 SoResponse response) {
244 // set default result and message
245 setOutcome(outcome, result);
247 int code = (result == OperationResult.FAILURE_TIMEOUT ? SO_RESPONSE_CODE : rawResponse.getStatus());
249 outcome.setResponse(response);
250 outcome.setMessage(code + " " + outcome.getMessage());
254 protected SoModelInfo prepareSoModelInfo() {
255 var soModelInfo = new SoModelInfo();
256 soModelInfo.setModelCustomizationId(modelCustomizationId);
257 soModelInfo.setModelInvariantId(modelInvariantId);
258 soModelInfo.setModelName(modelName);
259 soModelInfo.setModelVersion(modelVersion);
260 soModelInfo.setModelVersionId(modelVersionId);
261 soModelInfo.setModelType("vfModule");
266 * Construct requestInfo for the SO requestDetails.
268 * @return SO request information
270 protected SoRequestInfo constructRequestInfo() {
271 var soRequestInfo = new SoRequestInfo();
272 soRequestInfo.setSource("POLICY");
273 soRequestInfo.setSuppressRollback(false);
274 soRequestInfo.setRequestorId("policy");
275 return soRequestInfo;
279 * Builds the request parameters from the policy payload.
281 protected Optional<SoRequestParameters> buildRequestParameters() {
282 if (params.getPayload() == null) {
283 return Optional.empty();
286 Object data = params.getPayload().get(REQ_PARAM_NM);
288 return Optional.empty();
292 return Optional.of(coder.decode(data.toString(), SoRequestParameters.class));
293 } catch (CoderException e) {
294 throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM);
299 * Builds the configuration parameters from the policy payload.
301 protected Optional<List<Map<String, String>>> buildConfigurationParameters() {
302 if (params.getPayload() == null) {
303 return Optional.empty();
306 Object data = params.getPayload().get(CONFIG_PARAM_NM);
308 return Optional.empty();
312 @SuppressWarnings("unchecked")
313 List<Map<String, String>> result = coder.decode(data.toString(), ArrayList.class);
314 return Optional.of(result);
315 } catch (CoderException | RuntimeException e) {
316 throw new IllegalArgumentException("invalid payload value: " + CONFIG_PARAM_NM);
321 * Construct cloudConfiguration for the SO requestDetails.
323 * @param tenantItem tenant item from A&AI named-query response
324 * @return SO cloud configuration
326 protected SoCloudConfiguration constructCloudConfiguration(Tenant tenantItem, CloudRegion cloudRegionItem) {
327 var cloudConfiguration = new SoCloudConfiguration();
328 cloudConfiguration.setTenantId(getRequiredText("tenant ID", tenantItem.getTenantId()));
329 cloudConfiguration.setLcpCloudRegionId(getRequiredText("cloud region ID", cloudRegionItem.getCloudRegionId()));
330 return cloudConfiguration;
334 * Verifies that a value is not {@code null}.
336 * @param name value name
337 * @param value value to check
340 protected String getRequiredText(String name, String value) {
342 throw new IllegalArgumentException("missing " + name);
349 * Create simple HTTP headers for unauthenticated requests to SO.
351 * @return the HTTP headers
353 protected Map<String, Object> createSimpleHeaders() {
354 Map<String, Object> headers = new HashMap<>();
355 headers.put("Accept", MediaType.APPLICATION_JSON);
360 * These methods extract data from the Custom Query and throw an
361 * IllegalArgumentException if the desired data item is not found.
364 protected GenericVnf getVnfItem() {
365 return getRequiredProperty(OperationProperties.AAI_VNF, "generic VNF");
368 protected ServiceInstance getServiceInstance() {
369 return getRequiredProperty(OperationProperties.AAI_SERVICE, "VNF Service Item");
372 protected Tenant getDefaultTenant() {
373 return getRequiredProperty(OperationProperties.AAI_DEFAULT_TENANT, "Default Tenant Item");
376 protected CloudRegion getDefaultCloudRegion() {
377 return getRequiredProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, "Default Cloud Region");
380 protected ModelVer getVnfModel() {
381 return getRequiredProperty(OperationProperties.AAI_VNF_MODEL, "generic VNF Model");
384 protected ModelVer getServiceModel() {
385 return getRequiredProperty(OperationProperties.AAI_SERVICE_MODEL, "Service Model");
388 // these may be overridden by junit tests
391 protected Coder getCoder() {
395 private static class SoCoder extends StandardCoder {
398 * Gson object used to encode and decode messages.
400 private static final Gson SO_GSON;
403 * Gson object used to encode messages in "pretty" format.
405 private static final Gson SO_GSON_PRETTY;
408 GsonBuilder builder = GsonMessageBodyHandler
409 .configBuilder(new GsonBuilder().registerTypeAdapter(StandardCoderObject.class,
410 new StandardTypeAdapter()))
411 .registerTypeAdapter(LocalDateTime.class, new SoLocalDateTimeTypeAdapter());
413 SO_GSON = builder.create();
414 SO_GSON_PRETTY = builder.setPrettyPrinting().create();
418 super(SO_GSON, SO_GSON_PRETTY);