NCMP : Handle non-existing and non-ready cm handles
[cps.git] / cps-ncmp-service / src / main / java / org / onap / cps / ncmp / api / impl / operations / DmiDataOperations.java
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2021-2023 Nordix Foundation
4  *  Modifications Copyright (C) 2022 Bell Canada
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21
22 package org.onap.cps.ncmp.api.impl.operations;
23
24 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING;
25 import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ;
26
27 import java.util.Collection;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.stream.Collectors;
32 import lombok.extern.slf4j.Slf4j;
33 import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
34 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
35 import org.onap.cps.ncmp.api.impl.executor.TaskExecutor;
36 import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder;
37 import org.onap.cps.ncmp.api.impl.utils.data.operation.ResourceDataOperationRequestUtils;
38 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
39 import org.onap.cps.ncmp.api.inventory.CmHandleState;
40 import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
41 import org.onap.cps.ncmp.api.models.DataOperationRequest;
42 import org.onap.cps.spi.exceptions.CpsException;
43 import org.onap.cps.utils.JsonObjectMapper;
44 import org.springframework.http.ResponseEntity;
45 import org.springframework.stereotype.Component;
46 import org.springframework.util.MultiValueMap;
47
48 /**
49  * Operations class for DMI data.
50  */
51 @Component
52 @Slf4j
53 public class DmiDataOperations extends DmiOperations {
54
55     private static final long DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS = 30000L;
56
57     public DmiDataOperations(final InventoryPersistence inventoryPersistence,
58                              final JsonObjectMapper jsonObjectMapper,
59                              final NcmpConfiguration.DmiProperties dmiProperties,
60                              final DmiRestClient dmiRestClient,
61                              final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
62         super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
63     }
64
65     /**
66      * This method fetches the resource data from operational data store for given cm handle
67      * identifier on given resource using dmi client.
68      *
69      * @param dataStoreName       name of data store
70      * @param cmHandleId          network resource identifier
71      * @param resourceId          resource identifier
72      * @param optionsParamInQuery options query
73      * @param topicParamInQuery   topic name for (triggering) async responses
74      * @param requestId           requestId for async responses
75      * @return {@code ResponseEntity} response entity
76      */
77     public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
78                                                          final String cmHandleId,
79                                                          final String resourceId,
80                                                          final String optionsParamInQuery,
81                                                          final String topicParamInQuery,
82                                                          final String requestId) {
83         final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
84         final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
85         validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
86         final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null,
87                 yangModelCmHandle);
88         final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, resourceId, optionsParamInQuery,
89                 topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
90         return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ);
91     }
92
93     /**
94      * This method fetches all the resource data from operational data store for given cm handle
95      * identifier using dmi client.
96      *
97      * @param dataStoreName data store name
98      * @param cmHandleId    network resource identifier
99      * @param requestId     requestId for async responses
100      * @return {@code ResponseEntity} response entity
101      */
102     public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
103                                                          final String cmHandleId,
104                                                          final String requestId) {
105         final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
106         final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null,
107                 yangModelCmHandle);
108         final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/",
109                 null, null,
110                 yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
111         final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
112         validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
113         return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ);
114     }
115
116     /**
117      * This method requests the resource data by data store for given list of cm handles using dmi client.
118      * The data wil be returned as message on the topic specified.
119      *
120      * @param topicParamInQuery        topic name for (triggering) async responses
121      * @param dataOperationRequest     data operation request to execute operations
122      * @param requestId                requestId for as a response
123      */
124     public void requestResourceDataFromDmi(final String topicParamInQuery,
125                                            final DataOperationRequest dataOperationRequest,
126                                            final String requestId)  {
127
128         final Set<String> cmHandlesIds
129                 = getDistinctCmHandleIdsFromDataOperationRequest(dataOperationRequest);
130
131         final Collection<YangModelCmHandle> yangModelCmHandles
132                 = inventoryPersistence.getYangModelCmHandles(cmHandlesIds);
133
134         final Map<String, List<DmiDataOperation>> operationsOutPerDmiServiceName
135                 = ResourceDataOperationRequestUtils.processPerDefinitionInDataOperationsRequest(topicParamInQuery,
136                 requestId, dataOperationRequest, yangModelCmHandles);
137
138         buildDataOperationRequestUrlAndSendToDmiService(topicParamInQuery, requestId, operationsOutPerDmiServiceName);
139     }
140
141     /**
142      * This method creates the resource data from pass-through running data store for given cm handle
143      * identifier on given resource using dmi client.
144      *
145      * @param cmHandleId    network resource identifier
146      * @param resourceId    resource identifier
147      * @param operationType operation enum
148      * @param requestData   the request data
149      * @param dataType      data type
150      * @return {@code ResponseEntity} response entity
151      */
152     public ResponseEntity<Object> writeResourceDataPassThroughRunningFromDmi(final String cmHandleId,
153                                                                              final String resourceId,
154                                                                              final OperationType operationType,
155                                                                              final String requestData,
156                                                                              final String dataType) {
157         final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
158         final String jsonRequestBody = getDmiRequestBody(operationType, null, requestData, dataType,
159                 yangModelCmHandle);
160         final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleId, resourceId,
161                 null, null,
162                 yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
163         final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
164         validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
165         return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operationType);
166     }
167
168     private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
169         return inventoryPersistence.getYangModelCmHandle(cmHandleId);
170     }
171
172     private String getDmiRequestBody(final OperationType operationType,
173                                      final String requestId,
174                                      final String requestData,
175                                      final String dataType,
176                                      final YangModelCmHandle yangModelCmHandle) {
177         final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
178                 .operationType(operationType)
179                 .requestId(requestId)
180                 .data(requestData)
181                 .dataType(dataType)
182                 .build();
183         dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties());
184         return jsonObjectMapper.asJsonString(dmiRequestBody);
185     }
186
187     private String getDmiRequestUrl(final String dataStoreName,
188                                     final String cmHandleId,
189                                     final String resourceId,
190                                     final String optionsParamInQuery,
191                                     final String topicParamInQuery,
192                                     final String dmiServiceName) {
193         return dmiServiceUrlBuilder.getDmiDatastoreUrl(
194                 dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
195                         topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName,
196                         cmHandleId));
197     }
198
199     private String getDmiServiceDataOperationRequestUrl(final String dmiServiceName,
200                                                         final String topicParamInQuery,
201                                                         final String requestId) {
202         final MultiValueMap<String, String> dataOperationRequestQueryParams = dmiServiceUrlBuilder
203                 .getDataOperationRequestQueryParams(topicParamInQuery, requestId);
204         return dmiServiceUrlBuilder.getDataOperationRequestUrl(dataOperationRequestQueryParams,
205                 dmiServiceUrlBuilder.populateDataOperationRequestUriVariables(dmiServiceName));
206     }
207
208     private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle,
209                                               final CmHandleState cmHandleState) {
210         if (cmHandleState != CmHandleState.READY) {
211             throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. "
212                     + "cm handle state is "
213                     + yangModelCmHandle.getCompositeState().getCmHandleState());
214         }
215     }
216
217     private static Set<String> getDistinctCmHandleIdsFromDataOperationRequest(final DataOperationRequest
218                                                                               dataOperationRequest) {
219         return dataOperationRequest.getDataOperationDefinitions().stream()
220                 .flatMap(dataOperationDefinition ->
221                         dataOperationDefinition.getCmHandleIds().stream()).collect(Collectors.toSet());
222     }
223
224     private void buildDataOperationRequestUrlAndSendToDmiService(final String topicParamInQuery,
225                                                                  final String requestId,
226                                                                  final Map<String, List<DmiDataOperation>>
227                                                                 groupsOutPerDmiServiceName) {
228
229         groupsOutPerDmiServiceName.entrySet().forEach(groupsOutPerDmiServiceNameEntry -> {
230             final String dmiServiceName = groupsOutPerDmiServiceNameEntry.getKey();
231             final List<DmiDataOperation> dmiDataOperationRequestBodies = groupsOutPerDmiServiceNameEntry.getValue();
232             final String dmiDataOperationResourceUrl =
233                     getDmiServiceDataOperationRequestUrl(dmiServiceName, topicParamInQuery, requestId);
234             sendDataOperationRequestToDmiService(dmiDataOperationResourceUrl, dmiDataOperationRequestBodies);
235         });
236     }
237
238     private void sendDataOperationRequestToDmiService(final String dataOperationResourceUrl,
239                                                       final List<DmiDataOperation> dmiDataOperationRequestBodies) {
240         final String dataOperationRequestBodiesAsJsonString =
241                 jsonObjectMapper.asJsonString(dmiDataOperationRequestBodies);
242         TaskExecutor.executeTask(() -> dmiRestClient.postOperationWithJsonData(dataOperationResourceUrl,
243                         dataOperationRequestBodiesAsJsonString, READ),
244                         DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS)
245                 .whenCompleteAsync(this::handleTaskCompletion);
246     }
247
248     private void handleTaskCompletion(final Object response, final Throwable throwable) {
249         // TODO Need to publish an error response to client given topic.
250         //  Code should be implemented into https://jira.onap.org/browse/CPS-1558 (
251         //  NCMP : Handle non responding DMI-Plugin)
252     }
253 }