From: egernug Date: Tue, 6 Jan 2026 16:16:30 +0000 (+0000) Subject: Synchronize CompositeState and cm-handle-state X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=1c057e0b5312b86a2a98a21ef88442ee96f9dfeb;p=cps.git Synchronize CompositeState and cm-handle-state - Inverted behaviour of flag to ignore instead of enable new model - Amended method to sync both CompositeState and cm-handle-state if new model is used Issue-ID: CPS-3106 Change-Id: I93e08ffdd06c5a5566f3559b5892d6e26e5fccdc Signed-off-by: egernug Synchronize CompositeState and cm-handle-state - Inverted behaviour of flag to ignore instead of enable new model - Amended method to sync both CompositeState and cm-handle-state if new model is used Issue-ID: CPS-3106 Change-Id: I93e08ffdd06c5a5566f3559b5892d6e26e5fccdc Signed-off-by: egernug --- diff --git a/cps-charts/values.yaml b/cps-charts/values.yaml index 87513b85d0..4b03d5a12f 100644 --- a/cps-charts/values.yaml +++ b/cps-charts/values.yaml @@ -108,7 +108,7 @@ cps: CPS_MONITORING_MICROMETER_JVM_EXTRAS: "true" JAVA_TOOL_OPTIONS: "-XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0" HAZELCAST_MODE_KUBERNETES_ENABLED: "true" - NCMP_INVENTORY_MODEL_UPGRADE_R20250722_ENABLED: 'false' + IGNORE_R20250722_MODEL: "true" kafka: enabled: true diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java index 07e0118418..453715791b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java @@ -51,7 +51,7 @@ public interface InventoryPersistence extends NcmpPersistence { void saveCmHandleState(String cmHandleId, CompositeState compositeState); /** - * Save all cm handles states in batch. + * Save all cm handle CompositeStates and cmHandleStatus in batch. * * @param cmHandleStatePerCmHandleId contains cm handle id and updated state */ diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java index 238436ddc5..b3e9b118f8 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java @@ -55,6 +55,7 @@ import org.onap.cps.utils.ContentType; import org.onap.cps.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Slf4j @@ -67,6 +68,9 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv private final CpsValidator cpsValidator; private final IMap cmHandleIdPerAlternateId; + @Value("${ignore.r20250722.model:true}") + private boolean ignoreModelR20250722; + /** * initialize an inventory persistence object. * @@ -100,25 +104,39 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv @Override public void saveCmHandleState(final String cmHandleId, final CompositeState compositeState) { - final String cmHandleJsonData = createStateJsonData(jsonObjectMapper.asJsonString(compositeState)); - cpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now(), ContentType.JSON); + saveCmHandleStateBatch(Collections.singletonMap(cmHandleId, compositeState)); } @Override public void saveCmHandleStateBatch(final Map cmHandleStatePerCmHandleId) { - final Map cmHandlesJsonDataMap = new HashMap<>(); - cmHandleStatePerCmHandleId.forEach((cmHandleId, compositeState) -> { + final Map cmHandlesJsonDataMap = new HashMap<>(cmHandleStatePerCmHandleId.size()); + final List> topLevelStateUpdates = new ArrayList<>(cmHandleStatePerCmHandleId.size()); + + for (final Map.Entry entry : cmHandleStatePerCmHandleId.entrySet()) { + final String cmHandleId = entry.getKey(); + final CompositeState compositeState = entry.getValue(); if (exists(cmHandleId)) { cmHandlesJsonDataMap.put(getXPathForCmHandleById(cmHandleId), createStateJsonData(jsonObjectMapper.asJsonString(compositeState))); + if (!ignoreModelR20250722) { + final Map topLevelUpdate = new HashMap<>(); + topLevelUpdate.put("id", cmHandleId); + topLevelUpdate.put("cm-handle-state", compositeState.getCmHandleState().name()); + topLevelStateUpdates.add(topLevelUpdate); + } } else { log.warn("Failure to save state for cmHandle id '{}' as it does not exist in cache", cmHandleId); } - }); + } if (!cmHandlesJsonDataMap.isEmpty()) { cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, OffsetDateTime.now(), ContentType.JSON); + if (!ignoreModelR20250722) { + cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + NCMP_DMI_REGISTRY_PARENT, + jsonObjectMapper.asJsonString(Map.of("cm-handles", topLevelStateUpdates)), + OffsetDateTime.now(), ContentType.JSON); + } } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java index 37e1ae3a6f..ba7d1852c2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java @@ -102,8 +102,8 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState if (isNew(cmHandleTransitionPair.currentYangModelCmHandle().getCompositeState())) { newCmHandles.add(cmHandleTransitionPair.targetYangModelCmHandle()); } else if (!isDeleted(cmHandleTransitionPair.targetYangModelCmHandle().getCompositeState())) { - compositeStatePerCmHandleId.put(cmHandleTransitionPair.targetYangModelCmHandle().getId(), - cmHandleTransitionPair.targetYangModelCmHandle().getCompositeState()); + final YangModelCmHandle targetCmHandle = cmHandleTransitionPair.targetYangModelCmHandle(); + compositeStatePerCmHandleId.put(targetCmHandle.getId(), targetCmHandle.getCompositeState()); } }); inventoryPersistence.saveCmHandleBatch(newCmHandles); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java index 6d08e2cd34..6bf100818e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java @@ -43,13 +43,13 @@ public class InventoryModelLoader extends AbstractModelLoader { private final DataMigration dataMigration; private final ApplicationEventPublisher applicationEventPublisher; - private static final String PREVIOUS_SCHEMA_SET_NAME = "dmi-registry-2024-02-23"; + private static final String SCHEMA_SET_NAME = "dmi-registry-2024-02-23"; private static final String NEW_INVENTORY_SCHEMA_SET_NAME = "dmi-registry-2025-07-22"; private static final String INVENTORY_YANG_MODULE_NAME = "dmi-registry"; private static final int MIGRATION_BATCH_SIZE = 300; - @Value("${ncmp.inventory.model.upgrade.r20250722.enabled:false}") - private boolean newRevisionEnabled; + @Value("${ignore.r20250722.model:true}") + private boolean ignoreModelR20250722; /** * Creates a new {@code InventoryModelLoader} instance responsible for onboarding or upgrading @@ -75,13 +75,14 @@ public class InventoryModelLoader extends AbstractModelLoader { if (isMaster) { log.info("Model Loader #2 Started: NCMP Inventory Models"); final String schemaToInstall = - newRevisionEnabled ? NEW_INVENTORY_SCHEMA_SET_NAME : PREVIOUS_SCHEMA_SET_NAME; + ignoreModelR20250722 ? SCHEMA_SET_NAME : NEW_INVENTORY_SCHEMA_SET_NAME; final String moduleRevision = getModuleRevision(schemaToInstall); + log.info("Model Loader #2 Schema Set: {}", schemaToInstall); if (isModuleRevisionInstalled(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, INVENTORY_YANG_MODULE_NAME, moduleRevision)) { log.info("Model Loader #2: Revision {} is already installed.", moduleRevision); - } else if (newRevisionEnabled && doesAnchorExist(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR)) { + } else if (!ignoreModelR20250722 && doesAnchorExist(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR)) { log.info("Model Loader #2: Upgrading already installed inventory to revision {}.", moduleRevision); upgradeAndMigrateInventoryModel(); } else { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy index f0b24408e5..cca16c8eab 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy @@ -39,6 +39,7 @@ import org.onap.cps.ncmp.impl.models.CmHandleMigrationDetail import org.onap.cps.utils.ContentType import org.onap.cps.utils.CpsValidator import org.onap.cps.utils.JsonObjectMapper +import org.springframework.test.util.ReflectionTestUtils import spock.lang.Shared import spock.lang.Specification @@ -183,15 +184,31 @@ class InventoryPersistenceImplSpec extends Specification { given: 'a cm handle and a composite state' def cmHandleId = 'ch-1' def compositeState = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime) + and: 'alternate id cache contains the given cm handle reference' + mockCmHandleIdPerAlternateId.containsKey(_) >> true when: 'update cm handle state is invoked with the #scenario state' objectUnderTest.saveCmHandleState(cmHandleId, compositeState) - then: 'update node leaves is invoked with the correct params' - 1 * mockCpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@id=\'ch-1\']', expectedJsonData, _ as OffsetDateTime, ContentType.JSON) + then: 'update data nodes and descendants is invoked with the correct params' + 1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, ['/dmi-registry/cm-handles[@id=\'ch-1\']': expectedJsonData], _ as OffsetDateTime, ContentType.JSON) where: 'the following states are used' scenario | cmHandleState || expectedJsonData 'READY' | CmHandleState.READY || '{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}' 'LOCKED' | CmHandleState.LOCKED || '{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}' - 'DELETING' | CmHandleState.DELETING || '{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}' + 'DELETING' | CmHandleState.DELETING || '{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}'} + + + def 'Update Cm Handle State when model upgrade is enabled.'() { + given: 'a cm handle and a composite state' + def cmHandleId = 'ch-1' + def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, lastUpdateTime: formattedDateAndTime) + and: 'alternate id cache contains the given cm handle reference' + mockCmHandleIdPerAlternateId.containsKey(_) >> true + when: 'update cm handle state is invoked' + objectUnderTest.saveCmHandleState(cmHandleId, compositeState) + then: 'update data nodes and descendants is invoked' + 1 * mockCpsDataService.updateDataNodesAndDescendants(*_) + and: 'update node leaves is also invoked for top-level state' + 1 * mockCpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry', _, _ as OffsetDateTime, ContentType.JSON) } def 'Update Cm Handles with #scenario States.'() { @@ -203,7 +220,7 @@ class InventoryPersistenceImplSpec extends Specification { when: 'update cm handle state is invoked with the #scenario state' def cmHandleStateMap = ['ch-11' : compositeState1, 'ch-12' : compositeState2] objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap) - then: 'update node leaves is invoked with the correct params' + then: 'update data nodes and descendants is invoked with the correct params' 1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, _ as OffsetDateTime, ContentType.JSON) where: 'the following states are used' scenario | cmHandleState || cmHandlesJsonDataMap @@ -221,14 +238,15 @@ class InventoryPersistenceImplSpec extends Specification { mockCmHandleIdPerAlternateId.containsValue(_) >> valueExists when: 'we update the state of a cm handle when #scenario' objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap) - then: 'update node leaves is invoked correct number of times' + then: 'update data nodes and descendants is invoked correct number of times' expectedCalls * mockCpsDataService.updateDataNodesAndDescendants(*_) + and: 'update node leaves is invoked correct number of times' + expectedCalls * mockCpsDataService.updateNodeLeaves(*_) where: 'the following cm handle ids are used' scenario | keyExists | valueExists || expectedCalls 'id exists as key' | true | false || 1 'id exists as value'| false | true || 1 'id does not exist' | false | false || 0 - } def 'Getting module definitions by module.'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy index bddb52d49b..45786bfaaf 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy @@ -72,7 +72,7 @@ class InventoryModelLoaderSpec extends Specification { objectUnderTest.isMaster = true expectedPreviousYangResourceToContentMap = objectUnderTest.mapYangResourcesToContent('dmi-registry@2024-02-23.yang') expectedNewYangResourceToContentMap = objectUnderTest.mapYangResourcesToContent('dmi-registry@2025-07-22.yang') - objectUnderTest.newRevisionEnabled = true + objectUnderTest.ignoreModelR20250722 = false logger.setLevel(Level.DEBUG) loggingListAppender = new ListAppender() logger.addAppender(loggingListAppender) @@ -86,8 +86,8 @@ class InventoryModelLoaderSpec extends Specification { } def 'Onboard subscription model via application ready event.'() { - given: 'dataspace is ready for use with default newRevisionEnabled flag' - objectUnderTest.newRevisionEnabled = false + given: 'dataspace is ready for use with current model' + objectUnderTest.ignoreModelR20250722 = true mockCpsAdminService.getDataspace(NCMP_DATASPACE_NAME) >> new Dataspace('') and: 'module revision does not exist' mockCpsModuleService.getModuleDefinitionsByAnchorAndModule(_, _, _, _) >> Collections.emptyList() diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 432633574e..e78a789cc0 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -74,7 +74,7 @@ services: JAVA_TOOL_OPTIONS: "-XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0" ### DEBUG: Uncomment next line to enable java debugging (and comment out above line!) ###JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 - NCMP_INVENTORY_MODEL_UPGRADE_R20250722_ENABLED: 'false' + IGNORE_R20250722_MODEL: 'true' restart: on-failure:3 deploy: replicas: 0 diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/inventory/DataMigrationIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/inventory/DataMigrationIntegrationSpec.groovy index 8b31db68d0..95db10f20d 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/inventory/DataMigrationIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/inventory/DataMigrationIntegrationSpec.groovy @@ -21,22 +21,26 @@ package org.onap.cps.integration.functional.ncmp.inventory import org.onap.cps.integration.base.CpsIntegrationSpecBase -import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle -import org.onap.cps.ncmp.init.DataMigration +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.init.InventoryModelLoader import org.onap.cps.utils.ContentType import org.springframework.beans.factory.annotation.Autowired -import org.springframework.test.context.TestPropertySource +import org.springframework.test.util.ReflectionTestUtils -@TestPropertySource(properties = ["ncmp.inventory.model.upgrade.r20250722.enabled=true"]) class DataMigrationIntegrationSpec extends CpsIntegrationSpecBase { @Autowired - DataMigration objectUnderTest + InventoryModelLoader objectUnderTest + + @Autowired + InventoryPersistence inventoryPersistence def 'Migrate inventory with batch processing.'() { - given: 'DMI will return modules when requested' + given: 'start with the old models (ignore upgrade)' + ReflectionTestUtils.setField(inventoryPersistence, 'ignoreModelR20250722', true) + and: 'DMI will return modules when requested' dmiDispatcher1.moduleNamesPerCmHandleId = (1..2).collectEntries{ ['ch-'+it, ['M1']] } - and: 'multiple CM handles registered, (top level) status is null' + and: 'multiple CM handles registered with old model' (1..2).each { registerCmHandle(DMI1_URL, 'ch-'+it, NO_MODULE_SET_TAG) cpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='ch-${it}']", @@ -44,21 +48,19 @@ class DataMigrationIntegrationSpec extends CpsIntegrationSpecBase { def someCmHandle = networkCmProxyInventoryFacade.getNcmpServiceCmHandle('ch-'+it) assert someCmHandle.getCmHandleStatus() == null assert someCmHandle.getCompositeState().getCmHandleState().name() == 'READY' - assert someCmHandle.getDmiProperties() == null - assert someCmHandle.getAdditionalProperties() == ['prop1':'value1'] } - when: 'migration is executed' - objectUnderTest.migrateInventoryToModelRelease20250722(1) - then: 'all CM handles are processed successfully,' + - 'the (top level) status is set to the same value as the name of the complex state value' + when: 'the new (more performant) model is enabled (no longer ignored)' + ReflectionTestUtils.setField(inventoryPersistence, 'ignoreModelR20250722', false) + ReflectionTestUtils.setField(objectUnderTest, 'ignoreModelR20250722', false) + and: 'inventory is upgraded to the new revision' + objectUnderTest.onboardOrUpgradeModel() + then: 'all CM handles have top-level cm-handle-state populated' (1..2).each { def someCmHandle = networkCmProxyInventoryFacade.getNcmpServiceCmHandle('ch-'+it) assert someCmHandle.getCmHandleStatus() == 'READY' assert someCmHandle.getCompositeState().getCmHandleState().name() == 'READY' - assert someCmHandle.getDmiProperties() == '{"prop1":"value1"}' - assert someCmHandle.getAdditionalProperties() == ['prop1':'value1'] } cleanup: 'deregister CM handles' deregisterCmHandles(DMI1_URL, (1..2).collect{ 'ch-'+it }) } -} \ No newline at end of file +}