From: Toine Siebelink Date: Mon, 16 Oct 2023 13:51:05 +0000 (+0000) Subject: Merge "Add withTrustLevel condition to CmHandle Query API" X-Git-Tag: 3.3.9~18 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=624f04c03146e002bab2ca88e55b7f586c6e1aff;hp=7aae9b8af2c82336882d0ae864b970b1885a63a8;p=cps.git Merge "Add withTrustLevel condition to CmHandle Query API" --- diff --git a/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json new file mode 100644 index 000000000..caffb6334 --- /dev/null +++ b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-merge-dmi-in-event-schema:1.0.0", + "$ref": "#/definitions/CmSubscriptionMergeDmiInEvent", + "definitions": { + "CmSubscriptionMergeDmiInEvent": { + "description": "The payload for cm subscription merge event incoming message from NCMP.", + "type": "object", + "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_dmi.CmSubscriptionMergeDmiInEvent", + "additionalProperties": false, + "properties": { + "data": { + "$ref": "#/definitions/data" + } + }, + "required": [ + "data" + ] + }, + "data": { + "type": "object", + "description": "The actual data containing information about the targets and subscription", + "additionalProperties": false, + "properties": { + "cmhandles": { + "type": "array", + "items": { + "type": "object", + "description": "Details for the target cmhandles", + "additionalProperties": false, + "properties": { + "cmhandleId": { + "type": "string" + }, + "private-properties": { + "type": "object", + "existingJavaType": "java.util.Map", + "items": { + "type": "string" + } + } + } + } + }, + "predicates": { + "type": "array", + "description": "Additional values to be added into the subscription", + "items": { + "type": "object", + "properties": { + "targetFilter": { + "description": "CM Handles to be targeted by the subscription", + "type": "array", + "items": { + "type": "string" + } + }, + "scopeFilter": { + "type": "object", + "properties": { + "datastore": { + "description": "datastore which is to be used by the subscription", + "type": "string", + "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"] + }, + "xpath-filter": { + "description": "filter to be applied to the CM Handles through this event", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "xpath-filter" + ] + } + }, + "additionalProperties": false, + "required": [ + "targetFilter" + ] + }, + "additionalProperties": false + } + }, + "required": [ + "cmhandles", + "predicates" + ] + } + } +} \ No newline at end of file diff --git a/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json new file mode 100644 index 000000000..274206256 --- /dev/null +++ b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json @@ -0,0 +1,74 @@ +{ + "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-merge-ncmp-in-event:1.0.0", + "$ref": "#/definitions/CmSubscriptionMergeNcmpInEvent", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "definitions": { + "CmSubscriptionMergeNcmpInEvent": { + "description": "The payload for subscription merge event.", + "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.client_to_ncmp.CmSubscriptionMergeNcmpInEvent", + "properties": { + "data": { + "properties": { + "subscriptionId": { + "description": "The subscription details.", + "type": "string" + }, + "predicates": { + "type": "array", + "description": "Additional values to be added into the subscription", + "items": { + "type": "object", + "properties": { + "targetFilter": { + "description": "CM Handles to be targeted by the subscription", + "type": "array", + "items": { + "type": "string" + } + }, + "scopeFilter": { + "type": "object", + "properties": { + "datastore": { + "description": "datastore which is to be used by the subscription", + "type": "string", + "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"] + }, + "xpath-filter": { + "description": "filter to be applied to the CM Handles through this event", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "xpath-filter" + ] + } + }, + "additionalProperties": false, + "required": [ + "targetFilter" + ] + }, + "additionalProperties": false + } + }, + "required": [ + "subscriptionId", + "predicates" + ], + "type": "object", + "additionalProperties": false + } + }, + "type": "object", + "additionalProperties": false, + "required": [ + "data" + ] + } + } +} \ No newline at end of file diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index 9bae794a3..022e2bab7 100644 --- a/cps-ncmp-rest/docs/openapi/components.yaml +++ b/cps-ncmp-rest/docs/openapi/components.yaml @@ -87,6 +87,8 @@ components: items: type: string example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ] + upgradedCmHandles: + $ref: '#/components/schemas/UpgradedCmHandles' DmiPluginRegistrationErrorResponse: type: object properties: @@ -102,6 +104,10 @@ components: type: array items: $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse' + failedUpgradeCmHandles: + type: array + items: + $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse' CmHandlerRegistrationErrorResponse: type: object properties: @@ -135,6 +141,20 @@ components: additionalProperties: type: string example: my-property + #Module upgrade schema + UpgradedCmHandles: + required: + - cmHandles + type: object + properties: + cmHandles: + type: array + items: + type: string + example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ] + moduleSetTag: + type: string + example: 'my-module-set-tag' #Response Schemas RestModuleReference: diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java index b3f36f9c8..af785d565 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java @@ -48,6 +48,9 @@ public interface NcmpRestInputMapper { @Mapping(source = "removedCmHandles", target = "removedCmHandles", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT) + @Mapping(source = "upgradedCmHandles", target = "upgradedCmHandles", + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT) DmiPluginRegistration toDmiPluginRegistration(final RestDmiPluginRegistration restDmiPluginRegistration); @Mapping(source = "cmHandle", target = "cmHandleId") diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java index cd61c5a4a..453abcabb 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java @@ -110,16 +110,16 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor getFailedResponses(dmiPluginRegistrationResponse.getUpdatedCmHandles())); dmiPluginRegistrationErrorResponse.setFailedRemovedCmHandles( getFailedResponses(dmiPluginRegistrationResponse.getRemovedCmHandles())); - + dmiPluginRegistrationErrorResponse.setFailedUpgradeCmHandles( + getFailedResponses(dmiPluginRegistrationResponse.getUpgradedCmHandles())); return dmiPluginRegistrationErrorResponse; } private List getFailedResponses( - final List cmHandleRegistrationResponseList) { + final List cmHandleRegistrationResponseList) { return cmHandleRegistrationResponseList.stream() - .filter(cmHandleRegistrationResponse -> cmHandleRegistrationResponse.getStatus() == Status.FAILURE) - .map(this::toCmHandleRegistrationErrorResponse) - .collect(Collectors.toList()); + .filter(cmHandleRegistrationResponse -> cmHandleRegistrationResponse.getStatus() == Status.FAILURE) + .map(this::toCmHandleRegistrationErrorResponse).collect(Collectors.toList()); } private CmHandlerRegistrationErrorResponse toCmHandleRegistrationErrorResponse( diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java index 82dc4837f..b436540fc 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java @@ -20,6 +20,8 @@ package org.onap.cps.ncmp.rest.mapper; +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING; + import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; @@ -75,8 +77,10 @@ public interface CmHandleStateMapper { @Named("toExternalLockReason") static LockReason toExternalLockReason(CompositeState.LockReason internalLockReason) { final LockReason externalLockReason = new LockReason(); - if (internalLockReason.getLockReasonCategory() != null) { - externalLockReason.setReason("LOCKED_MISBEHAVING"); + if (internalLockReason.getLockReasonCategory() == null) { + externalLockReason.setReason(LOCKED_MISBEHAVING.name()); + } else { + externalLockReason.setReason(internalLockReason.getLockReasonCategory().name()); } externalLockReason.setDetails(internalLockReason.getDetails()); return externalLockReason; 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 6bfa5936e..de044d0aa 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 @@ -615,8 +615,7 @@ class NetworkCmProxyControllerSpec extends Specification { def expectedContent = [ '"state":', '"cmHandleState":"ADVISED"', - '"reason":"LOCKED_MISBEHAVING"', - '"details":"lock details"', + '"lockReason":{"reason":"MODULE_SYNC_FAILED","details":"lock details"}', '"lastUpdateTime":"2022-12-31T20:30:40.000+0000"', '"dataSyncEnabled":false', '"dataSyncState":', diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy index 1fa83a539..f394f9193 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package org.onap.cps.ncmp.rest.mapper +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_SYNC_FAILED import org.mapstruct.factory.Mappers @@ -55,7 +56,7 @@ class CmHandleStateMapperSpec extends Specification { and: 'mapped result should have correct values' assert !result.dataSyncEnabled assert result.lastUpdateTime == formattedDateAndTime - assert result.lockReason.reason == 'LOCKED_MISBEHAVING' + assert result.lockReason.reason == MODULE_SYNC_FAILED.name() assert result.lockReason.details == 'locked details' assert result.cmHandleState == 'ADVISED' assert result.dataSyncState.operational.getSyncState() != null @@ -69,17 +70,17 @@ class CmHandleStateMapperSpec extends Specification { def 'Internal to External Lock Reason Mapping of #scenario'() { given: 'a LOCKED composite state with locked reason of #scenario' - def compositeState = new CompositeStateBuilder() + def compositeState = new CompositeStateBuilder() .withCmHandleState(CmHandleState.LOCKED) .withLockReason(lockReason, '').build() when: 'the composite state is mapped to a CMHandle composite state' - def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState) + def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState) then: 'the composite state contains the expected lock Reason and details' - result.getLockReason().getReason() == expectedExternalLockReason + result.getLockReason().getReason() == (expectedExternalLockReason as String) where: scenario | lockReason || expectedExternalLockReason - 'MODULE_SYNC_FAILED' | MODULE_SYNC_FAILED || 'LOCKED_MISBEHAVING' - 'null value' | null || null + 'MODULE_SYNC_FAILED' | MODULE_SYNC_FAILED || MODULE_SYNC_FAILED + 'null value' | null || LOCKED_MISBEHAVING } } diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index 04a8345c9..d7c177473 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -34,7 +34,7 @@ cps-ncmp-service - 0.98 + 0.96 diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java index 247d1c296..1f6c94869 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java @@ -174,7 +174,8 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH } try { cpsPathQueryResult = collectCmHandleIdsFromDataNodes( - cmHandleQueries.queryCmHandleDataNodesByCpsPath(cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS)); + cmHandleQueries.queryCmHandleAncestorsByCpsPath( + cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS)); } catch (final PathParsingException pathParsingException) { throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(), pathParsingException); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index fa2fb6e8d..a37b27199 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -25,14 +25,17 @@ package org.onap.cps.ncmp.api.impl; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE; import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters; import com.google.common.collect.Lists; import com.hazelcast.map.IMap; +import java.text.MessageFormat; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; @@ -51,6 +54,7 @@ import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; import org.onap.cps.ncmp.api.impl.inventory.CompositeState; +import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder; import org.onap.cps.ncmp.api.impl.inventory.CompositeStateUtils; import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState; import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; @@ -68,6 +72,7 @@ import org.onap.cps.ncmp.api.models.DataOperationRequest; import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.models.UpgradedCmHandles; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.CpsException; @@ -104,18 +109,23 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService if (!dmiPluginRegistration.getRemovedCmHandles().isEmpty()) { dmiPluginRegistrationResponse.setRemovedCmHandles( - parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration.getRemovedCmHandles())); + parseAndProcessDeletedCmHandlesInRegistration(dmiPluginRegistration.getRemovedCmHandles())); } if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) { dmiPluginRegistrationResponse.setCreatedCmHandles( - parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)); + parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)); } if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) { dmiPluginRegistrationResponse.setUpdatedCmHandles( networkCmProxyDataServicePropertyHandler .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); } + if (dmiPluginRegistration.getUpgradedCmHandles() != null + && !dmiPluginRegistration.getUpgradedCmHandles().getCmHandles().isEmpty()) { + dmiPluginRegistrationResponse.setUpgradedCmHandles( + parseAndProcessUpgradedCmHandlesInRegistration(dmiPluginRegistration)); + } setTrustLevelPerDmiPlugin(dmiPluginRegistration); @@ -212,8 +222,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ @Override public void setDataSyncEnabled(final String cmHandleId, final boolean dataSyncEnabled) { - final CompositeState compositeState = inventoryPersistence - .getCmHandleState(cmHandleId); + final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); if (compositeState.getDataSyncEnabled().equals(dataSyncEnabled)) { log.info("Data-Sync Enabled flag is already: {} ", dataSyncEnabled); } else if (compositeState.getCmHandleState() != CmHandleState.READY) { @@ -276,8 +285,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ @Override public Map getCmHandlePublicProperties(final String cmHandleId) { - final YangModelCmHandle yangModelCmHandle = - inventoryPersistence.getYangModelCmHandle(cmHandleId); + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); final List yangModelPublicProperties = yangModelCmHandle.getPublicProperties(); final Map cmHandlePublicProperties = new HashMap<>(); YangDataConverter.asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties); @@ -301,7 +309,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService * @param dmiPluginRegistration dmi plugin registration information. * @return cm-handle registration response for create cm-handle requests. */ - public List parseAndCreateCmHandlesInDmiRegistrationAndSyncModules( + public List parseAndProcessCreatedCmHandlesInRegistration( final DmiPluginRegistration dmiPluginRegistration) { final Map cmHandleStatePerCmHandle = new HashMap<>(); dmiPluginRegistration.getCreatedCmHandles() @@ -310,18 +318,19 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService dmiPluginRegistration.getDmiPlugin(), dmiPluginRegistration.getDmiDataPlugin(), dmiPluginRegistration.getDmiModelPlugin(), - cmHandle); + cmHandle, + cmHandle.getModuleSetTag()); cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.ADVISED); }); return registerNewCmHandles(cmHandleStatePerCmHandle); } - protected List parseAndRemoveCmHandlesInDmiRegistration( + protected List parseAndProcessDeletedCmHandlesInRegistration( final List tobeRemovedCmHandles) { final List cmHandleRegistrationResponses = new ArrayList<>(tobeRemovedCmHandles.size()); final Collection yangModelCmHandles = - inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles); + inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles); updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING); @@ -351,6 +360,42 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return cmHandleRegistrationResponses; } + protected List parseAndProcessUpgradedCmHandlesInRegistration( + final DmiPluginRegistration dmiPluginRegistration) { + + final UpgradedCmHandles upgradedCmHandles = dmiPluginRegistration.getUpgradedCmHandles(); + final String moduleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag(); + final Map cmHandleStatePerCmHandle = + new HashMap<>(upgradedCmHandles.getCmHandles().size()); + final Collection notReadyCmHandles = new ArrayList<>(upgradedCmHandles.getCmHandles().size()); + final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle(); + final String formattedModuleSetTag = MessageFormat.format("new moduleSetTag: {0}", moduleSetTag); + + upgradedCmHandles.getCmHandles().forEach(cmHandleId -> { + if (cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY)) { + ncmpServiceCmHandle.setCmHandleId(cmHandleId); + ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder() + .withCmHandleState(CmHandleState.READY) + .withLockReason(MODULE_UPGRADE, formattedModuleSetTag).build()); + final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle( + dmiPluginRegistration.getDmiPlugin(), + dmiPluginRegistration.getDmiDataPlugin(), + dmiPluginRegistration.getDmiModelPlugin(), + ncmpServiceCmHandle, + moduleSetTag); + cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); + } else { + notReadyCmHandles.add(cmHandleId); + } + }); + + final List cmHandleRegistrationResponses + = upgradeCmHandles(cmHandleStatePerCmHandle); + cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses(notReadyCmHandles, + CM_HANDLES_NOT_READY)); + return cmHandleRegistrationResponses; + } + private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) { try { deleteCmHandleFromDbAndModuleSyncMap(cmHandleId); @@ -404,8 +449,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private List registerNewCmHandles(final Map cmHandleStatePerCmHandle) { - final List cmHandleIds = cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId) - .toList(); + final List cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); try { lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds); @@ -418,6 +462,22 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } + private List upgradeCmHandles(final Map + cmHandleStatePerCmHandle) { + final List cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); + log.info("Moving cm handles : {} into locked (for upgrade) state.", cmHandleIds); + try { + lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); + return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds); + } catch (final Exception exception) { + return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception); + } + } + + private static List getCmHandleIds(final Map cmHandleStatePerCmHandle) { + return cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId).toList(); + } + private void setTrustLevelPerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) { if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) { trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiPlugin(), TrustLevel.COMPLETE); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java index d42fa17b5..81467dbb3 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java @@ -60,13 +60,22 @@ public interface CmHandleQueries { */ List queryCmHandlesByState(CmHandleState cmHandleState); + /** + * Method to return data nodes with ancestor representing the cm handles. + * + * @param cpsPath cps path for which the cmHandle is requested + * @return a list of data nodes representing the cm handles. + */ + List queryCmHandleAncestorsByCpsPath(String cpsPath, + FetchDescendantsOption fetchDescendantsOption); + /** * Method to return data nodes representing the cm handles. * * @param cpsPath cps path for which the cmHandle is requested * @return a list of data nodes representing the cm handles. */ - List queryCmHandleDataNodesByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); + List queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); /** * Method to check the state of a cm handle with given id. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java index f684f2ea6..e5cf8edd6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java @@ -72,15 +72,21 @@ public class CmHandleQueriesImpl implements CmHandleQueries { @Override public List queryCmHandlesByState(final CmHandleState cmHandleState) { - return queryCmHandleDataNodesByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]", + return queryCmHandleAncestorsByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]", INCLUDE_ALL_DESCENDANTS); } @Override - public List queryCmHandleDataNodesByCpsPath(final String cpsPath, - final FetchDescendantsOption fetchDescendantsOption) { + public List queryNcmpRegistryByCpsPath(final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption) { return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption); + cpsPath, fetchDescendantsOption); + } + + @Override + public List queryCmHandleAncestorsByCpsPath(final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption) { + return queryNcmpRegistryByCpsPath(cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption); } @Override @@ -92,7 +98,7 @@ public class CmHandleQueriesImpl implements CmHandleQueries { @Override public List queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) { - return queryCmHandleDataNodesByCpsPath("//state/datastores" + "/operational[@sync-state=\"" + return queryCmHandleAncestorsByCpsPath("//state/datastores" + "/operational[@sync-state=\"" + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS); } @@ -125,7 +131,8 @@ public class CmHandleQueriesImpl implements CmHandleQueries { + publicPropertyQueryPair.getKey() + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]"; - final Collection dataNodes = queryCmHandleDataNodesByCpsPath(cpsPath, OMIT_DESCENDANTS); + final Collection dataNodes = queryCmHandleAncestorsByCpsPath(cpsPath, + OMIT_DESCENDANTS); if (cmHandleIds == null) { cmHandleIds = collectCmHandleIdsFromDataNodes(dataNodes); } else { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java index ef4b299e1..99cca8c0b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java @@ -100,7 +100,9 @@ public class CompositeStateUtils { compositeState.setLastUpdateTimeNow(); final String oldLockReasonDetails = compositeState.getLockReason().getDetails(); final CompositeState.LockReason lockReason = - CompositeState.LockReason.builder().details(oldLockReasonDetails).build(); + CompositeState.LockReason.builder() + .lockReasonCategory(compositeState.getLockReason().getLockReasonCategory()) + .details(oldLockReasonDetails).build(); compositeState.setLockReason(lockReason); }; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java index 8306619f2..e2b2c6b4a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java @@ -21,5 +21,8 @@ package org.onap.cps.ncmp.api.impl.inventory; public enum LockReasonCategory { - MODULE_SYNC_FAILED, MODULE_UPGRADE, MODULE_UPGRADE_FAILED + MODULE_SYNC_FAILED, + MODULE_UPGRADE, + MODULE_UPGRADE_FAILED, + LOCKED_MISBEHAVING } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java index b2949c278..8e17ab916 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java @@ -20,20 +20,38 @@ package org.onap.cps.ncmp.api.impl.inventory.sync; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsAdminService; +import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; +import org.onap.cps.ncmp.api.impl.inventory.CompositeState; +import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory; import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations; +import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.spi.CascadeDeleteAllowed; +import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.SchemaSetNotFoundException; +import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Service; @Slf4j @@ -43,16 +61,31 @@ public class ModuleSyncService { private final DmiModelOperations dmiModelOperations; private final CpsModuleService cpsModuleService; - private final CpsAdminService cpsAdminService; + private final CmHandleQueries cmHandleQueries; + private final CpsDataService cpsDataService; + private final JsonObjectMapper jsonObjectMapper; /** * This method registers a cm handle and initiates modules sync. * - * @param yangModelCmHandle the yang model of cm handle. + * @param upgradedCmHandle the yang model of cm handle. */ - public void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) { + public void syncAndCreateOrUpgradeSchemaSetAndAnchor(final YangModelCmHandle upgradedCmHandle) { + + final String moduleSetTag = extractModuleSetTag(upgradedCmHandle.getCompositeState()); + final Optional existingCmHandleWithSameModuleSetTag + = getFirstReadyDataNodeWithModuleSetTag(moduleSetTag); + if (existingCmHandleWithSameModuleSetTag.isPresent()) { + upgradeUsingModuleSetTag(upgradedCmHandle, moduleSetTag); + } else { + syncAndCreateSchemaSetAndAnchor(upgradedCmHandle); + } + setCmHandleModuleSetTag(upgradedCmHandle, moduleSetTag); + } + + private void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) { final Collection allModuleReferencesFromCmHandle = dmiModelOperations.getModuleReferences(yangModelCmHandle); @@ -73,8 +106,8 @@ public class ModuleSyncService { final Map newModuleNameToContentMap, final Collection allModuleReferencesFromCmHandle) { final String schemaSetAndAnchorName = yangModelCmHandle.getId(); - cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName, - newModuleNameToContentMap, allModuleReferencesFromCmHandle); + cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, + schemaSetAndAnchorName, newModuleNameToContentMap, allModuleReferencesFromCmHandle); cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName, schemaSetAndAnchorName); } @@ -94,4 +127,41 @@ public class ModuleSyncService { } } + private Optional getFirstReadyDataNodeWithModuleSetTag(final String moduleSetTag) { + final List dataNodes = StringUtils.isNotBlank(moduleSetTag) ? cmHandleQueries + .queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='" + moduleSetTag + "']", + FetchDescendantsOption.OMIT_DESCENDANTS) : Collections.emptyList(); + return dataNodes.stream().filter(dataNode -> { + final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(dataNode.getXpath()); + return cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY); + }).findFirst(); + } + + private void setCmHandleModuleSetTag(final YangModelCmHandle upgradedCmHandle, final String moduleSetTag) { + final Map> dmiRegistryProperties = new HashMap<>(1); + final Map cmHandleProperties = new HashMap<>(2); + cmHandleProperties.put("id", upgradedCmHandle.getId()); + cmHandleProperties.put("module-set-tag", moduleSetTag); + dmiRegistryProperties.put("cm-handles", cmHandleProperties); + cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, + jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now()); + } + + private void upgradeUsingModuleSetTag(final YangModelCmHandle upgradedCmHandle, final String moduleSetTag) { + log.info("Found cm handle having module set tag: {}", moduleSetTag); + final Collection moduleReferencesFromExistingCmHandle = + cpsModuleService.getYangResourcesModuleReferences(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR); + final String upgradedSchemaSetAndAnchorName = upgradedCmHandle.getId(); + final Map noNewModules = Collections.emptyMap(); + cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, + upgradedSchemaSetAndAnchorName, noNewModules, moduleReferencesFromExistingCmHandle); + } + + private static String extractModuleSetTag(final CompositeState compositeState) { + return compositeState.getLockReason() != null && compositeState.getLockReason().getLockReasonCategory() + == LockReasonCategory.MODULE_UPGRADE + ? Arrays.stream(compositeState.getLockReason().getDetails().split(":")).toList().get(1).trim() + : StringUtils.EMPTY; + } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java index 7306f7174..c19dbeb90 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java @@ -60,7 +60,8 @@ public class ModuleSyncTasks { public CompletableFuture performModuleSync(final Collection cmHandlesAsDataNodes, final AtomicInteger batchCounter) { try { - final Map cmHandelStatePerCmHandle = new HashMap<>(); + final Map cmHandelStatePerCmHandle + = new HashMap<>(cmHandlesAsDataNodes.size()); for (final DataNode cmHandleAsDataNode : cmHandlesAsDataNodes) { final String cmHandleId = String.valueOf(cmHandleAsDataNode.getLeaves().get("id")); final YangModelCmHandle yangModelCmHandle = @@ -68,16 +69,18 @@ public class ModuleSyncTasks { final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); try { moduleSyncService.deleteSchemaSetIfExists(cmHandleId); - moduleSyncService.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle); + moduleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle); + yangModelCmHandle.getCompositeState().setLockReason(null); cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.READY); } catch (final Exception e) { - log.warn("Processing of {} module sync failed due to reason {}.", cmHandleId, e.getMessage()); + log.warn("Processing of {} module sync failed due to reason {}.", + cmHandleId, e.getMessage()); syncUtils.updateLockReasonDetailsAndAttempts(compositeState, LockReasonCategory.MODULE_SYNC_FAILED, e.getMessage()); setCmHandleStateLocked(yangModelCmHandle, compositeState.getLockReason()); cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); } - log.info("{} is now in {} state", cmHandleId, compositeState.getCmHandleState().name()); + log.info("{} is now in {} state", cmHandleId, cmHandelStatePerCmHandle.get(yangModelCmHandle).name()); } lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandelStatePerCmHandle); } finally { @@ -96,7 +99,7 @@ public class ModuleSyncTasks { final Map cmHandleStatePerCmHandle = new HashMap<>(failedCmHandles.size()); for (final YangModelCmHandle failedCmHandle : failedCmHandles) { final CompositeState compositeState = failedCmHandle.getCompositeState(); - final boolean isReadyForRetry = syncUtils.needsModuleSyncRetry(compositeState); + final boolean isReadyForRetry = syncUtils.needsModuleSyncRetryOrUpgrade(compositeState); log.info("Retry for cmHandleId : {} is {}", failedCmHandle.getId(), isReadyForRetry); if (isReadyForRetry) { final String resetCmHandleId = failedCmHandle.getId(); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java index 6ba52ee16..75781eb1b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java @@ -90,7 +90,7 @@ public class ModuleSyncWatchdog { @Scheduled(fixedDelayString = "${ncmp.timers.locked-modules-sync.sleep-time-ms:300000}") public void resetPreviouslyFailedCmHandles() { log.info("Processing module sync retry-watchdog waking up."); - final List failedCmHandles = syncUtils.getModuleSyncFailedCmHandles(); + final List failedCmHandles = syncUtils.getCmHandlesThatFailedModelSyncOrUpgrade(); log.info("Retrying {} cmHandles", failedCmHandles.size()); moduleSyncTasks.resetFailedCmHandles(failedCmHandles); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java index c50bd4278..ab85c2127 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java @@ -35,7 +35,6 @@ 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.inventory.CmHandleQueries; @@ -100,13 +99,14 @@ public class SyncUtils { } /** - * Query data nodes for cm handles with an "LOCKED" cm handle state with reason MODULE_SYNC_FAILED". + * Query data nodes for cm handles with an "LOCKED" cm handle state with reason. * * @return a random LOCKED yang model cm handle, return null if not found */ - public List getModuleSyncFailedCmHandles() { - final List lockedCmHandlesAsDataNodeList = cmHandleQueries.queryCmHandleDataNodesByCpsPath( - "//lock-reason[@reason=\"MODULE_SYNC_FAILED\"]", + public List getCmHandlesThatFailedModelSyncOrUpgrade() { + final List lockedCmHandlesAsDataNodeList + = cmHandleQueries.queryCmHandleAncestorsByCpsPath( + "//lock-reason[@reason=\"MODULE_SYNC_FAILED\" or @reason=\"MODULE_UPGRADE\"]", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList); } @@ -139,32 +139,39 @@ public class SyncUtils { * @param compositeState the composite state currently in the locked state * @return if the retry mechanism should be attempted */ - public boolean needsModuleSyncRetry(final CompositeState compositeState) { - final OffsetDateTime time = - OffsetDateTime.parse(compositeState.getLastUpdateTime(), - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); - final Matcher matcher = retryAttemptPattern.matcher(compositeState.getLockReason().getDetails()); + public boolean needsModuleSyncRetryOrUpgrade(final CompositeState compositeState) { + final OffsetDateTime time = OffsetDateTime.parse(compositeState.getLastUpdateTime(), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); + final CompositeState.LockReason lockReason = compositeState.getLockReason(); + final boolean failedDuringModuleSync = LockReasonCategory.MODULE_SYNC_FAILED - == compositeState.getLockReason().getLockReasonCategory(); - if (!failedDuringModuleSync) { - log.info("Locked for other reason"); - return false; - } - final int timeInMinutesUntilNextAttempt; - if (matcher.find()) { - timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(matcher.group(1))); - } else { - timeInMinutesUntilNextAttempt = 1; - log.info("First Attempt: no current attempts found."); - } - final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes(); - if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) { - log.info("Time until next attempt is {} minutes: ", - timeInMinutesUntilNextAttempt - timeSinceLastAttempt); - return false; + == lockReason.getLockReasonCategory(); + final boolean moduleUpgrade = LockReasonCategory.MODULE_UPGRADE + == lockReason.getLockReasonCategory(); + + if (failedDuringModuleSync) { + final int timeInMinutesUntilNextAttempt; + final Matcher matcher = retryAttemptPattern.matcher(lockReason.getDetails()); + if (matcher.find()) { + timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(matcher.group(1))); + } else { + timeInMinutesUntilNextAttempt = 1; + log.info("First Attempt: no current attempts found."); + } + final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes(); + if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) { + log.info("Time until next attempt is {} minutes: ", + timeInMinutesUntilNextAttempt - timeSinceLastAttempt); + return false; + } + log.info("Retry due now"); + return true; + } else if (moduleUpgrade) { + log.info("Locked for module upgrade."); + return true; } - log.info("Retry due now"); - return true; + log.info("Locked for other reason"); + return false; } /** @@ -196,6 +203,6 @@ public class SyncUtils { final List cmHandlesAsDataNodeList) { return cmHandlesAsDataNodeList.stream() .map(cmHandle -> YangDataConverter.convertCmHandleToYangModel(cmHandle, - cmHandle.getLeaves().get("id").toString())).collect(Collectors.toList()); + cmHandle.getLeaves().get("id").toString())).toList(); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java index 1b190759e..b6a04d367 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java @@ -86,7 +86,8 @@ public class YangDataConverter { (String) cmHandleDataNode.getLeaves().get("dmi-service-name"), (String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"), (String) cmHandleDataNode.getLeaves().get("dmi-model-service-name"), - ncmpServiceCmHandle + ncmpServiceCmHandle, + (String) cmHandleDataNode.getLeaves().get("module-set-tag") ); } @@ -105,7 +106,12 @@ public class YangDataConverter { return yangModelCmHandles; } - private static String extractCmHandleIdFromXpath(final String xpath) { + /** + * This method extract cm handle id from xpath of data node. + * @param xpath for data node of the cm handle + * @return cm handle Id + */ + public static String extractCmHandleIdFromXpath(final String xpath) { final Matcher matcher = cmHandleIdInXpathPattern.matcher(xpath); matcher.find(); return matcher.group(1); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java index 52fc81f50..d148f371b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java @@ -64,6 +64,9 @@ public class YangModelCmHandle { @JsonProperty("dmi-model-service-name") private String dmiModelServiceName; + @JsonProperty("module-set-tag") + private String moduleSetTag; + @JsonProperty("additional-properties") private List dmiProperties; @@ -102,12 +105,14 @@ public class YangModelCmHandle { public static YangModelCmHandle toYangModelCmHandle(final String dmiServiceName, final String dmiDataServiceName, final String dmiModelServiceName, - final NcmpServiceCmHandle ncmpServiceCmHandle) { + final NcmpServiceCmHandle ncmpServiceCmHandle, + final String moduleSetTag) { final YangModelCmHandle yangModelCmHandle = new YangModelCmHandle(); yangModelCmHandle.setId(ncmpServiceCmHandle.getCmHandleId()); yangModelCmHandle.setDmiServiceName(dmiServiceName); yangModelCmHandle.setDmiDataServiceName(dmiDataServiceName); yangModelCmHandle.setDmiModelServiceName(dmiModelServiceName); + yangModelCmHandle.setModuleSetTag(moduleSetTag); yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties())); yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties( ncmpServiceCmHandle.getPublicProperties())); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java index 5bab51bc2..e007491ce 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java @@ -26,12 +26,11 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import lombok.Builder; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.NcmpResponseStatus; +import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; @Data @Builder @@ -43,8 +42,6 @@ public class CmHandleRegistrationResponse { private NcmpResponseStatus ncmpResponseStatus; private String errorText; - private static final Pattern cmHandleIdInXpathPattern = Pattern.compile("\\[@id='(.*?)']"); - /** * Creates a failure response based on exception. * @@ -88,11 +85,11 @@ public class CmHandleRegistrationResponse { final NcmpResponseStatus ncmpResponseStatus) { final List cmHandleRegistrationResponses = new ArrayList<>(failedXpaths.size()); for (final String xpath : failedXpaths) { - final Matcher matcher = cmHandleIdInXpathPattern.matcher(xpath); - if (matcher.find()) { + try { + final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(xpath); cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(matcher.group(1), ncmpResponseStatus)); - } else { + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus)); + } catch (IllegalArgumentException | IllegalStateException e) { log.warn("Unexpected xpath {}", xpath); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java index 953b3c4e9..4615af61c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java @@ -50,6 +50,8 @@ public class DmiPluginRegistration { private List removedCmHandles = Collections.emptyList(); + private UpgradedCmHandles upgradedCmHandles; + /** * Validates plugin service names. * @throws NcmpException if validation fails. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java index 8a3d26414..ee034176e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada + * Modifications Copyright (C) 2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,4 +32,5 @@ public class DmiPluginRegistrationResponse { private List createdCmHandles = Collections.emptyList(); private List updatedCmHandles = Collections.emptyList(); private List removedCmHandles = Collections.emptyList(); + private List upgradedCmHandles = Collections.emptyList(); } \ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java index c46a8c26d..0b50346f8 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java @@ -52,6 +52,9 @@ public class NcmpServiceCmHandle { @JsonSetter(nulls = Nulls.AS_EMPTY) private CompositeState compositeState; + @JsonSetter(nulls = Nulls.AS_EMPTY) + private String moduleSetTag; + /** * NcmpServiceCmHandle copy constructor. * @@ -63,5 +66,6 @@ public class NcmpServiceCmHandle { this.publicProperties = new LinkedHashMap<>(ncmpServiceCmHandle.getPublicProperties()); this.compositeState = ncmpServiceCmHandle.getCompositeState() != null ? new CompositeState( ncmpServiceCmHandle.getCompositeState()) : null; + this.moduleSetTag = ncmpServiceCmHandle.getModuleSetTag(); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java new file mode 100644 index 000000000..61cd99ac8 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java @@ -0,0 +1,36 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.models; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.Collections; +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class UpgradedCmHandles { + private List cmHandles = Collections.emptyList(); + private String moduleSetTag; +} + diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy index 4a4c2e3e3..7c410cc58 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy @@ -53,7 +53,7 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) and: 'the query get the cm handle datanodes excluding all descendants returns a datanode' - cmHandleQueries.queryCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])] + cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])] when: 'the query is executed for cm handle ids' def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) then: 'the correct expected cm handles ids are returned' @@ -66,7 +66,7 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) and: 'cmHandleQueries throws a path parsing exception' - cmHandleQueries.queryCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> { throw thrownException } + cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> { throw thrownException } when: 'the query is executed for cm handle ids' objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) then: 'a data validation exception is thrown' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy index 941139c0b..9e4737fff 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy @@ -80,11 +80,11 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { when: 'registration is processed' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) then: 'cm-handles are removed first' - 1 * objectUnderTest.parseAndRemoveCmHandlesInDmiRegistration(*_) + 1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) and: 'de-registered cm handle entry is removed from in progress map' 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2') then: 'cm-handles are created' - 1 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(*_) + 1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) then: 'cm-handles are updated' 1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) } @@ -100,10 +100,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> updateResponses and: 'create cm-handles can be processed successfully' def createdResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-1')] - objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(*_) >> createdResponses + objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) >> createdResponses and: 'delete cm-handles can be processed successfully' def removeResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-3')] - objectUnderTest.parseAndRemoveCmHandlesInDmiRegistration(*_) >> removeResponses + objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) >> removeResponses when: 'registration is processed' def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) then: 'response has values from all operations' @@ -120,7 +120,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { when: 'update registration and sync module is called with correct DMI plugin information' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'create cm handles registration and sync modules is called with the correct plugin information' - 1 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration) + 1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration) and: 'dmi is added to the trustLevel map' 1 * mockTrustLevelPerDmiPlugin.put(dmiPluginRegisteredName, TrustLevel.COMPLETE) where: @@ -141,7 +141,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def exceptionThrown = thrown(DmiRequestException.class) assert exceptionThrown.getMessage().contains(expectedMessageDetails) and: 'registration is not called' - 0 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration) + 0 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration) where: scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails 'empty DMI plugins' | '' | '' | '' || 'No DMI plugin service names' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 3b507a566..af65cfc1a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -261,7 +261,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle] when: 'parse and create cm handle in dmi registration then sync module' - objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(mockDmiPluginRegistration) + objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration) then: 'system persists the cm handle state' 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args -> { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy index 6c4575515..2b17e5d41 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy @@ -32,7 +32,7 @@ import spock.lang.Specification class DmiServiceUrlBuilderSpec extends Specification { static YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('dmiServiceName', - 'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')) + 'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'),'') NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties() diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy index a58f22b95..ca0015e99 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy @@ -47,7 +47,7 @@ class YangModelCmHandleSpec extends Specification { .withOperationalDataStores(DataStoreSyncState.SYNCHRONIZED, 'some-sync-time').build() ncmpServiceCmHandle.setCompositeState(compositeState) when: 'it is converted to a yang model cm handle' - def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('', '', '', ncmpServiceCmHandle) + def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('', '', '', ncmpServiceCmHandle,'') then: 'the result has the right size' assert objectUnderTest.dmiProperties.size() == 1 and: 'the DMI property in the result has the correct name and value' @@ -63,7 +63,8 @@ class YangModelCmHandleSpec extends Specification { def 'Resolve DMI service name: #scenario and #requiredService service require.'() { given: 'a yang model cm handle' - def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')) + def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, + dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'),'') expect: assert objectUnderTest.resolveDmiServiceName(requiredService) == expectedService where: diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy index e0f20aed0..a3a5efc74 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy @@ -167,7 +167,7 @@ class CmHandleQueriesImplSpec extends Specification { cpsPath + '/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS) >> Arrays.asList(cmHandleDataNode) when: 'get cm handles by cps path is invoked' - def result = objectUnderTest.queryCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.queryCmHandleAncestorsByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS) 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/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy index e96105559..b547da7a1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy @@ -20,17 +20,24 @@ package org.onap.cps.ncmp.api.inventory.sync +import org.onap.cps.spi.FetchDescendantsOption + import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE import org.onap.cps.api.CpsAdminService +import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsModuleService import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncService import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries +import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.spi.CascadeDeleteAllowed import org.onap.cps.spi.exceptions.SchemaSetNotFoundException import org.onap.cps.spi.model.ModuleReference +import org.onap.cps.utils.JsonObjectMapper import spock.lang.Specification class ModuleSyncServiceSpec extends Specification { @@ -38,17 +45,23 @@ class ModuleSyncServiceSpec extends Specification { def mockCpsModuleService = Mock(CpsModuleService) def mockDmiModelOperations = Mock(DmiModelOperations) def mockCpsAdminService = Mock(CpsAdminService) + def mockCmHandleQueries = Mock(CmHandleQueries) + def mockCpsDataService = Mock(CpsDataService) + def mockJsonObjectMapper = Mock(JsonObjectMapper) - def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService) + def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService, + mockCmHandleQueries, mockCpsDataService, mockJsonObjectMapper) def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME def 'Sync model for a (new) cm handle with #scenario'() { - given: 'a cm handle' + given: 'a cm handle having lock reason : MODULE_UPGRADE' def ncmpServiceCmHandle = new NcmpServiceCmHandle() + ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder() + .withLockReason(MODULE_UPGRADE, 'new moduleSetTag: someModuleSetTag').build()) def dmiServiceName = 'some service name' ncmpServiceCmHandle.cmHandleId = 'cmHandleId-1' - def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle) + def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'someModuleSetTag') and: 'DMI operations returns some module references' def moduleReferences = [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ] mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences @@ -56,11 +69,14 @@ class ModuleSyncServiceSpec extends Specification { mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps) and: 'DMI-Plugin returns resource(s) for "new" module(s)' mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> newModuleNameContentToMap + and: 'empty data node list is returned by cps path a query' + mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='someModuleSetTag']", + FetchDescendantsOption.OMIT_DESCENDANTS) >> Collections.emptyList() when: 'module sync is triggered' mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences) - objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle) + objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle) then: 'create schema set from module is invoked with correct parameters' - 1 * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', newModuleNameContentToMap, moduleReferences) + 1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', newModuleNameContentToMap, moduleReferences) and: 'anchor is created with the correct parameters' 1 * mockCpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', 'cmHandleId-1') where: 'the following parameters are used' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy index 231e34a92..a11f14838 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy @@ -69,8 +69,8 @@ class ModuleSyncTasksSpec extends Specification { 1 * mockModuleSyncService.deleteSchemaSetIfExists('cm-handle-1') 1 * mockModuleSyncService.deleteSchemaSetIfExists('cm-handle-2') and: 'module sync service is invoked for each cm handle' - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-2') } + 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } + 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-2') } and: 'the state handler is called for the both cm handles' 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args -> assertBatch(args, ['cm-handle-1', 'cm-handle-2'], CmHandleState.READY) @@ -86,7 +86,7 @@ class ModuleSyncTasksSpec extends Specification { def cmHandleState = new CompositeState(cmHandleState: CmHandleState.ADVISED) 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> cmHandleState and: 'module sync service attempts to sync the cm handle and throws an exception' - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } + 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } when: 'module sync is executed' objectUnderTest.performModuleSync([cmHandle], batchCount) then: 'update lock reason, details and attempts is invoked' @@ -112,7 +112,7 @@ class ModuleSyncTasksSpec extends Specification { moduleSyncStartedOnCmHandles.put('cm-handle-1', 'started') moduleSyncStartedOnCmHandles.put('cm-handle-2', 'started') and: 'sync utils retry locked cm handle returns #isReadyForRetry' - mockSyncUtils.needsModuleSyncRetry(lockedState) >>> isReadyForRetry + mockSyncUtils.needsModuleSyncRetryOrUpgrade(lockedState) >>> isReadyForRetry when: 'resetting failed cm handles' objectUnderTest.resetFailedCmHandles([yangModelCmHandle1, yangModelCmHandle2]) then: 'updated to state "ADVISED" from "READY" is called as often as there are cm handles ready for retry' @@ -135,7 +135,7 @@ class ModuleSyncTasksSpec extends Specification { when: 'module sync poll is executed' objectUnderTest.performModuleSync([cmHandle1], batchCount) then: 'module sync service is invoked for cm handle' - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } + 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } and: 'the entry for other cm handle is still in the progress map' assert moduleSyncStartedOnCmHandles.get('other-cm-handle') != null } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy index d85686aa3..390e88b3d 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy @@ -111,7 +111,7 @@ class ModuleSyncWatchdogSpec extends Specification { def 'Reset failed cm handles.'() { given: 'sync utilities returns failed cm handles' def failedCmHandles = [new YangModelCmHandle()] - mockSyncUtils.getModuleSyncFailedCmHandles() >> failedCmHandles + mockSyncUtils.getCmHandlesThatFailedModelSyncOrUpgrade() >> failedCmHandles when: 'reset failed cm handles is started' objectUnderTest.resetPreviouslyFailedCmHandles() then: 'it is delegated to the module sync task (service)' 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 8fdbb6f53..00d14cd2a 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 @@ -115,11 +115,11 @@ class SyncUtilsSpec extends Specification{ def 'Get all locked Cm-Handle where Lock Reason is MODULE_SYNC_FAILED cm handle #scenario'() { given: 'the cps (persistence service) returns a collection of data nodes' - mockCmHandleQueries.queryCmHandleDataNodesByCpsPath( - '//lock-reason[@reason="MODULE_SYNC_FAILED"]', + mockCmHandleQueries.queryCmHandleAncestorsByCpsPath( + '//lock-reason[@reason="MODULE_SYNC_FAILED" or @reason="MODULE_UPGRADE"]', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode] when: 'get locked Misbehaving cm handle is called' - def result = objectUnderTest.getModuleSyncFailedCmHandles() + def result = objectUnderTest.getCmHandlesThatFailedModelSyncOrUpgrade() then: 'the returned cm handle collection is the correct size' result.size() == 1 and: 'the correct cm handle is returned' @@ -133,7 +133,7 @@ class SyncUtilsSpec extends Specification{ lastUpdatedTime = neverUpdatedBefore } when: 'checking to see if cm handle is ready for retry' - def result = objectUnderTest.needsModuleSyncRetry(new CompositeStateBuilder() + def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder() .withLockReason(MODULE_SYNC_FAILED, lockDetails) .withLastUpdatedTime(lastUpdatedTime).build()) then: 'retry is only attempted when expected' @@ -151,16 +151,18 @@ class SyncUtilsSpec extends Specification{ def 'Retry Locked Cm-Handle with other lock reasons (category) #lockReasonCategory'() { when: 'checking to see if cm handle is ready for retry' - def result = objectUnderTest.needsModuleSyncRetry(new CompositeStateBuilder() + def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder() .withLockReason(lockReasonCategory, 'some details') .withLastUpdatedTime(nowAsString).build()) - then: 'retry attempt is never triggered' - assert result == false + then: 'verify retry attempts' + assert result == retryAttempt and: 'logs contain related information' - def logs = loggingListAppender.list.toString() - assert logs.contains('Locked for other reason') + def logs = loggingListAppender.list.toString() + assert logs.contains(logReason) where: 'the following lock reasons occurred' - lockReasonCategory << [MODULE_UPGRADE, MODULE_UPGRADE_FAILED] + scenario | lockReasonCategory || logReason | retryAttempt + 'module upgrade' | MODULE_UPGRADE || 'Locked for module upgrade.' | true + 'module sync failed' | MODULE_SYNC_FAILED || 'First Attempt:' | false } def 'Get a Cm-Handle where #scenario'() { diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java index ca88a4da2..0d77530b3 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java @@ -46,7 +46,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; -import org.onap.cps.spi.CpsAdminPersistenceService; import org.onap.cps.spi.CpsModulePersistenceService; import org.onap.cps.spi.entities.DataspaceEntity; import org.onap.cps.spi.entities.SchemaSetEntity; @@ -89,8 +88,6 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ private final DataspaceRepository dataspaceRepository; - private final CpsAdminPersistenceService cpsAdminPersistenceService; - private final ModuleReferenceRepository moduleReferenceRepository; @Override diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy index 52651c6b1..9696b28cd 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy @@ -69,7 +69,7 @@ class CpsModulePersistenceServiceSpec extends Specification { def setup() { objectUnderTest = new CpsModulePersistenceServiceImpl(yangResourceRepositoryMock, schemaSetRepositoryMock, - dataspaceRepositoryMock, cpsAdminPersistenceServiceMock, moduleReferenceRepositoryMock) + dataspaceRepositoryMock, moduleReferenceRepositoryMock) } def 'Store schema set error scenario: #scenario.'() { diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java index 5ff08c9ac..e8c3e7775 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java @@ -47,13 +47,13 @@ public interface CpsModuleService { Map yangResourcesNameToContentMap); /** - * Create a schema set from new modules and existing modules. + * Create or upgrade a schema set from new modules and existing modules or only existing modules. * @param dataspaceName Dataspace name * @param schemaSetName schema set name * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content * @param allModuleReferences All YANG resource module references */ - void createSchemaSetFromModules(String dataspaceName, String schemaSetName, + void createOrUpgradeSchemaSetFromModules(String dataspaceName, String schemaSetName, Map newModuleNameToContentMap, Collection allModuleReferences); diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java index 7d9c472e6..444c895fb 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java @@ -66,7 +66,7 @@ public class CpsModuleServiceImpl implements CpsModuleService { } @Override - public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName, + public void createOrUpgradeSchemaSetFromModules(final String dataspaceName, final String schemaSetName, final Map newModuleNameToContentMap, final Collection allModuleReferences) { cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy index a794c58fc..61f67416d 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy @@ -65,7 +65,7 @@ class CpsModuleServiceImplSpec extends Specification { def moduleReferenceForExistingModule = new ModuleReference('test', '2021-10-12','test.org') def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule] when: 'create schema set from modules method is invoked' - objectUnderTest.createSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference) + objectUnderTest.createOrUpgradeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference) then: 'processing is delegated to persistence service' 1 * mockCpsModulePersistenceService.storeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference) and: 'the CpsValidator is called on the dataspaceName and schemaSetName' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy index e39e11440..f4cc8b733 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy @@ -90,7 +90,7 @@ class TestConfig extends Specification{ @Bean CpsModulePersistenceService cpsModulePersistenceService() { - return (CpsModulePersistenceService) new CpsModulePersistenceServiceImpl(yangResourceRepository, schemaSetRepository, dataspaceRepository, cpsAdminPersistenceService(), moduleReferenceRepository) + return (CpsModulePersistenceService) new CpsModulePersistenceServiceImpl(yangResourceRepository, schemaSetRepository, dataspaceRepository, moduleReferenceRepository) } @Bean diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy index cfc8ab7ad..d33a77446 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy @@ -94,7 +94,7 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase { moduleReferences.addAll(existingModuleReferences) when: 'the new schema set is created' def schemaSetName = "NewSchemaWith${numberOfNewModules}Modules" - objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences) + objectUnderTest.createOrUpgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences) and: 'associated with a new anchor' cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, 'newAnchor') then: 'the new anchor has the correct number of modules'