Merge "Changing putOperationWithJson to postOperationWithJson"
[cps.git] / cps-ncmp-service / src / main / java / org / onap / cps / ncmp / api / impl / NetworkCmProxyDataServiceImpl.java
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 highstreet technologies GmbH
4  *  Modifications Copyright (C) 2021 Nordix Foundation
5  *  Modifications Copyright (C) 2021 Pantheon.tech
6  *  Modifications Copyright (C) 2021 Bell Canada
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  *
20  *  SPDX-License-Identifier: Apache-2.0
21  *  ============LICENSE_END=========================================================
22  */
23
24 package org.onap.cps.ncmp.api.impl;
25
26 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
27 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
28
29 import com.fasterxml.jackson.core.JsonProcessingException;
30 import com.fasterxml.jackson.databind.ObjectMapper;
31 import java.time.OffsetDateTime;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import lombok.extern.slf4j.Slf4j;
38 import org.onap.cps.api.CpsAdminService;
39 import org.onap.cps.api.CpsDataService;
40 import org.onap.cps.api.CpsModuleService;
41 import org.onap.cps.api.CpsQueryService;
42 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
43 import org.onap.cps.ncmp.api.impl.exception.NcmpException;
44 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
45 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations;
46 import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
47 import org.onap.cps.ncmp.api.models.CmHandle;
48 import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
49 import org.onap.cps.ncmp.api.models.PersistenceCmHandle;
50 import org.onap.cps.ncmp.api.models.PersistenceCmHandlesList;
51 import org.onap.cps.spi.FetchDescendantsOption;
52 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
53 import org.onap.cps.spi.exceptions.DataValidationException;
54 import org.onap.cps.spi.model.DataNode;
55 import org.onap.cps.spi.model.ModuleReference;
56 import org.springframework.http.ResponseEntity;
57 import org.springframework.stereotype.Service;
58 import org.springframework.util.StringUtils;
59
60 @Slf4j
61 @Service
62 public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService {
63
64     private static final String NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME = "NFP-Operational";
65
66     private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
67
68     private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
69
70     private static final OffsetDateTime NO_TIMESTAMP = null;
71
72     private CpsDataService cpsDataService;
73
74     private ObjectMapper objectMapper;
75
76     private CpsQueryService cpsQueryService;
77
78     private DmiDataOperations dmiDataOperations;
79
80     private DmiModelOperations dmiModelOperations;
81
82     private CpsModuleService cpsModuleService;
83
84     private CpsAdminService cpsAdminService;
85
86     /**
87      * Constructor Injection for Dependencies.
88      * @param dmiDataOperations DMI operation
89      * @param cpsDataService Data Service Interface
90      * @param cpsQueryService Query Service Interface
91      * @param objectMapper Object Mapper
92      */
93     public NetworkCmProxyDataServiceImpl(final DmiDataOperations dmiDataOperations,
94                                          final DmiModelOperations dmiModelOperations,
95                                          final CpsModuleService cpsModuleService,
96                                          final CpsDataService cpsDataService,
97                                          final CpsQueryService cpsQueryService,
98                                          final CpsAdminService cpsAdminService,
99                                          final ObjectMapper objectMapper) {
100         this.dmiDataOperations = dmiDataOperations;
101         this.dmiModelOperations = dmiModelOperations;
102         this.cpsModuleService = cpsModuleService;
103         this.cpsDataService = cpsDataService;
104         this.cpsQueryService = cpsQueryService;
105         this.cpsAdminService = cpsAdminService;
106         this.objectMapper = objectMapper;
107     }
108
109     @Override
110     public DataNode getDataNode(final String cmHandle, final String xpath,
111         final FetchDescendantsOption fetchDescendantsOption) {
112         return cpsDataService
113             .getDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, xpath, fetchDescendantsOption);
114     }
115
116     @Override
117     public Collection<DataNode> queryDataNodes(final String cmHandle, final String cpsPath,
118         final FetchDescendantsOption fetchDescendantsOption) {
119         return cpsQueryService
120             .queryDataNodes(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, cpsPath, fetchDescendantsOption);
121     }
122
123     @Override
124     public void createDataNode(final String cmHandle, final String parentNodeXpath, final String jsonData) {
125         if (!StringUtils.hasText(parentNodeXpath) || "/".equals(parentNodeXpath)) {
126             cpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, jsonData, NO_TIMESTAMP);
127         } else {
128             cpsDataService
129                 .saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
130         }
131     }
132
133     @Override
134     public void addListNodeElements(final String cmHandle, final String parentNodeXpath, final String jsonData) {
135         cpsDataService.saveListElements(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData,
136             NO_TIMESTAMP);
137     }
138
139     @Override
140     public void updateNodeLeaves(final String cmHandle, final String parentNodeXpath, final String jsonData) {
141         cpsDataService
142             .updateNodeLeaves(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData,
143                 NO_TIMESTAMP);
144     }
145
146     @Override
147     public void replaceNodeTree(final String cmHandle, final String parentNodeXpath, final String jsonData) {
148         cpsDataService.replaceNodeTree(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData,
149             NO_TIMESTAMP);
150     }
151
152     @Override
153     public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) {
154         dmiPluginRegistration.validateDmiPluginRegistration();
155         try {
156             if (dmiPluginRegistration.getCreatedCmHandles() != null) {
157                 parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration);
158             }
159             if (dmiPluginRegistration.getUpdatedCmHandles() != null) {
160                 parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration);
161             }
162             if (dmiPluginRegistration.getRemovedCmHandles() != null) {
163                 parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration);
164             }
165         } catch (final JsonProcessingException e) {
166             handleJsonProcessingException(dmiPluginRegistration, e);
167         }
168     }
169
170     @Override
171     public Object getResourceDataOperationalForCmHandle(final String cmHandle,
172                                                         final String resourceIdentifier,
173                                                         final String acceptParamInHeader,
174                                                         final String optionsParamInQuery) {
175         return handleResponse(dmiDataOperations.getResourceDataFromDmi(
176             cmHandle,
177             resourceIdentifier,
178             optionsParamInQuery,
179             acceptParamInHeader,
180             DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL), "Not able to get resource data.");
181     }
182
183     @Override
184     public Object getResourceDataPassThroughRunningForCmHandle(final String cmHandle,
185                                                                final String resourceIdentifier,
186                                                                final String acceptParamInHeader,
187                                                                final String optionsParamInQuery) {
188         return handleResponse(dmiDataOperations.getResourceDataFromDmi(
189             cmHandle,
190             resourceIdentifier,
191             optionsParamInQuery,
192             acceptParamInHeader,
193             DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING), "Not able to get resource data.");
194     }
195
196     @Override
197     public void writeResourceDataPassThroughRunningForCmHandle(final String cmHandle,
198                                                                final String resourceIdentifier,
199                                                                final OperationEnum operation,
200                                                                final String requestData,
201                                                                final String dataType) {
202         handleResponse(
203             dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(
204                 cmHandle, resourceIdentifier, operation, requestData, dataType),
205             "Not able to " + operation + " resource data.");
206     }
207
208
209     @Override
210     public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandle) {
211         return cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle);
212     }
213
214     /**
215      * Retrieve cm handle identifiers for the given list of module names.
216      *
217      * @param moduleNames module names.
218      * @return a collection of anchor identifiers
219      */
220     @Override
221     public Collection<String> executeCmHandleHasAllModulesSearch(final Collection<String> moduleNames) {
222         return cpsAdminService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNames);
223     }
224
225     /**
226      * THis method registers a cm handle and intiates modules sync.
227      *
228      * @param dmiPluginRegistration dmi plugin registration information.
229      * @throws JsonProcessingException thrown if json is malformed or missing.
230      */
231     public void parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(
232         final DmiPluginRegistration dmiPluginRegistration) throws JsonProcessingException {
233         final PersistenceCmHandlesList createdPersistenceCmHandlesList =
234             getUpdatedPersistenceCmHandlesList(dmiPluginRegistration, dmiPluginRegistration.getCreatedCmHandles());
235         registerAndSyncNewCmHandles(createdPersistenceCmHandlesList);
236     }
237
238     private static Object handleResponse(final ResponseEntity<?> responseEntity,
239                                          final String exceptionMessage) {
240         if (responseEntity.getStatusCode().is2xxSuccessful()) {
241             return responseEntity.getBody();
242         } else {
243             throw new NcmpException(exceptionMessage,
244                     "DMI status code: " + responseEntity.getStatusCodeValue()
245                             + ", DMI response body: " + responseEntity.getBody());
246         }
247     }
248
249     private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration)
250         throws JsonProcessingException {
251         final PersistenceCmHandlesList updatedPersistenceCmHandlesList =
252             getUpdatedPersistenceCmHandlesList(dmiPluginRegistration, dmiPluginRegistration.getUpdatedCmHandles());
253         final String cmHandlesAsJson = objectMapper.writeValueAsString(updatedPersistenceCmHandlesList);
254         cpsDataService.updateNodeLeavesAndExistingDescendantLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
255                 "/dmi-registry", cmHandlesAsJson, NO_TIMESTAMP);
256     }
257
258     private PersistenceCmHandlesList getUpdatedPersistenceCmHandlesList(
259         final DmiPluginRegistration dmiPluginRegistration,
260         final List<CmHandle> updatedCmHandles) {
261         return PersistenceCmHandlesList.toPersistenceCmHandlesList(
262             dmiPluginRegistration.getDmiPlugin(),
263             dmiPluginRegistration.getDmiDataPlugin(),
264             dmiPluginRegistration.getDmiModelPlugin(),
265             updatedCmHandles);
266     }
267
268     private static void handleJsonProcessingException(final DmiPluginRegistration dmiPluginRegistration,
269                                                       final JsonProcessingException e) {
270         final String message = "Parsing error occurred while processing DMI Plugin Registration"
271             + dmiPluginRegistration;
272         log.error(message);
273         throw new DataValidationException(message, e.getMessage(), e);
274     }
275
276     private void registerAndSyncNewCmHandles(final PersistenceCmHandlesList persistenceCmHandlesList)
277         throws JsonProcessingException  {
278         final String cmHandleJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList);
279         cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry",
280             cmHandleJsonData, NO_TIMESTAMP);
281
282         for (final PersistenceCmHandle persistenceCmHandle : persistenceCmHandlesList.getPersistenceCmHandles()) {
283             syncModulesAndCreateAnchor(persistenceCmHandle);
284         }
285     }
286
287     protected void syncModulesAndCreateAnchor(final PersistenceCmHandle persistenceCmHandle) {
288         syncAndCreateSchemaSet(persistenceCmHandle);
289         createAnchor(persistenceCmHandle);
290     }
291
292     private void parseAndRemoveCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
293         for (final String cmHandle : dmiPluginRegistration.getRemovedCmHandles()) {
294             try {
295                 attemptToDeleteSchemaSetWithCascade(cmHandle);
296                 cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
297                     "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
298             } catch (final DataNodeNotFoundException e) {
299                 log.warn("Datanode {} not deleted message {}", cmHandle, e.getMessage());
300             }
301         }
302     }
303
304     private void attemptToDeleteSchemaSetWithCascade(final String schemaSetName) {
305         try {
306             cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
307                 CASCADE_DELETE_ALLOWED);
308         } catch (final Exception e) {
309             log.warn("Schema set {} delete failed, reason {}", schemaSetName, e.getMessage());
310         }
311     }
312
313     private void syncAndCreateSchemaSet(final PersistenceCmHandle persistenceCmHandle) {
314         final List<ModuleReference> moduleReferencesFromCmHandle =
315             dmiModelOperations.getModuleReferences(persistenceCmHandle);
316         final List<ModuleReference> existingModuleReferences = new ArrayList<>();
317         final List<ModuleReference> unknownModuleReferences = new ArrayList<>();
318         prepareModuleSubsets(moduleReferencesFromCmHandle, existingModuleReferences, unknownModuleReferences);
319
320         final Map<String, String> newYangResourcesModuleNameToContentMap;
321         if (unknownModuleReferences.isEmpty()) {
322             newYangResourcesModuleNameToContentMap = new HashMap<>();
323         } else {
324             newYangResourcesModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle,
325                 unknownModuleReferences);
326         }
327         cpsModuleService
328             .createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, persistenceCmHandle.getId(),
329                 newYangResourcesModuleNameToContentMap, existingModuleReferences);
330     }
331
332     private void prepareModuleSubsets(final List<ModuleReference> moduleReferencesFromCmHandle,
333                                       final List<ModuleReference> existingModuleReferences,
334                                       final List<ModuleReference> unknownModuleReferences) {
335
336         final Collection<ModuleReference> knownModuleReferencesInCps =
337             cpsModuleService.getYangResourceModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME);
338
339         for (final ModuleReference moduleReferenceFromDmiForCmHandle : moduleReferencesFromCmHandle) {
340             if (knownModuleReferencesInCps.contains(moduleReferenceFromDmiForCmHandle)) {
341                 existingModuleReferences.add(moduleReferenceFromDmiForCmHandle);
342             } else {
343                 unknownModuleReferences.add(moduleReferenceFromDmiForCmHandle);
344             }
345         }
346     }
347
348     private void createAnchor(final PersistenceCmHandle persistenceCmHandle) {
349         cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, persistenceCmHandle.getId(),
350             persistenceCmHandle.getId());
351     }
352
353
354
355 }