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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * SPDX-License-Identifier: Apache-2.0
21 * ============LICENSE_END=========================================================
24 package org.onap.cps.ncmp.api.impl;
26 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
28 import com.fasterxml.jackson.core.JsonProcessingException;
29 import com.fasterxml.jackson.databind.ObjectMapper;
30 import com.google.gson.Gson;
31 import com.google.gson.JsonArray;
32 import com.google.gson.JsonElement;
33 import com.google.gson.JsonObject;
34 import java.time.OffsetDateTime;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.HashMap;
38 import java.util.List;
40 import lombok.extern.slf4j.Slf4j;
41 import org.onap.cps.api.CpsAdminService;
42 import org.onap.cps.api.CpsDataService;
43 import org.onap.cps.api.CpsModuleService;
44 import org.onap.cps.api.CpsQueryService;
45 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
46 import org.onap.cps.ncmp.api.impl.exception.NcmpException;
47 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
48 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations;
49 import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
50 import org.onap.cps.ncmp.api.models.CmHandle;
51 import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
52 import org.onap.cps.ncmp.api.models.PersistenceCmHandle;
53 import org.onap.cps.ncmp.api.models.PersistenceCmHandlesList;
54 import org.onap.cps.ncmp.api.models.YangResource;
55 import org.onap.cps.spi.FetchDescendantsOption;
56 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
57 import org.onap.cps.spi.exceptions.DataValidationException;
58 import org.onap.cps.spi.model.DataNode;
59 import org.onap.cps.spi.model.ModuleReference;
60 import org.springframework.http.ResponseEntity;
61 import org.springframework.stereotype.Service;
62 import org.springframework.util.StringUtils;
66 public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService {
68 private static final String NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME = "NFP-Operational";
70 private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
72 private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
74 private static final OffsetDateTime NO_TIMESTAMP = null;
76 private CpsDataService cpsDataService;
78 private ObjectMapper objectMapper;
80 private CpsQueryService cpsQueryService;
82 private DmiDataOperations dmiDataOperations;
84 private DmiModelOperations dmiModelOperations;
86 private CpsModuleService cpsModuleService;
88 private CpsAdminService cpsAdminService;
91 * Constructor Injection for Dependencies.
92 * @param dmiDataOperations DMI operation
93 * @param cpsDataService Data Service Interface
94 * @param cpsQueryService Query Service Interface
95 * @param objectMapper Object Mapper
97 public NetworkCmProxyDataServiceImpl(final DmiDataOperations dmiDataOperations,
98 final DmiModelOperations dmiModelOperations,
99 final CpsModuleService cpsModuleService,
100 final CpsDataService cpsDataService,
101 final CpsQueryService cpsQueryService,
102 final CpsAdminService cpsAdminService,
103 final ObjectMapper objectMapper) {
104 this.dmiDataOperations = dmiDataOperations;
105 this.dmiModelOperations = dmiModelOperations;
106 this.cpsModuleService = cpsModuleService;
107 this.cpsDataService = cpsDataService;
108 this.cpsQueryService = cpsQueryService;
109 this.cpsAdminService = cpsAdminService;
110 this.objectMapper = objectMapper;
114 public DataNode getDataNode(final String cmHandle, final String xpath,
115 final FetchDescendantsOption fetchDescendantsOption) {
116 return cpsDataService
117 .getDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, xpath, fetchDescendantsOption);
121 public Collection<DataNode> queryDataNodes(final String cmHandle, final String cpsPath,
122 final FetchDescendantsOption fetchDescendantsOption) {
123 return cpsQueryService
124 .queryDataNodes(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, cpsPath, fetchDescendantsOption);
128 public void createDataNode(final String cmHandle, final String parentNodeXpath, final String jsonData) {
129 if (!StringUtils.hasText(parentNodeXpath) || "/".equals(parentNodeXpath)) {
130 cpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, jsonData, NO_TIMESTAMP);
133 .saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData, NO_TIMESTAMP);
138 public void addListNodeElements(final String cmHandle, final String parentNodeXpath, final String jsonData) {
139 cpsDataService.saveListElements(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData,
144 public void updateNodeLeaves(final String cmHandle, final String parentNodeXpath, final String jsonData) {
146 .updateNodeLeaves(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData,
151 public void replaceNodeTree(final String cmHandle, final String parentNodeXpath, final String jsonData) {
152 cpsDataService.replaceNodeTree(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData,
157 public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) {
158 dmiPluginRegistration.validateDmiPluginRegistration();
160 if (dmiPluginRegistration.getCreatedCmHandles() != null) {
161 parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration);
163 if (dmiPluginRegistration.getUpdatedCmHandles() != null) {
164 parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration);
166 if (dmiPluginRegistration.getRemovedCmHandles() != null) {
167 parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration);
169 } catch (final JsonProcessingException e) {
170 handleJsonProcessingException(dmiPluginRegistration, e);
175 public Object getResourceDataOperationalForCmHandle(final String cmHandle,
176 final String resourceIdentifier,
177 final String acceptParamInHeader,
178 final String optionsParamInQuery) {
179 return handleResponse(dmiDataOperations.getResourceDataFromDmi(
184 DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL), "Not able to get resource data.");
188 public Object getResourceDataPassThroughRunningForCmHandle(final String cmHandle,
189 final String resourceIdentifier,
190 final String acceptParamInHeader,
191 final String optionsParamInQuery) {
192 return handleResponse(dmiDataOperations.getResourceDataFromDmi(
197 DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING), "Not able to get resource data.");
201 public void writeResourceDataPassThroughRunningForCmHandle(final String cmHandle,
202 final String resourceIdentifier,
203 final OperationEnum operation,
204 final String requestData,
205 final String dataType) {
207 dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(
208 cmHandle, resourceIdentifier, operation, requestData, dataType),
209 "Not able to " + operation + " resource data.");
214 public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandle) {
215 return cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle);
219 * Retrieve cm handle identifiers for the given list of module names.
221 * @param moduleNames module names.
222 * @return a collection of anchor identifiers
225 public Collection<String> executeCmHandleHasAllModulesSearch(final Collection<String> moduleNames) {
226 return cpsAdminService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNames);
230 * THis method registers a cm handle and intiates modules sync.
232 * @param dmiPluginRegistration dmi plugin registration information.
233 * @throws JsonProcessingException thrown if json is malformed or missing.
235 public void parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(
236 final DmiPluginRegistration dmiPluginRegistration) throws JsonProcessingException {
237 final PersistenceCmHandlesList createdPersistenceCmHandlesList =
238 getUpdatedPersistenceCmHandlesList(dmiPluginRegistration, dmiPluginRegistration.getCreatedCmHandles());
239 registerAndSyncNewCmHandles(createdPersistenceCmHandlesList);
242 private static Object handleResponse(final ResponseEntity<?> responseEntity,
243 final String exceptionMessage) {
244 if (responseEntity.getStatusCode().is2xxSuccessful()) {
245 return responseEntity.getBody();
247 throw new NcmpException(exceptionMessage,
248 "DMI status code: " + responseEntity.getStatusCodeValue()
249 + ", DMI response body: " + responseEntity.getBody());
253 private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration)
254 throws JsonProcessingException {
255 final PersistenceCmHandlesList updatedPersistenceCmHandlesList =
256 getUpdatedPersistenceCmHandlesList(dmiPluginRegistration, dmiPluginRegistration.getUpdatedCmHandles());
257 final String cmHandlesAsJson = objectMapper.writeValueAsString(updatedPersistenceCmHandlesList);
258 cpsDataService.updateNodeLeavesAndExistingDescendantLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
259 "/dmi-registry", cmHandlesAsJson, NO_TIMESTAMP);
262 private PersistenceCmHandlesList getUpdatedPersistenceCmHandlesList(
263 final DmiPluginRegistration dmiPluginRegistration,
264 final List<CmHandle> updatedCmHandles) {
265 return PersistenceCmHandlesList.toPersistenceCmHandlesList(
266 dmiPluginRegistration.getDmiPlugin(),
267 dmiPluginRegistration.getDmiDataPlugin(),
268 dmiPluginRegistration.getDmiModelPlugin(),
272 private static void handleJsonProcessingException(final DmiPluginRegistration dmiPluginRegistration,
273 final JsonProcessingException e) {
274 final String message = "Parsing error occurred while processing DMI Plugin Registration"
275 + dmiPluginRegistration;
277 throw new DataValidationException(message, e.getMessage(), e);
280 private void registerAndSyncNewCmHandles(final PersistenceCmHandlesList persistenceCmHandlesList)
281 throws JsonProcessingException {
282 final String cmHandleJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList);
283 cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry",
284 cmHandleJsonData, NO_TIMESTAMP);
286 for (final PersistenceCmHandle persistenceCmHandle : persistenceCmHandlesList.getPersistenceCmHandles()) {
287 syncModulesAndCreateAnchor(persistenceCmHandle);
291 protected void syncModulesAndCreateAnchor(final PersistenceCmHandle persistenceCmHandle) {
292 fetchAndSyncModules(persistenceCmHandle);
293 createAnchor(persistenceCmHandle);
296 private void parseAndRemoveCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
297 for (final String cmHandle : dmiPluginRegistration.getRemovedCmHandles()) {
299 cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
300 "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
301 } catch (final DataNodeNotFoundException e) {
302 log.warn("Datanode {} not deleted message {}", cmHandle, e.getMessage());
307 private void fetchAndSyncModules(final PersistenceCmHandle persistenceCmHandle) {
309 final List<ModuleReference> moduleReferencesFromCmHandle =
310 toModuleReferences(dmiModelOperations.getModuleReferences(persistenceCmHandle));
311 final List<ModuleReference> existingModuleReferences = new ArrayList<>();
312 final List<ModuleReference> unknownModuleReferences = new ArrayList<>();
313 prepareModuleSubsets(moduleReferencesFromCmHandle, existingModuleReferences, unknownModuleReferences);
315 final Map<String, String> newYangResourcesModuleNameToContentMap;
316 if (unknownModuleReferences.isEmpty()) {
317 newYangResourcesModuleNameToContentMap = new HashMap<>();
319 newYangResourcesModuleNameToContentMap = getNewYangResourcesFromDmi(persistenceCmHandle,
320 unknownModuleReferences);
323 .createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, persistenceCmHandle.getId(),
324 newYangResourcesModuleNameToContentMap, existingModuleReferences);
327 private void prepareModuleSubsets(final List<ModuleReference> moduleReferencesFromCmHandle,
328 final List<ModuleReference> existingModuleReferences,
329 final List<ModuleReference> unknownModuleReferences) {
331 final Collection<ModuleReference> knownModuleReferencesInCps =
332 cpsModuleService.getYangResourceModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME);
334 for (final ModuleReference moduleReferenceFromDmiForCmHandle : moduleReferencesFromCmHandle) {
335 if (knownModuleReferencesInCps.contains(moduleReferenceFromDmiForCmHandle)) {
336 existingModuleReferences.add(moduleReferenceFromDmiForCmHandle);
338 unknownModuleReferences.add(moduleReferenceFromDmiForCmHandle);
343 private void createAnchor(final PersistenceCmHandle persistenceCmHandle) {
344 cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, persistenceCmHandle.getId(),
345 persistenceCmHandle.getId());
348 private Map<String, String> getNewYangResourcesFromDmi(final PersistenceCmHandle persistenceCmHandle,
349 final List<ModuleReference> unknownModuleReferences) {
350 final ResponseEntity<String> responseEntity =
351 dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle, unknownModuleReferences);
353 final JsonArray moduleResources = new Gson().fromJson(responseEntity.getBody(),
355 final Map<String, String> newYangResourcesModuleNameToContentMap = new HashMap<>();
357 for (final JsonElement moduleResource : moduleResources) {
358 final YangResource yangResource = toYangResource((JsonObject) moduleResource);
359 newYangResourcesModuleNameToContentMap.put(yangResource.getModuleName(), yangResource.getYangSource());
361 return newYangResourcesModuleNameToContentMap;
364 private static YangResource toYangResource(final JsonObject yangResourceAsJson) {
365 final YangResource yangResource = new YangResource();
366 yangResource.setModuleName(yangResourceAsJson.get("moduleName").getAsString());
367 yangResource.setRevision(yangResourceAsJson.get("revision").getAsString());
368 final String yangSourceJson = yangResourceAsJson.get("yangSource").getAsString();
370 String yangSource = JsonUtils.removeWrappingTokens(yangSourceJson);
371 yangSource = JsonUtils.removeRedundantEscapeCharacters(yangSource);
372 yangResource.setYangSource(yangSource);
377 private static List<ModuleReference> toModuleReferences(
378 final ResponseEntity<String> dmiFetchModulesResponseEntity) {
379 final List<ModuleReference> moduleReferences = new ArrayList<>();
380 final JsonObject bodyAsJsonObject = new Gson().fromJson(dmiFetchModulesResponseEntity.getBody(),
382 final JsonArray moduleReferencesAsJson = bodyAsJsonObject.getAsJsonArray("schemas");
383 for (final JsonElement moduleReferenceAsJson : moduleReferencesAsJson) {
384 final ModuleReference moduleReference = toModuleReference((JsonObject) moduleReferenceAsJson);
385 moduleReferences.add(moduleReference);
387 return moduleReferences;
390 private static ModuleReference toModuleReference(final JsonObject moduleReferenceAsJson) {
391 final ModuleReference moduleReference = new ModuleReference();
392 moduleReference.setModuleName(moduleReferenceAsJson.get("moduleName").getAsString());
393 moduleReference.setRevision(moduleReferenceAsJson.get("revision").getAsString());
394 return moduleReference;