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
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
*/
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
private final CpsValidator cpsValidator;
private final IMap<String, String> cmHandleIdPerAlternateId;
+ @Value("${ignore.r20250722.model:true}")
+ private boolean ignoreModelR20250722;
+
/**
* initialize an inventory persistence object.
*
@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<String, CompositeState> cmHandleStatePerCmHandleId) {
- final Map<String, String> cmHandlesJsonDataMap = new HashMap<>();
- cmHandleStatePerCmHandleId.forEach((cmHandleId, compositeState) -> {
+ final Map<String, String> cmHandlesJsonDataMap = new HashMap<>(cmHandleStatePerCmHandleId.size());
+ final List<Map<String, String>> topLevelStateUpdates = new ArrayList<>(cmHandleStatePerCmHandleId.size());
+
+ for (final Map.Entry<String, CompositeState> 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<String, String> 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);
+ }
}
}
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);
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
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 {
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
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.'() {
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
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.'() {
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)
}
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()
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
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}']",
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
+}