824c4340e0e37bda9fda255c26c11bf999074ea8
[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  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.controlloop.actor.so;
23
24 import com.google.gson.Gson;
25 import com.google.gson.GsonBuilder;
26 import java.time.LocalDateTime;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Optional;
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.ModelVer;
37 import org.onap.aai.domain.yang.ServiceInstance;
38 import org.onap.aai.domain.yang.Tenant;
39 import org.onap.policy.common.gson.GsonMessageBodyHandler;
40 import org.onap.policy.common.utils.coder.Coder;
41 import org.onap.policy.common.utils.coder.CoderException;
42 import org.onap.policy.common.utils.coder.StandardCoder;
43 import org.onap.policy.common.utils.coder.StandardCoderObject;
44 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
45 import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
46 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
47 import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
48 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
49 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpPollingConfig;
50 import org.onap.policy.so.SoCloudConfiguration;
51 import org.onap.policy.so.SoModelInfo;
52 import org.onap.policy.so.SoRequest;
53 import org.onap.policy.so.SoRequestInfo;
54 import org.onap.policy.so.SoRequestParameters;
55 import org.onap.policy.so.SoRequestStatus;
56 import org.onap.policy.so.SoResponse;
57 import org.onap.policy.so.util.SoLocalDateTimeTypeAdapter;
58
59 /**
60  * Superclass for SDNC Operators. Note: subclasses should invoke {@link #resetPollCount()}
61  * each time they issue an HTTP request.
62  */
63 public abstract class SoOperation extends HttpOperation<SoResponse> {
64     private static final Coder coder = new SoCoder();
65
66     public static final String FAILED = "FAILED";
67     public static final String COMPLETE = "COMPLETE";
68     public static final int SO_RESPONSE_CODE = 999;
69
70     // fields within the policy payload
71     public static final String REQ_PARAM_NM = "requestParameters";
72     public static final String CONFIG_PARAM_NM = "configurationParameters";
73
74     /* Values extracted from the parameter Target. These fields are required by any
75        subclasses that make use of prepareSoModelInfo().
76     */
77     private final String modelCustomizationId;
78     private final String modelInvariantId;
79     private final String modelVersionId;
80     private final String modelName;
81     private final String modelVersion;
82
83
84
85     /**
86      * Constructs the object.
87      *
88      * @param params operation parameters
89      * @param config configuration for this operation
90      * @param propertyNames names of properties required by this operation
91      */
92     protected SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List<String> propertyNames) {
93         super(params, config, SoResponse.class, propertyNames);
94
95         this.modelCustomizationId = null;
96         this.modelInvariantId = null;
97         this.modelVersionId = null;
98         this.modelVersion = null;
99         this.modelName = null;
100
101         verifyNotNull("Target information", params.getTargetType());
102     }
103
104     /**
105      * Constructs the object.
106      *
107      * @param params operation parameters
108      * @param config configuration for this operation
109      * @param propertyNames names of properties required by this operation
110      * @param targetEntityIds Target Entity information
111      */
112     protected SoOperation(ControlLoopOperationParams params, HttpPollingConfig config, List<String> propertyNames,
113                        Map<String, String> targetEntityIds) {
114         super(params, config, SoResponse.class, propertyNames);
115
116         verifyNotNull("Target entity Ids information", targetEntityIds);
117
118         this.modelCustomizationId = targetEntityIds
119                 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_CUSTOMIZATION_ID);
120         this.modelInvariantId = targetEntityIds
121                 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_INVARIANT_ID);
122         this.modelVersionId = targetEntityIds
123                 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION_ID);
124         this.modelVersion = targetEntityIds
125                 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION);
126         this.modelName = targetEntityIds
127                 .get(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_NAME);
128
129         verifyNotNull("Target information", params.getTargetType());
130     }
131
132     @Override
133     protected void resetPollCount() {
134         super.resetPollCount();
135         setSubRequestId(null);
136     }
137
138     /**
139      * Validates that the parameters contain the required target information to construct
140      * the request.
141      */
142     protected void validateTarget() {
143         verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_CUSTOMIZATION_ID, modelCustomizationId);
144         verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_INVARIANT_ID, modelInvariantId);
145         verifyNotNull(ControlLoopOperationParams.PARAMS_ENTITY_MODEL_VERSION_ID, modelVersionId);
146     }
147
148     private void verifyNotNull(String type, Object value) {
149         if (value == null) {
150             throw new IllegalArgumentException("missing Target." + type);
151         }
152     }
153
154     protected int getVfCount() {
155         return getRequiredProperty(OperationProperties.DATA_VF_COUNT, "VF Count");
156     }
157
158     protected void setVfCount(int vfCount) {
159         setProperty(OperationProperties.DATA_VF_COUNT, vfCount);
160     }
161
162     @Override
163     protected Status detmStatus(Response rawResponse, SoResponse response) {
164         if (rawResponse.getStatus() == 200) {
165             String requestState = getRequestState(response);
166             if (COMPLETE.equalsIgnoreCase(requestState)) {
167                 extractSubRequestId(response);
168                 return Status.SUCCESS;
169             }
170
171             if (FAILED.equalsIgnoreCase(requestState)) {
172                 extractSubRequestId(response);
173                 return Status.FAILURE;
174             }
175         }
176
177         // still incomplete
178
179         // need a request ID with which to query
180         if (getSubRequestId() == null && !extractSubRequestId(response)) {
181             throw new IllegalArgumentException("missing request ID in response");
182         }
183
184         return Status.STILL_WAITING;
185     }
186
187     @Override
188     protected String getPollingPath() {
189         return super.getPollingPath() + getSubRequestId();
190     }
191
192     @Override
193     public void generateSubRequestId(int attempt) {
194         setSubRequestId(null);
195     }
196
197     private boolean extractSubRequestId(SoResponse response) {
198         if (response == null || response.getRequestReferences() == null
199                         || response.getRequestReferences().getRequestId() == null) {
200             return false;
201         }
202
203         setSubRequestId(response.getRequestReferences().getRequestId());
204         return true;
205     }
206
207     /**
208      * Gets the request state of a response.
209      *
210      * @param response response from which to get the state
211      * @return the request state of the response, or {@code null} if it does not exist
212      */
213     protected String getRequestState(SoResponse response) {
214         SoRequest request = response.getRequest();
215         if (request == null) {
216             return null;
217         }
218
219         SoRequestStatus status = request.getRequestStatus();
220         if (status == null) {
221             return null;
222         }
223
224         return status.getRequestState();
225     }
226
227     /**
228      * Treats everything as a success, so we always go into
229      * {@link #postProcessResponse(OperationOutcome, String, Response, SoResponse)}.
230      */
231     @Override
232     protected boolean isSuccess(Response rawResponse, SoResponse response) {
233         return true;
234     }
235
236     /**
237      * Prepends the message with the http status code.
238      */
239     @Override
240     public OperationOutcome setOutcome(OperationOutcome outcome, OperationResult result, Response rawResponse,
241                     SoResponse response) {
242
243         // set default result and message
244         setOutcome(outcome, result);
245
246         int code = (result == OperationResult.FAILURE_TIMEOUT ? SO_RESPONSE_CODE : rawResponse.getStatus());
247
248         outcome.setResponse(response);
249         outcome.setMessage(code + " " + outcome.getMessage());
250         return outcome;
251     }
252
253     protected SoModelInfo prepareSoModelInfo() {
254         var soModelInfo = new SoModelInfo();
255         soModelInfo.setModelCustomizationId(modelCustomizationId);
256         soModelInfo.setModelInvariantId(modelInvariantId);
257         soModelInfo.setModelName(modelName);
258         soModelInfo.setModelVersion(modelVersion);
259         soModelInfo.setModelVersionId(modelVersionId);
260         soModelInfo.setModelType("vfModule");
261         return soModelInfo;
262     }
263
264     /**
265      * Construct requestInfo for the SO requestDetails.
266      *
267      * @return SO request information
268      */
269     protected SoRequestInfo constructRequestInfo() {
270         var soRequestInfo = new SoRequestInfo();
271         soRequestInfo.setSource("POLICY");
272         soRequestInfo.setSuppressRollback(false);
273         soRequestInfo.setRequestorId("policy");
274         return soRequestInfo;
275     }
276
277     /**
278      * Builds the request parameters from the policy payload.
279      */
280     protected Optional<SoRequestParameters> buildRequestParameters() {
281         if (params.getPayload() == null) {
282             return Optional.empty();
283         }
284
285         Object data = params.getPayload().get(REQ_PARAM_NM);
286         if (data == null) {
287             return Optional.empty();
288         }
289
290         try {
291             return Optional.of(coder.decode(data.toString(), SoRequestParameters.class));
292         } catch (CoderException e) {
293             throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM);
294         }
295     }
296
297     /**
298      * Builds the configuration parameters from the policy payload.
299      */
300     protected Optional<List<Map<String, String>>> buildConfigurationParameters() {
301         if (params.getPayload() == null) {
302             return Optional.empty();
303         }
304
305         Object data = params.getPayload().get(CONFIG_PARAM_NM);
306         if (data == null) {
307             return Optional.empty();
308         }
309
310         try {
311             @SuppressWarnings("unchecked")
312             List<Map<String, String>> result = coder.decode(data.toString(), ArrayList.class);
313             return Optional.of(result);
314         } catch (CoderException | RuntimeException e) {
315             throw new IllegalArgumentException("invalid payload value: " + CONFIG_PARAM_NM);
316         }
317     }
318
319     /**
320      * Construct cloudConfiguration for the SO requestDetails.
321      *
322      * @param tenantItem tenant item from A&AI named-query response
323      * @return SO cloud configuration
324      */
325     protected SoCloudConfiguration constructCloudConfiguration(Tenant tenantItem, CloudRegion cloudRegionItem) {
326         var cloudConfiguration = new SoCloudConfiguration();
327         cloudConfiguration.setTenantId(getRequiredText("tenant ID", tenantItem.getTenantId()));
328         cloudConfiguration.setLcpCloudRegionId(getRequiredText("cloud region ID", cloudRegionItem.getCloudRegionId()));
329         return cloudConfiguration;
330     }
331
332     /**
333      * Verifies that a value is not {@code null}.
334      *
335      * @param name value name
336      * @param value value to check
337      * @return the value
338      */
339     protected String getRequiredText(String name, String value) {
340         if (value == null) {
341             throw new IllegalArgumentException("missing " + name);
342         }
343
344         return value;
345     }
346
347     /**
348      * Create simple HTTP headers for unauthenticated requests to SO.
349      *
350      * @return the HTTP headers
351      */
352     protected Map<String, Object> createSimpleHeaders() {
353         Map<String, Object> headers = new HashMap<>();
354         headers.put("Accept", MediaType.APPLICATION_JSON);
355         return headers;
356     }
357
358     /*
359      * These methods extract data from the Custom Query and throw an
360      * IllegalArgumentException if the desired data item is not found.
361      */
362
363     protected GenericVnf getVnfItem() {
364         return getRequiredProperty(OperationProperties.AAI_VNF, "generic VNF");
365     }
366
367     protected ServiceInstance getServiceInstance() {
368         return getRequiredProperty(OperationProperties.AAI_SERVICE, "VNF Service Item");
369     }
370
371     protected Tenant getDefaultTenant() {
372         return getRequiredProperty(OperationProperties.AAI_DEFAULT_TENANT, "Default Tenant Item");
373     }
374
375     protected CloudRegion getDefaultCloudRegion() {
376         return getRequiredProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, "Default Cloud Region");
377     }
378
379     protected ModelVer getVnfModel() {
380         return getRequiredProperty(OperationProperties.AAI_VNF_MODEL, "generic VNF Model");
381     }
382
383     protected ModelVer getServiceModel() {
384         return getRequiredProperty(OperationProperties.AAI_SERVICE_MODEL, "Service Model");
385     }
386
387     // these may be overridden by junit tests
388
389     @Override
390     protected Coder getCoder() {
391         return coder;
392     }
393
394     private static class SoCoder extends StandardCoder {
395
396         /**
397          * Gson object used to encode and decode messages.
398          */
399         private static final Gson SO_GSON;
400
401         /**
402          * Gson object used to encode messages in "pretty" format.
403          */
404         private static final Gson SO_GSON_PRETTY;
405
406         static {
407             GsonBuilder builder = GsonMessageBodyHandler
408                             .configBuilder(new GsonBuilder().registerTypeAdapter(StandardCoderObject.class,
409                                             new StandardTypeAdapter()))
410                             .registerTypeAdapter(LocalDateTime.class, new SoLocalDateTimeTypeAdapter());
411
412             SO_GSON = builder.create();
413             SO_GSON_PRETTY = builder.setPrettyPrinting().create();
414         }
415
416         public SoCoder() {
417             super(SO_GSON, SO_GSON_PRETTY);
418         }
419     }
420 }