aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsourabh_sourabh <sourabh.sourabh@est.tech>2022-06-20 15:57:24 +0100
committersourabh_sourabh <sourabh.sourabh@est.tech>2022-06-22 12:20:19 +0100
commit40a8211b8f8f4b244b43620776a71371bc5371d6 (patch)
treeebdb1b504ef57a6192c80184031f2f36c75c36cb
parent31cf29d21096ab7200658f586ecb9e40ee72249e (diff)
Data Sync Watchdog Process
- Get all the Cm Handles state in READY and Operational datastores sync state in UNSYNCHRONIZED - Get a random Cm Handle - Get the first resource data from the node - Save the data in Cps Db - Update the Operational datastores sync state to SYNCHRONIZED Issue-ID: CPS-1052 Issue-ID: CPS-1053 Issue-ID: CPS-1054 Change-Id: I9a20391ef30e6d56c4d789a92b8bf42cd3756c62 Signed-off-by: Lathish <lathishbabu.ganesan@est.tech> Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
-rw-r--r--cps-application/src/main/resources/application.yml5
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java2
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy3
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy5
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java83
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java5
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java12
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java33
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/SyncState.java25
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/DataSyncWatchdog.java74
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java70
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy14
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy4
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy35
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/DataSyncSpec.groovy101
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy48
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/YangModelCmHandleSpec.groovy1
-rw-r--r--cps-service/src/main/java/org/onap/cps/utils/JsonObjectMapper.java17
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy18
22 files changed, 499 insertions, 62 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 2b4dc4b7c7..802a18b847 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -158,6 +158,7 @@ dmi:
timers:
advised-modules-sync:
sleep-time-ms: 30000
-
locked-modules-sync:
- sleep-time-ms: 300000 \ No newline at end of file
+ sleep-time-ms: 300000
+ cm-handle-data-sync:
+ sleep-time-ms: 30000
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java
index 5f4b311186..afad1098c7 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java
@@ -55,7 +55,7 @@ public interface RestOutputCmHandleStateMapper {
if (compositeStateDataStore.getOperationalDataStore() != null) {
final SyncState operationalSyncState = new SyncState();
- operationalSyncState.setState(compositeStateDataStore.getOperationalDataStore().getSyncState());
+ operationalSyncState.setState(compositeStateDataStore.getOperationalDataStore().getSyncState().name());
operationalSyncState.setLastSyncTime(compositeStateDataStore.getOperationalDataStore().getLastSyncTime());
dataStores.setOperational(operationalSyncState);
}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
index 036928fe35..71258102ed 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
@@ -26,6 +26,7 @@ package org.onap.cps.ncmp.rest.controller
import org.mapstruct.factory.Mappers
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
+import org.onap.cps.ncmp.api.inventory.SyncState
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
@@ -392,7 +393,7 @@ class NetworkCmProxyControllerSpec extends Specification {
def dataStores() {
DataStores.builder()
.operationalDataStore(Operational.builder()
- .syncState('NONE_REQUESTED')
+ .syncState(SyncState.NONE_REQUESTED)
.lastSyncTime(formattedDateAndTime.toString()).build()).build()
}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy
index 22c9fe6056..695ca5ad67 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy
@@ -24,6 +24,7 @@ import org.mapstruct.factory.Mappers
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder
import org.onap.cps.ncmp.api.inventory.LockReasonCategory
+import org.onap.cps.ncmp.api.inventory.SyncState
import org.onap.cps.ncmp.rest.model.RestOutputCmHandleState
import spock.lang.Specification
@@ -43,7 +44,7 @@ class RestOutputCmHandleStateMapperTest extends Specification {
.withCmHandleState(CmHandleState.ADVISED)
.withLastUpdatedTime(formattedDateAndTime.toString())
.withLockReason(LockReasonCategory.LOCKED_MISBEHAVING, 'locked other details')
- .withOperationalDataStores('SYNCHRONIZED', formattedDateAndTime).build()
+ .withOperationalDataStores(SyncState.SYNCHRONIZED, formattedDateAndTime).build()
compositeState.setDataSyncEnabled(false)
when: 'mapper is called'
def result = objectUnderTest.toRestOutputCmHandleState(compositeState)
@@ -54,7 +55,7 @@ class RestOutputCmHandleStateMapperTest extends Specification {
assert result.lastUpdateTime == formattedDateAndTime
assert result.lockReason.reason == 'LOCKED_MISBEHAVING'
assert result.lockReason.details == 'locked other details'
- assert result.cmHandleState == CmHandleState.ADVISED.name()
+ assert result.cmHandleState == 'ADVISED'
assert result.dataSyncState.operational.getState() != null
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
index d0e17d4c08..d46d634104 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
@@ -75,21 +75,33 @@ public class DmiDataOperations extends DmiOperations {
final DataStoreEnum dataStore,
final String requestId,
final String topicParamInQuery) {
- CpsValidator.validateNameCharacters(cmHandleId);
- final YangModelCmHandle yangModelCmHandle =
- inventoryPersistence.getYangModelCmHandle(cmHandleId);
+ final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
+ final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
+ final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, resourceId, optionsParamInQuery, dataStore,
+ topicParamInQuery, yangModelCmHandle);
+ final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
+ isCmHandleStateReady(yangModelCmHandle, cmHandleState);
+ return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ);
+ }
+
+ /**
+ * This method fetches all the resource data from operational data store for given cm handle
+ * identifier using dmi client.
+ *
+ * @param cmHandleId network resource identifier
+ * @param dataStore data store enum
+ * @param requestId requestId for async responses
+ * @return {@code ResponseEntity} response entity
+ */
+ public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId,
+ final DataStoreEnum dataStore,
+ final String requestId) {
+ final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
+ final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
+ final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, "/", null, dataStore,
+ null, yangModelCmHandle);
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
isCmHandleStateReady(yangModelCmHandle, cmHandleState);
- final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
- .operation(READ)
- .requestId(requestId)
- .build();
- dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties());
- final String jsonBody = jsonObjectMapper.asJsonString(dmiRequestBody);
- final String dmiResourceDataUrl = dmiServiceUrlBuilder.getDmiDatastoreUrl(
- dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
- topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(
- yangModelCmHandle, cmHandleId, dataStore));
return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ);
}
@@ -109,23 +121,42 @@ public class DmiDataOperations extends DmiOperations {
final OperationEnum operation,
final String requestData,
final String dataType) {
- CpsValidator.validateNameCharacters(cmHandleId);
- final YangModelCmHandle yangModelCmHandle =
- inventoryPersistence.getYangModelCmHandle(cmHandleId);
+ final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
+ final String jsonBody = getDmiRequestBody(operation, null, requestData, dataType, yangModelCmHandle);
+ final String dmiUrl = getDmiRequestUrl(cmHandleId, resourceId, null, PASSTHROUGH_RUNNING,
+ null, yangModelCmHandle);
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
isCmHandleStateReady(yangModelCmHandle, cmHandleState);
+ return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation);
+ }
+
+ private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
+ CpsValidator.validateNameCharacters(cmHandleId);
+ return inventoryPersistence.getYangModelCmHandle(cmHandleId);
+ }
+
+ private String getDmiRequestBody(final OperationEnum operation, final String requestId, final String requestData,
+ final String dataType, final YangModelCmHandle yangModelCmHandle) {
final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
- .operation(operation)
- .data(requestData)
- .dataType(dataType)
- .build();
+ .operation(operation)
+ .requestId(requestId)
+ .data(requestData)
+ .dataType(dataType)
+ .build();
dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties());
- final String jsonBody = jsonObjectMapper.asJsonString(dmiRequestBody);
- final String dmiUrl =
- dmiServiceUrlBuilder.getDmiDatastoreUrl(dmiServiceUrlBuilder.populateQueryParams(resourceId,
- null, null),
- dmiServiceUrlBuilder.populateUriVariables(yangModelCmHandle, cmHandleId, PASSTHROUGH_RUNNING));
- return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation);
+ return jsonObjectMapper.asJsonString(dmiRequestBody);
+ }
+
+ private String getDmiRequestUrl(final String cmHandleId,
+ final String resourceId,
+ final String optionsParamInQuery,
+ final DataStoreEnum dataStore,
+ final String topicParamInQuery,
+ final YangModelCmHandle yangModelCmHandle) {
+ return dmiServiceUrlBuilder.getDmiDatastoreUrl(
+ dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
+ topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(
+ yangModelCmHandle, cmHandleId, dataStore));
}
private void isCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, final CmHandleState cmHandleState) {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java
index eeaa4cd0be..df303b5daa 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java
@@ -57,7 +57,8 @@ public class CompositeState {
/**
* Date and Time in the format of yyyy-MM-dd'T'HH:mm:ss.SSSZ
*/
- public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+ private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter
+ .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
/**
@@ -93,7 +94,7 @@ public class CompositeState {
public static class Operational {
@JsonProperty("sync-state")
- private String syncState;
+ private SyncState syncState;
@JsonProperty("last-sync-time")
private String lastSyncTime;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java
index 4ab0cecbf5..f4d96389de 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java
@@ -98,7 +98,7 @@ public class CompositeStateBuilder {
* @param lastSyncTime for the locked state
* @return CompositeStateBuilder
*/
- public CompositeStateBuilder withOperationalDataStores(final String syncState, final String lastSyncTime) {
+ public CompositeStateBuilder withOperationalDataStores(final SyncState syncState, final String lastSyncTime) {
this.datastores = DataStores.builder().operationalDataStore(
Operational.builder().syncState(syncState).lastSyncTime(lastSyncTime).build()).build();
return this;
@@ -111,20 +111,20 @@ public class CompositeStateBuilder {
* @return CompositeState
*/
public CompositeStateBuilder fromDataNode(final DataNode dataNode) {
- this.cmHandleState = CmHandleState.valueOf((String) dataNode.getLeaves()
- .get("cm-handle-state"));
+ this.cmHandleState = CmHandleState.valueOf((String) dataNode.getLeaves()
+ .get("cm-handle-state"));
for (final DataNode stateChildNode : dataNode.getChildDataNodes()) {
if (stateChildNode.getXpath().endsWith("/lock-reason")) {
this.lockReason = new LockReason(LockReasonCategory.valueOf(
- (String) stateChildNode.getLeaves().get("reason")),
- (String) stateChildNode.getLeaves().get("details"));
+ (String) stateChildNode.getLeaves().get("reason")),
+ (String) stateChildNode.getLeaves().get("details"));
}
if (stateChildNode.getXpath().endsWith("/datastores")) {
for (final DataNode dataStoreNodes : stateChildNode.getChildDataNodes()) {
Operational operationalDataStore = null;
if (dataStoreNodes.getXpath().contains("/operational")) {
operationalDataStore = Operational.builder()
- .syncState((String) dataStoreNodes.getLeaves().get("sync-state"))
+ .syncState(SyncState.valueOf((String) dataStoreNodes.getLeaves().get("sync-state")))
.lastSyncTime((String) dataStoreNodes.getLeaves().get("last-sync-time"))
.build();
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
index ce34154b9b..1985bd959a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
@@ -93,14 +93,39 @@ public class InventoryPersistence {
}
/**
- * Method to return cm handles from the cps path.
+ * Method to return data nodes representing the cm handles.
*
* @param cpsPath cps path for which the cmHandle is requested
- * @return a list of cm handles
+ * @return a list of data nodes representing the cm handles.
*/
- public List<DataNode> getCmHandlesByCpsPath(final String cpsPath) {
+ public List<DataNode> getCmHandleDataNodesByCpsPath(final String cpsPath) {
return cpsDataPersistenceService.queryDataNodes(
- NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsPath, FetchDescendantsOption.OMIT_DESCENDANTS);
+ NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsPath, FetchDescendantsOption.OMIT_DESCENDANTS);
+ }
+
+ /**
+ * Method which returns cm handles by the cm handle id and state.
+ * @param cmHandleId cm handle id
+ * @param cmHandleState cm handle state
+ * @return a list of cm handles
+ */
+ public List<DataNode> getCmHandlesByIdAndState(final String cmHandleId, final CmHandleState cmHandleState) {
+ return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME,
+ NCMP_DMI_REGISTRY_ANCHOR, "//cm-handles[@id='" + cmHandleId + "']/state[@cm-handle-state=\""
+ + cmHandleState + "\"]/ancestor::cm-handles",
+ FetchDescendantsOption.OMIT_DESCENDANTS);
+ }
+
+ /**
+ * Method which returns cm handles by the operational sync state of cm handle.
+ * @param syncState sync state
+ * @return a list of cm handles
+ */
+ public List<DataNode> getCmHandlesByOperationalSyncState(final SyncState syncState) {
+ return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME,
+ NCMP_DMI_REGISTRY_ANCHOR, "//state/datastores"
+ + "/operational[@sync-state=\"" + syncState + "\"]/ancestor::cm-handles",
+ FetchDescendantsOption.OMIT_DESCENDANTS);
}
/**
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/SyncState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/SyncState.java
new file mode 100644
index 0000000000..9c7a476183
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/SyncState.java
@@ -0,0 +1,25 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.inventory;
+
+public enum SyncState {
+ SYNCHRONIZED, UNSYNCHRONIZED, NONE_REQUESTED
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/DataSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/DataSyncWatchdog.java
new file mode 100644
index 0000000000..553db65dd0
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/DataSyncWatchdog.java
@@ -0,0 +1,74 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.inventory.sync;
+
+import java.time.OffsetDateTime;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.api.CpsDataService;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.CompositeState;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.api.inventory.SyncState;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class DataSyncWatchdog {
+
+ private final InventoryPersistence inventoryPersistence;
+
+ private final CpsDataService cpsDataService;
+
+ private final SyncUtils syncUtils;
+
+ /**
+ * Execute Cm Handle poll which queries the cm handle state in 'READY' and Operational Datastore Sync State in
+ * 'UNSYNCHRONIZED'.
+ */
+ @Scheduled(fixedDelayString = "${timers.cm-handle-data-sync.sleep-time-ms:30000}")
+ public void executeUnSynchronizedReadyCmHandlePoll() {
+ YangModelCmHandle unSynchronizedReadyCmHandle = syncUtils.getAnUnSynchronizedReadyCmHandle();
+ while (unSynchronizedReadyCmHandle != null) {
+ final String cmHandleId = unSynchronizedReadyCmHandle.getId();
+ log.debug("Cm-Handles found in READY and UNSYNCHRONIZED state: {}", cmHandleId);
+ final CompositeState compositeState = inventoryPersistence
+ .getCmHandleState(cmHandleId);
+ final String resourceData = syncUtils.getResourceData(cmHandleId);
+ if (resourceData == null) {
+ log.debug("Error accessing the node for Cm-Handle: {}", cmHandleId);
+ } else {
+ cpsDataService.saveData("NFP-Operational", cmHandleId,
+ resourceData, OffsetDateTime.now());
+ }
+ compositeState.setLastUpdateTimeNow();
+ compositeState.getDataStores()
+ .setOperationalDataStore(CompositeState.Operational.builder()
+ .syncState(SyncState.SYNCHRONIZED)
+ .lastSyncTime(CompositeState.nowInSyncTimeFormat()).build());
+ inventoryPersistence.saveCmHandleState(cmHandleId, compositeState);
+ unSynchronizedReadyCmHandle = syncUtils.getAnUnSynchronizedReadyCmHandle();
+ }
+ log.debug("No Cm-Handles currently found in an READY State and Operational Sync State is UNSYNCHRONIZED");
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
index d6aaa32bef..9cfa0a05c8 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
@@ -86,7 +86,7 @@ public class ModuleSyncWatchdog {
*/
@Scheduled(fixedDelayString = "${timers.locked-modules-sync.sleep-time-ms:300000}")
public void executeLockedMisbehavingCmHandlePoll() {
- final List<YangModelCmHandle> lockedMisbehavingCmHandles = syncUtils.getLockedMisbehavingCmHandles();
+ final List<YangModelCmHandle> lockedMisbehavingCmHandles = syncUtils.getLockedMisbehavingYangModelCmHandles();
for (final YangModelCmHandle lockedMisbehavingModelCmHandle: lockedMisbehavingCmHandles) {
final CompositeState updatedCompositeState = lockedMisbehavingModelCmHandle.getCompositeState();
updatedCompositeState.setCmHandleState(CmHandleState.ADVISED);
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
index 22eeabb0df..b5456ab149 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
@@ -21,24 +21,35 @@
package org.onap.cps.ncmp.api.inventory.sync;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.ImmutableMap;
import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
+import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.inventory.CmHandleState;
import org.onap.cps.ncmp.api.inventory.CompositeState;
import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.ncmp.api.inventory.LockReasonCategory;
+import org.onap.cps.ncmp.api.inventory.SyncState;
import org.onap.cps.spi.model.DataNode;
-import org.springframework.stereotype.Component;
+import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
@Slf4j
-@Component
+@Service
@RequiredArgsConstructor
public class SyncUtils {
@@ -46,6 +57,10 @@ public class SyncUtils {
private final InventoryPersistence inventoryPersistence;
+ private final DmiDataOperations dmiDataOperations;
+
+ private final JsonObjectMapper jsonObjectMapper;
+
private static final Pattern retryAttemptPattern = Pattern.compile("^Attempt #(\\d+) failed:");
/**
@@ -64,14 +79,38 @@ public class SyncUtils {
return inventoryPersistence.getYangModelCmHandle(cmHandleId);
}
+ /**
+ * First query data nodes for cm handles with CM Handle Operational Sync State in "UNSYNCHRONIZED" and
+ * randomly select a CM Handle and query the data nodes for CM Handle State in "READY".
+ *
+ * @return a random yang model cm handle with State in READY and Operation Sync State in "UNSYNCHRONIZED",
+ * return null if not found
+ */
+ public YangModelCmHandle getAnUnSynchronizedReadyCmHandle() {
+ final List<DataNode> unSynchronizedCmHandles = inventoryPersistence
+ .getCmHandlesByOperationalSyncState(SyncState.UNSYNCHRONIZED);
+ if (unSynchronizedCmHandles.isEmpty()) {
+ return null;
+ }
+ Collections.shuffle(unSynchronizedCmHandles);
+ for (final DataNode cmHandle : unSynchronizedCmHandles) {
+ final String cmHandleId = cmHandle.getLeaves().get("id").toString();
+ final List<DataNode> readyCmHandles = inventoryPersistence
+ .getCmHandlesByIdAndState(cmHandleId, CmHandleState.READY);
+ if (!readyCmHandles.isEmpty()) {
+ return inventoryPersistence.getYangModelCmHandle(cmHandleId);
+ }
+ }
+ return null;
+ }
/**
* Query data nodes for cm handles with an "LOCKED" cm handle state with reason LOCKED_MISBEHAVING".
*
* @return a random yang model cm handle with an ADVISED state, return null if not found
*/
- public List<YangModelCmHandle> getLockedMisbehavingCmHandles() {
- final List<DataNode> lockedCmHandleAsDataNodeList = inventoryPersistence.getCmHandlesByCpsPath(
+ public List<YangModelCmHandle> getLockedMisbehavingYangModelCmHandles() {
+ final List<DataNode> lockedCmHandleAsDataNodeList = inventoryPersistence.getCmHandleDataNodesByCpsPath(
"//lock-reason[@reason=\"LOCKED_MISBEHAVING\"]/ancestor::cm-handles");
return lockedCmHandleAsDataNodeList.stream()
.map(cmHandle -> YangDataConverter.convertCmHandleToYangModel(cmHandle,
@@ -99,4 +138,27 @@ public class SyncUtils {
.lockReasonCategory(lockReasonCategory).build());
}
+ /**
+ * Get the Resourece Data from Node through DMI Passthrough service.
+ *
+ * @param cmHandleId cm handle id
+ * @return optional string containing the resource data
+ */
+ public String getResourceData(final String cmHandleId) {
+ final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi(
+ cmHandleId, DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
+ UUID.randomUUID().toString());
+ if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) {
+ return getFirstResource(resourceDataResponseEntity.getBody());
+ }
+ return null;
+ }
+
+ private String getFirstResource(final Object responseBody) {
+ final String jsonObjectAsString = jsonObjectMapper.asJsonString(responseBody);
+ final JsonNode overallJsonNode = jsonObjectMapper.convertToJsonNode(jsonObjectAsString);
+ final Iterator<Map.Entry<String, JsonNode>> overallJsonTreeMap = overallJsonNode.fields();
+ final Map.Entry<String, JsonNode> firstElement = overallJsonTreeMap.next();
+ return jsonObjectMapper.asJsonString(ImmutableMap.of(firstElement.getKey(), firstElement.getValue()));
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
index b7ebf2965a..03825c2bbf 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
@@ -80,6 +80,20 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
'datastore running with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-running' | '&options=(a=1,b=2)'
}
+ def 'call get all resource data.'() {
+ given: 'the system returns a cm handle with a sample property'
+ mockYangModelCmHandleRetrieval([yangModelCmHandleProperty])
+ and: 'a positive response from DMI service when it is called with the expected parameters'
+ def responseFromDmi = new ResponseEntity<Object>(HttpStatus.OK)
+ def expectedUrl = dmiServiceBaseUrl + "passthrough-operational?resourceIdentifier=/"
+ mockDmiRestClient.postOperationWithJsonData(expectedUrl, '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}', READ) >> responseFromDmi
+ dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
+ when: 'get resource data is invoked'
+ def result = objectUnderTest.getResourceDataFromDmi(cmHandleId, PASSTHROUGH_OPERATIONAL, NO_REQUEST_ID)
+ then: 'the result is the response from the DMI service'
+ assert result == responseFromDmi
+ }
+
def 'Write data for pass-through:running datastore in DMI.'() {
given: 'a cm handle for #cmHandleId'
mockYangModelCmHandleRetrieval([yangModelCmHandleProperty])
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy
index d6f4ba6ede..60fec6f867 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy
@@ -47,11 +47,11 @@ class CompositeStateBuilderSpec extends Specification {
def "Composite State Specification"() {
when: 'using composite state builder '
def compositeState = new CompositeStateBuilder().withCmHandleState(CmHandleState.ADVISED)
- .withLockReason(LockReasonCategory.LOCKED_MISBEHAVING,"").withOperationalDataStores("UNSYNCHRONIZED",
+ .withLockReason(LockReasonCategory.LOCKED_MISBEHAVING,"").withOperationalDataStores(SyncState.UNSYNCHRONIZED,
formattedDateAndTime.toString()).withLastUpdatedTime(formattedDateAndTime).build()
then: 'it matches expected cm handle state and data store sync state'
assert compositeState.cmHandleState == CmHandleState.ADVISED
- assert compositeState.dataStores.operationalDataStore.syncState == 'UNSYNCHRONIZED'
+ assert compositeState.dataStores.operationalDataStore.syncState == SyncState.UNSYNCHRONIZED
}
def "Build composite state from DataNode "() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy
index 5387fc675f..0a6f8c350a 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy
@@ -54,7 +54,7 @@ class CompositeStateSpec extends Specification {
def dataStores() {
DataStores.builder().operationalDataStore(Operational.builder()
- .syncState('NONE_REQUESTED')
+ .syncState(SyncState.NONE_REQUESTED)
.lastSyncTime(formattedDateAndTime.toString()).build())
.build()
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
index e6346cb024..578225e87d 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
@@ -28,7 +28,6 @@ import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.model.DataNodeBuilder
import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Shared
import spock.lang.Specification
@@ -71,6 +70,9 @@ class InventoryPersistenceSpec extends Specification {
@Shared
def childDataNodesForCmHandleWithState = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/state", leaves: ['cm-handle-state': 'ADVISED'])]
+ @Shared
+ def static sampleDataNodes = [new DataNode()]
+
def "Retrieve CmHandle using datanode with #scenario."() {
given: 'the cps data service returns a data node from the DMI registry'
def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
@@ -148,13 +150,36 @@ class InventoryPersistenceSpec extends Specification {
given: 'a cm handle state to query'
def cmHandleState = CmHandleState.ADVISED
and: 'cps data service returns a list of data nodes'
- def dataNodes = [new DataNode()]
mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
- '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> dataNodes
+ '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
when: 'get cm handles by state is invoked'
def result = objectUnderTest.getCmHandlesByState(cmHandleState)
then: 'the returned result is a list of data nodes returned by cps data service'
- assert result == dataNodes
+ assert result == sampleDataNodes
+ }
+
+ def 'Get Cm Handles By State and Cm-Handle Id'() {
+ given: 'a cm handle state to query'
+ def cmHandleState = CmHandleState.READY
+ and: 'cps data service returns a list of data nodes'
+ mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+ '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+ when: 'get cm handles by state and id is invoked'
+ def result = objectUnderTest.getCmHandlesByIdAndState(cmHandleId, cmHandleState)
+ then: 'the returned result is a list of data nodes returned by cps data service'
+ assert result == sampleDataNodes
+ }
+
+ def 'Get Cm Handles By Operational Sync State : UNSYNCHRONIZED'() {
+ given: 'a cm handle state to query'
+ def cmHandleState = CmHandleState.READY
+ and: 'cps data service returns a list of data nodes'
+ mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+ '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+ when: 'get cm handles by operational sync state as UNSYNCHRONIZED is invoked'
+ def result = objectUnderTest.getCmHandlesByOperationalSyncState(SyncState.UNSYNCHRONIZED)
+ then: 'the returned result is a list of data nodes returned by cps data service'
+ assert result == sampleDataNodes
}
def 'Retrieve cm handle by cps path '() {
@@ -166,7 +191,7 @@ class InventoryPersistenceSpec extends Specification {
cpsPath, OMIT_DESCENDANTS)
>> Arrays.asList(cmHandleDataNode)
when: 'get cm handles by cps path is invoked'
- def result = objectUnderTest.getCmHandlesByCpsPath(cpsPath)
+ def result = objectUnderTest.getCmHandleDataNodesByCpsPath(cpsPath)
then: 'the returned result is a list of data nodes returned by cps data service'
assert result.contains(cmHandleDataNode)
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/DataSyncSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/DataSyncSpec.groovy
new file mode 100644
index 0000000000..b062635db0
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/DataSyncSpec.groovy
@@ -0,0 +1,101 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.inventory.sync
+
+import org.onap.cps.api.CpsDataService
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+import org.onap.cps.ncmp.api.inventory.CompositeState
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
+import org.onap.cps.ncmp.api.inventory.SyncState
+import spock.lang.Specification
+
+class DataSyncSpec extends Specification {
+
+ def mockInventoryPersistence = Mock(InventoryPersistence)
+
+ def mockCpsDataService = Mock(CpsDataService)
+
+ def mockSyncUtils = Mock(SyncUtils)
+
+ def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
+
+ def objectUnderTest = new DataSyncWatchdog(mockInventoryPersistence, mockCpsDataService, mockSyncUtils)
+
+ def compositeState = getCompositeState()
+
+ def yangModelCmHandle1 = createSampleYangModelCmHandle('some-cm-handle-1')
+
+ def yangModelCmHandle2 = createSampleYangModelCmHandle('some-cm-handle-2')
+
+ def 'Schedule Data Sync for Cm Handle State in READY and Operational Sync State in UNSYNCHRONIZED'() {
+ given: 'sample resource data'
+ def resourceData = jsonString;
+ and: 'sync utilities return a cm handle twice'
+ mockSyncUtils.getAnUnSynchronizedReadyCmHandle() >>> [yangModelCmHandle1, yangModelCmHandle2, null]
+ when: 'data sync poll is executed'
+ objectUnderTest.executeUnSynchronizedReadyCmHandlePoll()
+ then: 'the inventory persistence cm handle returns a composite state for the first cm handle'
+ 1 * mockInventoryPersistence.getCmHandleState('some-cm-handle-1') >> compositeState
+ and: 'the sync util returns first resource data'
+ 1 * mockSyncUtils.getResourceData('some-cm-handle-1') >> resourceData
+ and: 'the cm-handle data is saved'
+ 1 * mockCpsDataService.saveData('NFP-Operational', 'some-cm-handle-1', jsonString, _)
+ and: 'the first cm handle operational sync state is updated'
+ 1 * mockInventoryPersistence.saveCmHandleState('some-cm-handle-1', compositeState)
+ then: 'the inventory persistence cm handle returns a composite state for the second cm handle'
+ 1 * mockInventoryPersistence.getCmHandleState('some-cm-handle-2') >> compositeState
+ and: 'the sync util returns first resource data'
+ 1 * mockSyncUtils.getResourceData('some-cm-handle-2') >> resourceData
+ and: 'the cm-handle data is saved'
+ 1 * mockCpsDataService.saveData('NFP-Operational', 'some-cm-handle-2', jsonString, _)
+ and: 'the second cm handle operational sync state is updated from "UNSYNCHRONIZED" to "SYNCHRONIZED"'
+ 1 * mockInventoryPersistence.saveCmHandleState('some-cm-handle-2', compositeState)
+ }
+
+ def 'Schedule Data Sync for Cm Handle State in READY and Operational Sync State in UNSYNCHRONIZED which return empty data from Node'() {
+ given: 'cm handles in an ready state and operational sync state in unsynchronized'
+ and: 'sync utilities return a cm handle twice'
+ mockSyncUtils.getAnUnSynchronizedReadyCmHandle() >>> [yangModelCmHandle1, null]
+ when: 'data sync poll is executed'
+ objectUnderTest.executeUnSynchronizedReadyCmHandlePoll()
+ then: 'the inventory persistence cm handle returns a composite state for the first cm handle'
+ 1 * mockInventoryPersistence.getCmHandleState('some-cm-handle-1') >> compositeState
+ and: 'the sync util returns first resource data'
+ 1 * mockSyncUtils.getResourceData('some-cm-handle-1') >> null
+ and: 'the cm-handle data is not saved'
+ 0 * mockCpsDataService.saveData('NFP-Operational', 'some-cm-handle-1', jsonString, _)
+ }
+
+ def createSampleYangModelCmHandle(cmHandleId) {
+ def compositeState = getCompositeState()
+ return new YangModelCmHandle(id: cmHandleId, compositeState: compositeState)
+ }
+
+ def getCompositeState() {
+ def cmHandleState = CmHandleState.READY
+ def compositeState = new CompositeState(cmHandleState: cmHandleState)
+ compositeState.setDataStores(CompositeState.DataStores.builder()
+ .operationalDataStore(CompositeState.Operational.builder().syncState(SyncState.SYNCHRONIZED)
+ .build()).build())
+ return compositeState
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy
index 802035c726..8752bff582 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy
@@ -106,7 +106,7 @@ class ModuleSyncSpec extends Specification {
.withLockReason(LockReasonCategory.LOCKED_MISBEHAVING, '').build()
def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', compositeState: compositeState)
and: 'sync utilities return a cm handle twice'
- mockSyncUtils.getLockedMisbehavingCmHandles() >> [yangModelCmHandle, yangModelCmHandle]
+ mockSyncUtils.getLockedMisbehavingYangModelCmHandles() >> [yangModelCmHandle, yangModelCmHandle]
when: 'module sync poll is executed'
objectUnderTest.executeLockedMisbehavingCmHandlePoll()
then: 'the first cm handle is updated to state "ADVISED" from "READY"'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
index 15d1efe068..14f2015755 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
@@ -21,11 +21,19 @@
package org.onap.cps.ncmp.api.inventory.sync
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
+import org.onap.cps.ncmp.api.impl.operations.DmiOperations
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
import org.onap.cps.ncmp.api.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.inventory.LockReasonCategory
+import org.onap.cps.ncmp.api.inventory.SyncState
import org.onap.cps.spi.model.DataNode
+import org.onap.cps.utils.JsonObjectMapper
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
import spock.lang.Shared
import spock.lang.Specification
@@ -33,7 +41,11 @@ class SyncUtilsSpec extends Specification{
def mockInventoryPersistence = Mock(InventoryPersistence)
- def objectUnderTest = new SyncUtils(mockInventoryPersistence)
+ def mockDmiDataOperations = Mock(DmiDataOperations)
+
+ def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+
+ def objectUnderTest = new SyncUtils(mockInventoryPersistence, mockDmiDataOperations, jsonObjectMapper)
@Shared
def dataNode = new DataNode(leaves: ['id': 'cm-handle-123'])
@@ -67,15 +79,45 @@ class SyncUtilsSpec extends Specification{
'does not exist' | null || 'Attempt #1 failed: new error message'
'exists' | CompositeState.LockReason.builder().details("Attempt #2 failed: some error message").build() || 'Attempt #3 failed: new error message'
}
+
def 'Get all locked Cm-Handle where Lock Reason is LOCKED_MISBEHAVING cm handle #scenario'() {
given: 'the cps (persistence service) returns a collection of data nodes'
- mockInventoryPersistence.getCmHandlesByCpsPath(
+ mockInventoryPersistence.getCmHandleDataNodesByCpsPath(
'//lock-reason[@reason="LOCKED_MISBEHAVING"]/ancestor::cm-handles') >> [dataNode ]
when: 'get locked Misbehaving cm handle is called'
- def result = objectUnderTest.getLockedMisbehavingCmHandles()
+ def result = objectUnderTest.getLockedMisbehavingYangModelCmHandles()
then: 'the returned cm handle collection is the correct size'
result.size() == 1
and: 'the correct cm handle is returned'
result[0].id == 'cm-handle-123'
}
+
+ def 'Get a Cm-Handle where Operational Sync state is UnSynchronized and Cm-handle state is READY and #scenario'() {
+ given: 'the inventory persistence service returns a collection of data nodes'
+ mockInventoryPersistence.getCmHandlesByOperationalSyncState(SyncState.UNSYNCHRONIZED) >> unSynchronizedDataNodes
+ mockInventoryPersistence.getCmHandlesByIdAndState("cm-handle-123", CmHandleState.READY) >> readyDataNodes
+ when: 'get advised cm handle is called'
+ objectUnderTest.getAnUnSynchronizedReadyCmHandle()
+ then: 'the returned data node collection is the correct size'
+ readyDataNodes.size() == expectedDataNodeSize
+ and: 'get yang model cm handles is invoked the correct number of times'
+ expectedCallsToGetYangModelCmHandle * mockInventoryPersistence.getYangModelCmHandle('cm-handle-123')
+ where: 'the following scenarios are used'
+ scenario | unSynchronizedDataNodes | readyDataNodes || expectedCallsToGetYangModelCmHandle | expectedDataNodeSize
+ 'exists' | [dataNode] | [dataNode] || 1 | 1
+ 'unsynchronized exist but not ready' | [dataNode] | [] || 0 | 0
+ 'does not exist' | [] | [] || 0 | 0
+ }
+
+ def 'Get resource data through DMI Operations #scenario'() {
+ given: 'the inventory persistence service returns a collection of data nodes'
+ def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
+ JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString);
+ def responseEntity = new ResponseEntity<>(jsonNode, HttpStatus.OK)
+ mockDmiDataOperations.getResourceDataFromDmi('cm-handle-123', DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, _) >> responseEntity
+ when: 'get resource data is called'
+ def result = objectUnderTest.getResourceData('cm-handle-123')
+ then: 'the returned data is correct'
+ result == jsonString
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/YangModelCmHandleSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/YangModelCmHandleSpec.groovy
index cdfcf59ef4..3376691ee7 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/YangModelCmHandleSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/YangModelCmHandleSpec.groovy
@@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.models
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.inventory.CmHandleState
-import org.onap.ncmp.cmhandle.lcm.event.Event
import spock.lang.Specification
import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA
diff --git a/cps-service/src/main/java/org/onap/cps/utils/JsonObjectMapper.java b/cps-service/src/main/java/org/onap/cps/utils/JsonObjectMapper.java
index 2459b51af4..338a841a7a 100644
--- a/cps-service/src/main/java/org/onap/cps/utils/JsonObjectMapper.java
+++ b/cps-service/src/main/java/org/onap/cps/utils/JsonObjectMapper.java
@@ -21,6 +21,7 @@
package org.onap.cps.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -87,4 +88,20 @@ public class JsonObjectMapper {
+ "JSON content to specific class type.", e.getMessage());
}
}
+
+ /**
+ * Deserialize JSON content from given JSON content String to JsonNode.
+ *
+ * @param jsonContent JSON content
+ * @return a json node
+ */
+ public JsonNode convertToJsonNode(final String jsonContent) {
+ try {
+ return objectMapper.readTree(jsonContent);
+ } catch (final JsonProcessingException e) {
+ log.error("Parsing error occurred while converting JSON content to Json Node.");
+ throw new DataValidationException("Parsing error occurred while converting "
+ + "JSON content to Json Node.", e.getMessage());
+ }
+ }
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
index f9b8febd43..acb52413f8 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
@@ -84,4 +84,22 @@ class JsonObjectMapperSpec extends Specification {
then: 'an exception is thrown'
thrown(DataValidationException)
}
+
+ def 'Map a structurally compatible json String to JsonNode.'() {
+ given: 'Unstructured object'
+ def content = '{ "nest": { "birds": "bird" } }'
+ when: 'the object is mapped to string'
+ def result = jsonObjectMapper.convertToJsonNode(content);
+ then: 'the result is a valid JsonNode'
+ result.fieldNames().next() == "nest"
+ }
+
+ def 'Map a unstructured json String to JsonNode.'() {
+ given: 'Unstructured object'
+ def content = '{ "nest": { "birds": "bird" }] }'
+ when: 'the object is mapped to string'
+ jsonObjectMapper.convertToJsonNode(content);
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ }
}