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