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 java.util.ArrayList;
24 import java.util.List;
26 import java.util.concurrent.CompletableFuture;
27 import java.util.concurrent.TimeUnit;
28 import java.util.function.Function;
29 import javax.ws.rs.core.Response;
31 import org.onap.aai.domain.yang.CloudRegion;
32 import org.onap.aai.domain.yang.GenericVnf;
33 import org.onap.aai.domain.yang.ServiceInstance;
34 import org.onap.aai.domain.yang.Tenant;
35 import org.onap.policy.aai.AaiCqResponse;
36 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
37 import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
38 import org.onap.policy.common.utils.coder.Coder;
39 import org.onap.policy.common.utils.coder.CoderException;
40 import org.onap.policy.common.utils.coder.StandardCoder;
41 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
42 import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
43 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
44 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
45 import org.onap.policy.controlloop.policy.PolicyResult;
46 import org.onap.policy.controlloop.policy.Target;
47 import org.onap.policy.so.SoModelInfo;
48 import org.onap.policy.so.SoRequest;
49 import org.onap.policy.so.SoRequestInfo;
50 import org.onap.policy.so.SoRequestParameters;
51 import org.onap.policy.so.SoRequestStatus;
52 import org.onap.policy.so.SoResponse;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * Superclass for SDNC Operators. Note: subclasses should invoke {@link #resetGetCount()}
58 * each time they issue an HTTP request.
60 public abstract class SoOperation extends HttpOperation<SoResponse> {
61 private static final Logger logger = LoggerFactory.getLogger(SoOperation.class);
62 private static final Coder coder = new StandardCoder();
64 public static final String FAILED = "FAILED";
65 public static final String COMPLETE = "COMPLETE";
66 public static final int SO_RESPONSE_CODE = 999;
68 // fields within the policy payload
69 public static final String REQ_PARAM_NM = "requestParameters";
70 public static final String CONFIG_PARAM_NM = "configurationParameters";
72 private final SoConfig config;
75 * Number of "get" requests issued so far, on the current operation attempt.
82 * Constructs the object.
84 * @param params operation parameters
85 * @param config configuration for this operation
87 public SoOperation(ControlLoopOperationParams params, HttpConfig config) {
88 super(params, config, SoResponse.class);
89 this.config = (SoConfig) config;
93 * Subclasses should invoke this before issuing their first HTTP request.
95 protected void resetGetCount() {
103 protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
104 return startGuardAsync();
108 * If the response does not indicate that the request has been completed, then sleep a
109 * bit and issue a "get".
112 protected CompletableFuture<OperationOutcome> postProcessResponse(OperationOutcome outcome, String url,
113 Response rawResponse, SoResponse response) {
115 // see if the request has "completed", whether or not it was successful
116 if (rawResponse.getStatus() == 200) {
117 String requestState = getRequestState(response);
118 if (COMPLETE.equalsIgnoreCase(requestState)) {
119 return CompletableFuture
120 .completedFuture(setOutcome(outcome, PolicyResult.SUCCESS, rawResponse, response));
123 if (FAILED.equalsIgnoreCase(requestState)) {
124 return CompletableFuture
125 .completedFuture(setOutcome(outcome, PolicyResult.FAILURE, rawResponse, response));
131 // need a request ID with which to query
132 if (response.getRequestReferences() == null || response.getRequestReferences().getRequestId() == null) {
133 throw new IllegalArgumentException("missing request ID in response");
136 // see if the limit for the number of "gets" has been reached
137 if (getCount++ >= getMaxGets()) {
138 logger.warn("{}: execeeded 'get' limit {} for {}", getFullName(), getMaxGets(), params.getRequestId());
139 setOutcome(outcome, PolicyResult.FAILURE_TIMEOUT);
140 outcome.setMessage(SO_RESPONSE_CODE + " " + outcome.getMessage());
141 return CompletableFuture.completedFuture(outcome);
144 // sleep and then perform a "get" operation
145 Function<Void, CompletableFuture<OperationOutcome>> doGet = unused -> issueGet(outcome, response);
146 return sleep(getWaitMsGet(), TimeUnit.MILLISECONDS).thenComposeAsync(doGet);
150 * Issues a "get" request to see if the original request is complete yet.
152 * @param outcome outcome to be populated with the response
153 * @param response previous response
154 * @return a future that can be used to cancel the "get" request or await its response
156 private CompletableFuture<OperationOutcome> issueGet(OperationOutcome outcome, SoResponse response) {
157 String path = getPathGet() + response.getRequestReferences().getRequestId();
158 String url = getClient().getBaseUrl() + path;
160 logger.debug("{}: 'get' count {} for {}", getFullName(), getCount, params.getRequestId());
162 logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
164 // TODO should this use "path" or the full "url"?
165 return handleResponse(outcome, url, callback -> getClient().get(callback, path, null));
169 * Gets the request state of a response.
171 * @param response response from which to get the state
172 * @return the request state of the response, or {@code null} if it does not exist
174 protected String getRequestState(SoResponse response) {
175 SoRequest request = response.getRequest();
176 if (request == null) {
180 SoRequestStatus status = request.getRequestStatus();
181 if (status == null) {
185 return status.getRequestState();
189 * Treats everything as a success, so we always go into
190 * {@link #postProcessResponse(OperationOutcome, String, Response, SoResponse)}.
193 protected boolean isSuccess(Response rawResponse, SoResponse response) {
198 * Prepends the message with the http status code.
201 public OperationOutcome setOutcome(OperationOutcome outcome, PolicyResult result, Response rawResponse,
202 SoResponse response) {
204 // set default result and message
205 setOutcome(outcome, result);
207 outcome.setMessage(rawResponse.getStatus() + " " + outcome.getMessage());
211 protected SoModelInfo prepareSoModelInfo() {
212 Target target = params.getTarget();
213 if (target == null) {
214 throw new IllegalArgumentException("missing Target");
217 if (target.getModelCustomizationId() == null || target.getModelInvariantId() == null
218 || target.getModelName() == null || target.getModelVersion() == null
219 || target.getModelVersionId() == null) {
220 throw new IllegalArgumentException("missing VF Module model");
223 SoModelInfo soModelInfo = new SoModelInfo();
224 soModelInfo.setModelCustomizationId(target.getModelCustomizationId());
225 soModelInfo.setModelInvariantId(target.getModelInvariantId());
226 soModelInfo.setModelName(target.getModelName());
227 soModelInfo.setModelVersion(target.getModelVersion());
228 soModelInfo.setModelVersionId(target.getModelVersionId());
229 soModelInfo.setModelType("vfModule");
234 * Construct requestInfo for the SO requestDetails.
236 * @return SO request information
238 protected SoRequestInfo constructRequestInfo() {
239 SoRequestInfo soRequestInfo = new SoRequestInfo();
240 soRequestInfo.setSource("POLICY");
241 soRequestInfo.setSuppressRollback(false);
242 soRequestInfo.setRequestorId("policy");
243 return soRequestInfo;
247 * Builds the request parameters from the policy payload.
249 protected SoRequestParameters buildRequestParameters() {
250 if (params.getPayload() == null) {
254 String json = params.getPayload().get(REQ_PARAM_NM);
260 return coder.decode(json, SoRequestParameters.class);
261 } catch (CoderException e) {
262 throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM);
267 * Builds the configuration parameters from the policy payload.
269 protected List<Map<String, String>> buildConfigurationParameters() {
270 if (params.getPayload() == null) {
274 String json = params.getPayload().get(CONFIG_PARAM_NM);
280 @SuppressWarnings("unchecked")
281 List<Map<String, String>> result = coder.decode(json, ArrayList.class);
283 } catch (CoderException | RuntimeException e) {
284 throw new IllegalArgumentException("invalid payload value: " + CONFIG_PARAM_NM);
289 * These methods extract data from the Custom Query and throw an
290 * IllegalArgumentException if the desired data item is not found.
293 protected GenericVnf getVnfItem(AaiCqResponse aaiCqResponse, SoModelInfo soModelInfo) {
294 GenericVnf vnf = aaiCqResponse.getGenericVnfByVfModuleModelInvariantId(soModelInfo.getModelInvariantId());
296 throw new IllegalArgumentException("missing generic VNF");
302 protected ServiceInstance getServiceInstance(AaiCqResponse aaiCqResponse) {
303 ServiceInstance vnfService = aaiCqResponse.getServiceInstance();
304 if (vnfService == null) {
305 throw new IllegalArgumentException("missing VNF Service Item");
311 protected Tenant getDefaultTenant(AaiCqResponse aaiCqResponse) {
312 Tenant tenant = aaiCqResponse.getDefaultTenant();
313 if (tenant == null) {
314 throw new IllegalArgumentException("missing Tenant Item");
320 protected CloudRegion getDefaultCloudRegion(AaiCqResponse aaiCqResponse) {
321 CloudRegion cloudRegion = aaiCqResponse.getDefaultCloudRegion();
322 if (cloudRegion == null) {
323 throw new IllegalArgumentException("missing Cloud Region");
329 // these may be overridden by junit tests
332 * Gets the wait time, in milliseconds, between "get" requests.
334 * @return the wait time, in milliseconds, between "get" requests
336 public long getWaitMsGet() {
337 return TimeUnit.MILLISECONDS.convert(getWaitSecGet(), TimeUnit.SECONDS);
340 public int getMaxGets() {
341 return config.getMaxGets();
344 public String getPathGet() {
345 return config.getPathGet();
348 public int getWaitSecGet() {
349 return config.getWaitSecGet();