diff options
12 files changed, 220 insertions, 77 deletions
diff --git a/.gitignore b/.gitignore index 624bc1ac15..a377967df0 100755 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.log *.log.zip +cps-ncmp-rest-stub/dependency-reduced-pom.xml cps-application/archunit_store cps-ri/src/main/resources/changelog/db/changes/data/dmi/generated-csv/generated_yang_resource_* target/ @@ -1,5 +1,5 @@ # ============LICENSE_START======================================================= -# Copyright (C) 2021 Nordix Foundation. +# Copyright (C) 2021-2022 Nordix Foundation. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -52,11 +52,6 @@ committers: company: 'Ericsson Software Technology' id: 'toinesiebelink' timezone: 'Europe/Dublin' - - name: 'Bruno Sakoto' - email: 'bruno.sakoto@bell.ca' - company: 'Bell Canada' - id: 'brusak' - timezone: 'America/Toronto' - name: 'Aditya Puthuparambil' email: 'aditya.puthuparambil@bell.ca' company: 'Bell Canada' diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml index 73cca23919..8a98baf4de 100755 --- a/cps-dependencies/pom.xml +++ b/cps-dependencies/pom.xml @@ -210,6 +210,11 @@ <artifactId>janino</artifactId> <version>3.1.7</version> </dependency> + <dependency> + <groupId>com.hazelcast</groupId> + <artifactId>hazelcast-spring</artifactId> + <version>4.2.5</version> + </dependency> </dependencies> </dependencyManagement> </project> diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index 502e9824d7..93c265a7bf 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -57,6 +57,10 @@ <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> </dependency> + <dependency> + <groupId>com.hazelcast</groupId> + <artifactId>hazelcast-spring</artifactId> + </dependency> <!-- T E S T - D E P E N D E N C I E S --> <dependency> <groupId>org.spockframework</groupId> 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 aae2f209ae..d9aeaf2589 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 @@ -23,10 +23,6 @@ package org.onap.cps.ncmp.api.impl; import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY; import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY; -import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_ANCHOR; -import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_PARENT; -import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP; import com.google.common.collect.ImmutableMap; import java.util.ArrayList; @@ -39,11 +35,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.api.CpsDataService; +import org.onap.cps.ncmp.api.inventory.InventoryPersistence; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; @@ -58,9 +53,7 @@ import org.springframework.stereotype.Service; @SuppressWarnings("squid:S5852") public class NetworkCmProxyDataServicePropertyHandler { - private static final String CM_HANDLE_XPATH_TEMPLATE = NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='%s']"; - - private final CpsDataService cpsDataService; + private final InventoryPersistence inventoryPersistence; /** * Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes. @@ -72,31 +65,28 @@ public class NetworkCmProxyDataServicePropertyHandler { final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) { final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(); for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) { - final String cmHandle = ncmpServiceCmHandle.getCmHandleId(); + final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); try { - CpsValidator.validateNameCharacters(cmHandle); - final String cmHandleXpath = String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandle); - final DataNode existingCmHandleDataNode = - cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXpath, - FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); + CpsValidator.validateNameCharacters(cmHandleId); + final DataNode existingCmHandleDataNode = inventoryPersistence.getCmHandleDataNode(cmHandleId); processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle); - cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandle)); + cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); } catch (final DataNodeNotFoundException e) { log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", - cmHandle, e.getMessage()); + cmHandleId, e.getMessage()); cmHandleRegistrationResponses.add(CmHandleRegistrationResponse - .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_DOES_NOT_EXIST)); + .createFailureResponse(cmHandleId, RegistrationError.CM_HANDLE_DOES_NOT_EXIST)); } catch (final DataValidationException e) { log.error("Unable to update cm handle : {}, caused by : {}", - cmHandle, e.getMessage()); + cmHandleId, e.getMessage()); cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandle, + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, RegistrationError.CM_HANDLE_INVALID_ID)); } catch (final Exception exception) { log.error("Unable to update cmHandle : {} , caused by : {}", - cmHandle, exception.getMessage()); + cmHandleId, exception.getMessage()); cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandle, exception)); + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception)); } } return cmHandleRegistrationResponses; @@ -120,8 +110,7 @@ public class NetworkCmProxyDataServicePropertyHandler { if (replacementPropertyDataNodes.isEmpty()) { removeAllProperties(existingCmHandleDataNode, propertyType); } else { - cpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes, NO_TIMESTAMP); + inventoryPersistence.replaceListContent(existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes); } } @@ -130,8 +119,7 @@ public class NetworkCmProxyDataServicePropertyHandler { final Matcher matcher = propertyType.propertyXpathPattern.matcher(dataNode.getXpath()); if (matcher.find()) { log.info("Deleting dataNode with xpath : [{}]", dataNode.getXpath()); - cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, dataNode.getXpath(), - NO_TIMESTAMP); + inventoryPersistence.deleteDataNode(dataNode.getXpath()); } }); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfig.java new file mode 100644 index 0000000000..978c3d16b7 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfig.java @@ -0,0 +1,68 @@ +/* + * ============LICENSE_START======================================================== + * Copyright (C) 2022 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.config.MapConfig; +import com.hazelcast.core.Hazelcast; +import java.util.Map; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Core infrastructure of the hazelcast distributed map for Module Sync and Data Sync use cases. + */ +@Configuration +public class SynchronizationSemaphoresConfig { + + /** + * Module Sync Distributed Map Instance. + * @return Instance of Map + */ + @Bean + public Map<String, String> moduleSyncSemaphore() { + return Hazelcast.newHazelcastInstance( + initializeDefaultMapConfig("moduleSyncSemaphore", "moduleSyncSemaphoreConfig")) + .getMap("moduleSyncSemaphore"); + } + + /** + * Data Sync Distributed Map Instance. + * @return Instance of Map + */ + @Bean + public Map<String, String> dataSyncSemaphore() { + return Hazelcast.newHazelcastInstance( + initializeDefaultMapConfig("dataSyncSemaphore", "dataSyncSemaphoreConfig")) + .getMap("dataSyncSemaphore"); + } + + private Config initializeDefaultMapConfig(final String instanceName, final String configName) { + final Config config = new Config(instanceName); + final MapConfig mapConfig = new MapConfig(configName); + mapConfig.setTimeToLiveSeconds(30); + mapConfig.setBackupCount(3); + mapConfig.setAsyncBackupCount(3); + config.addMapConfig(mapConfig); + config.setClusterName("synchronization-semaphores"); + return config; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/constants/DmiRegistryConstants.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/constants/DmiRegistryConstants.java index c29c725d79..a133cfb80d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/constants/DmiRegistryConstants.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/constants/DmiRegistryConstants.java @@ -32,11 +32,5 @@ public final class DmiRegistryConstants { public static final String NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME = "NFP-Operational"; - public static final String NCMP_DATASPACE_NAME = "NCMP-Admin"; - - public static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry"; - - public static final String NCMP_DMI_REGISTRY_PARENT = "/dmi-registry"; - public static final OffsetDateTime NO_TIMESTAMP = null; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java index 19cf225985..af01fb4398 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java @@ -58,7 +58,7 @@ public class InventoryPersistence { private static final String NCMP_DMI_REGISTRY_PARENT = "/dmi-registry"; - private String xpathCmHandle = "/dmi-registry/cm-handles[@id='" + "%s" + "']"; + private static final String CM_HANDLE_XPATH_TEMPLATE = "/dmi-registry/cm-handles[@id='" + "%s" + "']"; private static final String ANCESTOR_CM_HANDLES = "\"]/ancestor::cm-handles"; @@ -80,7 +80,7 @@ public class InventoryPersistence { */ public CompositeState getCmHandleState(final String cmHandleId) { final DataNode stateAsDataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - String.format(xpathCmHandle, cmHandleId) + "/state", + String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId) + "/state", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); return new CompositeStateBuilder().fromDataNode(stateAsDataNode).build(); } @@ -95,7 +95,7 @@ public class InventoryPersistence { final String cmHandleJsonData = String.format("{\"state\":%s}", jsonObjectMapper.asJsonString(compositeState)); cpsDataService.replaceNodeTree(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - String.format(xpathCmHandle, cmHandleId), + String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId), cmHandleJsonData, OffsetDateTime.now()); } @@ -241,14 +241,11 @@ public class InventoryPersistence { /** * Get data node of given cm handle. * - * @param cmHandle cm handle + * @param cmHandleId cmHandle ID * @return data node */ - private DataNode getCmHandleDataNode(final String cmHandle) { - return cpsDataService.getDataNode(NCMP_DATASPACE_NAME, - NCMP_DMI_REGISTRY_ANCHOR, - String.format(xpathCmHandle, cmHandle), - FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); + public DataNode getCmHandleDataNode(final String cmHandleId) { + return this.getDataNode(String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId)); } /** @@ -269,4 +266,25 @@ public class InventoryPersistence { public Collection<Anchor> getAnchors() { return cpsAdminPersistenceService.getAnchors(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME); } + + /** + * Replaces list content by removing all existing elements and inserting the given new elements as data nodes. + * + * @param parentNodeXpath parent node xpath + * @param dataNodes datanodes representing the updated data + */ + public void replaceListContent(final String parentNodeXpath, final Collection<DataNode> dataNodes) { + cpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + parentNodeXpath, dataNodes, NO_TIMESTAMP); + } + + /** + * Deletes data node for given anchor and dataspace. + * + * @param dataNodeXpath data node xpath + */ + public void deleteDataNode(final String dataNodeXpath) { + cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, dataNodeXpath, + NO_TIMESTAMP); + } }
\ No newline at end of file diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy index 854c568e40..7cf572ddb1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy @@ -22,8 +22,6 @@ package org.onap.cps.ncmp.api.impl import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService import org.onap.cps.ncmp.api.inventory.InventoryPersistence -import org.onap.cps.spi.CpsAdminPersistenceService -import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.model.Anchor import org.onap.cps.spi.model.CmHandleQueryServiceParameters import org.onap.cps.spi.model.ConditionProperties 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 3efc9c43b9..64461fa7e2 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 @@ -21,6 +21,7 @@ package org.onap.cps.ncmp.api.impl +import org.onap.cps.ncmp.api.inventory.InventoryPersistence import org.onap.cps.spi.exceptions.DataValidationException import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST @@ -28,9 +29,7 @@ import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Registra import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status -import org.onap.cps.api.CpsDataService import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle -import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.exceptions.DataNodeNotFoundException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder @@ -38,14 +37,11 @@ import spock.lang.Specification class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { - def mockCpsDataService = Mock(CpsDataService) + def mockInventoryPersistence = Mock(InventoryPersistence) - def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockCpsDataService) - def dataspaceName = 'NCMP-Admin' - def anchorName = 'ncmp-dmi-registry' + def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence) def static cmHandleId = 'myHandle1' def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']" - def noTimeStamp = null def static propertyDataNodes = [new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp1']").withLeaves(['name': 'additionalProp1', 'value': 'additionalValue1']).build(), new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp2']").withLeaves(['name': 'additionalProp2', 'value': 'additionalValue2']).build(), @@ -55,16 +51,16 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def 'Update CM Handle Public Properties: #scenario'() { given: 'the CPS service return a CM handle' - mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode + mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode and: 'an update cm handle request with public properties updates' def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: updatedPublicProperties)] when: 'update data node leaves is called with the update request' objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) then: 'the replace list method is called with correct params' - 1 * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp) >> { args -> + 1 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_) >> { args -> { - assert args[3].leaves.size() == expectedPropertiesAfterUpdate.size() - assert args[3].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate)) + assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size() + assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate)) } } where: 'following public properties updates are made' @@ -77,16 +73,16 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def 'Update DMI Properties: #scenario'() { given: 'the CPS service return a CM handle' - mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode + mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode and: 'an update cm handle request with DMI properties updates' def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: updatedDmiProperties)] when: 'update data node leaves is called with the update request' objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) then: 'replace list method should is called with correct params' - expectedCallsToReplaceMethod * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp) >> { args -> + expectedCallsToReplaceMethod * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) >> { args -> { - assert args[3].leaves.size() == expectedPropertiesAfterUpdate.size() - assert args[3].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate)) + assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size() + assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate)) } } where: 'following DMI properties updates are made' @@ -101,17 +97,17 @@ 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) - mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode + mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode and: 'an update cm handle request that removes all public properties(existing and non-existing)' def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])] when: 'update data node leaves is called with the update request' objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) then: 'the replace list method is not called' - 0 * mockCpsDataService.replaceListContent(*_) + 0 * mockInventoryPersistence.replaceListContent(*_) then: 'delete data node will be called for any existing property' - expectedCallsToDeleteDataNode * mockCpsDataService.deleteDataNode(dataspaceName, anchorName, _, noTimeStamp) >> { arg -> + expectedCallsToDeleteDataNode * mockInventoryPersistence.deleteDataNode(_) >> { arg -> { - assert arg[2].contains("@name='publicProp") + assert arg[0].contains("@name='publicProp") } } where: 'following public properties updates are made' @@ -124,7 +120,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { given: 'cm handles request' def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: [:], dmiProperties: [:])] and: 'data node cannot be found' - mockCpsDataService.getDataNode(*_) >> { throw exception } + mockInventoryPersistence.getCmHandleDataNode(*_) >> { throw exception } when: 'update data node leaves is called using correct parameters' def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) then: 'one failed registration response' @@ -149,7 +145,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]), new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])] and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle' - mockCpsDataService.getDataNode(*_) >> cmHandleDataNode >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') } >> cmHandleDataNode + mockInventoryPersistence.getCmHandleDataNode(*_) >> cmHandleDataNode >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') } >> cmHandleDataNode when: 'update data node leaves is called using correct parameters' def cmHandleResponseList = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) then: 'response has 3 values' @@ -171,7 +167,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { assert it.errorText == "cm-handle does not exist" } then: 'the replace list method is called twice' - 2 * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp) + 2 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_) } def convertToProperties(expectedPropertiesAfterUpdateAsMap) { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfigSpec.groovy new file mode 100644 index 0000000000..fe7ed9eeb3 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfigSpec.groovy @@ -0,0 +1,49 @@ +/* + * ============LICENSE_START======================================================== + * Copyright (C) 2022 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 org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +@SpringBootTest +@ContextConfiguration(classes = [SynchronizationSemaphoresConfig]) +class SynchronizationSemaphoresConfigSpec extends Specification { + + @Autowired + private Map<String, String> moduleSyncSemaphore; + + @Autowired + private Map<String, String> dataSyncSemaphore; + + def 'Embedded Sync Semaphores'() { + expect: 'system is able to create an instance of ModuleSyncSemaphore' + assert null != moduleSyncSemaphore + and: 'system is able to create an instance of DataSyncSemaphore' + assert null != dataSyncSemaphore + and: 'we have 2 instances' + assert Hazelcast.allHazelcastInstances.size() == 2 + and: 'the names match' + assert Hazelcast.allHazelcastInstances.name == ['moduleSyncSemaphore', 'dataSyncSemaphore'] + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy index 9b50c5ad47..50494c0c37 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy @@ -41,6 +41,7 @@ import java.time.OffsetDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter +import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS @@ -85,7 +86,7 @@ class InventoryPersistenceSpec extends Specification { def "Retrieve CmHandle using datanode with #scenario."() { given: 'the cps data service returns a data node from the DMI registry' def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves) - mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode + mockCpsDataPersistenceService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode when: 'retrieving the yang modelled cm handle' def result = objectUnderTest.getYangModelCmHandle(cmHandleId) then: 'the result has the correct id and service names' @@ -119,7 +120,7 @@ class InventoryPersistenceSpec extends Specification { def "Handling missing service names as null CPS-1043."() { 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: [:]) - mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode + mockCpsDataPersistenceService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode when: 'retrieving the yang modelled cm handle' def result = objectUnderTest.getYangModelCmHandle(cmHandleId) then: 'the service names ae returned as null' @@ -161,7 +162,7 @@ class InventoryPersistenceSpec extends Specification { def cmHandleState = CmHandleState.ADVISED and: 'cps data service returns a list of data nodes' mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry', - '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes + '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes when: 'get cm handles by state is invoked' def result = objectUnderTest.getCmHandlesByState(cmHandleState) then: 'the returned result is a list of data nodes returned by cps data service' @@ -173,7 +174,7 @@ class InventoryPersistenceSpec extends Specification { def cmHandleState = CmHandleState.READY and: 'cps data service returns a list of data nodes' mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry', - '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes + '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes when: 'get cm handles by state and id is invoked' def result = objectUnderTest.getCmHandlesByIdAndState(cmHandleId, cmHandleState) then: 'the returned result is a list of data nodes returned by cps data service' @@ -185,7 +186,7 @@ class InventoryPersistenceSpec extends Specification { def cmHandleState = CmHandleState.READY and: 'cps data service returns a list of data nodes' mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry', - '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes + '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes when: 'get cm handles by operational sync state as UNSYNCHRONIZED is invoked' def result = objectUnderTest.getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED) then: 'the returned result is a list of data nodes returned by cps data service' @@ -270,6 +271,15 @@ class InventoryPersistenceSpec extends Specification { 1 * mockCpsDataPersistenceService.getDataNode('NCMP-Admin','ncmp-dmi-registry','sample xPath', INCLUDE_ALL_DESCENDANTS) } + def 'Get cmHandle data node'() { + given: 'expected xPath to get cmHandle data node' + def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']'; + when: 'the method to get data nodes is called' + objectUnderTest.getCmHandleDataNode('sample cmHandleId') + then: 'the data persistence service method to get cmHandle data node is invoked once with expected xPath' + 1 * mockCpsDataPersistenceService.getDataNode('NCMP-Admin','ncmp-dmi-registry',expectedXPath, INCLUDE_ALL_DESCENDANTS) + } + def 'Query anchors'() { when: 'the method to query anchors is called' objectUnderTest.queryAnchors(['sample-module-name']) @@ -279,8 +289,25 @@ class InventoryPersistenceSpec extends Specification { def 'Get anchors'() { when: 'the method to get anchors with no parameters is called' - objectUnderTest.getAnchors() + objectUnderTest.getAnchors() then: 'the admin persistence service method to query anchors is invoked once with a specific dataspace name' 1 * mockCpsAdminPersistenceService.getAnchors('NFP-Operational') } + + def 'Replace list content'() { + when: 'replace list content method is called with xpath and data nodes collection' + objectUnderTest.replaceListContent('sample xpath', [new DataNode()]) + then: 'the cps data service method to replace list content is invoked once with same parameters' + 1 * mockCpsDataService.replaceListContent('NCMP-Admin', 'ncmp-dmi-registry', + 'sample xpath', [new DataNode()], NO_TIMESTAMP); + } + + def 'Delete data node via xPath'() { + when: 'Delete data node method is called with xpath as parameter' + objectUnderTest.deleteDataNode('sample dataNode xpath') + then: 'the cps data service method to delete data node is invoked once with the same xPath' + 1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'ncmp-dmi-registry', + 'sample dataNode xpath', NO_TIMESTAMP); + } + } |