diff options
40 files changed, 751 insertions, 756 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java index b9c834c559..462679e74f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023 Nordix Foundation + * Copyright (C) 2023-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. @@ -36,7 +36,8 @@ public enum NcmpResponseStatus { SUBSCRIPTION_PENDING("106", "subscription pending for all cm handles"), UNKNOWN_ERROR("108", "Unknown error"), CM_HANDLE_ALREADY_EXIST("109", "cm-handle already exists"), - CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id"); + CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id"), + ALTERNATE_ID_ALREADY_ASSOCIATED("111", "alternate id already associated"); private final String code; private final String message; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java index 6f92a9efec..8890d14ae1 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java @@ -241,8 +241,7 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH } private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) { - return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter - .convertCmHandleToYangModel(dataNode, dataNode.getLeaves().get("id").toString())); + return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter.convertCmHandleToYangModel(dataNode)); } private Collection<String> executeQueries(final CmHandleQueryServiceParameters cmHandleQueryServiceParameters, 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 7622e7cb49..08afde49a1 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 @@ -24,6 +24,7 @@ package org.onap.cps.ncmp.api.impl; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST; @@ -35,11 +36,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; @@ -48,8 +47,8 @@ import java.util.Set; import java.util.stream.Collectors; 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; @@ -106,7 +105,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private final Map<String, TrustLevel> trustLevelPerDmiPlugin; private final TrustLevelManager trustLevelManager; private final AlternateIdChecker alternateIdChecker; - private final Map<String, Collection<ModuleReference>> moduleSetTagCache; @Override public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( @@ -309,51 +307,19 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState(); } - /** - * THis method registers a cm handle and initiates modules sync. - * - * @param dmiPluginRegistration dmi plugin registration information. - * @return cm-handle registration response for create cm-handle requests. - */ - public List<CmHandleRegistrationResponse> parseAndProcessCreatedCmHandlesInRegistration( - 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 -> { - 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); - } - - protected List<CmHandleRegistrationResponse> parseAndProcessDeletedCmHandlesInRegistration( - final List<String> tobeRemovedCmHandles) { + protected void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + final List<String> tobeRemovedCmHandleIds = dmiPluginRegistration.getRemovedCmHandles(); final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = - new ArrayList<>(tobeRemovedCmHandles.size()); + new ArrayList<>(tobeRemovedCmHandleIds.size()); final Collection<YangModelCmHandle> yangModelCmHandles = - inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles); - final Set<String> moduleSetTags = yangModelCmHandles.stream().map(YangModelCmHandle::getModuleSetTag) - .collect(Collectors.toSet()); + inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandleIds); updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING); final Set<String> notDeletedCmHandles = new HashSet<>(); - for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandles, DELETE_BATCH_SIZE)) { + for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandleIds, 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))); @@ -369,101 +335,155 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } } - yangModelCmHandles.removeIf(yangModelCmHandle -> notDeletedCmHandles.contains(yangModelCmHandle.getId())); updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETED); - - return cmHandleRegistrationResponses; + dmiPluginRegistrationResponse.setRemovedCmHandles(cmHandleRegistrationResponses); } - private void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + protected void processCreatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { - if (!dmiPluginRegistration.getRemovedCmHandles().isEmpty()) { - dmiPluginRegistrationResponse.setRemovedCmHandles( - parseAndProcessDeletedCmHandlesInRegistration(dmiPluginRegistration.getRemovedCmHandles())); - } - } + final List<NcmpServiceCmHandle> ncmpServiceCmHandles = dmiPluginRegistration.getCreatedCmHandles(); + final List<CmHandleRegistrationResponse> failedCmHandleRegistrationResponses = new ArrayList<>(); - 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)); - } - } + try { + final Collection<String> rejectedCmHandleIds + = checkAlternateIds(ncmpServiceCmHandles, failedCmHandleRegistrationResponses); - private void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { - if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) { - dmiPluginRegistrationResponse.setUpdatedCmHandles( - networkCmProxyDataServicePropertyHandler - .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); - } - } + final Collection<String> succeededCmHandleIds = persistCmHandlesWithState(dmiPluginRegistration, + dmiPluginRegistrationResponse, ncmpServiceCmHandles, rejectedCmHandleIds); + + processTrustLevels(ncmpServiceCmHandles, succeededCmHandleIds); - private void processUpgradedCmHandles(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { - if (dmiPluginRegistration.getUpgradedCmHandles() != null - && !dmiPluginRegistration.getUpgradedCmHandles().getCmHandles().isEmpty()) { - dmiPluginRegistrationResponse.setUpgradedCmHandles( - parseAndProcessUpgradedCmHandlesInRegistration(dmiPluginRegistration)); + } catch (final AlreadyDefinedException alreadyDefinedException) { + failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponsesFromXpaths( + alreadyDefinedException.getAlreadyDefinedObjectNames(), CM_HANDLE_ALREADY_EXIST)); + } catch (final Exception exception) { + final Collection<String> cmHandleIds = + ncmpServiceCmHandles.stream().map(NcmpServiceCmHandle::getCmHandleId).collect(Collectors.toList()); + failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse + .createFailureResponses(cmHandleIds, exception)); } - } + final List<CmHandleRegistrationResponse> mergedCmHandleRegistrationResponses + = new ArrayList<>(failedCmHandleRegistrationResponses); + mergedCmHandleRegistrationResponses.addAll(dmiPluginRegistrationResponse.getCreatedCmHandles()); + dmiPluginRegistrationResponse.setCreatedCmHandles(mergedCmHandleRegistrationResponses); + } - protected List<CmHandleRegistrationResponse> parseAndProcessUpgradedCmHandlesInRegistration( - final DmiPluginRegistration dmiPluginRegistration) { + protected void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + dmiPluginRegistrationResponse.setUpdatedCmHandles(networkCmProxyDataServicePropertyHandler + .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); + } - final List<String> upgradedCmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles(); + protected void processUpgradedCmHandles( + final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + final List<String> cmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles(); + final String upgradedModuleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag(); final Map<YangModelCmHandle, CmHandleState> acceptedCmHandleStatePerCmHandle - = new HashMap<>(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<>(); + = new HashMap<>(cmHandleIds.size()); + final List<CmHandleRegistrationResponse> cmHandleUpgradeResponses = new ArrayList<>(cmHandleIds.size()); - upgradedCmHandleIds.forEach(cmHandleId -> { + for (final String cmHandleId : cmHandleIds) { 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)); + } + } + cmHandleUpgradeResponses.addAll(upgradeCmHandles(acceptedCmHandleStatePerCmHandle)); + dmiPluginRegistrationResponse.setUpgradedCmHandles(cmHandleUpgradeResponses); + } + + private Collection<String> checkAlternateIds( + final List<NcmpServiceCmHandle> cmHandlesToBeCreated, + final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses) { + final Collection<String> rejectedCmHandleIds = alternateIdChecker + .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated); + cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses( + rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED)); + return rejectedCmHandleIds; + } + + private List<String> persistCmHandlesWithState(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse, + final List<NcmpServiceCmHandle> cmHandlesToBeCreated, + final Collection<String> rejectedCmHandleIds) { + final List<String> succeededCmHandleIds = new ArrayList<>(cmHandlesToBeCreated.size()); + final List<YangModelCmHandle> yangModelCmHandlesToRegister = new ArrayList<>(cmHandlesToBeCreated.size()); + final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = + new ArrayList<>(cmHandlesToBeCreated.size()); + for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) { + if (!rejectedCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) { + yangModelCmHandlesToRegister.add(getYangModelCmHandle(dmiPluginRegistration, ncmpServiceCmHandle)); + cmHandleRegistrationResponses.add( + CmHandleRegistrationResponse.createSuccessResponse(ncmpServiceCmHandle.getCmHandleId())); + succeededCmHandleIds.add(ncmpServiceCmHandle.getCmHandleId()); + } + } + lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandlesToRegister); + dmiPluginRegistrationResponse.setCreatedCmHandles(cmHandleRegistrationResponses); + return succeededCmHandleIds; + } + + private YangModelCmHandle getYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration, + final NcmpServiceCmHandle ncmpServiceCmHandle) { + return YangModelCmHandle.toYangModelCmHandle( + dmiPluginRegistration.getDmiPlugin(), + dmiPluginRegistration.getDmiDataPlugin(), + dmiPluginRegistration.getDmiModelPlugin(), + ncmpServiceCmHandle, + ncmpServiceCmHandle.getModuleSetTag(), + ncmpServiceCmHandle.getAlternateId()); + } + + private void processTrustLevels(final Collection<NcmpServiceCmHandle> cmHandlesToBeCreated, + final Collection<String> succeededCmHandleIds) { + final Map<String, TrustLevel> initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size()); + for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) { + if (succeededCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) { + initialTrustLevelPerCmHandleId.put(ncmpServiceCmHandle.getCmHandleId(), + ncmpServiceCmHandle.getRegistrationTrustLevel()); } - }); - 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) + } + trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId); + } + + 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) { @@ -493,15 +513,14 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private void deleteCmHandleFromDbAndModuleSyncMap(final String cmHandleId) { inventoryPersistence.deleteSchemaSetWithCascade(cmHandleId); - inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId - + "']"); + inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']"); removeDeletedCmHandleFromModuleSyncMap(cmHandleId); } - private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection<String> tobeRemovedCmHandles) { - inventoryPersistence.deleteSchemaSetsWithCascade(tobeRemovedCmHandles); - inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(tobeRemovedCmHandles)); - tobeRemovedCmHandles.forEach(this::removeDeletedCmHandleFromModuleSyncMap); + private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection<String> cmHandleIds) { + inventoryPersistence.deleteSchemaSetsWithCascade(cmHandleIds); + inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(cmHandleIds)); + cmHandleIds.forEach(this::removeDeletedCmHandleFromModuleSyncMap); } private Collection<String> mapCmHandleIdsToXpaths(final Collection<String> cmHandles) { @@ -511,39 +530,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } // CPS-1239 Robustness cleaning of in progress cache - private void removeDeletedCmHandleFromModuleSyncMap(final String deletedCmHandleId) { - if (moduleSyncStartedOnCmHandles.remove(deletedCmHandleId) != null) { - log.debug("{} removed from in progress map", deletedCmHandleId); - } - } - - private List<CmHandleRegistrationResponse> registerNewCmHandles(final List<YangModelCmHandle> yangModelCmHandles, - final Map<String, TrustLevel> - initialTrustLevelPerCmHandleId) { - final Set<String> cmHandleIds = initialTrustLevelPerCmHandleId.keySet(); - try { - lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandles); - trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId); - return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds); - } catch (final AlreadyDefinedException alreadyDefinedException) { - return CmHandleRegistrationResponse.createFailureResponses( - alreadyDefinedException.getAlreadyDefinedObjectNames(), CM_HANDLE_ALREADY_EXIST); - } catch (final Exception exception) { - return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception); + private void removeDeletedCmHandleFromModuleSyncMap(final String cmHandleId) { + if (moduleSyncStartedOnCmHandles.remove(cmHandleId) != null) { + log.debug("{} removed from in progress map", cmHandleId); } } - 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); @@ -569,6 +561,4 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } - - } 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 84075a4897..f5d22af281 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 @@ -77,7 +77,8 @@ public class NetworkCmProxyDataServicePropertyHandler { */ public List<CmHandleRegistrationResponse> updateCmHandleProperties( final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) { - final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(); + final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses + = new ArrayList<>(ncmpServiceCmHandles.size()); for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) { final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); try { @@ -106,8 +107,7 @@ public class NetworkCmProxyDataServicePropertyHandler { private void updateAlternateId(final DataNode existingCmHandleDataNode, final NcmpServiceCmHandle ncmpServiceCmHandle) { final YangModelCmHandle yangModelCmHandle = - YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode, - ncmpServiceCmHandle.getCmHandleId()); + YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode); final String currentAlternateId = yangModelCmHandle.getAlternateId(); final String newAlternateId = ncmpServiceCmHandle.getAlternateId(); if (alternateIdChecker.canApplyAlternateId(ncmpServiceCmHandle.getCmHandleId(), 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/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducer.java new file mode 100644 index 0000000000..5c192a953f --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducer.java @@ -0,0 +1,74 @@ +/* + * ============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.events.cmsubscription; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import java.net.URI; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.events.EventsPublisher; +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true) +public class CmNotificationSubscriptionDmiInEventProducer { + + private final EventsPublisher<CloudEvent> eventsPublisher; + private final JsonObjectMapper jsonObjectMapper; + + @Value("${app.ncmp.avc.subscription-forward-topic-prefix}") + private String cmNotificationSubscriptionDmiInEventTopic; + + /** + * Publish the event to the provided dmi plugin with key as subscription id and the event is in Cloud Event format. + * + * @param subscriptionId Cm Subscription Id + * @param dmiPluginName Dmi Plugin Name + * @param eventType Type of event + * @param cmNotificationSubscriptionDmiInEvent Cm Notification Subscription event for Dmi + */ + public void publishCmNotificationSubscriptionDmiInEvent(final String subscriptionId, final String dmiPluginName, + final String eventType, final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent) { + eventsPublisher.publishCloudEvent(cmNotificationSubscriptionDmiInEventTopic, subscriptionId, + buildAndGetCmNotificationDmiInEventAsCloudEvent(subscriptionId, dmiPluginName, eventType, + cmNotificationSubscriptionDmiInEvent)); + + } + + private CloudEvent buildAndGetCmNotificationDmiInEventAsCloudEvent(final String subscriptionId, + final String dmiPluginName, final String eventType, + final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent) { + return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withType(eventType) + .withSource(URI.create("NCMP")).withDataSchema(URI.create("org.onap.ncmp.dmi.cm.subscription:1.0.0")) + .withExtension("correlationid", subscriptionId.concat("#").concat(dmiPluginName)) + .withData(jsonObjectMapper.asJsonBytes(cmNotificationSubscriptionDmiInEvent)).build(); + } + + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducer.java new file mode 100644 index 0000000000..21468c316c --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducer.java @@ -0,0 +1,75 @@ +/* + * ============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.events.cmsubscription; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import java.net.URI; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.events.EventsPublisher; +import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true) +public class CmNotificationSubscriptionNcmpOutEventProducer { + + private final EventsPublisher<CloudEvent> eventsPublisher; + private final JsonObjectMapper jsonObjectMapper; + + @Value("${app.ncmp.avc.subscription-outcome-topic}") + private String cmNotificationSubscriptionNcmpOutEventTopic; + + /** + * Publish the event to the client who requested the subscription with key as subscription id and event is Cloud + * Event compliant. + * + * @param subscriptionId Cm Subscription Id + * @param eventType Type of event + * @param cmNotificationSubscriptionNcmpOutEvent Cm Notification Subscription Event for the client + */ + public void publishCmNotificationSubscriptionNcmpOutEvent(final String subscriptionId, final String eventType, + final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent) { + + eventsPublisher.publishCloudEvent(cmNotificationSubscriptionNcmpOutEventTopic, subscriptionId, + buildAndGetCmNotificationNcmpOutEventAsCloudEvent(subscriptionId, eventType, + cmNotificationSubscriptionNcmpOutEvent)); + + } + + private CloudEvent buildAndGetCmNotificationNcmpOutEventAsCloudEvent(final String subscriptionId, + final String eventType, + final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent) { + + return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withType(eventType) + .withSource(URI.create("NCMP")).withDataSchema(URI.create("org.onap.ncmp.cm.subscription:1.0.0")) + .withExtension("correlationid", subscriptionId) + .withData(jsonObjectMapper.asJsonBytes(cmNotificationSubscriptionNcmpOutEvent)).build(); + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java index 189fbb53d9..38f3db98de 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java @@ -40,6 +40,14 @@ public interface CmNotificationSubscriptionPersistenceService { final String xpath); /** + * Check if the subscription ID is unique against ongoing subscriptions. + * + * @param subscriptionId subscription ID + * @return true if subscriptionId is not used in active subscriptions, otherwise false + */ + boolean isUniqueSubscriptionId(final String subscriptionId); + + /** * Get all ongoing cm notification subscription based on the parameters. * * @param datastoreType valid datastore type diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java index 63f12ad623..6e4997a4dd 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java @@ -36,9 +36,13 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotificationSubscriptionPersistenceService { + private static final String SUBSCRIPTION_ANCHOR_NAME = "cm-data-subscriptions"; private static final String IS_ONGOING_CM_SUBSCRIPTION_CPS_PATH_QUERY = """ /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']/filters/filter[@xpath='%s'] """.trim(); + private static final String SUBSCRIPTION_IDS_CPS_PATH_QUERY = """ + //filter/subscriptionIds[text()='%s'] + """.trim(); private final CpsQueryService cpsQueryService; @@ -49,6 +53,13 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif } @Override + public boolean isUniqueSubscriptionId(final String subscriptionId) { + return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, + SUBSCRIPTION_IDS_CPS_PATH_QUERY.formatted(subscriptionId), + FetchDescendantsOption.OMIT_DESCENDANTS).isEmpty(); + } + + @Override public Collection<String> getOngoingCmNotificationSubscriptionIds(final DatastoreType datastoreType, final String cmHandleId, final String xpath) { 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 08ab15eaa7..3ae3dd9116 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 @@ -109,7 +109,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { cpsValidator.validateNameCharacters(cmHandleId); final DataNode dataNode = getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next(); - return YangDataConverter.convertCmHandleToYangModel(dataNode, cmHandleId); + return YangDataConverter.convertCmHandleToYangModel(dataNode); } @Override 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..794ca5b1b6 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); @@ -238,9 +241,7 @@ public class ModuleOperationsUtils { private List<YangModelCmHandle> convertCmHandlesDataNodesToYangModelCmHandles( final List<DataNode> cmHandlesAsDataNodeList) { - return cmHandlesAsDataNodeList.stream() - .map(cmHandle -> YangDataConverter.convertCmHandleToYangModel(cmHandle, - cmHandle.getLeaves().get("id").toString())).toList(); + return cmHandlesAsDataNodeList.stream().map(YangDataConverter::convertCmHandleToYangModel).toList(); } private boolean isRetryDue(final CompositeState.LockReason compositeStateLockReason, final OffsetDateTime time) { 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..e257112fc3 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 @@ -28,10 +28,9 @@ import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPE import java.time.OffsetDateTime; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; +import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -40,7 +39,6 @@ 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,54 +61,39 @@ 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(); + @AllArgsConstructor + private static final class ModuleDelta { + Collection<ModuleReference> allModuleReferences; + Map<String, String> newModuleNameToContentMap; + } + /** - * This method registers a cm handle and initiates modules sync. + * This method creates a cm handle and initiates modules sync. * * @param yangModelCmHandle the yang model of cm handle. */ - public void syncAndCreateOrUpgradeSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) { - + public void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) { + final ModuleDelta moduleDelta = getModuleDelta(yangModelCmHandle, yangModelCmHandle.getModuleSetTag()); 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); + cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, + moduleDelta.newModuleNameToContentMap, moduleDelta.allModuleReferences); + cpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cmHandleId); + } - 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); - } - } 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) { - cpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cmHandleId); - } - setCmHandleModuleSetTag(yangModelCmHandle, moduleSetTag); + /** + * This method upgrades a cm handle and initiates modules sync. + * + * @param yangModelCmHandle the yang model of cm handle. + */ + public void syncAndUpgradeSchemaSet(final YangModelCmHandle yangModelCmHandle) { + final String upgradedModuleSetTag = ModuleOperationsUtils.getUpgradedModuleSetTagFromLockReason( + yangModelCmHandle.getCompositeState().getLockReason()); + final ModuleDelta moduleDelta = getModuleDelta(yangModelCmHandle, upgradedModuleSetTag); + cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, + yangModelCmHandle.getId(), moduleDelta.newModuleNameToContentMap, moduleDelta.allModuleReferences); + setCmHandleModuleSetTag(yangModelCmHandle, upgradedModuleSetTag); } /** @@ -128,76 +111,41 @@ public class ModuleSyncService { } } - private Optional<DataNode> getFirstReadyDataNodeWithModuleSetTag(final String moduleSetTag) { - final List<DataNode> dataNodes = StringUtils.isNotBlank(moduleSetTag) ? cmHandleQueries - .queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='" + moduleSetTag + "']", - FetchDescendantsOption.OMIT_DESCENDANTS) : Collections.emptyList(); - return dataNodes.stream().filter(dataNode -> { - final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(dataNode.getXpath()); - return cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY); - }).findFirst(); - } - - private void setCmHandleModuleSetTag(final YangModelCmHandle upgradedCmHandle, final String moduleSetTag) { - final Map<String, Map<String, String>> dmiRegistryProperties = new HashMap<>(1); - final Map<String, String> cmHandleProperties = new HashMap<>(2); - cmHandleProperties.put("id", upgradedCmHandle.getId()); - cmHandleProperties.put("module-set-tag", moduleSetTag); - dmiRegistryProperties.put("cm-handles", cmHandleProperties); - cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, - jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now()); - } - - private Collection<ModuleReference> syncAndCreateSchemaSet(final YangModelCmHandle yangModelCmHandle) { - final Collection<ModuleReference> allModuleReferencesFromCmHandle = - dmiModelOperations.getModuleReferences(yangModelCmHandle); - final Collection<ModuleReference> identifiedNewModuleReferencesFromCmHandle = cpsModuleService - .identifyNewModuleReferences(allModuleReferencesFromCmHandle); - final Map<String, String> newModuleNameToContentMap; - if (identifiedNewModuleReferencesFromCmHandle.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 ModuleDelta getModuleDelta(final YangModelCmHandle yangModelCmHandle, final String targetModuleSetTag) { + final Collection<ModuleReference> allModuleReferences; + final Map<String, String> newYangResources; - 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); + final YangModelCmHandle cmHandleWithSameModuleSetTag = getAnyReadyCmHandleByModuleSetTag(targetModuleSetTag); + if (cmHandleWithSameModuleSetTag == null) { + allModuleReferences = dmiModelOperations.getModuleReferences(yangModelCmHandle); + newYangResources = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, + cpsModuleService.identifyNewModuleReferences(allModuleReferences)); } else { - cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, - schemaSetName, NO_NEW_MODULES, moduleReferencesFromExistingCmHandle); + log.info("Found other cm handle having same module set tag: {}", targetModuleSetTag); + allModuleReferences = cpsModuleService.getYangResourcesModuleReferences( + NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleWithSameModuleSetTag.getId()); + newYangResources = NO_NEW_MODULES; } - return moduleReferencesFromExistingCmHandle; + return new ModuleDelta(allModuleReferences, newYangResources); } - private String getModuleSetTag(final YangModelCmHandle yangModelCmHandle, - final CompositeState compositeState, - final boolean inUpgrade) { - if (inUpgrade) { - return ModuleOperationsUtils.getLockedCompositeStateDetails(compositeState.getLockReason()) - .get(ModuleOperationsUtils.MODULE_SET_TAG_KEY); + private YangModelCmHandle getAnyReadyCmHandleByModuleSetTag(final String moduleSetTag) { + if (StringUtils.isBlank(moduleSetTag)) { + return null; } - return yangModelCmHandle.getModuleSetTag(); + final String escapedModuleSetTag = moduleSetTag.replace("'", "''"); + final List<DataNode> dataNodes = cmHandleQueries.queryNcmpRegistryByCpsPath( + NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@module-set-tag='" + escapedModuleSetTag + "']", + FetchDescendantsOption.DIRECT_CHILDREN_ONLY); + return dataNodes.stream().map(YangDataConverter::convertCmHandleToYangModel) + .filter(cmHandle -> cmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY) + .findFirst().orElse(null); } - private void updateModuleSetTagCache(final String moduleSetTag, - final Collection<ModuleReference> allModuleReferencesFromCmHandle) { - if (StringUtils.isNotBlank(moduleSetTag)) { - moduleSetTagCache.putIfAbsent(moduleSetTag, allModuleReferencesFromCmHandle); - } + private void setCmHandleModuleSetTag(final YangModelCmHandle yangModelCmHandle, final String newModuleSetTag) { + final String jsonForUpdate = jsonObjectMapper.asJsonString(Map.of( + "cm-handles", Map.of("id", yangModelCmHandle.getId(), "module-set-tag", newModuleSetTag))); + cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, + jsonForUpdate, OffsetDateTime.now()); } - } 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..590cb56c48 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 @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-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. @@ -65,14 +65,16 @@ public class ModuleSyncTasks { for (final DataNode cmHandleAsDataNode : cmHandlesAsDataNodes) { final String cmHandleId = String.valueOf(cmHandleAsDataNode.getLeaves().get("id")); final YangModelCmHandle yangModelCmHandle = - YangDataConverter.convertCmHandleToYangModel(cmHandleAsDataNode, cmHandleId); + YangDataConverter.convertCmHandleToYangModel(cmHandleAsDataNode); final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); - final boolean inUpgrade = ModuleOperationsUtils.isInUpgradeOrUpgradeFailed(compositeState); + final boolean inUpgrade = ModuleOperationsUtils.inUpgradeOrUpgradeFailed(compositeState); try { - if (!inUpgrade) { + if (inUpgrade) { + moduleSyncService.syncAndUpgradeSchemaSet(yangModelCmHandle); + } else { moduleSyncService.deleteSchemaSetIfExists(cmHandleId); + moduleSyncService.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle); } - moduleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle); yangModelCmHandle.getCompositeState().setLockReason(null); cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.READY); } catch (final Exception e) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java index 32b5cb7304..dbe386d7ca 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,6 +27,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -83,6 +84,9 @@ public class DmiModelOperations extends DmiOperations { */ public Map<String, String> getNewYangResourcesFromDmi(final YangModelCmHandle yangModelCmHandle, final Collection<ModuleReference> newModuleReferences) { + if (newModuleReferences.isEmpty()) { + return Collections.emptyMap(); + } final String jsonWithDataAndDmiProperties = getRequestBodyToFetchYangResources( newModuleReferences, yangModelCmHandle.getDmiProperties()); final ResponseEntity<Object> responseEntity = getResourceFromDmiWithJsonData( 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 index 1be1a90853..4ac6537494 100644 --- 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 @@ -103,10 +103,10 @@ public class AlternateIdChecker { * @param newNcmpServiceCmHandles the proposed new cm handles * @return collection of cm handles ids which are acceptable */ - public Collection<String> getIdsOfCmHandlesWithAcceptableAlternateId( + public Collection<String> getIdsOfCmHandlesWithRejectedAlternateId( final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles) { final Set<String> acceptedAlternateIds = new HashSet<>(newNcmpServiceCmHandles.size()); - final Collection<String> acceptedCmHandleIds = new ArrayList<>(newNcmpServiceCmHandles.size()); + final Collection<String> rejectedCmHandleIds = new ArrayList<>(); for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) { final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); final String proposedAlternateId = ncmpServiceCmHandle.getAlternateId(); @@ -124,10 +124,11 @@ public class AlternateIdChecker { } if (isAcceptable) { acceptedAlternateIds.add(proposedAlternateId); - acceptedCmHandleIds.add(cmHandleId); + } else { + rejectedCmHandleIds.add(cmHandleId); } } - return acceptedCmHandleIds; + return rejectedCmHandleIds; } private boolean alternateIdAlreadyInDb(final String alternateId) { 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..3954142976 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 @@ -20,13 +20,13 @@ package org.onap.cps.ncmp.api.impl.utils; -import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -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); @@ -75,12 +76,11 @@ public class YangDataConverter { /** * This method convert cm handle data node to yang model cm handle. * @param cmHandleDataNode the datanode of the cm handle - * @param cmHandleId the id of the cm handle * @return yang model cm handle */ - public static YangModelCmHandle convertCmHandleToYangModel(final DataNode cmHandleDataNode, - final String cmHandleId) { + public static YangModelCmHandle convertCmHandleToYangModel(final DataNode cmHandleDataNode) { final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle(); + final String cmHandleId = cmHandleDataNode.getLeaves().get("id").toString(); ncmpServiceCmHandle.setCmHandleId(cmHandleId); populateCmHandleDetails(cmHandleDataNode, ncmpServiceCmHandle); return YangModelCmHandle.toYangModelCmHandle( @@ -100,12 +100,8 @@ public class YangDataConverter { */ public static Collection<YangModelCmHandle> convertDataNodesToYangModelCmHandles( final Collection<DataNode> cmHandleDataNodes) { - final Collection<YangModelCmHandle> yangModelCmHandles = new ArrayList<>(cmHandleDataNodes.size()); - cmHandleDataNodes.forEach(dataNode -> { - final String cmHandleId = extractCmHandleIdFromXpath(dataNode.getXpath()); - yangModelCmHandles.add(convertCmHandleToYangModel(dataNode, cmHandleId)); - }); - return yangModelCmHandles; + return cmHandleDataNodes.stream().map(YangDataConverter::convertCmHandleToYangModel) + .collect(Collectors.toList()); } /** 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/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java index 82283228a1..52b8d6926a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2022-2023 Nordix Foundation + * Modifications Copyright (C) 2022-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. @@ -76,20 +76,22 @@ public class CmHandleRegistrationResponse { } /** - * Creates a failure response based on registration error. + * Create a failure response of cm handle registration based on xpath and registration error. + * Conditions: + * - the xpath should be valid according to the cps path, otherwise xpath is not included in the response. * - * @param failedXpaths list of failed Xpaths - * @param ncmpResponseStatus enum describing the type of registration error - * @return CmHandleRegistrationResponse + * @param failedXpaths the failed xpaths + * @param ncmpResponseStatus type of the registration error + * @return collection of cm handle registration response */ - public static List<CmHandleRegistrationResponse> createFailureResponses(final Collection<String> failedXpaths, - final NcmpResponseStatus ncmpResponseStatus) { + public static List<CmHandleRegistrationResponse> createFailureResponsesFromXpaths( + final Collection<String> failedXpaths, final NcmpResponseStatus ncmpResponseStatus) { final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(failedXpaths.size()); for (final String xpath : failedXpaths) { try { final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(xpath); - cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus)); + cmHandleRegistrationResponses + .add(CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus)); } catch (IllegalArgumentException | IllegalStateException e) { log.warn("Unexpected xpath {}", xpath); } @@ -98,6 +100,24 @@ public class CmHandleRegistrationResponse { } /** + * Create a failure response of cm handle registration based on cm handle id and registration error. + * + * @param failedCmHandleIds the failed cm handle ids + * @param ncmpResponseStatus type of the registration error + * @return collection of cm handle registration response + */ + public static List<CmHandleRegistrationResponse> createFailureResponses( + final Collection<String> failedCmHandleIds, final NcmpResponseStatus ncmpResponseStatus) { + final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = + new ArrayList<>(failedCmHandleIds.size()); + for (final String failedCmHandleId : failedCmHandleIds) { + cmHandleRegistrationResponses.add( + CmHandleRegistrationResponse.createFailureResponse(failedCmHandleId, ncmpResponseStatus)); + } + return cmHandleRegistrationResponses; + } + + /** * Creates a failure response based on other exception. * * @param cmHandleIds list of failed cmHandleIds diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java index 4615af61c1..7d6a8e1407 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation + * Copyright (C) 2021-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. @@ -50,7 +50,7 @@ public class DmiPluginRegistration { private List<String> removedCmHandles = Collections.emptyList(); - private UpgradedCmHandles upgradedCmHandles; + private UpgradedCmHandles upgradedCmHandles = new UpgradedCmHandles(); /** * Validates plugin service names. 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 572adb8f0f..cb7e1ef8a9 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 @@ -21,14 +21,13 @@ package org.onap.cps.ncmp.api.impl -import java.util.stream.Collectors - import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND 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 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.AlternateIdChecker import org.onap.cps.ncmp.api.models.UpgradedCmHandles @@ -71,13 +70,18 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def trustLevelPerDmiPlugin = [:] def mockTrustLevelManager = Mock(TrustLevelManager) def mockAlternateIdChecker = Mock(AlternateIdChecker) - def objectUnderTest = getObjectUnderTest() - def mockModuleSetTagCache = [:] + + def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations, + mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries, + stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService, + mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockAlternateIdChecker)) def setup() { // always accept all cm handles - mockAlternateIdChecker.getIdsOfCmHandlesWithAcceptableAlternateId(_) >> - { args -> args[0].stream().map(it -> it.cmHandleId).collect(Collectors.toList()) } + mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> [] + + // always can find all cm handles in DB + mockInventoryPersistence.getYangModelCmHandles(_) >> { args -> args[0].collect { new YangModelCmHandle(id:it) } } } def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() { @@ -89,20 +93,22 @@ 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' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) then: 'cm-handles are removed first' - 1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) + 1 * objectUnderTest.processRemovedCmHandles(*_) 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(*_) + 1 * objectUnderTest.processCreatedCmHandles(*_) then: 'cm-handles are updated' - 1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) + 1 * objectUnderTest.processUpdatedCmHandles(*_) + 1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> [] then: 'cm-handles are upgraded' - 1 * objectUnderTest.parseAndProcessUpgradedCmHandlesInRegistration(*_) + 1 * objectUnderTest.processUpgradedCmHandles(*_) } def 'DMI Registration upgrade operation with upgrade node state #scenario'() { @@ -110,15 +116,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'() { @@ -126,7 +132,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' @@ -137,29 +143,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { 'cm handle is invalid' | new DataValidationException('some error message', 'some error details') || '110' } - def 'DMI Registration: Response from all operations types are in response'() { - given: 'a registration with operations of all three types' - def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') - dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])]) - dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])]) - dmiRegistration.setRemovedCmHandles(['cmhandle-2']) - and: 'update cm-handles can be processed successfully' - def updateResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-2')] - mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> updateResponses - and: 'create cm-handles can be processed successfully' - def createdResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-1')] - objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) >> createdResponses - and: 'delete cm-handles can be processed successfully' - def removeResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-3')] - objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) >> removeResponses - when: 'registration is processed' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) - then: 'response has values from all operations' - response.removedCmHandles == removeResponses - response.createdCmHandles == createdResponses - response.updatedCmHandles == updateResponses - } - def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() { given: 'a registration ' def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin, @@ -168,7 +151,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.processCreatedCmHandles(dmiPluginRegistration, _) and: 'dmi is added to the dmi trustLevel map' assert trustLevelPerDmiPlugin.size() == 1 assert trustLevelPerDmiPlugin.containsKey(expectedDmiPluginRegisteredName) @@ -190,7 +173,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def exceptionThrown = thrown(DmiRequestException.class) assert exceptionThrown.getMessage().contains(expectedMessageDetails) and: 'registration is not called' - 0 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration) + 0 * objectUnderTest.processCreatedCmHandles(*_) where: scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails 'empty DMI plugins' | '' | '' | '' || 'No DMI plugin service names' @@ -231,22 +214,18 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { 'without dmi & public properties' | [:] | [:] || [:] | [:] } - def 'Add CM-Handle to trustLevelPerCmHandle Successfully with: #scenario.'() { - given: 'a registration with trustLevel and populated cache' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') - dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: TrustLevel.NONE), - new NcmpServiceCmHandle(cmHandleId: cmHandleId, registrationTrustLevel: registrationTrustLevel)] + def 'Add CM-Handle #scenario.'() { + given: ' registration details for one cm handles' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', + createdCmHandles:[new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: registrationTrustLevel)]) when: 'registration is updated' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'a successful response is received' - assert response.createdCmHandles.size() == expectedNumberOfCreatedCmHandles - and: 'trustLevel is set for the created cm-handle' - 1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(_) + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'trustLevel is set for the created cm-handle' + 1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(expectedMapping) where: - scenario | cmHandleId | registrationTrustLevel || expectedNumberOfCreatedCmHandles - 'new trusted cm handle' | 'ch-new' | TrustLevel.COMPLETE || 2 - 'existing cm handle without trust level' | 'ch-1' | null || 1 - 'new cm handle without trust level' | 'ch-new' | null || 2 + scenario | registrationTrustLevel || expectedMapping + 'with trusted cm handle' | TrustLevel.COMPLETE || [ 'ch-1' : TrustLevel.COMPLETE ] + 'without trust level' | null || [ 'ch-1' : null ] } def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed with some failures'() { @@ -311,17 +290,15 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def 'Remove CmHandle Successfully: #scenario'() { given: 'a registration' - addPersistedYangModelCmHandles(['cmhandle']) - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', - removedCmHandles: ['cmhandle']) + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle']) and: '#scenario' - mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >> - { if (!schemaSetExist) { throw new SchemaSetNotFoundException("", "") } } + mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >> { if (!schemaSetExist) { throw new SchemaSetNotFoundException('', '') } } when: 'registration is updated to delete cmhandle' def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'the cmHandle state is updated to "DELETING"' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) - and: 'method to delete relevant schema set is called once' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> + { args -> args[0].values()[0] == CmHandleState.DELETING } + then: 'method to delete relevant schema set is called once' 1 * mockInventoryPersistence.deleteSchemaSetsWithCascade(_) and: 'method to delete relevant list/list element is called once' 1 * mockInventoryPersistence.deleteDataNodes(_) @@ -332,7 +309,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { assert it.cmHandle == 'cmhandle' } and: 'the cmHandle state is updated to "DELETED"' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> + { args -> args[0].values()[0] == CmHandleState.DELETED } + and: 'No cm handles state updates for "upgraded cm handles"' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:]) where: scenario | schemaSetExist 'schema-set exists and can be deleted successfully' | true @@ -340,9 +320,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { } def 'Remove CmHandle: Partial Success'() { - given: 'some unique yang model cm handles' - addPersistedYangModelCmHandles(['cmhandle1', 'cmhandle2', 'cmhandle3']) - and: 'a registration with three cm-handles to be deleted' + given: 'a registration with three cm-handles to be deleted' def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3']) and: 'cm-handle deletion fails on batch' @@ -359,7 +337,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle1') and: 'successfully de-registered cm handle 3 is removed from in progress map even though it was already being removed' 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle3') >> 'already in progress' - and: 'failed de-registered cm handle entries should not be removed from in progress map' + and: 'failed de-registered cm handle entries should NOT be removed from in progress map' 0 * mockModuleSyncStartedOnCmHandles.remove('cmhandle2') and: '1st and 3rd cm-handle deletes successfully' with(response.removedCmHandles[0]) { @@ -382,11 +360,13 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { assert it.size() == 2 assert it.every { entry -> entry.value == CmHandleState.DELETED } }) + and: 'No cm handles state updates for "upgraded cm handles"' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:]) + } def 'Remove CmHandle Error Handling: Schema Set Deletion failed'() { given: 'a registration' - addPersistedYangModelCmHandles(['cmhandle']) def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle']) and: 'schema set batch deletion failed with unknown error' @@ -413,7 +393,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def 'Remove CmHandle Error Handling: #scenario'() { given: 'a registration' - addPersistedYangModelCmHandles(['cmhandle']) def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle']) and: 'cm-handle deletion fails on batch' @@ -446,18 +425,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { when: 'the DMI plugin registration happens' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'the new alternate id is added to the cache' - 1 * mockAlternateIdChecker.getIdsOfCmHandlesWithAcceptableAlternateId(ncmpServiceCmHandles) >> ['cmhandle1'] + 1 * mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(ncmpServiceCmHandles) >> ['cmhandle1'] } - def getObjectUnderTest() { - return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations, - mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries, - stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService, - mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockAlternateIdChecker, mockModuleSetTagCache)) - } - - def addPersistedYangModelCmHandles(ids) { - def yangModelCmHandles = ids.collect { new YangModelCmHandle(id:it) } - mockInventoryPersistence.getYangModelCmHandles(ids) >> yangModelCmHandles - } } 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 64bedb8ad0..9b4fe146b9 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 @@ -23,6 +23,8 @@ package org.onap.cps.ncmp.api.impl +import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse + 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 @@ -81,7 +83,6 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>) def mockTrustLevelManager = Mock(TrustLevelManager) def mockAlternateIdChecker = Mock(AlternateIdChecker) - def mockModuleSetTagCache = [:] def NO_TOPIC = null def NO_REQUEST_ID = null @@ -100,8 +101,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { stubModuleSyncStartedOnCmHandles, stubTrustLevelPerDmiPlugin, mockTrustLevelManager, - mockAlternateIdChecker, - mockModuleSetTagCache) + mockAlternateIdChecker) def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']" @@ -175,14 +175,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') @@ -190,13 +193,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'() { @@ -266,8 +272,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { dmiDataPlugin: 'service2') dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle] + and: 'no rejected cm handles because of alternate ids' + mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> [] when: 'parse and create cm handle in dmi registration then sync module' - objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration, ['test-cm-handle-id']) + mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id'] + objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse()) then: 'system persists the cm handle state' 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> { args -> { 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 d822f2e639..e00a42674b 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 @@ -56,7 +56,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp2']").withLeaves(['name': 'additionalProp2', 'value': 'additionalValue2']).build(), new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/public-properties[@name='publicProp3']").withLeaves(['name': 'publicProp3', 'value': 'publicValue3']).build(), new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/public-properties[@name='publicProp4']").withLeaves(['name': 'publicProp4', 'value': 'publicValue4']).build()] - def static cmHandleDataNodeAsCollection = [new DataNode(xpath: cmHandleXpath, childDataNodes: propertyDataNodes)] + def static cmHandleDataNodeAsCollection = [new DataNode(xpath: cmHandleXpath, childDataNodes: propertyDataNodes, leaves: ['id': cmHandleId])] def 'Update CM Handle Public Properties: #scenario'() { given: 'the CPS service return a CM handle' @@ -105,7 +105,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) + def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId], childDataNodes: originalPropertyDataNodes) 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])] @@ -182,9 +182,9 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def 'Update CM Handle Alternate ID with #scenario'() { given: 'an existing cm handle' - DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath) + DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId]) and: 'an update request with an alternate id' - def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1' ) + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1') when: 'update alternate id method is called with the update request' objectUnderTest.updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle) then: 'the update node leaves method is invoked as many times as expected' @@ -202,7 +202,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def 'Alternate ID removed from cache when persisting fails.'() { 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]) + DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': null]) and: 'an applicable alternate id for the cm handle' mockAlternateIdChecker.canApplyAlternateId(cmHandleId, '','alt-1') >> true and: 'but an exception occurs while saving' 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/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy new file mode 100644 index 0000000000..cd9b8ddf41 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy @@ -0,0 +1,64 @@ +/* + * ============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.events.cmsubscription + +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import org.onap.cps.events.EventsPublisher +import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Cmhandle +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Data +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +class CmNotificationSubscriptionDmiInEventProducerSpec extends Specification { + + def mockEventsPublisher = Mock(EventsPublisher) + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + + def objectUnderTest = new CmNotificationSubscriptionDmiInEventProducer(mockEventsPublisher, jsonObjectMapper) + + def 'Create and Publish Cm Notification Subscription DMI In Event'() { + given: 'a cm subscription for a dmi plugin' + def subscriptionId = 'test-subscription-id' + def dmiPluginName = 'test-dmiplugin' + def eventType = 'subscriptionCreateRequest' + def cmNotificationSubscriptionDmiInEvent = new CmNotificationSubscriptionDmiInEvent(data: new Data(cmhandles: [new Cmhandle(cmhandleId: 'test-1', privateProperties: [:])])) + and: 'also we have target topic for dmiPlugin' + objectUnderTest.cmNotificationSubscriptionDmiInEventTopic = 'dmiplugin-test-topic' + when: 'the event is published' + objectUnderTest.publishCmNotificationSubscriptionDmiInEvent(subscriptionId, dmiPluginName, eventType, cmNotificationSubscriptionDmiInEvent) + then: 'the event contains the required attributes' + 1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> { + args -> + { + assert args[0] == 'dmiplugin-test-topic' + assert args[1] == subscriptionId + def cmNotificationSubscriptionDmiInEventAsCloudEvent = (args[2] as CloudEvent) + assert cmNotificationSubscriptionDmiInEventAsCloudEvent.getExtension('correlationid') == subscriptionId + '#' + dmiPluginName + assert cmNotificationSubscriptionDmiInEventAsCloudEvent.type == 'subscriptionCreateRequest' + assert cmNotificationSubscriptionDmiInEventAsCloudEvent.source.toString() == 'NCMP' + assert CloudEventMapper.toTargetEvent(cmNotificationSubscriptionDmiInEventAsCloudEvent, CmNotificationSubscriptionDmiInEvent) == cmNotificationSubscriptionDmiInEvent + } + } + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy new file mode 100644 index 0000000000..7c1a148ad7 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy @@ -0,0 +1,44 @@ +package org.onap.cps.ncmp.api.impl.events.cmsubscription + +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import org.onap.cps.events.EventsPublisher +import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper +import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent +import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.Data +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +class CmNotificationSubscriptionNcmpOutEventProducerSpec extends Specification { + + def mockEventsPublisher = Mock(EventsPublisher) + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + + def objectUnderTest = new CmNotificationSubscriptionNcmpOutEventProducer(mockEventsPublisher, jsonObjectMapper) + + def 'Create and Publish Cm Notification Subscription DMI In Event'() { + given: 'a cm subscription response for the client' + def subscriptionId = 'test-subscription-id' + def eventType = 'subscriptionCreateResponse' + def cmNotificationSubscriptionNcmpOutEvent = new CmNotificationSubscriptionNcmpOutEvent(data: new Data(subscriptionId: 'sub-1', acceptedTargets: ['ch-1', 'ch-2'])) + and: 'also we have target topic for publishing to client' + objectUnderTest.cmNotificationSubscriptionNcmpOutEventTopic = 'client-test-topic' + when: 'the event is published' + objectUnderTest.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, eventType, cmNotificationSubscriptionNcmpOutEvent) + then: 'the event contains the required attributes' + 1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> { + args -> + { + assert args[0] == 'client-test-topic' + assert args[1] == subscriptionId + def cmNotificationSubscriptionNcmpOutEventAsCloudEvent = (args[2] as CloudEvent) + assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.getExtension('correlationid') == subscriptionId + assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.type == 'subscriptionCreateResponse' + assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.source.toString() == 'NCMP' + assert CloudEventMapper.toTargetEvent(cmNotificationSubscriptionNcmpOutEventAsCloudEvent, CmNotificationSubscriptionNcmpOutEvent) == cmNotificationSubscriptionNcmpOutEvent + } + } + } + + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy index e951c31094..eb0e1100ed 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy @@ -38,7 +38,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification def cpsPathQuery = "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='/cps/path']"; and: 'datanodes optionally returned' 1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode + cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode when: 'we check for an ongoing cm subscription' def response = objectUnderTest.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/cps/path') then: 'we get expected response' @@ -48,4 +48,20 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification 'valid datanodes present' | [new DataNode(xpath: '/cps/path', leaves: ['subscriptionIds': ['sub-1', 'sub-2']])] || true 'no datanodes present' | [] || false } -} + + def 'Checking uniqueness of incoming subscription ID'() { + given: 'a cps path with a subscription ID for querying' + def cpsPathQuery = objectUnderTest.SUBSCRIPTION_IDS_CPS_PATH_QUERY.formatted('some-sub') + and: 'relevant datanodes are returned' + 1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> + dataNodes + when: 'a subscription ID is tested for uniqueness' + def result = objectUnderTest.isUniqueSubscriptionId('some-sub') + then: 'result is as expected' + assert result == isValidSubscriptionId + where: 'following scenarios are used' + scenario | dataNodes || isValidSubscriptionId + 'datanodes present' | [new DataNode()] || false + 'no datanodes present' | [] || true + } +}
\ No newline at end of file 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 83acb2238a..cbc985f783 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 @@ -71,7 +71,7 @@ class InventoryPersistenceImplSpec extends Specification { .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC)) def cmHandleId = 'some-cm-handle' - def leaves = ["dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"] + def leaves = ["id":cmHandleId,"dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"] def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']" def cmHandleId2 = 'another-cm-handle' @@ -119,7 +119,7 @@ class InventoryPersistenceImplSpec extends Specification { def "Handling missing service names as null."() { given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes' - def dataNode = new DataNode(childDataNodes:[], leaves: [:]) + def dataNode = new DataNode(childDataNodes:[], leaves: ['id':cmHandleId]) mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode] when: 'retrieving the yang modelled cm handle' def result = objectUnderTest.getYangModelCmHandle(cmHandleId) @@ -133,7 +133,7 @@ class InventoryPersistenceImplSpec extends Specification { def "Retrieve multiple YangModelCmHandles"() { given: 'the cps data service returns 2 data nodes from the DMI registry' - def dataNodes = [new DataNode(xpath: xpath), new DataNode(xpath: xpath2)] + def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2])] mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, [xpath, xpath2] , INCLUDE_ALL_DESCENDANTS) >> dataNodes when: 'retrieving the yang modelled cm handle' def results = objectUnderTest.getYangModelCmHandles([cmHandleId, cmHandleId2]) 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..0c60e88772 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 @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-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. @@ -26,7 +26,6 @@ import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPE import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE import org.onap.cps.ncmp.api.impl.inventory.CmHandleState -import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.DataNode import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsModuleService @@ -50,16 +49,18 @@ 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() + def static cmHandleWithModuleSetTag = new DataNodeBuilder() + .withXpath("/dmi-registry/cm-handles[@id='otherId']") + .withLeaves(['id': 'otherId', 'module-set-tag': 'tag-1']) + .withAnchor('otherId').build() - def 'Sync model for a NEW cm handle without cache with #scenario'() { - given: 'a cm handle having some state' + def 'Sync model for a NEW cm handle using module set tags: #scenario.'() { + given: 'a cm handle state to be synced' def ncmpServiceCmHandle = new NcmpServiceCmHandle() ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.ADVISED).build()) ncmpServiceCmHandle.cmHandleId = 'ch-1' @@ -68,22 +69,22 @@ class ModuleSyncServiceSpec extends Specification { def moduleReferences = [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ] mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences and: 'DMI-Plugin returns resource(s) for "new" module(s)' - mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> newModuleNameContentToMap + mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, identifiedNewModuleReferences) >> newModuleNameContentToMap and: 'the module service identifies #identifiedNewModuleReferences.size() new modules' - mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReferences(identifiedNewModuleReferences) - and: 'system contains #existingCmHandlesWithSameTag.size() cm handles with same tag' - mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='new-tag-1']", - FetchDescendantsOption.OMIT_DESCENDANTS) >> [] + mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> identifiedNewModuleReferences + and: 'system contains other cm handle with "same tag" (that is READY)' + mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag when: 'module sync is triggered' - objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle) + objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle) then: 'create schema set from module is invoked with correct parameters' 1 * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', newModuleNameContentToMap, moduleReferences) and: 'anchor is created with the correct parameters' 1 * mockCpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', 'ch-1') where: 'the following parameters are used' - scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | newModuleNameContentToMap | moduleSetTag - 'one new module' | [['module2': '2'], ['module3': '3']] | [['module1': '1']] | [module1: 'some yang source'] | '' - 'no new module' | [['module1': '1'], ['module2': '2']] | [] | [:] | 'new-tag-1' + scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | newModuleNameContentToMap | moduleSetTag | existingCmHandlesWithSameTag + 'one new module, new tag' | [['module2': '2'], ['module3': '3']] | [new ModuleReference('module1', '1')] | [module1: 'some yang source'] | '' | [] + 'no new module, new tag' | [['module1': '1'], ['module2': '2']] | [] | [:] | 'new-tag-1' | [] + 'same tag' | [['module1': '1'], ['module2': '2']] | [] | [:] | 'same-tag' | [cmHandleWithModuleSetTag] } def 'Upgrade model for an existing cm handle with Module Set Tag where the modules are #scenario'() { @@ -95,33 +96,29 @@ class ModuleSyncServiceSpec extends Specification { def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'tag-1', '') 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 - } + and: 'DMI operations returns some module references for upgraded cm handle' + mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences + mockDmiModelOperations.getNewYangResourcesFromDmi(_, []) >> [:] and: 'none of these module references are new (unknown to the system)' - mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> [] + mockCpsModuleService.identifyNewModuleReferences(_) >> [] and: 'CPS-Core returns list of existing module resources for TBD' mockCpsModuleService.getYangResourcesModuleReferences(*_) >> [ new ModuleReference('module1','1') ] and: 'system contains #existingCmHandlesWithSameTag.size() cm handles with same tag' - mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='tag-1']", FetchDescendantsOption.OMIT_DESCENDANTS) >> existingCmHandlesWithSameTag + mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag and: 'the other cm handle is a state ready' mockCmHandleQueries.cmHandleHasState('otherId', CmHandleState.READY) >> true when: 'module sync is triggered' - objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle) + objectUnderTest.syncAndUpgradeSchemaSet(yangModelCmHandle) then: 'update schema set from module is invoked for the upgraded cm handle' - expectedCallsToUpgradeSchemaSet * mockCpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences) - and: 'create schema set from module is invoked for the upgraded cm handle' - expectedCallsToCeateSchemaSet * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences) + 1 * mockCpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences) + and: 'create schema set from module is not invoked for the upgraded cm handle' + 0 * mockCpsModuleService.createSchemaSetFromModules(*_) 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 + 'new' | [] + 'in database' | [cmHandleWithModuleSetTag] } def 'upgrade model for a existing cm handle'() { @@ -136,10 +133,10 @@ class ModuleSyncServiceSpec extends Specification { def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] mockCpsModuleService.getYangResourcesModuleReferences(*_)>> moduleReferences and: 'a cm handle with the same moduleSetTag can be found in the registry' - mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='targetModuleSetTag']", - FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1', 'cm-handle-state': 'READY'])] + mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1'], + childDataNodes: [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']/state', leaves: ['cm-handle-state': 'READY'])])] when: 'module upgrade is triggered' - objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle) + objectUnderTest.syncAndUpgradeSchemaSet(yangModelCmHandle) then: 'the upgrade is delegated to the module service (with the correct parameters)' 1 * mockCpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', Collections.emptyMap(), moduleReferences) } @@ -171,13 +168,4 @@ class ModuleSyncServiceSpec extends Specification { result == unsupportedOperationException } - def toModuleReferences(moduleReferenceAsMap) { - def moduleReferences = [].withDefault { [:] } - moduleReferenceAsMap.forEach(property -> - property.forEach((moduleName, revision) -> { - moduleReferences.add(new ModuleReference('moduleName' : moduleName, 'revision' : revision)) - })) - return moduleReferences - } - } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasksSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasksSpec.groovy index ece2d9f86c..6c0d6dfb87 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasksSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasksSpec.groovy @@ -85,8 +85,8 @@ class ModuleSyncTasksSpec extends Specification { 1 * mockModuleSyncService.deleteSchemaSetIfExists('cm-handle-1') 1 * mockModuleSyncService.deleteSchemaSetIfExists('cm-handle-2') and: 'module sync service is invoked for each cm handle' - 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } - 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-2') } + 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assert args[0].id == 'cm-handle-1' } + 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assert args[0].id == 'cm-handle-2' } and: 'the state handler is called for the both cm handles' 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args -> assertBatch(args, ['cm-handle-1', 'cm-handle-2'], CmHandleState.READY) @@ -102,7 +102,7 @@ class ModuleSyncTasksSpec extends Specification { def cmHandleState = new CompositeState(cmHandleState: CmHandleState.ADVISED) 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> cmHandleState and: 'module sync service attempts to sync the cm handle and throws an exception' - 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } + 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } when: 'module sync is executed' objectUnderTest.performModuleSync([cmHandle], batchCount) then: 'update lock reason, details and attempts is invoked' @@ -123,7 +123,8 @@ class ModuleSyncTasksSpec extends Specification { .LockReason.builder().lockReasonCategory(lockReasonCategory).details(lockReasonDetails).build()) 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> expectedCmHandleState and: 'module sync service attempts to sync/upgrade the cm handle and throws an exception' - 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } + mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } + mockModuleSyncService.syncAndUpgradeSchemaSet(*_) >> { throw new Exception('some exception') } when: 'module sync is executed' objectUnderTest.performModuleSync([cmHandle], batchCount) then: 'update lock reason, details and attempts is invoked' @@ -170,7 +171,7 @@ class ModuleSyncTasksSpec extends Specification { when: 'module sync poll is executed' objectUnderTest.performModuleSync([cmHandle1], batchCount) then: 'module sync service is invoked for cm handle' - 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } + 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } and: 'the entry for other cm handle is still in the progress map' assert moduleSyncStartedOnCmHandles.get('other-cm-handle') != null } @@ -202,14 +203,6 @@ class ModuleSyncTasksSpec extends Specification { return new DataNode(anchorName: cmHandleId, leaves: ['id': cmHandleId, 'cm-handle-state': cmHandleState]) } - def assertYamgModelCmHandleArgument(args, expectedCmHandleId) { - { - def yangModelCmHandle = args[0] - assert yangModelCmHandle.id == expectedCmHandleId - } - return true - } - def assertBatch(args, expectedCmHandleStatePerCmHandleIds, expectedCmHandleState) { { Map<YangModelCmHandle, CmHandleState> actualCmHandleStatePerCmHandle = args[0] diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy index d1025f9d65..a105f84ebe 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -51,12 +51,6 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { @SpringBean JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) - @SpringBean - TaskExecutor stubbedTaskExecutor = Stub() - - @SpringBean - DmiServiceNameOrganizer stubbedDmiServiceNameOrganizer = Stub() - def 'Retrieving module references.'() { given: 'a cm handle' mockYangModelCmHandleRetrieval([]) @@ -148,23 +142,33 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { and: 'a positive response from DMI service when it is called with the expected parameters' def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK) mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", - '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi + '{"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi when: 'get new yang resources from DMI service' - def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, unknownModuleReferences) + def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences) then: 'the result is the response from DMI service' assert result == [mod1:'some yang source'] where: 'the following DMI properties are used' - scenario | dmiProperties | unknownModuleReferences || expectedAdditionalPropertiesInRequest | expectedModuleReferencesInRequest - 'with module references and properties' | [yangModelCmHandleProperty] | newModuleReferences || '{"prop1":"val1"}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' - 'without module references' | [yangModelCmHandleProperty] | [] || '{"prop1":"val1"}' | '' - 'without properties' | [] | newModuleReferences || '{}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' + scenario | dmiProperties || expectedAdditionalPropertiesInRequest + 'with module references and properties' | [yangModelCmHandleProperty] || '{"prop1":"val1"}' + 'without properties' | [] || '{}' + } + + def 'Retrieving yang resources from DMI with no module references.'() { + given: 'a cm handle' + mockYangModelCmHandleRetrieval([]) + when: 'a get new yang resources from DMI is called with no module references' + def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, []) + then: 'no resources are returned' + assert result == [:] + and: 'no request is sent to DMI' + 0 * mockDmiRestClient.postOperationWithJsonData(*_) } def 'Retrieving yang resources from DMI with null DMI properties.'() { given: 'a cm handle' mockYangModelCmHandleRetrieval(null) when: 'a get new yang resources from DMI is called' - objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, []) + objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('mod1', 'A')]) then: 'a null pointer is thrown (we might need to address this later)' thrown(NullPointerException) } 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 index f41fd6cdf3..aaa034437c 100644 --- 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 @@ -76,17 +76,17 @@ class AlternateIdCheckerSpec extends Specification { mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId(_) >> { args -> altAlreadyInDb.contains(args[0]) ? new DataNode() : throwDataNodeNotFoundException() } when: 'the batch of new cm handles is checked' - def result = objectUnderTest.getIdsOfCmHandlesWithAcceptableAlternateId(batch) + def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(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 + assert result.contains('ch-1') == rejectCh1 + assert result.contains('ch-2') == rejectCh2 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 + scenario | alt1 | alt2 | altAlreadyInDb || rejectCh1 | rejectCh2 + 'no alternate ids' | '' | '' | ['dont matter'] || false | false + 'new alternate ids' | 'fdn1' | 'fdn2' | ['other fdn'] || false | false + 'one already used alternate id' | 'fdn1' | 'fdn2' | ['fdn1'] || true | false + 'two already used alternate ids' | 'fdn1' | 'fdn2' | ['fdn1','fdn2'] || true | true + 'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || false | true } def throwDataNodeNotFoundException() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/YangDataConverterSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/YangDataConverterSpec.groovy index 6d733dc475..0ae348c074 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/YangDataConverterSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/YangDataConverterSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================== - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-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. @@ -31,9 +31,10 @@ class YangDataConverterSpec extends Specification{ leaves: ['name': 'dmiProp1', 'value': 'dmiValue1']) def dataNodePublicProperties = new DataNode(xpath:'/public-properties[@name="pubProp1"]', leaves: ['name': 'pubProp1', 'value': 'pubValue1']) - def dataNodeCmHandle = new DataNode(childDataNodes:[dataNodeAdditionalProperties, dataNodePublicProperties]) + def dataNodeCmHandle = new DataNode(leaves:['id':'sample-id'], childDataNodes:[dataNodeAdditionalProperties, dataNodePublicProperties]) when: 'the dataNode is converted' - def yangModelCmHandle = YangDataConverter.convertCmHandleToYangModel(dataNodeCmHandle,'sample-id') + def yangModelCmHandle = + YangDataConverter.convertCmHandleToYangModel(dataNodeCmHandle) then: 'the converted object has the correct id' assert yangModelCmHandle.id == 'sample-id' and: 'the additional (dmi, private) properties are included' @@ -46,8 +47,8 @@ class YangDataConverterSpec extends Specification{ def 'Convert multiple cm handle data nodes'(){ given: 'two data nodes in a collection' - def dataNodes = [new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'some-cm-handle\']'), - new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'another-cm-handle\']')] + def dataNodes = [new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'some-cm-handle\']', leaves: ['id':'some-cm-handle']), + new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'another-cm-handle\']', leaves: ['id':'another-cm-handle'])] when: 'the data nodes are converted' def yangModelCmHandles = YangDataConverter.convertDataNodesToYangModelCmHandles(dataNodes) then: 'verify both have returned and CmHandleIds are correct' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy index d76f912234..7803ae319f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2023 Nordix Foundation + * Modifications Copyright (C) 2023-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. @@ -21,6 +21,7 @@ package org.onap.cps.ncmp.api.models +import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR @@ -71,7 +72,7 @@ class CmHandleRegistrationResponseSpec extends Specification { def 'Failed cm-handle Registration with multiple responses.'() { when: 'cm-handle failure response is created for 2 xpaths' def cmHandleRegistrationResponses = - CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","somePathWithId[@id='456']"], CM_HANDLE_ALREADY_EXIST) + CmHandleRegistrationResponse.createFailureResponsesFromXpaths(["somePathWithId[@id='123']", "somePathWithId[@id='456']"], CM_HANDLE_ALREADY_EXIST) then: 'the response has the correct cm handle ids' assert cmHandleRegistrationResponses.size() == 2 assert cmHandleRegistrationResponses.stream().map(it -> it.cmHandle).collect(Collectors.toList()) @@ -81,9 +82,20 @@ class CmHandleRegistrationResponseSpec extends Specification { def 'Failed cm-handle Registration with multiple responses with an unexpected xpath.'() { when: 'cm-handle failure response is created for one valid and one unexpected xpath' def cmHandleRegistrationResponses = - CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","valid/xpath/without-id[@key='123']"], CM_HANDLE_ALREADY_EXIST) + CmHandleRegistrationResponse.createFailureResponsesFromXpaths(["somePathWithId[@id='123']", "valid/xpath/without-id[@key='123']"], CM_HANDLE_ALREADY_EXIST) then: 'the response has only one entry' assert cmHandleRegistrationResponses.size() == 1 } + def 'Failed cm-handle registration based on cm handle id and registration error'() { + when: 'the failure response is created with "alternate id already associated" error code for 1 cm handle' + def cmHandleRegistrationResponses = + CmHandleRegistrationResponse.createFailureResponses(['ch 1'], ALTERNATE_ID_ALREADY_ASSOCIATED) + then: 'the response with expected values' + assert cmHandleRegistrationResponses[0].cmHandle == 'ch 1' + assert cmHandleRegistrationResponses[0].status == Status.FAILURE + assert cmHandleRegistrationResponses[0].ncmpResponseStatus == ALTERNATE_ID_ALREADY_ASSOCIATED + assert cmHandleRegistrationResponses[0].errorText == 'alternate id already associated' + } + } diff --git a/docs/cps-ncmp-message-status-codes.rst b/docs/cps-ncmp-message-status-codes.rst index 20a5ae3c11..90590a2016 100644 --- a/docs/cps-ncmp-message-status-codes.rst +++ b/docs/cps-ncmp-message-status-codes.rst @@ -1,6 +1,6 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. .. http://creativecommons.org/licenses/by/4.0 -.. Copyright (C) 2023 Nordix Foundation +.. Copyright (C) 2023-2024 Nordix Foundation .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING .. _dataOperationMessageStatusCodes: @@ -38,6 +38,8 @@ CPS-NCMP Message Status Codes +-----------------+------------------------------------------------------+-----------------------------------+ | 110 | cm-handle has an invalid character(s) in id | Inventory | +-----------------+------------------------------------------------------+-----------------------------------+ + | 111 | alternate id already associated | Inventory | + +-----------------+------------------------------------------------------+-----------------------------------+ .. note:: diff --git a/docs/deployment.rst b/docs/deployment.rst index 3b5aad1bfc..c1ee1eb2ea 100644 --- a/docs/deployment.rst +++ b/docs/deployment.rst @@ -335,9 +335,5 @@ Below are the list of distributed datastructures that we have. +--------------+------------------------------------+-----------------------------------------------------------+ | cps-ncmp | cmNotificationSubscriptionCache | Stores and tracks cm notification subscription requests. | +--------------+------------------------------------+-----------------------------------------------------------+ -| cps-ncmp | alternateIdPerCmHandleId | Stores the alternate id for each cm handle id. | -+--------------+------------------------------------+-----------------------------------------------------------+ -| cps-ncmp | cmHandleIdPerAlternateId | Stores the cm handle id for each alternate id. | -+--------------+------------------------------------+-----------------------------------------------------------+ -Total number of caches : 10 +Total number of caches : 8 diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy index 6707753257..4369b799bd 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy @@ -122,8 +122,11 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState }) + and: 'the CM-handle has expected moduleSetTag' + assert objectUnderTest.getNcmpServiceCmHandle('ch-3').moduleSetTag == 'B' + and: 'the CM-handle has expected modules from module set "B": M1 and M3' - ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort() + assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort() cleanup: 'deregister CM handles' deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3']) diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy index 6b550a76bc..ffa4382d49 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy @@ -28,7 +28,6 @@ import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse import org.onap.cps.ncmp.api.models.DmiPluginRegistration import org.onap.cps.ncmp.api.models.UpgradedCmHandles import org.springframework.http.HttpStatus -import spock.lang.Ignore import spock.util.concurrent.PollingConditions import static org.springframework.test.web.client.match.MockRestRequestMatchers.anything @@ -50,7 +49,6 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { objectUnderTest = networkCmProxyDataService } - @Ignore def 'Upgrade CM-handle with new moduleSetTag or no moduleSetTag.'() { given: 'an existing CM-handle with expected initial modules: M1 and M2' registerCmHandle(DMI_URL, CM_HANDLE_ID, initialModuleSetTag, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) @@ -82,6 +80,9 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState }) + and: 'the CM-handle has expected moduleSetTag' + assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == updatedModuleSetTag + and: 'CM-handle has expected updated modules: M1 and M3' assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() @@ -124,6 +125,9 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState }) + and: 'the CM-handle has expected moduleSetTag' + assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == updatedModuleSetTag + and: 'CM-handle has expected updated modules: M1 and M3' assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() @@ -136,7 +140,6 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { 'moduleSet1' | 'moduleSet2' } - @Ignore def 'Skip upgrade of CM-handle with same moduleSetTag as before.'() { given: 'an existing CM-handle with expected initial modules: M1 and M2' registerCmHandle(DMI_URL, CM_HANDLE_ID, 'same', INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) @@ -150,7 +153,10 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { then: 'CM-handle remains in READY state' assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState - and: 'CM-handle has same modules as before: M1 and M2' + and: 'the CM-handle has same moduleSetTag as before' + assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == 'same' + + then: 'CM-handle has same modules as before: M1 and M2' assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() cleanup: 'deregister CM-handle' @@ -159,13 +165,13 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { def 'Upgrade of CM-handle fails due to DMI error.'() { given: 'an existing CM-handle' - registerCmHandle(DMI_URL, CM_HANDLE_ID, NO_MODULE_SET_TAG, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) + registerCmHandle(DMI_URL, CM_HANDLE_ID, 'oldTag', INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) and: 'DMI returns error code' mockDmiServer.expect(anything()).andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE)) when: "CM-handle is upgraded" - def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: NO_MODULE_SET_TAG) + def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'newTag') networkCmProxyDataService.updateDmiRegistrationAndSyncModule( new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) @@ -180,6 +186,9 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_UPGRADE_FAILED }) + and: 'the CM-handle has same moduleSetTag as before' + assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == 'oldTag' + cleanup: 'deregister CM-handle' deregisterCmHandle(DMI_URL, CM_HANDLE_ID) } |