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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.cps.ncmp.api.impl.operations;
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;
27 import java.util.Collection;
28 import java.util.List;
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.ResourceDataBatchRequestUtils;
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;
49 * Operations class for DMI data.
53 public class DmiDataOperations extends DmiOperations {
55 private static final long DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS = 30000L;
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);
66 * This method fetches the resource data from operational data store for given cm handle
67 * identifier on given resource using dmi client.
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
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,
88 final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, resourceId, optionsParamInQuery,
89 topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
90 return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ);
94 * This method fetches all the resource data from operational data store for given cm handle
95 * identifier using dmi client.
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
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,
108 final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/",
110 yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
111 final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
112 validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
113 return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ);
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.
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
124 public void requestResourceDataFromDmi(final String topicParamInQuery,
125 final DataOperationRequest dataOperationRequest,
126 final String requestId) {
128 final Set<String> cmHandlesIds
129 = getDistinctCmHandleIdsFromDataOperationRequest(dataOperationRequest);
131 final Collection<YangModelCmHandle> yangModelCmHandles
132 = getYangModelCmHandlesInReadyState(cmHandlesIds);
134 final Map<String, List<DmiBatchOperation>> operationsOutPerDmiServiceName
135 = ResourceDataBatchRequestUtils.processPerOperationInBatchRequest(dataOperationRequest,
138 buildBatchRequestUrlAndSendToDmiService(topicParamInQuery, requestId, operationsOutPerDmiServiceName);
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.
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
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,
160 final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleId, resourceId,
162 yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
163 final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
164 validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
165 return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operationType);
168 private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
169 return inventoryPersistence.getYangModelCmHandle(cmHandleId);
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)
183 dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties());
184 return jsonObjectMapper.asJsonString(dmiRequestBody);
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,
199 private String getDmiServiceBatchRequestUrl(final String dmiServiceName,
200 final String topicParamInQuery,
201 final String requestId) {
202 final MultiValueMap<String, String> batchRequestQueryParams = dmiServiceUrlBuilder
203 .getBatchRequestQueryParams(topicParamInQuery, requestId);
204 return dmiServiceUrlBuilder.getBatchRequestUrl(batchRequestQueryParams,
205 dmiServiceUrlBuilder.populateBatchUriVariables(dmiServiceName));
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());
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());
224 private Collection<YangModelCmHandle> getYangModelCmHandlesInReadyState(final Set<String> requestedCmHandleIds) {
225 // TODO Need to publish an error response to client given topic.
226 // Code should be implemented into https://jira.onap.org/browse/CPS-1614 (
227 // NCMP : Error handling for non-ready cm handle state)
228 return inventoryPersistence.getYangModelCmHandles(requestedCmHandleIds).stream()
229 .filter(yangModelCmHandle -> yangModelCmHandle.getCompositeState().getCmHandleState()
230 == CmHandleState.READY).collect(Collectors.toList());
233 private void buildBatchRequestUrlAndSendToDmiService(final String topicParamInQuery,
234 final String requestId,
235 final Map<String, List<DmiBatchOperation>>
236 groupsOutPerDmiServiceName) {
238 groupsOutPerDmiServiceName.entrySet().forEach(groupsOutPerDmiServiceNameEntry -> {
239 final String dmiServiceName = groupsOutPerDmiServiceNameEntry.getKey();
240 final List<DmiBatchOperation> dmiBatchRequestBodies = groupsOutPerDmiServiceNameEntry.getValue();
241 final String dmiBatchResourceDataUrl = getDmiServiceBatchRequestUrl(dmiServiceName, topicParamInQuery,
243 sendBatchRequestToDmiService(dmiBatchResourceDataUrl, dmiBatchRequestBodies);
247 private void sendBatchRequestToDmiService(final String batchResourceDataUrl,
248 final List<DmiBatchOperation> dmiBatchRequestBodies) {
249 final String batchRequestBodiesAsJsonString = jsonObjectMapper.asJsonString(dmiBatchRequestBodies);
250 TaskExecutor.executeTask(() -> dmiRestClient.postOperationWithJsonData(batchResourceDataUrl,
251 batchRequestBodiesAsJsonString, READ), DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS)
252 .whenCompleteAsync(this::handleTaskCompletion);
255 private void handleTaskCompletion(final Object response, final Throwable throwable) {
256 // TODO Need to publish an error response to client given topic.
257 // Code should be implemented into https://jira.onap.org/browse/CPS-1558 (
258 // NCMP : Handle non responding DMI-Plugin)