diff options
Diffstat (limited to 'cps-ncmp-service')
23 files changed, 553 insertions, 753 deletions
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 05b83b98e4..1f2748b4af 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 @@ -35,11 +35,9 @@ import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.vali 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; -import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -50,7 +48,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsDataService; -import org.onap.cps.ncmp.api.NcmpResponseStatus; import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; @@ -66,7 +63,7 @@ import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; import org.onap.cps.ncmp.api.impl.operations.OperationType; import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager; -import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper; +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker; import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions; import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; @@ -106,37 +103,24 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private final IMap<String, Object> moduleSyncStartedOnCmHandles; private final Map<String, TrustLevel> trustLevelPerDmiPlugin; private final TrustLevelManager trustLevelManager; - private final CmHandleIdMapper cmHandleIdMapper; - private final Map<String, Collection<ModuleReference>> moduleSetTagCache; + private final AlternateIdChecker alternateIdChecker; @Override public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( final DmiPluginRegistration dmiPluginRegistration) { - cacheAlternateIds(dmiPluginRegistration.getCreatedCmHandles()); + dmiPluginRegistration.validateDmiPluginRegistration(); final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse(); setTrustLevelPerDmiPlugin(dmiPluginRegistration); - if (!dmiPluginRegistration.getRemovedCmHandles().isEmpty()) { - dmiPluginRegistrationResponse.setRemovedCmHandles( - parseAndProcessDeletedCmHandlesInRegistration(dmiPluginRegistration.getRemovedCmHandles())); - } + processRemovedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); - if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) { - dmiPluginRegistrationResponse.setCreatedCmHandles( - parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)); - } - if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) { - dmiPluginRegistrationResponse.setUpdatedCmHandles( - networkCmProxyDataServicePropertyHandler - .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); - } - if (dmiPluginRegistration.getUpgradedCmHandles() != null - && !dmiPluginRegistration.getUpgradedCmHandles().getCmHandles().isEmpty()) { - dmiPluginRegistrationResponse.setUpgradedCmHandles( - parseAndProcessUpgradedCmHandlesInRegistration(dmiPluginRegistration)); - } + processCreatedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); + + processUpdatedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); + + processUpgradedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); return dmiPluginRegistrationResponse; } @@ -329,21 +313,24 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService * @return cm-handle registration response for create cm-handle requests. */ public List<CmHandleRegistrationResponse> parseAndProcessCreatedCmHandlesInRegistration( - final DmiPluginRegistration dmiPluginRegistration) { + final DmiPluginRegistration dmiPluginRegistration, final Collection<String> acceptedCmHandleIds) { final List<NcmpServiceCmHandle> cmHandlesToBeCreated = dmiPluginRegistration.getCreatedCmHandles(); final Map<String, TrustLevel> initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size()); final List<YangModelCmHandle> yangModelCmHandles = new ArrayList<>(cmHandlesToBeCreated.size()); cmHandlesToBeCreated .forEach(cmHandle -> { - final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle( - dmiPluginRegistration.getDmiPlugin(), - dmiPluginRegistration.getDmiDataPlugin(), - dmiPluginRegistration.getDmiModelPlugin(), - cmHandle, - cmHandle.getModuleSetTag(), - cmHandle.getAlternateId()); - yangModelCmHandles.add(yangModelCmHandle); - initialTrustLevelPerCmHandleId.put(cmHandle.getCmHandleId(), cmHandle.getRegistrationTrustLevel()); + if (acceptedCmHandleIds.contains(cmHandle.getCmHandleId())) { + final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle( + dmiPluginRegistration.getDmiPlugin(), + dmiPluginRegistration.getDmiDataPlugin(), + dmiPluginRegistration.getDmiModelPlugin(), + cmHandle, + cmHandle.getModuleSetTag(), + cmHandle.getAlternateId()); + yangModelCmHandles.add(yangModelCmHandle); + initialTrustLevelPerCmHandleId.put(cmHandle.getCmHandleId(), + cmHandle.getRegistrationTrustLevel()); + } }); return registerNewCmHandles(yangModelCmHandles, initialTrustLevelPerCmHandleId); } @@ -354,16 +341,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService new ArrayList<>(tobeRemovedCmHandles.size()); final Collection<YangModelCmHandle> yangModelCmHandles = inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles); - final Set<String> moduleSetTags = yangModelCmHandles.stream().map(YangModelCmHandle::getModuleSetTag) - .collect(Collectors.toSet()); updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING); final Set<String> notDeletedCmHandles = new HashSet<>(); for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandles, DELETE_BATCH_SIZE)) { try { batchDeleteCmHandlesFromDbAndModuleSyncMap(tobeRemovedCmHandleBatch); - moduleSetTags.forEach(moduleSetTagCache::remove); - log.debug("Removed module set tags: {}", moduleSetTags); tobeRemovedCmHandleBatch.forEach(cmHandleId -> cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId))); @@ -382,68 +365,100 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService yangModelCmHandles.removeIf(yangModelCmHandle -> notDeletedCmHandles.contains(yangModelCmHandle.getId())); updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETED); - removeEntriesFromAlternateIdCache(yangModelCmHandles); return cmHandleRegistrationResponses; } - private void removeEntriesFromAlternateIdCache(final Collection<YangModelCmHandle> yangModelCmHandles) { - for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) { - cmHandleIdMapper.removeMapping(yangModelCmHandle.getId()); + private void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + if (!dmiPluginRegistration.getRemovedCmHandles().isEmpty()) { + dmiPluginRegistrationResponse.setRemovedCmHandles( + parseAndProcessDeletedCmHandlesInRegistration(dmiPluginRegistration.getRemovedCmHandles())); + } + } + + private void processCreatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + final Collection<String> acceptedCmHandleIds = alternateIdChecker + .getIdsOfCmHandlesWithAcceptableAlternateId(dmiPluginRegistration.getCreatedCmHandles()); + if (!acceptedCmHandleIds.isEmpty()) { + dmiPluginRegistrationResponse.setCreatedCmHandles( + parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration, acceptedCmHandleIds)); + } + } + + private void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) { + dmiPluginRegistrationResponse.setUpdatedCmHandles( + networkCmProxyDataServicePropertyHandler + .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); } } + private void processUpgradedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + if (dmiPluginRegistration.getUpgradedCmHandles() != null + && !dmiPluginRegistration.getUpgradedCmHandles().getCmHandles().isEmpty()) { + dmiPluginRegistrationResponse.setUpgradedCmHandles( + parseAndProcessUpgradedCmHandlesInRegistration(dmiPluginRegistration)); + } + } + + protected List<CmHandleRegistrationResponse> parseAndProcessUpgradedCmHandlesInRegistration( final DmiPluginRegistration dmiPluginRegistration) { final List<String> upgradedCmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles(); - + final String upgradedModuleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag(); final Map<YangModelCmHandle, CmHandleState> acceptedCmHandleStatePerCmHandle = new HashMap<>(upgradedCmHandleIds.size()); + final List<CmHandleRegistrationResponse> cmHandleUpgradeResponses = new ArrayList<>(upgradedCmHandleIds.size()); - final Map<NcmpResponseStatus, List<String>> failedCmHandlesPerResponseStatus - = new EnumMap<>(NcmpResponseStatus.class); - final List<String> nonExistingCmHandleIds = new ArrayList<>(); - final List<String> nonReadyCmHandleIds = new ArrayList<>(); - final List<String> invalidCmHandleIds = new ArrayList<>(); - - upgradedCmHandleIds.forEach(cmHandleId -> { + for (final String cmHandleId : upgradedCmHandleIds) { try { - if (cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY)) { - final YangModelCmHandle yangModelCmHandleWithUpgradeDetails - = createYangModelCmHandle(dmiPluginRegistration, cmHandleId); - acceptedCmHandleStatePerCmHandle.put(yangModelCmHandleWithUpgradeDetails, CmHandleState.LOCKED); + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); + if (yangModelCmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY) { + if (moduleUpgradeCanBeSkipped(yangModelCmHandle, upgradedModuleSetTag)) { + cmHandleUpgradeResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); + } else { + updateYangModelCmHandleForUpgrade(yangModelCmHandle, upgradedModuleSetTag); + acceptedCmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); + } } else { - nonReadyCmHandleIds.add(cmHandleId); + cmHandleUpgradeResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_READY)); } } catch (final DataNodeNotFoundException dataNodeNotFoundException) { log.error("Unable to find data node for cm handle id : {} , caused by : {}", cmHandleId, dataNodeNotFoundException.getMessage()); - nonExistingCmHandleIds.add(cmHandleId); + cmHandleUpgradeResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND)); } catch (final DataValidationException dataValidationException) { log.error("Unable to upgrade cm handle id: {}, caused by : {}", cmHandleId, dataValidationException.getMessage()); - invalidCmHandleIds.add(cmHandleId); + cmHandleUpgradeResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID)); } - }); - failedCmHandlesPerResponseStatus.put(CM_HANDLES_NOT_READY, nonReadyCmHandleIds); - failedCmHandlesPerResponseStatus.put(CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds); - failedCmHandlesPerResponseStatus.put(CM_HANDLE_INVALID_ID, invalidCmHandleIds); - return mergeAllCmHandleResponses(acceptedCmHandleStatePerCmHandle, failedCmHandlesPerResponseStatus); - } - - private static YangModelCmHandle createYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration, - final String cmHandleId) { - final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle(); - ncmpServiceCmHandle.setCmHandleId(cmHandleId); - final String moduleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag(); - final String lockReasonWithModuleSetTag = MessageFormat.format( - ModuleOperationsUtils.MODULE_SET_TAG_MESSAGE_FORMAT, moduleSetTag); - ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.READY) + } + cmHandleUpgradeResponses.addAll(upgradeCmHandles(acceptedCmHandleStatePerCmHandle)); + return cmHandleUpgradeResponses; + } + + private static boolean moduleUpgradeCanBeSkipped(final YangModelCmHandle yangModelCmHandle, + final String upgradedModuleSetTag) { + if (StringUtils.isBlank(upgradedModuleSetTag)) { + return false; + } + return yangModelCmHandle.getModuleSetTag().equals(upgradedModuleSetTag); + } + + private static void updateYangModelCmHandleForUpgrade(final YangModelCmHandle yangModelCmHandle, + final String upgradedModuleSetTag) { + final String lockReasonWithModuleSetTag = String.format(ModuleOperationsUtils.MODULE_SET_TAG_MESSAGE_FORMAT, + upgradedModuleSetTag); + yangModelCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.READY) .withLockReason(MODULE_UPGRADE, lockReasonWithModuleSetTag).build()); - return YangModelCmHandle.toYangModelCmHandle(dmiPluginRegistration.getDmiPlugin(), - dmiPluginRegistration.getDmiDataPlugin(), dmiPluginRegistration.getDmiModelPlugin(), - ncmpServiceCmHandle, moduleSetTag, ncmpServiceCmHandle.getAlternateId()); } private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) { @@ -513,17 +528,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } - private List<CmHandleRegistrationResponse> mergeAllCmHandleResponses( - final Map<YangModelCmHandle, CmHandleState> acceptedCmHandleStatePerCmHandle, - final Map<NcmpResponseStatus, List<String>> failedCmHandlesPerResponseStatus) { - final List<CmHandleRegistrationResponse> cmHandleUpgradeResponses - = upgradeCmHandles(acceptedCmHandleStatePerCmHandle); - failedCmHandlesPerResponseStatus.forEach((ncmpResponseStatus, cmHandleIds) -> - cmHandleIds.forEach(cmHandleId -> cmHandleUpgradeResponses.add(CmHandleRegistrationResponse - .createFailureResponse(cmHandleId, ncmpResponseStatus)))); - return cmHandleUpgradeResponses; - } - private List<CmHandleRegistrationResponse> upgradeCmHandles(final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle) { final List<String> cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); @@ -549,12 +553,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } - private void cacheAlternateIds(final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) { - for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) { - if (!StringUtils.isEmpty(ncmpServiceCmHandle.getAlternateId())) { - cmHandleIdMapper.addMapping(ncmpServiceCmHandle.getCmHandleId(), ncmpServiceCmHandle.getAlternateId()); - } - } - } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java index 13b3fcafb1..84075a4897 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java @@ -45,7 +45,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsDataService; import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper; +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; @@ -67,7 +67,7 @@ public class NetworkCmProxyDataServicePropertyHandler { private final InventoryPersistence inventoryPersistence; private final CpsDataService cpsDataService; private final JsonObjectMapper jsonObjectMapper; - private final CmHandleIdMapper cmHandleIdMapper; + private final AlternateIdChecker alternateIdChecker; /** * Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes. @@ -81,8 +81,8 @@ public class NetworkCmProxyDataServicePropertyHandler { for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) { final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); try { - final DataNode existingCmHandleDataNode = inventoryPersistence.getCmHandleDataNode(cmHandleId) - .iterator().next(); + final DataNode existingCmHandleDataNode = inventoryPersistence + .getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next(); updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle); processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle); cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); @@ -105,17 +105,14 @@ public class NetworkCmProxyDataServicePropertyHandler { private void updateAlternateId(final DataNode existingCmHandleDataNode, final NcmpServiceCmHandle ncmpServiceCmHandle) { + final YangModelCmHandle yangModelCmHandle = + YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode, + ncmpServiceCmHandle.getCmHandleId()); + final String currentAlternateId = yangModelCmHandle.getAlternateId(); final String newAlternateId = ncmpServiceCmHandle.getAlternateId(); - if (cmHandleIdMapper.addMapping(ncmpServiceCmHandle.getCmHandleId(), newAlternateId)) { - try { - final YangModelCmHandle yangModelCmHandle = - YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode, - ncmpServiceCmHandle.getCmHandleId()); - setAndUpdateAlternateId(yangModelCmHandle, newAlternateId); - } catch (final Exception e) { - cmHandleIdMapper.removeMapping(ncmpServiceCmHandle.getCmHandleId()); - throw e; - } + if (alternateIdChecker.canApplyAlternateId(ncmpServiceCmHandle.getCmHandleId(), + currentAlternateId, newAlternateId)) { + setAndUpdateAlternateId(yangModelCmHandle, newAlternateId); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfig.java deleted file mode 100644 index a69d144766..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * ============LICENSE_START======================================================== - * Copyright (C) 2024 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.impl.config.embeddedcache; - -import com.hazelcast.config.MapConfig; -import java.util.Map; -import org.onap.cps.cache.HazelcastCacheConfig; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class AlternateIdCacheConfig extends HazelcastCacheConfig { - - private static final MapConfig alternateIdPerCmHandleIdMapConfig = - createMapConfig("alternateIdPerCmHandleIdMapConfig"); - - private static final MapConfig cmHandleIdPerAlternateIdMapConfig = - createMapConfig("cmHandleIdPerAlternateIdMapConfig"); - - /** - * Distributed instance of alternate id cache containing the alternate id per cm handle id. - * - * @return the cached map. - */ - @Bean - public Map<String, String> alternateIdPerCmHandleId() { - return createHazelcastInstance("hazelcastInstanceAlternateIdPerCmHandleIdMap", - alternateIdPerCmHandleIdMapConfig).getMap("alternateIdPerCmHandleId"); - } - - /** - * Distributed instance of alternate id cache containing the cm handle id per alternate id. - * - * @return the cached map. - */ - @Bean - public Map<String, String> cmHandleIdPerAlternateId() { - return createHazelcastInstance("hazelcastInstanceCmHandleIdPerAlternateIdMap", - cmHandleIdPerAlternateIdMapConfig).getMap("cmHandleIdPerAlternateId"); - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfig.java deleted file mode 100644 index a791a3bbd1..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfig.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * ============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.impl.config.embeddedcache; - -import com.hazelcast.config.MapConfig; -import java.util.Collection; -import java.util.Map; -import org.onap.cps.cache.HazelcastCacheConfig; -import org.onap.cps.spi.model.ModuleReference; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class ModuleSetTagCacheConfig extends HazelcastCacheConfig { - - private static final MapConfig moduleSetTagCacheMapConfig = createMapConfig("moduleSetTagCacheMapConfig"); - - /** - * Map instance for cached ModulesSetTags. - * - * @return configured map of ModuleSetTags - */ - @Bean - public Map<String, Collection<ModuleReference>> moduleSetTagCache() { - return createHazelcastInstance("moduleSetTags", moduleSetTagCacheMapConfig) - .getMap("moduleSetTagCache"); - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java index dcd0368700..e230b3fcb3 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java @@ -114,12 +114,20 @@ public interface InventoryPersistence extends NcmpPersistence { void saveCmHandleBatch(List<YangModelCmHandle> yangModelCmHandles); /** - * Get data node of given cm handle. + * Get data node with the given cm handle id. * * @param cmHandleId cmHandle ID * @return data node */ - Collection<DataNode> getCmHandleDataNode(String cmHandleId); + Collection<DataNode> getCmHandleDataNodeByCmHandleId(String cmHandleId); + + /** + * Get data node with the given alternate id. + * + * @param alternateId alternate ID + * @return data node + */ + DataNode getCmHandleDataNodeByAlternateId(String alternateId); /** * Get collection of data nodes of given cm handles. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java index 3b70786038..08ab15eaa7 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java @@ -22,6 +22,8 @@ package org.onap.cps.ncmp.api.impl.inventory; +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; + import com.google.common.collect.Lists; import java.time.OffsetDateTime; import java.util.ArrayList; @@ -37,6 +39,7 @@ import org.onap.cps.api.CpsModuleService; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleDefinition; @@ -54,6 +57,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv private final CpsModuleService cpsModuleService; private final CpsAnchorService cpsAnchorService; private final CpsValidator cpsValidator; + private final CmHandleQueries cmHandleQueries; /** * initialize an inventory persistence object. @@ -66,18 +70,19 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv */ public InventoryPersistenceImpl(final JsonObjectMapper jsonObjectMapper, final CpsDataService cpsDataService, final CpsModuleService cpsModuleService, final CpsValidator cpsValidator, - final CpsAnchorService cpsAnchorService) { + final CpsAnchorService cpsAnchorService, final CmHandleQueries cmHandleQueries) { super(jsonObjectMapper, cpsDataService, cpsModuleService, cpsValidator); this.cpsModuleService = cpsModuleService; this.cpsAnchorService = cpsAnchorService; this.cpsValidator = cpsValidator; + this.cmHandleQueries = cmHandleQueries; } @Override public CompositeState getCmHandleState(final String cmHandleId) { final DataNode stateAsDataNode = cpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - createCmHandleXPath(cmHandleId) + "/state", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) + getXPathForCmHandleById(cmHandleId) + "/state", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) .iterator().next(); cpsValidator.validateNameCharacters(cmHandleId); return new CompositeStateBuilder().fromDataNode(stateAsDataNode).build(); @@ -87,14 +92,14 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv 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, - createCmHandleXPath(cmHandleId), cmHandleJsonData, OffsetDateTime.now()); + getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now()); } @Override public void saveCmHandleStateBatch(final Map<String, CompositeState> cmHandleStatePerCmHandleId) { final Map<String, String> cmHandlesJsonDataMap = new HashMap<>(); cmHandleStatePerCmHandleId.forEach((cmHandleId, compositeState) -> cmHandlesJsonDataMap.put( - createCmHandleXPath(cmHandleId), + getXPathForCmHandleById(cmHandleId), createStateJsonData(jsonObjectMapper.asJsonString(compositeState)))); cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, OffsetDateTime.now()); @@ -103,7 +108,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv @Override public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { cpsValidator.validateNameCharacters(cmHandleId); - final DataNode dataNode = getCmHandleDataNode(cmHandleId).iterator().next(); + final DataNode dataNode = getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next(); return YangDataConverter.convertCmHandleToYangModel(dataNode, cmHandleId); } @@ -158,14 +163,26 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv } @Override - public Collection<DataNode> getCmHandleDataNode(final String cmHandleId) { - return this.getDataNode(createCmHandleXPath(cmHandleId)); + public Collection<DataNode> getCmHandleDataNodeByCmHandleId(final String cmHandleId) { + return this.getDataNode(getXPathForCmHandleById(cmHandleId)); + } + + @Override + public DataNode getCmHandleDataNodeByAlternateId(final String alternateId) { + final String xPathForCmHandleByAlternateId = getXPathForCmHandleByAlternateId(alternateId); + final Collection<DataNode> dataNodes = cmHandleQueries + .queryNcmpRegistryByCpsPath(xPathForCmHandleByAlternateId, OMIT_DESCENDANTS); + if (dataNodes.isEmpty()) { + throw new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + xPathForCmHandleByAlternateId); + } + return dataNodes.iterator().next(); } @Override public Collection<DataNode> getCmHandleDataNodes(final Collection<String> cmHandleIds) { final Collection<String> xpaths = new ArrayList<>(cmHandleIds.size()); - cmHandleIds.forEach(cmHandleId -> xpaths.add(createCmHandleXPath(cmHandleId))); + cmHandleIds.forEach(cmHandleId -> xpaths.add(getXPathForCmHandleById(cmHandleId))); return this.getDataNodes(xpaths); } @@ -174,10 +191,14 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv return cpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery); } - private static String createCmHandleXPath(final String cmHandleId) { + private static String getXPathForCmHandleById(final String cmHandleId) { return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']"; } + private static String getXPathForCmHandleByAlternateId(final String alternateId) { + return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@alternate-id='" + alternateId + "']"; + } + private static String createStateJsonData(final String state) { return "{\"state\":" + state + "}"; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java index 22bbc73833..9f95131cf1 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java @@ -38,7 +38,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; 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; @@ -62,10 +61,10 @@ public class ModuleOperationsUtils { private final DmiDataOperations dmiDataOperations; private final JsonObjectMapper jsonObjectMapper; private static final String RETRY_ATTEMPT_KEY = "attempt"; - public static final String MODULE_SET_TAG_KEY = "moduleSetTag"; - public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: {0}"; - private static final String UPGRADE_FORMAT = "Upgrade to ModuleSetTag: %s"; - private static final String LOCK_REASON_DETAILS_MSG_FORMAT = UPGRADE_FORMAT + " Attempt #%d failed: %s"; + private static final String MODULE_SET_TAG_KEY = "moduleSetTag"; + public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: %s"; + private static final String LOCK_REASON_DETAILS_MSG_FORMAT = + MODULE_SET_TAG_MESSAGE_FORMAT + " Attempt #%d failed: %s"; private static final Pattern retryAttemptPattern = Pattern.compile("Attempt #(\\d+) failed:.+"); private static final Pattern moduleSetTagPattern = Pattern.compile("Upgrade to ModuleSetTag: (\\S+)"); private static final String CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE = """ @@ -134,10 +133,10 @@ public class ModuleOperationsUtils { if (!compositeStateDetails.isEmpty() && compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY)) { attempt = 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY)); } - final String moduleSetTag = compositeStateDetails.get(MODULE_SET_TAG_KEY); + final String moduleSetTag = compositeStateDetails.getOrDefault(MODULE_SET_TAG_KEY, ""); compositeState.setLockReason(CompositeState.LockReason.builder() - .details(String.format(LOCK_REASON_DETAILS_MSG_FORMAT, StringUtils.isNotBlank(moduleSetTag) - ? moduleSetTag : "not-specified", attempt, errorMessage)).lockReasonCategory(lockReasonCategory) + .details(String.format(LOCK_REASON_DETAILS_MSG_FORMAT, moduleSetTag, attempt, errorMessage)) + .lockReasonCategory(lockReasonCategory) .build()); } @@ -221,13 +220,17 @@ public class ModuleOperationsUtils { * @param compositeState current lock reason of cm handle * @return true or false based on lock reason category */ - public static boolean isInUpgradeOrUpgradeFailed(final CompositeState compositeState) { + public static boolean inUpgradeOrUpgradeFailed(final CompositeState compositeState) { return compositeState.getLockReason() != null && (LockReasonCategory.MODULE_UPGRADE.equals(compositeState.getLockReason().getLockReasonCategory()) || LockReasonCategory.MODULE_UPGRADE_FAILED.equals(compositeState.getLockReason() .getLockReasonCategory())); } + public static String getUpgradedModuleSetTagFromLockReason(final CompositeState.LockReason lockReason) { + return getLockedCompositeStateDetails(lockReason).getOrDefault(MODULE_SET_TAG_KEY, ""); + } + private String getFirstResource(final Object responseBody) { final String jsonObjectAsString = jsonObjectMapper.asJsonString(responseBody); final JsonNode overallJsonNode = jsonObjectMapper.convertToJsonNode(jsonObjectAsString); 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 72d322605f..8b70798eb1 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 @@ -35,12 +35,12 @@ import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutableTriple; import org.onap.cps.api.CpsAnchorService; 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.operations.DmiModelOperations; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; @@ -63,7 +63,6 @@ public class ModuleSyncService { private final CpsDataService cpsDataService; private final CpsAnchorService cpsAnchorService; private final JsonObjectMapper jsonObjectMapper; - private final Map<String, Collection<ModuleReference>> moduleSetTagCache; private static final Map<String, String> NO_NEW_MODULES = Collections.emptyMap(); /** @@ -73,44 +72,27 @@ public class ModuleSyncService { */ public void syncAndCreateOrUpgradeSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) { + final boolean inUpgrade = ModuleOperationsUtils.inUpgradeOrUpgradeFailed(yangModelCmHandle.getCompositeState()); + + final ImmutableTriple<String, Map<String, String>, Collection<ModuleReference>> + allModuleReferencesAndNewModuleNameByModuleSetTag + = getAllModuleReferencesAndNewYangResourcesByModuleSetTag(yangModelCmHandle, inUpgrade); + + final String moduleSetTag = allModuleReferencesAndNewModuleNameByModuleSetTag.getLeft(); + final Map<String, String> newYangResources = allModuleReferencesAndNewModuleNameByModuleSetTag.getMiddle(); + final Collection<ModuleReference> allModuleReferences + = allModuleReferencesAndNewModuleNameByModuleSetTag.getRight(); final String cmHandleId = yangModelCmHandle.getId(); - final CompositeState compositeState = yangModelCmHandle.getCompositeState(); - final boolean inUpgrade = ModuleOperationsUtils.isInUpgradeOrUpgradeFailed(compositeState); - final String moduleSetTag = getModuleSetTag(yangModelCmHandle, compositeState, inUpgrade); - - final Collection<ModuleReference> moduleReferencesFromCache = moduleSetTagCache.get(moduleSetTag); - - if (moduleReferencesFromCache == null) { - final Optional<DataNode> existingCmHandleWithSameModuleSetTag - = getFirstReadyDataNodeWithModuleSetTag(moduleSetTag); - - if (existingCmHandleWithSameModuleSetTag.isPresent()) { - final String existingAnchorName = existingCmHandleWithSameModuleSetTag.get().getAnchorName(); - final Collection<ModuleReference> moduleReferencesFromExistingCmHandle = - upgradeOrCreateSchemaSetUsingModuleSetTag(yangModelCmHandle.getId(), moduleSetTag, - existingAnchorName, inUpgrade); - updateModuleSetTagCache(moduleSetTag, moduleReferencesFromExistingCmHandle); - } else { - if (inUpgrade) { - deleteSchemaSetIfExists(cmHandleId); - } - final Collection<ModuleReference> allModuleReferencesFromCmHandle - = syncAndCreateSchemaSet(yangModelCmHandle); - updateModuleSetTagCache(moduleSetTag, allModuleReferencesFromCmHandle); - } + + if (inUpgrade) { + cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, + newYangResources, allModuleReferences); + setCmHandleModuleSetTag(yangModelCmHandle, moduleSetTag); } else { - if (inUpgrade) { - cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, - NO_NEW_MODULES, moduleReferencesFromCache); - } else { - cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, - cmHandleId, NO_NEW_MODULES, moduleReferencesFromCache); - } - } - if (!inUpgrade) { + cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, + newYangResources, allModuleReferences); cpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cmHandleId); } - setCmHandleModuleSetTag(yangModelCmHandle, moduleSetTag); } /** @@ -128,7 +110,30 @@ public class ModuleSyncService { } } - private Optional<DataNode> getFirstReadyDataNodeWithModuleSetTag(final String moduleSetTag) { + private ImmutableTriple<String, Map<String, String>, Collection<ModuleReference>> + getAllModuleReferencesAndNewYangResourcesByModuleSetTag(final YangModelCmHandle yangModelCmHandle, + final boolean inUpgrade) { + + final String moduleSetTag = getModuleSetTag(yangModelCmHandle, inUpgrade); + final Collection<ModuleReference> allModuleReferences; + Map<String, String> newYangResources = Collections.emptyMap(); + + final Optional<DataNode> optionalDataNode = getFirstReadyDataNodeByModuleSetTagProvidedInDb(moduleSetTag); + + if (optionalDataNode.isPresent()) { + log.info("Found other cm handle having same module set tag: {}", moduleSetTag); + final String otherAnchorWithSameModuleSetTag + = YangDataConverter.extractCmHandleIdFromXpath(optionalDataNode.get().getXpath()); + allModuleReferences = cpsModuleService.getYangResourcesModuleReferences( + NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, otherAnchorWithSameModuleSetTag); + } else { + allModuleReferences = dmiModelOperations.getModuleReferences(yangModelCmHandle); + newYangResources = getNewModuleNameToContentMap(yangModelCmHandle, allModuleReferences); + } + return ImmutableTriple.of(moduleSetTag, newYangResources, allModuleReferences); + } + + private Optional<DataNode> getFirstReadyDataNodeByModuleSetTagProvidedInDb(final String moduleSetTag) { final List<DataNode> dataNodes = StringUtils.isNotBlank(moduleSetTag) ? cmHandleQueries .queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='" + moduleSetTag + "']", FetchDescendantsOption.OMIT_DESCENDANTS) : Collections.emptyList(); @@ -148,56 +153,26 @@ public class ModuleSyncService { jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now()); } - private Collection<ModuleReference> syncAndCreateSchemaSet(final YangModelCmHandle yangModelCmHandle) { - final Collection<ModuleReference> allModuleReferencesFromCmHandle = - dmiModelOperations.getModuleReferences(yangModelCmHandle); - final Collection<ModuleReference> identifiedNewModuleReferencesFromCmHandle = cpsModuleService - .identifyNewModuleReferences(allModuleReferencesFromCmHandle); + private Map<String, String> getNewModuleNameToContentMap(final YangModelCmHandle yangModelCmHandle, + final Collection<ModuleReference> moduleReferences) { + final Collection<ModuleReference> identifiedNewModuleReferences = cpsModuleService + .identifyNewModuleReferences(moduleReferences); final Map<String, String> newModuleNameToContentMap; - if (identifiedNewModuleReferencesFromCmHandle.isEmpty()) { + if (identifiedNewModuleReferences.isEmpty()) { newModuleNameToContentMap = NO_NEW_MODULES; } else { newModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, - identifiedNewModuleReferencesFromCmHandle); - } - cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, - yangModelCmHandle.getId(), newModuleNameToContentMap, allModuleReferencesFromCmHandle); - return allModuleReferencesFromCmHandle; - } - - private Collection<ModuleReference> upgradeOrCreateSchemaSetUsingModuleSetTag(final String schemaSetName, - final String moduleSetTag, - final String existingAnchorName, - final boolean inUpgrade) { - log.info("Found cm handle having module set tag: {}", moduleSetTag); - final Collection<ModuleReference> moduleReferencesFromExistingCmHandle = - cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, - existingAnchorName); - if (inUpgrade) { - cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName, - NO_NEW_MODULES, moduleReferencesFromExistingCmHandle); - } else { - cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, - schemaSetName, NO_NEW_MODULES, moduleReferencesFromExistingCmHandle); + identifiedNewModuleReferences); } - return moduleReferencesFromExistingCmHandle; + return newModuleNameToContentMap; } - private String getModuleSetTag(final YangModelCmHandle yangModelCmHandle, - final CompositeState compositeState, - final boolean inUpgrade) { + private String getModuleSetTag(final YangModelCmHandle yangModelCmHandle, final boolean inUpgrade) { if (inUpgrade) { - return ModuleOperationsUtils.getLockedCompositeStateDetails(compositeState.getLockReason()) - .get(ModuleOperationsUtils.MODULE_SET_TAG_KEY); + return ModuleOperationsUtils.getUpgradedModuleSetTagFromLockReason( + yangModelCmHandle.getCompositeState().getLockReason()); } return yangModelCmHandle.getModuleSetTag(); } - private void updateModuleSetTagCache(final String moduleSetTag, - final Collection<ModuleReference> allModuleReferencesFromCmHandle) { - if (StringUtils.isNotBlank(moduleSetTag)) { - moduleSetTagCache.putIfAbsent(moduleSetTag, allModuleReferencesFromCmHandle); - } - } - } 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 e214044189..804653a168 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 @@ -67,7 +67,7 @@ public class ModuleSyncTasks { final YangModelCmHandle yangModelCmHandle = YangDataConverter.convertCmHandleToYangModel(cmHandleAsDataNode, cmHandleId); final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); - final boolean inUpgrade = ModuleOperationsUtils.isInUpgradeOrUpgradeFailed(compositeState); + final boolean inUpgrade = ModuleOperationsUtils.inUpgradeOrUpgradeFailed(compositeState); try { if (!inUpgrade) { moduleSyncService.deleteSchemaSetIfExists(cmHandleId); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java new file mode 100644 index 0000000000..1be1a90853 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java @@ -0,0 +1,142 @@ +/* + * ============LICENSE_START======================================================== + * Copyright (c) 2024 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.impl.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.spi.exceptions.DataNodeNotFoundException; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class AlternateIdChecker { + + private final InventoryPersistence inventoryPersistence; + + private static final String NO_CURRENT_ALTERNATE_ID = ""; + + /** + * Check if the alternate can be applied to the given cm handle (id). + * Conditions: + * - proposed alternate is blank (it wil be ignored) + * - proposed alternate is same as current (no change) + * - proposed alternate is not in use for a different cm handle (in the DB) + * + * @param cmHandleId cm handle id + * @param proposedAlternateId proposed alternate id + * @return true if the new alternate id not in use or equal to current alternate id, false otherwise + */ + public boolean canApplyAlternateId(final String cmHandleId, final String proposedAlternateId) { + String currentAlternateId = ""; + try { + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); + currentAlternateId = yangModelCmHandle.getAlternateId(); + } catch (final DataNodeNotFoundException dataNodeNotFoundException) { + // work with blank current alternate id + } + return this.canApplyAlternateId(cmHandleId, currentAlternateId, proposedAlternateId); + } + + /** + * Check if the alternate can be applied to the given cm handle. + * Conditions: + * - proposed alternate is blank (it wil be ignored) + * - proposed alternate is same as current (no change) + * - proposed alternate is not in use for a different cm handle (in the DB) + * + * @param cmHandleId cm handle id + * @param currentAlternateId current alternate id + * @param proposedAlternateId new alternate id + * @return true if the new alternate id not in use or equal to current alternate id, false otherwise + */ + public boolean canApplyAlternateId(final String cmHandleId, + final String currentAlternateId, + final String proposedAlternateId) { + if (StringUtils.isBlank(currentAlternateId)) { + if (alternateIdAlreadyInDb(proposedAlternateId)) { + log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already " + + "assigned to a different cm handle", cmHandleId); + return false; + } + return true; + } + if (currentAlternateId.equals(proposedAlternateId)) { + return true; + } + log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}", + cmHandleId, currentAlternateId); + return false; + } + + /** + * Check all alternate ids of a batch of NEW cm handles. + * Includes cross-checks in the batch itself for duplicates. Only the first entry encountered wil be accepted. + * This method can only be used for NEW cm handle registrations NOT for updating existing ones + * + * @param newNcmpServiceCmHandles the proposed new cm handles + * @return collection of cm handles ids which are acceptable + */ + public Collection<String> getIdsOfCmHandlesWithAcceptableAlternateId( + final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles) { + final Set<String> acceptedAlternateIds = new HashSet<>(newNcmpServiceCmHandles.size()); + final Collection<String> acceptedCmHandleIds = new ArrayList<>(newNcmpServiceCmHandles.size()); + for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) { + final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); + final String proposedAlternateId = ncmpServiceCmHandle.getAlternateId(); + final boolean isAcceptable; + if (StringUtils.isEmpty(proposedAlternateId)) { + isAcceptable = true; + } else { + if (acceptedAlternateIds.contains(proposedAlternateId)) { + isAcceptable = false; + log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already " + + "assigned to a different cm handle (in this batch)", cmHandleId); + } else { + isAcceptable = canApplyAlternateId(cmHandleId, NO_CURRENT_ALTERNATE_ID, proposedAlternateId); + } + } + if (isAcceptable) { + acceptedAlternateIds.add(proposedAlternateId); + acceptedCmHandleIds.add(cmHandleId); + } + } + return acceptedCmHandleIds; + } + + private boolean alternateIdAlreadyInDb(final String alternateId) { + try { + inventoryPersistence.getCmHandleDataNodeByAlternateId(alternateId); + } catch (final DataNodeNotFoundException dataNodeNotFoundException) { + return false; + } + return true; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java deleted file mode 100644 index a88adbd110..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * ============LICENSE_START======================================================== - * Copyright (c) 2024 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.impl.utils; - -import java.util.Map; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; -import org.springframework.stereotype.Service; - -@Service -@Slf4j -@RequiredArgsConstructor -public class CmHandleIdMapper { - - private final Map<String, String> alternateIdPerCmHandleId; - private final Map<String, String> cmHandleIdPerAlternateId; - private final NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService; - - private boolean cacheIsInitialized = false; - - public String cmHandleIdToAlternateId(final String cmHandleId) { - initializeCache(); - return alternateIdPerCmHandleId.get(cmHandleId); - } - - public String alternateIdToCmHandleId(final String alternateId) { - initializeCache(); - return cmHandleIdPerAlternateId.get(alternateId); - } - - public boolean addMapping(final String cmHandleId, final String alternateId) { - initializeCache(); - return addMappingWithValidation(cmHandleId, alternateId); - } - - - private boolean addMappingWithValidation(final String cmHandleId, final String alternateId) { - if (alternateIdPerCmHandleId.containsKey(cmHandleId)) { - final String originalAlternateId = alternateIdPerCmHandleId.get(cmHandleId); - if (!originalAlternateId.equals(alternateId)) { - log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}", - cmHandleId, originalAlternateId); - } - return false; - } - if (StringUtils.isBlank(alternateId)) { - return false; - } - alternateIdPerCmHandleId.put(cmHandleId, alternateId); - cmHandleIdPerAlternateId.put(alternateId, cmHandleId); - return true; - } - - public void removeMapping(final String cmHandleId) { - final String alternateId = alternateIdPerCmHandleId.remove(cmHandleId); - removeAlternateIdWithValidation(alternateId); - } - - private void removeAlternateIdWithValidation(final String alternateId) { - if (alternateId != null) { - cmHandleIdPerAlternateId.remove(alternateId); - } - } - - private void initializeCache() { - if (!cacheIsInitialized) { - networkCmProxyCmHandleQueryService.getAllCmHandles().forEach(cmHandle -> - addMappingWithValidation(cmHandle.getCmHandleId(), cmHandle.getAlternateId()) - ); - log.info("Alternate ID cache initialized from DB with {} cm handle/alternate id pairs ", - alternateIdPerCmHandleId.size()); - cacheIsInitialized = true; - } - } -} 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 3be97e8ee1..c77e9aa7a6 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 @@ -54,6 +54,7 @@ public class YangDataConverter { final List<YangModelCmHandle.Property> publicProperties = yangModelCmHandle.getPublicProperties(); ncmpServiceCmHandle.setCmHandleId(yangModelCmHandle.getId()); ncmpServiceCmHandle.setCompositeState(yangModelCmHandle.getCompositeState()); + ncmpServiceCmHandle.setModuleSetTag(yangModelCmHandle.getModuleSetTag()); ncmpServiceCmHandle.setAlternateId(yangModelCmHandle.getAlternateId()); setDmiProperties(dmiProperties, ncmpServiceCmHandle); setPublicProperties(publicProperties, ncmpServiceCmHandle); 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 03e53fc427..b2758d9d5f 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 @@ -24,7 +24,6 @@ package org.onap.cps.ncmp.api.impl.yangmodels; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Strings; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -94,6 +93,7 @@ public class YangModelCmHandle { copy.dmiProperties = original.getDmiProperties() == null ? null : new ArrayList<>(original.getDmiProperties()); copy.publicProperties = original.getPublicProperties() == null ? null : new ArrayList<>(original.getPublicProperties()); + copy.moduleSetTag = original.getModuleSetTag(); copy.alternateId = original.getAlternateId(); return copy; } @@ -134,7 +134,7 @@ public class YangModelCmHandle { * @return dmi service name */ public String resolveDmiServiceName(final RequiredDmiService requiredService) { - if (isNullEmptyOrBlank(dmiServiceName)) { + if (StringUtils.isBlank(dmiServiceName)) { if (RequiredDmiService.DATA.equals(requiredService)) { return dmiDataServiceName; } @@ -151,10 +151,6 @@ public class YangModelCmHandle { return yangModelCmHandleProperties; } - private static boolean isNullEmptyOrBlank(final String serviceName) { - return Strings.isNullOrEmpty(serviceName) || serviceName.isBlank(); - } - @AllArgsConstructor @Data public static class Property { 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 c7ac8ab8b6..313d1b4afd 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 @@ -27,8 +27,10 @@ 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.NcmpResponseStatus.UNKNOWN_ERROR +import java.util.stream.Collectors +import org.onap.cps.ncmp.api.impl.inventory.CompositeState import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager -import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker import org.onap.cps.ncmp.api.models.UpgradedCmHandles import com.fasterxml.jackson.databind.ObjectMapper import com.hazelcast.map.IMap @@ -68,9 +70,14 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def mockModuleSyncStartedOnCmHandles = Mock(IMap<String, Object>) def trustLevelPerDmiPlugin = [:] def mockTrustLevelManager = Mock(TrustLevelManager) - def mockCmHandleIdMapper = Mock(CmHandleIdMapper) + def mockAlternateIdChecker = Mock(AlternateIdChecker) def objectUnderTest = getObjectUnderTest() - def mockModuleSetTagCache = [:] + + def setup() { + // always accept all cm handles + mockAlternateIdChecker.getIdsOfCmHandlesWithAcceptableAlternateId(_) >> + { args -> args[0].stream().map(it -> it.cmHandleId).collect(Collectors.toList()) } + } def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() { given: 'a registration with operations of all types' @@ -81,6 +88,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) and: 'cm handles are persisted' mockInventoryPersistence.getYangModelCmHandles(['cmhandle-2']) >> [new YangModelCmHandle()] + mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: CmHandleState.READY)) and: 'cm handle is in READY state' mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> true when: 'registration is processed' @@ -88,7 +96,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { then: 'cm-handles are removed first' 1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) and: 'de-registered cm handle entry is removed from in progress map' - 2 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2') + 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2') then: 'cm-handles are created' 1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) then: 'cm-handles are updated' @@ -102,15 +110,15 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) and: 'exception while checking cm handle state' - mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> isReady + mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: cmHandleState)) when: 'registration is processed' def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) then: 'upgrade operation contains expected error code' - assert result.upgradedCmHandles.status[0] == expectedResponseStatus + assert result.upgradedCmHandles[0].status == expectedResponseStatus where: 'the following parameters are used' - scenario | isReady || expectedResponseStatus - 'READY' | true || Status.SUCCESS - 'Not READY' | false || Status.FAILURE + scenario | cmHandleState || expectedResponseStatus + 'READY' | CmHandleState.READY || Status.SUCCESS + 'Not READY' | CmHandleState.LOCKED || Status.FAILURE } def 'DMI Registration upgrade with exception #scenario'() { @@ -118,7 +126,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) and: 'exception while checking cm handle state' - mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> { throw exception } + mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> { throw exception } when: 'registration is processed' def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) then: 'upgrade operation contains expected error code' @@ -160,7 +168,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.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration) + 1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration, _) and: 'dmi is added to the dmi trustLevel map' assert trustLevelPerDmiPlugin.size() == 1 assert trustLevelPerDmiPlugin.containsKey(expectedDmiPluginRegisteredName) @@ -433,19 +441,19 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def 'Adding data to alternate id caches.'() { given: 'a registration with three CM Handles to be created' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', - createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1', alternateId: 'my-alternate-id-1')]) + def ncmpServiceCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1', alternateId: 'my-alternate-id-1')] + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', createdCmHandles: ncmpServiceCmHandles) when: 'the DMI plugin registration happens' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'the new alternate id is added to the cache' - 1 * mockCmHandleIdMapper.addMapping('cmhandle1', 'my-alternate-id-1') + 1 * mockAlternateIdChecker.getIdsOfCmHandlesWithAcceptableAlternateId(ncmpServiceCmHandles) >> ['cmhandle1'] } def getObjectUnderTest() { return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations, mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries, stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService, - mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockCmHandleIdMapper, mockModuleSetTagCache)) + mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockAlternateIdChecker)) } def addPersistedYangModelCmHandles(ids) { 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 c835056f37..fc548ebe9b 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 @@ -32,7 +32,7 @@ import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RU import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE -import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler @@ -54,7 +54,7 @@ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.ncmp.api.models.DataOperationRequest import org.onap.cps.spi.exceptions.CpsException import org.onap.cps.spi.model.ConditionProperties -import spock.lang.Shared + import java.util.stream.Collectors import org.onap.cps.utils.JsonObjectMapper import com.fasterxml.jackson.databind.ObjectMapper @@ -80,14 +80,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>) def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>) def mockTrustLevelManager = Mock(TrustLevelManager) - def mockCmHandleIdMapper = Mock(CmHandleIdMapper) - def mockModuleSetTagCache = [:] + def mockAlternateIdChecker = Mock(AlternateIdChecker) def NO_TOPIC = null def NO_REQUEST_ID = null - @Shared def OPTIONS_PARAM = '(a=1,b=2)' - @Shared def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id') def objectUnderTest = new NetworkCmProxyDataServiceImpl( @@ -102,8 +99,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { stubModuleSyncStartedOnCmHandles, stubTrustLevelPerDmiPlugin, mockTrustLevelManager, - mockCmHandleIdMapper, - mockModuleSetTagCache) + mockAlternateIdChecker) def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']" @@ -177,14 +173,17 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { given: 'the system returns a yang modelled cm handle' def dmiServiceName = 'some service name' def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, - lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(), - lastUpdateTime: 'some-timestamp', - dataSyncEnabled: false, - dataStores: dataStores()) + lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(), + lastUpdateTime: 'some-timestamp', + dataSyncEnabled: false, + dataStores: dataStores()) def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')] def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')] + def moduleSetTag = 'some-module-set-tag' + def alternateId = 'some-alternate-id' def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName, - dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState) + dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState, + moduleSetTag: moduleSetTag, alternateId: alternateId) 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle when: 'getting cm handle details for a given cm handle id from ncmp service' def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle') @@ -192,13 +191,16 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { result.class == NcmpServiceCmHandle.class and: 'the cm handle contains the cm handle id' result.cmHandleId == 'some-cm-handle' + and: 'the cm handle contains the alternate id' + result.alternateId == 'some-alternate-id' + and: 'the cm handle contains the module-set-tag' + result.moduleSetTag == 'some-module-set-tag' and: 'the cm handle contains the DMI Properties' result.dmiProperties ==[ Book:'Romance Novel' ] and: 'the cm handle contains the public Properties' result.publicProperties == [ "Public Book":'Public Romance Novel' ] and: 'the cm handle contains the cm handle composite state' result.compositeState == compositeState - } def 'Get cm handle public properties'() { @@ -269,7 +271,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle] when: 'parse and create cm handle in dmi registration then sync module' - objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration) + objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration, ['test-cm-handle-id']) then: 'system persists the cm handle state' 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> { args -> { @@ -280,7 +282,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { } } } - + def 'Execute cm handle id search'() { given: 'valid CmHandleQueryApiParameters input' def cmHandleQueryApiParameters = new CmHandleQueryApiParameters() diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy index f94c34c589..d822f2e639 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy @@ -22,35 +22,33 @@ package org.onap.cps.ncmp.api.impl - import com.fasterxml.jackson.databind.ObjectMapper -import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper - -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.NcmpResponseStatus.CM_HANDLES_NOT_FOUND -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID -import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status - import org.onap.cps.api.CpsDataService -import org.onap.cps.utils.JsonObjectMapper import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence -import org.onap.cps.spi.exceptions.DataValidationException +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.spi.exceptions.DataNodeNotFoundException +import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder +import org.onap.cps.utils.JsonObjectMapper import spock.lang.Specification +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR +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.models.CmHandleRegistrationResponse.Status + class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def mockInventoryPersistence = Mock(InventoryPersistence) def mockCpsDataService = Mock(CpsDataService) def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) - def mockCmHandleIdMapper = Mock(CmHandleIdMapper) + def mockAlternateIdChecker = Mock(AlternateIdChecker) - def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockCmHandleIdMapper) + def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockAlternateIdChecker) def static cmHandleId = 'myHandle1' def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']" @@ -62,7 +60,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def 'Update CM Handle Public Properties: #scenario'() { given: 'the CPS service return a CM handle' - mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNodeAsCollection + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> cmHandleDataNodeAsCollection and: 'an update cm handle request with public properties updates' def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: updatedPublicProperties)] when: 'update data node leaves is called with the update request' @@ -84,7 +82,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def 'Update DMI Properties: #scenario'() { given: 'the CPS service return a CM handle' - mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNodeAsCollection + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> cmHandleDataNodeAsCollection and: 'an update cm handle request with DMI properties updates' def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: updatedDmiProperties)] when: 'update data node leaves is called with the update request' @@ -108,7 +106,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def 'Update CM Handle Properties, remove all properties: #scenario'() { given: 'the CPS service return a CM handle' def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, childDataNodes: originalPropertyDataNodes) - mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> [cmHandleDataNode] + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [cmHandleDataNode] and: 'an update cm handle request that removes all public properties(existing and non-existing)' def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])] when: 'update data node leaves is called with the update request' @@ -131,7 +129,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { given: 'cm handles request' def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: [:], dmiProperties: [:])] and: 'data node cannot be found' - mockInventoryPersistence.getCmHandleDataNode(*_) >> { throw exception } + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(*_) >> { throw exception } when: 'update data node leaves is called using correct parameters' def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) then: 'one failed registration response' @@ -156,7 +154,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]), new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])] and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle' - mockInventoryPersistence.getCmHandleDataNode(*_) >> cmHandleDataNodeAsCollection >> { + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(*_) >> cmHandleDataNodeAsCollection >> { throw new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) } >> cmHandleDataNodeAsCollection when: 'update data node leaves is called using correct parameters' def cmHandleResponseList = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) @@ -194,7 +192,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { { args -> assert args[3].contains('alt-1') } - mockCmHandleIdMapper.addMapping(cmHandleId, 'alt-1') >> isNewMapping + mockAlternateIdChecker.canApplyAlternateId(cmHandleId, '','alt-1') >> isNewMapping where: 'following updates are attempted' scenario | isNewMapping || callsToDataService 'new alternate id ' | true || 1 @@ -205,8 +203,8 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { given: 'an existing data node and an update request with an alternate id' def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1') DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['alternate-id': null]) - and: 'a new mapping is added' - mockCmHandleIdMapper.addMapping(cmHandleId, 'alt-1') >> true + and: 'an applicable alternate id for the cm handle' + mockAlternateIdChecker.canApplyAlternateId(cmHandleId, '','alt-1') >> true and: 'but an exception occurs while saving' def originalException = new NullPointerException('some exception') mockCpsDataService.updateNodeLeaves(*_) >> { throw originalException } @@ -215,8 +213,6 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { then: 'the original exception is thrown up' def thrownException = thrown(NullPointerException) assert thrownException == originalException - and: 'the mapping is removed from the cache' - 1 * mockCmHandleIdMapper.removeMapping(cmHandleId) } def convertToProperties(expectedPropertiesAfterUpdateAsMap) { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy deleted file mode 100644 index 71c5293e27..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy +++ /dev/null @@ -1,59 +0,0 @@ -/* - * ============LICENSE_START======================================================== - * Copyright (C) 2024 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.impl.config.embeddedcache - -import com.hazelcast.core.Hazelcast -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import spock.lang.Specification - -@SpringBootTest(classes = [AlternateIdCacheConfig]) -class AlternateIdCacheConfigSpec extends Specification { - - @Autowired - private Map<String, String> cmHandleIdPerAlternateId - @Autowired - private Map<String, String> alternateIdPerCmHandleId - - def 'Embedded (hazelcast) cache for alternate id - cm handle id caches.'() { - expect: 'system is able to create an instance of the Alternate ID Cache' - assert null != cmHandleIdPerAlternateId - assert null != alternateIdPerCmHandleId - and: 'there are at least 2 instances' - assert Hazelcast.allHazelcastInstances.size() > 1 - and: 'Alternate ID Caches are present' - assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceAlternateIdPerCmHandleIdMap') - && Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceCmHandleIdPerAlternateIdMap') - } - - def 'Verify configs of the alternate id distributed objects.'(){ - when: 'retrieving the map config of module set tag' - def alternateIdConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceAlternateIdPerCmHandleIdMap').config - def alternateIdMapConfig = alternateIdConfig.mapConfigs.get('alternateIdPerCmHandleIdMapConfig') - def cmHandleIdConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceCmHandleIdPerAlternateIdMap').config - def cmHandleIdIdMapConfig = cmHandleIdConfig.mapConfigs.get('cmHandleIdPerAlternateIdMapConfig') - then: 'the map configs have the correct number of backups' - assert alternateIdMapConfig.backupCount == 3 - assert alternateIdMapConfig.asyncBackupCount == 3 - assert cmHandleIdIdMapConfig.backupCount == 3 - assert cmHandleIdIdMapConfig.asyncBackupCount == 3 - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfigSpec.groovy deleted file mode 100644 index 8a6a32bd8d..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfigSpec.groovy +++ /dev/null @@ -1,76 +0,0 @@ -/* - * ============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.impl.config.embeddedcache - -import com.hazelcast.config.Config -import com.hazelcast.core.Hazelcast -import org.onap.cps.spi.model.ModuleReference -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import spock.lang.Specification - -@SpringBootTest(classes = [ModuleSetTagCacheConfig]) -class ModuleSetTagCacheConfigSpec extends Specification { - - @Autowired - private Map<String, Collection<ModuleReference>> moduleSetTagCache - - def 'Embedded (hazelcast) caches for module set tag.'() { - expect: 'system is able to create an instance of a map to hold module set tags' - assert null != moduleSetTagCache - and: 'there is at least 1 instance' - assert Hazelcast.allHazelcastInstances.size() > 0 - and: 'hazelcast instance name of module set tag is correct' - assert Hazelcast.allHazelcastInstances.name.contains('moduleSetTags') - } - - def 'Verify configs of module set tag distributed object.'(){ - when: 'retrieving the map config of module set tag' - def cacheConfig = Hazelcast.getHazelcastInstanceByName('moduleSetTags').config - def moduleSetTagMapConfig = cacheConfig.mapConfigs.get('moduleSetTagCacheMapConfig') - then: 'the module set tag map config has correct backup counts' - assert moduleSetTagMapConfig.backupCount == 3 - assert moduleSetTagMapConfig.asyncBackupCount == 3 - } - - def 'Verify deployment network configs of distributed cache of module set tag object.'() { - given: 'network config of module set tag cache' - def moduleSetTagNetworkConfig = Hazelcast.getHazelcastInstanceByName('moduleSetTags').config.networkConfig - expect: 'module set tag cache has the correct settings' - assert moduleSetTagNetworkConfig.join.autoDetectionConfig.enabled - assert !moduleSetTagNetworkConfig.join.kubernetesConfig.enabled - } - - def 'Verify network of module set tag cache'() { - given: 'Synchronization config object and test configuration' - def objectUnderTest = new ModuleSetTagCacheConfig() - def testConfig = new Config() - when: 'kubernetes properties are enabled' - objectUnderTest.cacheKubernetesEnabled = true - objectUnderTest.cacheKubernetesServiceName = 'test-service-name' - and: 'method called to update the discovery mode' - objectUnderTest.updateDiscoveryMode(testConfig) - then: 'applied properties are reflected' - assert testConfig.networkConfig.join.kubernetesConfig.enabled - assert testConfig.networkConfig.join.kubernetesConfig.properties.get('service-name') == 'test-service-name' - - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy index a3b923f939..83acb2238a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy @@ -22,32 +22,34 @@ package org.onap.cps.ncmp.api.impl.inventory -import org.onap.cps.api.CpsAnchorService - -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME -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.NO_TIMESTAMP -import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS - import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsModuleService 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.DataNodeNotFoundException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.ModuleDefinition import org.onap.cps.spi.model.ModuleReference -import org.onap.cps.utils.JsonObjectMapper import org.onap.cps.spi.utils.CpsValidator +import org.onap.cps.utils.JsonObjectMapper import spock.lang.Shared import spock.lang.Specification + import java.time.OffsetDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter +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 static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NO_TIMESTAMP +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS + class InventoryPersistenceImplSpec extends Specification { def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) @@ -60,8 +62,10 @@ class InventoryPersistenceImplSpec extends Specification { def mockCpsValidator = Mock(CpsValidator) + def mockCmHandleQueries = Mock(CmHandleQueries) + def objectUnderTest = new InventoryPersistenceImpl(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService, - mockCpsValidator, mockCpsAnchorService) + mockCpsValidator, mockCpsAnchorService, mockCmHandleQueries) def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ") .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC)) @@ -283,13 +287,32 @@ class InventoryPersistenceImplSpec extends Specification { def 'Get cmHandle data node'() { given: 'expected xPath to get cmHandle data node' - def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']'; + def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']' when: 'the method to get data nodes is called' - objectUnderTest.getCmHandleDataNode('sample cmHandleId') + objectUnderTest.getCmHandleDataNodeByCmHandleId('sample cmHandleId') then: 'the data persistence service method to get cmHandle data node is invoked once with expected xPath' 1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, expectedXPath, INCLUDE_ALL_DESCENDANTS) } + def 'Get cm handle data node'() { + given: 'expected xPath to get cmHandle data node' + def expectedXPath = '/dmi-registry/cm-handles[@alternate-id=\'alternate id\']' + and: 'query service is invoked with expected xpath' + mockCmHandleQueries.queryNcmpRegistryByCpsPath(expectedXPath, OMIT_DESCENDANTS) >> [new DataNode()] + expect: 'getting the cm handle data node' + assert objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id') == new DataNode() + } + + def 'Attempt to get non existing cm handle data node by alternate id'() { + given: 'query service is invoked and returns empty collection of data nodes' + mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> [] + when: 'getting the cm handle data node' + objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id') + then: 'no data found exception thrown' + def thrownException = thrown(DataNodeNotFoundException) + assert thrownException.getMessage().contains('DataNode not found') + } + def 'Get CM handles that has given module names'() { when: 'the method to get cm handles is called' objectUnderTest.getCmHandleIdsWithGivenModules(['sample-module-name']) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy index 827a548ae4..44bc182000 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy @@ -125,7 +125,7 @@ class ModuleOperationsUtilsSpec extends Specification{ where: scenario | moduleSetTag || expectedDetails 'a module set tag' | 'someModuleSetTag' || 'someModuleSetTag' - 'empty module set tag' | '' || 'not-specified' + 'empty module set tag' | '' || '' } def 'Get all locked cm-Handles where lock reasons are model sync failed or upgrade'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy index de783ed2ca..b0ff6d886a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy @@ -50,10 +50,9 @@ class ModuleSyncServiceSpec extends Specification { def mockCmHandleQueries = Mock(CmHandleQueries) def mockCpsDataService = Mock(CpsDataService) def mockJsonObjectMapper = Mock(JsonObjectMapper) - def mockModuleSetTagCache = [:] def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, - mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper, mockModuleSetTagCache) + mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper) def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME def static cmHandleWithModuleSetTag = new DataNodeBuilder().withXpath("//cm-handles[@module-set-tag='tag-1'][@id='otherId']").withAnchor('otherId').build() @@ -96,11 +95,7 @@ class ModuleSyncServiceSpec extends Specification { and: 'some module references' def moduleReferences = [ new ModuleReference('module1','1') ] and: 'cache or DMI operations returns some module references for upgraded cm handle' - if (populateCache) { - mockModuleSetTagCache.put('tag-1', moduleReferences) - } else { - mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences - } + mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences and: 'none of these module references are new (unknown to the system)' mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> [] and: 'CPS-Core returns list of existing module resources for TBD' @@ -118,10 +113,9 @@ class ModuleSyncServiceSpec extends Specification { and: 'No anchor is created for the upgraded cm handle' 0 * mockCpsAnchorService.createAnchor(*_) where: 'the following parameters are used' - scenario | populateCache | existingCmHandlesWithSameTag || expectedCallsToUpgradeSchemaSet | expectedCallsToCeateSchemaSet - 'new' | false | [] || 0 | 1 - 'in cache' | true | [] || 1 | 0 - 'in database' | false | [cmHandleWithModuleSetTag] || 1 | 0 + scenario | existingCmHandlesWithSameTag || expectedCallsToUpgradeSchemaSet | expectedCallsToCeateSchemaSet + 'new' | [] || 1 | 0 + 'in database' | [cmHandleWithModuleSetTag] || 1 | 0 } def 'upgrade model for a existing cm handle'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy new file mode 100644 index 0000000000..f41fd6cdf3 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy @@ -0,0 +1,97 @@ +/* + * ============LICENSE_START======================================================== + * Copyright (c) 2024 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.impl.utils + + +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle +import org.onap.cps.spi.exceptions.DataNodeNotFoundException +import org.onap.cps.spi.model.DataNode +import org.onap.cps.spi.model.DataNodeBuilder +import spock.lang.Specification + +class AlternateIdCheckerSpec extends Specification { + + def mockInventoryPersistenceService = Mock(InventoryPersistence) + def someDataNode = new DataNodeBuilder().build() + def dataNodeFoundException = new DataNodeNotFoundException('', '') + + def objectUnderTest = new AlternateIdChecker(mockInventoryPersistenceService) + + def 'Check new cm handle with new alternate id.'() { + given: 'inventory persistence can not find cm handle id' + mockInventoryPersistenceService.getYangModelCmHandle('ch 1') >> {throw dataNodeFoundException} + and: 'inventory persistence can not find alternate id' + mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId('alternate id') >> {throw dataNodeFoundException} + expect: 'mapping can be added' + assert objectUnderTest.canApplyAlternateId('ch 1', 'alternate id') + } + + def 'Check new cm handle with used alternate id.'() { + given: 'inventory persistence can not find cm handle id' + mockInventoryPersistenceService.getYangModelCmHandle('ch 1') >> {throw dataNodeFoundException} + and: 'inventory persistence can find alternate id' + mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId('alternate id') >> { someDataNode } + expect: 'mapping can not be added' + assert objectUnderTest.canApplyAlternateId('ch 1', 'alternate id') == false + } + + def 'Check for existing cm handle with #currentAlternateId.'() { + given: 'a cm handle with the #currentAlternateId' + def yangModelCmHandle = new YangModelCmHandle(alternateId: currentAlternateId) + and: 'inventory service finds the cm handle' + mockInventoryPersistenceService.getYangModelCmHandle('my cm handle') >> yangModelCmHandle + expect: 'add mapping returns expected result' + assert canAdd == objectUnderTest.canApplyAlternateId('my cm handle', 'same alternate id') + where: 'following alternate ids is used' + currentAlternateId || canAdd + 'same alternate id' || true + 'other alternate id' || false + } + + def 'Check a batch of NEW cm handles with #scenario.'() { + given: 'a batch of 2 new cm handles alternate id ids #alt1 and #alt2' + def batch = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: alt1), + new NcmpServiceCmHandle(cmHandleId: 'ch-2', alternateId: alt2)] + and: 'the database already contains cm handle(s) with these alternate ids: #alreadyinDb' + mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId(_) >> + { args -> altAlreadyInDb.contains(args[0]) ? new DataNode() : throwDataNodeNotFoundException() } + when: 'the batch of new cm handles is checked' + def result = objectUnderTest.getIdsOfCmHandlesWithAcceptableAlternateId(batch) + then: 'the result only contains the ids of the acceptable cm handles' + assert result.contains('ch-1') == acceptCh1 + assert result.contains('ch-2') == acceptCh2 + where: 'the following alternate ids are used' + scenario | alt1 | alt2 | altAlreadyInDb || acceptCh1 | acceptCh2 + 'no alternate ids' | '' | '' | ['dont matter'] || true | true + 'new alternate ids' | 'fdn1' | 'fdn2' | ['other fdn'] || true | true + 'one already used alternate id' | 'fdn1' | 'fdn2' | ['fdn1'] || false | true + 'two already used alternate ids' | 'fdn1' | 'fdn2' | ['fdn1','fdn2'] || false | false + 'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || true | false + } + + def throwDataNodeNotFoundException() { + // cannot 'return' an exception in conditional stub behavior, so hence a method call that will always throw this exception + throw dataNodeFoundException + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy deleted file mode 100644 index 55ccdf3be5..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy +++ /dev/null @@ -1,126 +0,0 @@ -/* - * ============LICENSE_START======================================================== - * Copyright (c) 2024 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.impl.utils - -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.read.ListAppender -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle -import org.slf4j.LoggerFactory -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService -import spock.lang.Specification - -class CmHandleIdMapperSpec extends Specification { - - def alternateIdPerCmHandle = new HashMap<String, String>() - def cmHandlePerAlternateId = new HashMap<String, String>() - def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService) - - def objectUnderTest = new CmHandleIdMapper(alternateIdPerCmHandle, cmHandlePerAlternateId, mockCpsCmHandlerQueryService) - - def logger = Spy(ListAppender<ILoggingEvent>) - - def setup() { - ((Logger) LoggerFactory.getLogger(CmHandleIdMapper.class)).addAppender(logger) - logger.start() - mockCpsCmHandlerQueryService.getAllCmHandles() >> [] - assert objectUnderTest.addMapping('my cmhandle id', 'my alternate id') - } - - void cleanup() { - ((Logger) LoggerFactory.getLogger(CmHandleIdMapper.class)).detachAndStopAllAppenders() - } - - def 'Checking entries in the cache.'() { - expect: 'the alternate id can be converted to cmhandle id' - assert objectUnderTest.alternateIdToCmHandleId('my alternate id') == 'my cmhandle id' - and: 'the cmhandle id can be converted to alternate id' - assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id' - } - - def 'Attempt adding #scenario alternate id.'() { - expect: 'cmhandle id - alternate id mapping fails' - assert objectUnderTest.addMapping('ch-1', alternateId) == false - and: 'alternate id looked up by cmhandle id unsuccessfully' - assert objectUnderTest.cmHandleIdToAlternateId('ch-1') == null - where: 'alternate id has an invalid value' - scenario | alternateId - 'empty' | '' - 'blank' | ' ' - 'null' | null - } - - def 'Remove an entry from the cache.'() { - when: 'removing an entry' - objectUnderTest.removeMapping('my cmhandle id') - then: 'converting alternate id returns null' - assert objectUnderTest.alternateIdToCmHandleId('my alternate id') == null - and: 'converting cmhandle id returns null' - assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == null - } - - def 'Attempt to remove a non-existing entry from the cache.'() { - when: 'removing an entry that is not cached' - objectUnderTest.removeMapping('non-cached cmhandle id') - then: 'deleting from the cmhandle cache returns null' - assert alternateIdPerCmHandle.remove('non-cached cmhandle id') == null - and: 'removal from the alternate id cache is skipped' - 0 * cmHandlePerAlternateId.remove(_) - } - - def 'Cannot update existing alternate id.'() { - given: 'attempt to update an existing alternate id' - objectUnderTest.addMapping('my cmhandle id', 'other id') - expect: 'still returns the original alternate id' - assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id' - and: 'converting other alternate id returns null' - assert objectUnderTest.alternateIdToCmHandleId('other id') == null - and: 'a warning is logged with the original alternate id' - def lastLoggingEvent = logger.list[1] - assert lastLoggingEvent.level == Level.WARN - assert lastLoggingEvent.formattedMessage.contains('my alternate id') - } - - def 'Update existing alternate id with the same value.'() { - expect: 'update an existing alternate id with the same value returns false (no update)' - assert objectUnderTest.addMapping('my cmhandle id', 'my alternate id') == false - and: 'conversion still returns the original alternate id' - assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id' - } - - def 'Initializing cache #scenario.'() { - when: 'the cache is (re-)initialized' - objectUnderTest.cacheIsInitialized = false - objectUnderTest.initializeCache() - then: 'the alternate id can be converted to cmhandle id' - assert objectUnderTest.alternateIdToCmHandleId('alt-1') == convertedCmHandleId - and: 'the cm handle id can be converted to alternate id' - assert objectUnderTest.cmHandleIdToAlternateId('ch-1') == convertedAlternatId - and: 'the query service is called to get the initial data' - 1 * mockCpsCmHandlerQueryService.getAllCmHandles() >> persistedCmHandles - where: 'the initial data has a cm handle #scenario' - scenario | persistedCmHandles || convertedAlternatId | convertedCmHandleId - 'with alternate id' | [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: 'alt-1')] || 'alt-1' | 'ch-1' - 'without alternate id' | [new NcmpServiceCmHandle(cmHandleId: 'ch-1')] || null | null - 'with blank alternate id' | [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: ' ')] || null | null - } -}
\ No newline at end of file |