Java 17 Upgrade
[policy/models.git] / models-interactions / model-actors / actor.so / src / main / java / org / onap / policy / controlloop / actor / so / SoOperation.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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=========================================================
21  */
22
23 package org.onap.policy.controlloop.actor.so;
24
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;
33 import java.util.Map;
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;
59
60 /**
61  * Superclass for SDNC Operators. Note: subclasses should invoke {@link #resetPollCount()}
62  * each time they issue an HTTP request.
63  */
64 public abstract class SoOperation extends HttpOperation<SoResponse> {
65     private static final Coder coder = new SoCoder();
66
67     public static final String FAILED = "FAILED";
68     public static final String COMPLETE = "COMPLETE";
69     public static final int SO_RESPONSE_CODE = 999;
70
71     // fields within the policy payload
72     public static final String REQ_PARAM_NM = "requestParameters";
73     public static final String CONFIG_PARAM_NM = "configurationParameters";
74
75     /* Values extracted from the parameter Target. These fields are required by any
76        subclasses that make use of prepareSoModelInfo().
77     */
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;
83
84
85
86     /**
87      * Constructs the object.
88      *
89      * @param params operation parameters
90      * @param config configuration for this operation
91      * @param propertyNames names of properties required by this operation
92      */
93     protected SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List<String> propertyNames) {
94         super(params, config, SoResponse.class, propertyNames);
95
96         this.modelCustomizationId = null;
97         this.modelInvariantId = null;
98         this.modelVersionId = null;
99         this.modelVersion = null;
100         this.modelName = null;
101
102         verifyNotNull("Target information", params.getTargetType());
103     }
104
105     /**
106      * Constructs the object.
107      *
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
112      */
113     protected SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List<String> propertyNames,
114                        Map<String, String> targetEntityIds) {
115         super(params, config, SoResponse.class, propertyNames);
116
117         verifyNotNull("Target entity Ids information", targetEntityIds);
118
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);
129
130         verifyNotNull("Target information", params.getTargetType());
131     }
132
133     @Override
134     protected void resetPollCount() {
135         super.resetPollCount();
136         setSubRequestId(null);
137     }
138
139     /**
140      * Validates that the parameters contain the required target information to construct
141      * the request.
142      */
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);
147     }
148
149     private void verifyNotNull(String type, Object value) {
150         if (value == null) {
151             throw new IllegalArgumentException("missing Target." + type);
152         }
153     }
154
155     protected int getVfCount() {
156         return getRequiredProperty(OperationProperties.DATA_VF_COUNT, "VF Count");
157     }
158
159     protected void setVfCount(int vfCount) {
160         setProperty(OperationProperties.DATA_VF_COUNT, vfCount);
161     }
162
163     @Override
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;
170             }
171
172             if (FAILED.equalsIgnoreCase(requestState)) {
173                 extractSubRequestId(response);
174                 return Status.FAILURE;
175             }
176         }
177
178         // still incomplete
179
180         // need a request ID with which to query
181         if (getSubRequestId() == null && !extractSubRequestId(response)) {
182             throw new IllegalArgumentException("missing request ID in response");
183         }
184
185         return Status.STILL_WAITING;
186     }
187
188     @Override
189     protected String getPollingPath() {
190         return super.getPollingPath() + getSubRequestId();
191     }
192
193     @Override
194     public void generateSubRequestId(int attempt) {
195         setSubRequestId(null);
196     }
197
198     private boolean extractSubRequestId(SoResponse response) {
199         if (response == null || response.getRequestReferences() == null
200                         || response.getRequestReferences().getRequestId() == null) {
201             return false;
202         }
203
204         setSubRequestId(response.getRequestReferences().getRequestId());
205         return true;
206     }
207
208     /**
209      * Gets the request state of a response.
210      *
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
213      */
214     protected String getRequestState(SoResponse response) {
215         SoRequest request = response.getRequest();
216         if (request == null) {
217             return null;
218         }
219
220         SoRequestStatus status = request.getRequestStatus();
221         if (status == null) {
222             return null;
223         }
224
225         return status.getRequestState();
226     }
227
228     /**
229      * Treats everything as a success, so we always go into
230      * {@link #postProcessResponse(OperationOutcome, String, Response, SoResponse)}.
231      */
232     @Override
233     protected boolean isSuccess(Response rawResponse, SoResponse response) {
234         return true;
235     }
236
237     /**
238      * Prepends the message with the http status code.
239      */
240     @Override
241     public OperationOutcome setOutcome(OperationOutcome outcome, OperationResult result, Response rawResponse,
242                     SoResponse response) {
243
244         // set default result and message
245         setOutcome(outcome, result);
246
247         int code = (result == OperationResult.FAILURE_TIMEOUT ? SO_RESPONSE_CODE : rawResponse.getStatus());
248
249         outcome.setResponse(response);
250         outcome.setMessage(code + " " + outcome.getMessage());
251         return outcome;
252     }
253
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");
262         return soModelInfo;
263     }
264
265     /**
266      * Construct requestInfo for the SO requestDetails.
267      *
268      * @return SO request information
269      */
270     protected SoRequestInfo constructRequestInfo() {
271         var soRequestInfo = new SoRequestInfo();
272         soRequestInfo.setSource("POLICY");
273         soRequestInfo.setSuppressRollback(false);
274         soRequestInfo.setRequestorId("policy");
275         return soRequestInfo;
276     }
277
278     /**
279      * Builds the request parameters from the policy payload.
280      */
281     protected Optional<SoRequestParameters> buildRequestParameters() {
282         if (params.getPayload() == null) {
283             return Optional.empty();
284         }
285
286         Object data = params.getPayload().get(REQ_PARAM_NM);
287         if (data == null) {
288             return Optional.empty();
289         }
290
291         try {
292             return Optional.of(coder.decode(data.toString(), SoRequestParameters.class));
293         } catch (CoderException e) {
294             throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM);
295         }
296     }
297
298     /**
299      * Builds the configuration parameters from the policy payload.
300      */
301     protected Optional<List<Map<String, String>>> buildConfigurationParameters() {
302         if (params.getPayload() == null) {
303             return Optional.empty();
304         }
305
306         Object data = params.getPayload().get(CONFIG_PARAM_NM);
307         if (data == null) {
308             return Optional.empty();
309         }
310
311         try {
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);
317         }
318     }
319
320     /**
321      * Construct cloudConfiguration for the SO requestDetails.
322      *
323      * @param tenantItem tenant item from A&AI named-query response
324      * @return SO cloud configuration
325      */
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;
331     }
332
333     /**
334      * Verifies that a value is not {@code null}.
335      *
336      * @param name value name
337      * @param value value to check
338      * @return the value
339      */
340     protected String getRequiredText(String name, String value) {
341         if (value == null) {
342             throw new IllegalArgumentException("missing " + name);
343         }
344
345         return value;
346     }
347
348     /**
349      * Create simple HTTP headers for unauthenticated requests to SO.
350      *
351      * @return the HTTP headers
352      */
353     protected Map<String, Object> createSimpleHeaders() {
354         Map<String, Object> headers = new HashMap<>();
355         headers.put("Accept", MediaType.APPLICATION_JSON);
356         return headers;
357     }
358
359     /*
360      * These methods extract data from the Custom Query and throw an
361      * IllegalArgumentException if the desired data item is not found.
362      */
363
364     protected GenericVnf getVnfItem() {
365         return getRequiredProperty(OperationProperties.AAI_VNF, "generic VNF");
366     }
367
368     protected ServiceInstance getServiceInstance() {
369         return getRequiredProperty(OperationProperties.AAI_SERVICE, "VNF Service Item");
370     }
371
372     protected Tenant getDefaultTenant() {
373         return getRequiredProperty(OperationProperties.AAI_DEFAULT_TENANT, "Default Tenant Item");
374     }
375
376     protected CloudRegion getDefaultCloudRegion() {
377         return getRequiredProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, "Default Cloud Region");
378     }
379
380     protected ModelVer getVnfModel() {
381         return getRequiredProperty(OperationProperties.AAI_VNF_MODEL, "generic VNF Model");
382     }
383
384     protected ModelVer getServiceModel() {
385         return getRequiredProperty(OperationProperties.AAI_SERVICE_MODEL, "Service Model");
386     }
387
388     // these may be overridden by junit tests
389
390     @Override
391     protected Coder getCoder() {
392         return coder;
393     }
394
395     private static class SoCoder extends StandardCoder {
396
397         /**
398          * Gson object used to encode and decode messages.
399          */
400         private static final Gson SO_GSON;
401
402         /**
403          * Gson object used to encode messages in "pretty" format.
404          */
405         private static final Gson SO_GSON_PRETTY;
406
407         static {
408             GsonBuilder builder = GsonMessageBodyHandler
409                             .configBuilder(new GsonBuilder().registerTypeAdapter(StandardCoderObject.class,
410                                             new StandardTypeAdapter()))
411                             .registerTypeAdapter(LocalDateTime.class, new SoLocalDateTimeTypeAdapter());
412
413             SO_GSON = builder.create();
414             SO_GSON_PRETTY = builder.setPrettyPrinting().create();
415         }
416
417         public SoCoder() {
418             super(SO_GSON, SO_GSON_PRETTY);
419         }
420     }
421 }