aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorleventecsanyi <levente.csanyi@est.tech>2024-01-11 12:53:26 +0100
committerToine Siebelink <toine.siebelink@est.tech>2024-02-01 09:37:09 +0000
commit0f6f416f45d5ad0e9bba67d459858995196f7a73 (patch)
treeb2fedbb61706817393dfe2bd118616da005820dc
parent0f6a966b363e5347de2d44b1527d19b4cf2825a6 (diff)
Introduce Hazelcast for alternateId-cmHandle relation
- added new Hazelcast config - added cache initialization in CmHandleIdMapper - added unit tests - updated deployment.rst - refactored cache updating Issue-ID: CPS-1988 Change-Id: Iea6f884e584bf8cea8612ddbced4329e783c60a5 Signed-off-by: leventecsanyi <levente.csanyi@est.tech>
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java9
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java13
-rwxr-xr-xcps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java21
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java17
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfig.java59
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java89
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy16
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy10
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy83
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy59
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy117
-rw-r--r--docs/deployment.rst48
12 files changed, 450 insertions, 91 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java
index f29fd687b..06522f80c 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.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.
@@ -61,4 +61,11 @@ public interface NetworkCmProxyCmHandleQueryService {
* @return collection of cm handles
*/
Collection<NcmpServiceCmHandle> queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters);
+
+ /**
+ * Query and return all cm handle objects.
+ *
+ * @return collection of cm handles
+ */
+ Collection<NcmpServiceCmHandle> getAllCmHandles();
}
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 1f6c94869..6f92a9efe 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
@@ -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.
@@ -97,6 +97,12 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH
return getNcmpServiceCmHandles(cmHandleIds);
}
+ @Override
+ public Collection<NcmpServiceCmHandle> getAllCmHandles() {
+ final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT).iterator().next();
+ return dataNode.getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet());
+ }
+
private Collection<String> queryCmHandlesByDmiPlugin(
final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) {
final Map<String, String> dmiPropertyQueryPairs =
@@ -216,11 +222,6 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH
return Collections.emptyList();
}
- private Collection<NcmpServiceCmHandle> getAllCmHandles() {
- final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT).iterator().next();
- return dataNode.getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet());
- }
-
private Collection<String> getAllCmHandleIds() {
final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT, DIRECT_CHILDREN_ONLY)
.iterator().next();
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 469d75ab9..3fa050454 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
@@ -1,7 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 highstreet technologies GmbH
- * Modifications Copyright (C) 2021-2023 Nordix Foundation
+ * Modifications Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2021-2022 Bell Canada
* Modifications Copyright (C) 2023 TechMahindra Ltd.
@@ -48,6 +48,7 @@ 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;
@@ -65,6 +66,7 @@ import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
import org.onap.cps.ncmp.api.impl.operations.OperationType;
import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel;
import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager;
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper;
import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions;
import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
@@ -104,10 +106,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
private final IMap<String, Object> moduleSyncStartedOnCmHandles;
private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
private final TrustLevelManager trustLevelManager;
+ private final CmHandleIdMapper cmHandleIdMapper;
@Override
public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
final DmiPluginRegistration dmiPluginRegistration) {
+ cacheAlternateIds(dmiPluginRegistration.getCreatedCmHandles());
dmiPluginRegistration.validateDmiPluginRegistration();
final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse();
@@ -367,10 +371,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
yangModelCmHandles.removeIf(yangModelCmHandle -> notDeletedCmHandles.contains(yangModelCmHandle.getId()));
updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETED);
+ removeEntriesFromAlternateIdCache(yangModelCmHandles);
return cmHandleRegistrationResponses;
}
+ private void removeEntriesFromAlternateIdCache(final Collection<YangModelCmHandle> yangModelCmHandles) {
+ for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) {
+ cmHandleIdMapper.removeMapping(yangModelCmHandle.getId());
+ }
+ }
+
protected List<CmHandleRegistrationResponse> parseAndProcessUpgradedCmHandlesInRegistration(
final DmiPluginRegistration dmiPluginRegistration) {
@@ -527,4 +538,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
}
}
+ private void cacheAlternateIds(final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
+ for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
+ if (!StringUtils.isEmpty(ncmpServiceCmHandle.getAlternateId())) {
+ cmHandleIdMapper.addMapping(ncmpServiceCmHandle.getCmHandleId(), ncmpServiceCmHandle.getAlternateId());
+ }
+ }
+ }
+
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java
index 15209328f..13b3fcafb 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
@@ -43,9 +43,9 @@ 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.api.CpsDataService;
import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
@@ -67,6 +67,7 @@ public class NetworkCmProxyDataServicePropertyHandler {
private final InventoryPersistence inventoryPersistence;
private final CpsDataService cpsDataService;
private final JsonObjectMapper jsonObjectMapper;
+ private final CmHandleIdMapper cmHandleIdMapper;
/**
* Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes.
@@ -105,19 +106,15 @@ public class NetworkCmProxyDataServicePropertyHandler {
private void updateAlternateId(final DataNode existingCmHandleDataNode,
final NcmpServiceCmHandle ncmpServiceCmHandle) {
final String newAlternateId = ncmpServiceCmHandle.getAlternateId();
- if (!StringUtils.isEmpty(newAlternateId)) {
- final String existingAlternateId = (String) existingCmHandleDataNode.getLeaves().get("alternate-id");
- if (StringUtils.isEmpty(existingAlternateId)) {
+ if (cmHandleIdMapper.addMapping(ncmpServiceCmHandle.getCmHandleId(), newAlternateId)) {
+ try {
final YangModelCmHandle yangModelCmHandle =
YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode,
ncmpServiceCmHandle.getCmHandleId());
setAndUpdateAlternateId(yangModelCmHandle, newAlternateId);
- } else {
- if (!newAlternateId.equals(existingAlternateId)) {
- log.warn("Unable to update alternateId for cmHandle {}. "
- + "Value for alternateId has been set previously.",
- ncmpServiceCmHandle.getCmHandleId());
- }
+ } catch (final Exception e) {
+ cmHandleIdMapper.removeMapping(ncmpServiceCmHandle.getCmHandleId());
+ throw e;
}
}
}
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
new file mode 100644
index 000000000..a69d14476
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfig.java
@@ -0,0 +1,59 @@
+/*
+ * ============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/utils/CmHandleIdMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java
new file mode 100644
index 000000000..8175fb5e7
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java
@@ -0,0 +1,89 @@
+/*
+ * ============LICENSE_START========================================================
+ * Copyright (c) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.utils;
+
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class CmHandleIdMapper {
+
+ private final Map<String, String> alternateIdPerCmHandleId;
+ private final Map<String, String> cmHandleIdPerAlternateId;
+ private final NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService;
+
+ private boolean cacheIsInitialized = false;
+
+ public String cmHandleIdToAlternateId(final String cmHandleId) {
+ initializeCache();
+ return alternateIdPerCmHandleId.get(cmHandleId);
+ }
+
+ public String alternateIdToCmHandleId(final String alternateId) {
+ initializeCache();
+ return cmHandleIdPerAlternateId.get(alternateId);
+ }
+
+ public boolean addMapping(final String cmHandleId, final String alternateId) {
+ initializeCache();
+ return addMappingWithValidation(cmHandleId, alternateId);
+ }
+
+
+ private boolean addMappingWithValidation(final String cmHandleId, final String alternateId) {
+ if (alternateIdPerCmHandleId.containsKey(cmHandleId)) {
+ final String originalAlternateId = alternateIdPerCmHandleId.get(cmHandleId);
+ if (!originalAlternateId.equals(alternateId)) {
+ log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}",
+ cmHandleId, originalAlternateId);
+ }
+ return false;
+ }
+ if (StringUtils.isBlank(alternateId)) {
+ return false;
+ }
+ alternateIdPerCmHandleId.put(cmHandleId, alternateId);
+ cmHandleIdPerAlternateId.put(alternateId, cmHandleId);
+ return true;
+ }
+
+ public void removeMapping(final String cmHandleId) {
+ final String alternateId = alternateIdPerCmHandleId.remove(cmHandleId);
+ cmHandleIdPerAlternateId.remove(alternateId);
+ }
+
+ private void initializeCache() {
+ if (!cacheIsInitialized) {
+ networkCmProxyCmHandleQueryService.getAllCmHandles().forEach(cmHandle ->
+ addMappingWithValidation(cmHandle.getCmHandleId(), cmHandle.getAlternateId())
+ );
+ log.info("Alternate ID cache initialized from DB with {} cm handle/alternate id pairs ",
+ alternateIdPerCmHandleId.size());
+ cacheIsInitialized = true;
+ }
+ }
+}
diff --git a/cps-ncmp-service/src/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 f565ede39..59bf9420c 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
@@ -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");
@@ -22,6 +22,7 @@
package org.onap.cps.ncmp.api.impl
import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
import org.onap.cps.ncmp.api.models.UpgradedCmHandles
import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND
@@ -71,6 +72,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
def mockModuleSyncStartedOnCmHandles = Mock(IMap<String, Object>)
def trustLevelPerDmiPlugin = [:]
def mockTrustLevelManager = Mock(TrustLevelManager)
+ def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
def objectUnderTest = getObjectUnderTest()
def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() {
@@ -432,11 +434,21 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
'an unexpected exception' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed'
}
+ def 'Adding data to alternate id caches.'() {
+ given: 'a registration with three CM Handles to be created'
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+ createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1', alternateId: 'my-alternate-id-1')])
+ when: 'the DMI plugin registration happens'
+ objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+ then: 'the new alternate id is added to the cache'
+ 1 * mockCmHandleIdMapper.addMapping('cmhandle1', 'my-alternate-id-1')
+ }
+
def getObjectUnderTest() {
return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries,
stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService,
- mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager))
+ mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin as Map<String, TrustLevel>, mockTrustLevelManager, mockCmHandleIdMapper))
}
def addPersistedYangModelCmHandles(ids) {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
index 4f7b72698..c1af902a0 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
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2021-2022 Bell Canada
* Modifications Copyright (C) 2023 TechMahindra Ltd.
@@ -22,6 +22,9 @@
*/
package org.onap.cps.ncmp.api.impl
+
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
+
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
@@ -78,6 +81,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
def mockTrustLevelManager = Mock(TrustLevelManager)
+ def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
def NO_TOPIC = null
def NO_REQUEST_ID = null
@@ -97,7 +101,9 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
mockCpsDataService,
stubModuleSyncStartedOnCmHandles,
stubTrustLevelPerDmiPlugin,
- mockTrustLevelManager)
+ mockTrustLevelManager,
+ mockCmHandleIdMapper
+ )
def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
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 2a15e6c1a..f94c34c58 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
@@ -22,13 +22,9 @@
package org.onap.cps.ncmp.api.impl
-import ch.qos.logback.classic.Level
-import ch.qos.logback.classic.Logger
-import ch.qos.logback.classic.spi.ILoggingEvent
+
import com.fasterxml.jackson.databind.ObjectMapper
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.BeforeEach
-import org.slf4j.LoggerFactory
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
@@ -37,7 +33,6 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID
import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
-import ch.qos.logback.core.read.ListAppender
import org.onap.cps.api.CpsDataService
import org.onap.cps.utils.JsonObjectMapper
import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
@@ -53,20 +48,9 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
def mockInventoryPersistence = Mock(InventoryPersistence)
def mockCpsDataService = Mock(CpsDataService)
def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
- def logger = Spy(ListAppender<ILoggingEvent>)
-
- @BeforeEach
- void setup() {
- ((Logger) LoggerFactory.getLogger(NetworkCmProxyDataServicePropertyHandler.class)).addAppender(logger);
- logger.start();
- }
+ def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
- @AfterEach
- void teardown() {
- ((Logger) LoggerFactory.getLogger(NetworkCmProxyDataServicePropertyHandler.class)).detachAndStopAllAppenders();
- }
-
- def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper)
+ def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockCmHandleIdMapper)
def static cmHandleId = 'myHandle1'
def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']"
@@ -198,36 +182,41 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
2 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_)
}
- def 'Update CM Handle Alternate ID when #scenario'() {
- given: 'an existing cm handle with alternate id #existingAlternateId'
- DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['alternate-id': oldAlternateId])
- and: 'an update request with an alternate id #newAlternateId'
- def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: newAlternateId)
- when: 'update data node leaves is called with the update request'
+ def 'Update CM Handle Alternate ID with #scenario'() {
+ given: 'an existing cm handle'
+ DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath)
+ and: 'an update request with an alternate id'
+ 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'
- numberOfInvocations * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _) >> { args ->
- assert args[3].contains(newAlternateId)
- }
- and: 'correct information is logged'
- def lastLoggingEvent = logger.list[0]
- if (expectLogWarning) {
- assert lastLoggingEvent.level == Level.WARN
- assert lastLoggingEvent.formattedMessage.contains('Unable')
- } else if (numberOfInvocations == 1) {
- assert lastLoggingEvent.level == Level.INFO
- assert lastLoggingEvent.formattedMessage.contains('Updating alternateId')
- } else {
- assert lastLoggingEvent == null
- }
+ callsToDataService * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _) >>
+ { args ->
+ assert args[3].contains('alt-1')
+ }
+ mockCmHandleIdMapper.addMapping(cmHandleId, 'alt-1') >> isNewMapping
where: 'following updates are attempted'
- scenario | oldAlternateId | newAlternateId || numberOfInvocations | expectLogWarning
- 'old alternate id null' | null | 'new' || 1 | false
- 'old alternate id empty' | '' | 'new' || 1 | false
- 'old alternate id not empty' | 'old' | 'new' || 0 | true
- 'same alternate id' | 'old' | 'old' || 0 | false
- 'empty new alternate id' | 'old' | '' || 0 | false
- 'null new alternate id' | 'old' | null || 0 | false
+ scenario | isNewMapping || callsToDataService
+ 'new alternate id ' | true || 1
+ 'existing alternate id' | false || 0
+ }
+
+ 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])
+ and: 'a new mapping is added'
+ mockCmHandleIdMapper.addMapping(cmHandleId, 'alt-1') >> true
+ and: 'but an exception occurs while saving'
+ def originalException = new NullPointerException('some exception')
+ mockCpsDataService.updateNodeLeaves(*_) >> { throw originalException }
+ when: 'updating of alternate id called'
+ objectUnderTest.updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle)
+ then: 'the original exception is thrown up'
+ def thrownException = thrown(NullPointerException)
+ assert thrownException == originalException
+ and: 'the mapping is removed from the cache'
+ 1 * mockCmHandleIdMapper.removeMapping(cmHandleId)
}
def convertToProperties(expectedPropertiesAfterUpdateAsMap) {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy
new file mode 100644
index 000000000..71c5293e2
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy
@@ -0,0 +1,59 @@
+/*
+ * ============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/utils/CmHandleIdMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy
new file mode 100644
index 000000000..0a2962e98
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy
@@ -0,0 +1,117 @@
+/*
+ * ============LICENSE_START========================================================
+ * Copyright (c) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.utils
+
+import ch.qos.logback.classic.Level
+import ch.qos.logback.classic.Logger
+import ch.qos.logback.classic.spi.ILoggingEvent
+import ch.qos.logback.core.read.ListAppender
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+import org.slf4j.LoggerFactory
+import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
+import spock.lang.Specification
+
+class CmHandleIdMapperSpec extends Specification {
+
+ def alternateIdPerCmHandle = new HashMap<String, String>()
+ def cmHandlePerAlternateId = new HashMap<String, String>()
+ def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
+
+ def objectUnderTest = new CmHandleIdMapper(alternateIdPerCmHandle, cmHandlePerAlternateId, mockCpsCmHandlerQueryService)
+
+ def logger = Spy(ListAppender<ILoggingEvent>)
+
+ def setup() {
+ ((Logger) LoggerFactory.getLogger(CmHandleIdMapper.class)).addAppender(logger)
+ logger.start()
+ mockCpsCmHandlerQueryService.getAllCmHandles() >> []
+ assert objectUnderTest.addMapping('my cmhandle id', 'my alternate id')
+ }
+
+ void cleanup() {
+ ((Logger) LoggerFactory.getLogger(CmHandleIdMapper.class)).detachAndStopAllAppenders()
+ }
+
+ def 'Checking entries in the cache.'() {
+ expect: 'the alternate id can be converted to cmhandle id'
+ assert objectUnderTest.alternateIdToCmHandleId('my alternate id') == 'my cmhandle id'
+ and: 'the cmhandle id can be converted to alternate id'
+ assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id'
+ }
+
+ def 'Attempt adding #scenario alternate id.'() {
+ expect: 'cmhandle id - alternate id mapping fails'
+ assert objectUnderTest.addMapping('ch-1', alternateId) == false
+ and: 'alternate id looked up by cmhandle id unsuccessfully'
+ assert objectUnderTest.cmHandleIdToAlternateId('ch-1') == null
+ where: 'alternate id has an invalid value'
+ scenario | alternateId
+ 'empty' | ''
+ 'blank' | ' '
+ 'null' | null
+ }
+
+ def 'Remove an entry from the cache.'() {
+ when: 'removing an entry'
+ objectUnderTest.removeMapping('my cmhandle id')
+ then: 'converting alternate id returns null'
+ assert objectUnderTest.alternateIdToCmHandleId('my alternate id') == null
+ and: 'converting cmhandle id returns null'
+ assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == null
+ }
+
+ def 'Cannot update existing alternate id.'() {
+ given: 'attempt to update an existing alternate id'
+ objectUnderTest.addMapping('my cmhandle id', 'other id')
+ expect: 'still returns the original alternate id'
+ assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id'
+ and: 'converting other alternate id returns null'
+ assert objectUnderTest.alternateIdToCmHandleId('other id') == null
+ and: 'a warning is logged with the original alternate id'
+ def lastLoggingEvent = logger.list[1]
+ assert lastLoggingEvent.level == Level.WARN
+ assert lastLoggingEvent.formattedMessage.contains('my alternate id')
+ }
+
+ def 'Update existing alternate id with the same value.'() {
+ expect: 'update an existing alternate id with the same value returns false (no update)'
+ assert objectUnderTest.addMapping('my cmhandle id', 'my alternate id') == false
+ and: 'conversion still returns the original alternate id'
+ assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id'
+ }
+
+ def 'Initializing cache #scenario.'() {
+ when: 'the cache is (re-)initialized'
+ objectUnderTest.cacheIsInitialized = false
+ objectUnderTest.initializeCache()
+ then: 'the alternate id can be converted to cmhandle id'
+ assert objectUnderTest.alternateIdToCmHandleId('alt-1') == convertedCmHandleId
+ and: 'the cm handle id can be converted to alternate id'
+ assert objectUnderTest.cmHandleIdToAlternateId('ch-1') == convertedAlternatId
+ and: 'the query service is called to get the initial data'
+ 1 * mockCpsCmHandlerQueryService.getAllCmHandles() >> persistedCmHandles
+ where: 'the initial data has a cm handle #scenario'
+ scenario | persistedCmHandles || convertedAlternatId | convertedCmHandleId
+ 'with alternate id' | [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: 'alt-1')] || 'alt-1' | 'ch-1'
+ 'without alternate id' | [new NcmpServiceCmHandle(cmHandleId: 'ch-1')] || null | null
+ 'with blank alternate id' | [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: ' ')] || null | null
+ }
+} \ No newline at end of file
diff --git a/docs/deployment.rst b/docs/deployment.rst
index 7ba163d57..ca7824d6d 100644
--- a/docs/deployment.rst
+++ b/docs/deployment.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) 2021-2022 Nordix Foundation
+.. Copyright (C) 2021-2024 Nordix Foundation
.. Modifications Copyright (C) 2021 Bell Canada.
.. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
@@ -316,24 +316,28 @@ These instances require some additional ports to be available. The default range
Below are the list of distributed datastructures that we have.
-+--------------+---------------------------------+----------------------------------------------------------+
-| Component | Datastructure name | Use |
-+==============+=================================+==========================================================+
-| cps-core | anchorDataCache | Used to resolve prefix for the container name. |
-+--------------+---------------------------------+----------------------------------------------------------+
-| cps-ncmp | moduleSyncStartedOnCmHandles | Watchdog process to register cmHandles. |
-+--------------+---------------------------------+----------------------------------------------------------+
-| cps-ncmp | dataSyncSemaphores | Watchdog process to sync data from the nodes. |
-+--------------+---------------------------------+----------------------------------------------------------+
-| cps-ncmp | moduleSyncWorkQueue | Queue used internally for workers to pick the task. |
-+--------------+---------------------------------+----------------------------------------------------------+
-| cps-ncmp | untrustworthyCmHandlesSet | Stores untrustworthy cmHandles whose TrustLevel is NONE. |
-+--------------+---------------------------------+----------------------------------------------------------+
-| cps-ncmp | trustLevelPerDmiPlugin | Stores the TrustLevel for the dmi-plugins. |
-+--------------+---------------------------------+----------------------------------------------------------+
-| cps-ncmp | moduleSetTagCacheMapConfig | Stores the Module Set Tags for cmHandles. |
-+--------------+---------------------------------+----------------------------------------------------------+
-| cps-ncmp | cmSubscriptionEventCache | Stores and tracks CmSubscription requests. |
-+--------------+---------------------------------+----------------------------------------------------------+
-
-Total number of caches : 8
++--------------+------------------------------------+-----------------------------------------------------------+
+| Component | Datastructure name | Use |
++==============+====================================+===========================================================+
+| cps-core | anchorDataCache | Used to resolve prefix for the container name. |
++--------------+------------------------------------+-----------------------------------------------------------+
+| cps-ncmp | moduleSyncStartedOnCmHandles | Watchdog process to register cm handles. |
++--------------+------------------------------------+-----------------------------------------------------------+
+| cps-ncmp | dataSyncSemaphores | Watchdog process to sync data from the nodes. |
++--------------+------------------------------------+-----------------------------------------------------------+
+| cps-ncmp | moduleSyncWorkQueue | Queue used internally for workers to pick the task. |
++--------------+------------------------------------+-----------------------------------------------------------+
+| cps-ncmp | untrustworthyCmHandlesSet | Stores untrustworthy cm handles whose trust level is NONE.|
++--------------+------------------------------------+-----------------------------------------------------------+
+| cps-ncmp | trustLevelPerDmiPlugin | Stores the trust level for the dmi-plugins. |
++--------------+------------------------------------+-----------------------------------------------------------+
+| cps-ncmp | moduleSetTagCacheMapConfig | Stores the module set tags for cm handles. |
++--------------+------------------------------------+-----------------------------------------------------------+
+| cps-ncmp | cmSubscriptionEventCache | 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