/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2024 Nordix Foundation
+ * Copyright (C) 2024-2025 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.cps.ncmp.impl.datajobs;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation;
import org.onap.cps.ncmp.api.datajobs.models.ProducerKey;
import org.onap.cps.ncmp.api.datajobs.models.WriteOperation;
-import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
+import org.onap.cps.ncmp.impl.dmi.DmiServiceNameResolver;
+import org.onap.cps.ncmp.impl.inventory.ParameterizedCmHandleQueryService;
import org.onap.cps.ncmp.impl.models.RequiredDmiService;
import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher;
import org.springframework.stereotype.Service;
public class WriteRequestExaminer {
private final AlternateIdMatcher alternateIdMatcher;
+ private final ParameterizedCmHandleQueryService parameterizedCmHandleQueryService;
private static final String PATH_SEPARATOR = "/";
/**
* @return {@code Map} map of Dmi Write Operations per Producer Key
*/
public Map<ProducerKey, List<DmiWriteOperation>> splitDmiWriteOperationsFromRequest(
- final String dataJobId,
- final DataJobWriteRequest dataJobWriteRequest) {
+ final String dataJobId, final DataJobWriteRequest dataJobWriteRequest) {
final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey = new HashMap<>();
+ final Map<String, NcmpServiceCmHandle> cmHandlePerAlternateId = getAllNcmpServiceCmHandlesWithoutProperties();
for (final WriteOperation writeOperation : dataJobWriteRequest.data()) {
- examineWriteOperation(dataJobId, dmiWriteOperationsPerProducerKey, writeOperation);
+ examineWriteOperation(dataJobId, dmiWriteOperationsPerProducerKey, writeOperation, cmHandlePerAlternateId);
}
return dmiWriteOperationsPerProducerKey;
}
+ private Map<String, NcmpServiceCmHandle> getAllNcmpServiceCmHandlesWithoutProperties() {
+ final Map<String, NcmpServiceCmHandle> ncmpServiceCmHandles = new HashMap<>();
+ for (final NcmpServiceCmHandle ncmpServiceCmHandle
+ : parameterizedCmHandleQueryService.getAllCmHandlesWithoutProperties()) {
+ ncmpServiceCmHandles.put(ncmpServiceCmHandle.getAlternateId(), ncmpServiceCmHandle);
+ }
+ return ncmpServiceCmHandles;
+ }
+
private void examineWriteOperation(final String dataJobId,
final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey,
- final WriteOperation writeOperation) {
+ final WriteOperation writeOperation,
+ final Map<String, NcmpServiceCmHandle> cmHandlePerAlternateId) {
log.debug("data job id for write operation is: {}", dataJobId);
- final YangModelCmHandle yangModelCmHandle = alternateIdMatcher
- .getYangModelCmHandleByLongestMatchingAlternateId(writeOperation.path(), PATH_SEPARATOR);
+ final NcmpServiceCmHandle ncmpServiceCmHandle = alternateIdMatcher
+ .getCmHandleByLongestMatchingAlternateId(writeOperation.path(), PATH_SEPARATOR, cmHandlePerAlternateId);
- final DmiWriteOperation dmiWriteOperation = createDmiWriteOperation(writeOperation, yangModelCmHandle);
+ final DmiWriteOperation dmiWriteOperation = createDmiWriteOperation(writeOperation, ncmpServiceCmHandle);
- final ProducerKey producerKey = createProducerKey(yangModelCmHandle);
+ final ProducerKey producerKey = createProducerKey(ncmpServiceCmHandle);
final List<DmiWriteOperation> dmiWriteOperations;
if (dmiWriteOperationsPerProducerKey.containsKey(producerKey)) {
dmiWriteOperations = dmiWriteOperationsPerProducerKey.get(producerKey);
dmiWriteOperations.add(dmiWriteOperation);
}
- private ProducerKey createProducerKey(final YangModelCmHandle yangModelCmHandle) {
- return new ProducerKey(yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA),
- yangModelCmHandle.getDataProducerIdentifier());
+ private ProducerKey createProducerKey(final NcmpServiceCmHandle ncmpServiceCmHandle) {
+ final String dmiDataServiceName =
+ DmiServiceNameResolver.resolveDmiServiceName(RequiredDmiService.DATA, ncmpServiceCmHandle);
+ return new ProducerKey(dmiDataServiceName, ncmpServiceCmHandle.getDataProducerIdentifier());
}
private DmiWriteOperation createDmiWriteOperation(final WriteOperation writeOperation,
- final YangModelCmHandle yangModelCmHandle) {
+ final NcmpServiceCmHandle ncmpServiceCmHandle) {
return new DmiWriteOperation(
writeOperation.path(),
writeOperation.op(),
- yangModelCmHandle.getModuleSetTag(),
+ ncmpServiceCmHandle.getModuleSetTag(),
writeOperation.value(),
writeOperation.operationId(),
- getPrivatePropertiesFromDataNode(yangModelCmHandle));
+ Collections.emptyMap()); // TODO: Private properties will be removed as part of CPS-2693.
}
-
- private Map<String, String> getPrivatePropertiesFromDataNode(final YangModelCmHandle yangModelCmHandle) {
- final Map<String, String> cmHandleDmiProperties = new LinkedHashMap<>();
- yangModelCmHandle.getDmiProperties()
- .forEach(dmiProperty -> cmHandleDmiProperties.put(dmiProperty.getName(), dmiProperty.getValue()));
- return cmHandleDmiProperties;
- }
-
}
* @return collection of cm handles
*/
Collection<NcmpServiceCmHandle> getAllCmHandles();
+
+ /**
+ * Retrieves all {@code NcmpServiceCmHandle} instances without their associated properties.
+ * This method fetches the relevant data nodes from the inventory persistence layer and
+ * converts them into {@code NcmpServiceCmHandle} objects. Only the handles are returned,
+ * without any additional properties.
+ *
+ * @return a collection of {@code NcmpServiceCmHandle} instances without properties.
+ */
+ Collection<NcmpServiceCmHandle> getAllCmHandlesWithoutProperties();
}
@Override
public Collection<NcmpServiceCmHandle> getAllCmHandles() {
- final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT).iterator().next();
+ return toNcmpServiceCmHandles(inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT));
+ }
+
+ @Override
+ public Collection<NcmpServiceCmHandle> getAllCmHandlesWithoutProperties() {
+ return toNcmpServiceCmHandles(inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT, DIRECT_CHILDREN_ONLY));
+ }
+
+ private Collection<NcmpServiceCmHandle> toNcmpServiceCmHandles(final Collection<DataNode> dataNodes) {
+ final DataNode dataNode = dataNodes.iterator().next();
return dataNode.getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet());
}
package org.onap.cps.ncmp.impl.utils;
+import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
-import org.onap.cps.ncmp.api.exceptions.CmHandleNotFoundException;
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
import org.onap.cps.ncmp.exceptions.NoAlternateIdMatchFoundException;
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence;
-import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
import org.onap.cps.utils.CpsValidator;
import org.springframework.stereotype.Service;
private final CpsValidator cpsValidator;
/**
- * Get yang model cm handle that matches longest alternate id by removing elements
+ * Get cm handle that matches longest alternate id by removing elements
* (as defined by the separator string) from right to left.
* If alternate id contains a hash then all elements after that hash are ignored.
*
- * @param alternateId alternate ID
- * @param separator a string that separates each element from the next.
- * @return yang model cm handle
+ * @param alternateId alternate ID
+ * @param separator a string that separates each element from the next.
+ * @param cmHandlePerAlternateId all CM-handles by alternate ID
+ * @return ncmp service cm handle
*/
- public YangModelCmHandle getYangModelCmHandleByLongestMatchingAlternateId(final String alternateId,
- final String separator) {
+ public NcmpServiceCmHandle getCmHandleByLongestMatchingAlternateId(
+ final String alternateId, final String separator,
+ final Map<String, NcmpServiceCmHandle> cmHandlePerAlternateId) {
final String[] splitPath = alternateId.split("#", 2);
String bestMatch = splitPath[0];
while (StringUtils.isNotEmpty(bestMatch)) {
- try {
- return inventoryPersistence.getYangModelCmHandleByAlternateId(bestMatch);
- } catch (final CmHandleNotFoundException ignored) {
- bestMatch = getParentPath(bestMatch, separator);
+ final NcmpServiceCmHandle ncmpServiceCmHandle = cmHandlePerAlternateId.get(bestMatch);
+ if (ncmpServiceCmHandle != null) {
+ return ncmpServiceCmHandle;
}
+ bestMatch = getParentPath(bestMatch, separator);
}
throw new NoAlternateIdMatchFoundException(alternateId);
}
import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest
import org.onap.cps.ncmp.api.datajobs.models.WriteOperation
import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
-import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
+import org.onap.cps.ncmp.impl.inventory.ParameterizedCmHandleQueryService
import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
import spock.lang.Specification
class WriteRequestExaminerSpec extends Specification {
def mockAlternateIdMatcher = Mock(AlternateIdMatcher)
- def objectUnderTest = new WriteRequestExaminer(mockAlternateIdMatcher)
+ def mockParameterizedCmHandleQueryService = Mock(ParameterizedCmHandleQueryService)
+ def objectUnderTest = new WriteRequestExaminer(mockAlternateIdMatcher, mockParameterizedCmHandleQueryService)
def setup() {
- def ch1 = new YangModelCmHandle(id: 'ch1', dmiServiceName: 'dmiA', dataProducerIdentifier: 'p1', dmiProperties: [])
- def ch2 = new YangModelCmHandle(id: 'ch2', dmiServiceName: 'dmiA', dataProducerIdentifier: 'p1', dmiProperties: [])
- def ch3 = new YangModelCmHandle(id: 'ch3', dmiServiceName: 'dmiA', dataProducerIdentifier: 'p2', dmiProperties: [])
- def ch4 = new YangModelCmHandle(id: 'ch4', dmiServiceName: 'dmiB', dataProducerIdentifier: 'p1', dmiProperties: [])
- mockAlternateIdMatcher.getYangModelCmHandleByLongestMatchingAlternateId('fdn1', '/') >> ch1
- mockAlternateIdMatcher.getYangModelCmHandleByLongestMatchingAlternateId('fdn2', '/') >> ch2
- mockAlternateIdMatcher.getYangModelCmHandleByLongestMatchingAlternateId('fdn3', '/') >> ch3
- mockAlternateIdMatcher.getYangModelCmHandleByLongestMatchingAlternateId('fdn4', '/') >> ch4
+ def ch1 = new NcmpServiceCmHandle(cmHandleId: 'ch1', dmiServiceName: 'dmiA', moduleSetTag: 'someModuleSetTag', alternateId: 'fdn1', dataProducerIdentifier: 'p1')
+ def ch2 = new NcmpServiceCmHandle(cmHandleId: 'ch2', dmiServiceName: 'dmiA', moduleSetTag: 'someModuleSetTag', alternateId: 'fdn2', dataProducerIdentifier: 'p1')
+ def ch3 = new NcmpServiceCmHandle(cmHandleId: 'ch3', dmiServiceName: 'dmiA', moduleSetTag: 'someModuleSetTag', alternateId: 'fdn3', dataProducerIdentifier: 'p2')
+ def ch4 = new NcmpServiceCmHandle(cmHandleId: 'ch4', dmiServiceName: 'dmiB', moduleSetTag: 'someModuleSetTag', alternateId: 'fdn4', dataProducerIdentifier: 'p1')
+ def cmHandlePerAlternateId = ['fdn1': ch1, 'fdn2': ch2, 'fdn3': ch3, 'fdn4': ch4]
+ mockAlternateIdMatcher.getCmHandleByLongestMatchingAlternateId('fdn1', '/', cmHandlePerAlternateId) >> ch1
+ mockAlternateIdMatcher.getCmHandleByLongestMatchingAlternateId('fdn2', '/', cmHandlePerAlternateId) >> ch2
+ mockAlternateIdMatcher.getCmHandleByLongestMatchingAlternateId('fdn3', '/', cmHandlePerAlternateId) >> ch3
+ mockAlternateIdMatcher.getCmHandleByLongestMatchingAlternateId('fdn4', '/', cmHandlePerAlternateId) >> ch4
+ mockParameterizedCmHandleQueryService.getAllCmHandlesWithoutProperties() >> [ch1, ch2, ch3, ch4]
}
def 'Create a map of dmi write requests per producer key with #scenario.'() {
def 'Validate the creation of a ProducerKey with correct dmiservicename.'() {
given: 'yangModelCmHandles with service name: "#dmiServiceName" and data service name: "#dataServiceName"'
- def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dataServiceName, '', new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'), '', '', 'dpi1')
+ def ncmpServiceCmHandle = new NcmpServiceCmHandle(dmiServiceName: dmiServiceName, dmiDataServiceName: dataServiceName, dataProducerIdentifier: 'dpi1')
when: 'the ProducerKey is created'
- def result = objectUnderTest.createProducerKey(yangModelCmHandle).toString()
+ def result = objectUnderTest.createProducerKey(ncmpServiceCmHandle).toString()
then: 'we get the ProducerKey with the correct service name'
assert result == expectedProducerKey
where: 'the following services are registered'
package org.onap.cps.ncmp.impl.utils
-import org.onap.cps.ncmp.api.exceptions.CmHandleNotFoundException
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
import org.onap.cps.ncmp.exceptions.NoAlternateIdMatchFoundException
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
def objectUnderTest = new AlternateIdMatcher(mockInventoryPersistence, new CpsValidatorImpl())
- def setup() {
- given: 'cm handle in the registry with alternate id /a/b'
- mockInventoryPersistence.getYangModelCmHandleByAlternateId('/a/b') >> new YangModelCmHandle()
- and: 'no other cm handle'
- mockInventoryPersistence.getYangModelCmHandleByAlternateId(_) >> { throw new CmHandleNotFoundException('') }
- }
-
def 'Finding longest alternate id matches.'() {
+ given: 'a cm handle with alternate id /a/b in the cached map of all cm handles'
+ def ch1 = new NcmpServiceCmHandle(cmHandleId: 'ch1', alternateId: '/a/b')
+ def cmHandlePerAlternateId = ['/a/b': ch1]
expect: 'querying for alternate id a matching result found'
- assert objectUnderTest.getYangModelCmHandleByLongestMatchingAlternateId(targetAlternateId, '/') != null
+ assert objectUnderTest.getCmHandleByLongestMatchingAlternateId(targetAlternateId, '/', cmHandlePerAlternateId) != null
where: 'the following parameters are used'
scenario | targetAlternateId
'exact match' | '/a/b'
def 'Attempt to find longest alternate id match without any matches.'() {
when: 'attempt to find alternateId'
- objectUnderTest.getYangModelCmHandleByLongestMatchingAlternateId(targetAlternateId, '/')
+ objectUnderTest.getCmHandleByLongestMatchingAlternateId(targetAlternateId, '/', [:])
then: 'no alternate id match found exception thrown'
def thrown = thrown(NoAlternateIdMatchFoundException)
and: 'the exception has the relevant details from the error response'
+++ /dev/null
-/*
- * ============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.integration.performance.ncmp
-
-import org.onap.cps.integration.ResourceMeter
-import org.onap.cps.integration.performance.base.NcmpPerfTestBase
-import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
-
-import java.util.stream.Collectors
-
-import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME
-import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
-import static org.onap.cps.api.parameters.FetchDescendantsOption.OMIT_DESCENDANTS
-
-class CmHandleQueryByAlternateIdPerfTest extends NcmpPerfTestBase {
-
- AlternateIdMatcher objectUnderTest
- ResourceMeter resourceMeter = new ResourceMeter()
-
- def setup() { objectUnderTest = alternateIdMatcher }
-
- def 'Query cm handle by longest match alternate id'() {
- when: 'an alternate id as cps path query'
- resourceMeter.start()
- def cpsPath = "/a/b/c/d-5/e/f/g/h/i"
- def dataNodes = objectUnderTest.getYangModelCmHandleByLongestMatchingAlternateId(cpsPath, '/')
- and: 'the ids of the result are extracted and converted to xpath'
- def cpsXpaths = dataNodes.stream().map(dataNode -> "/dmi-registry/cm-handles[@id='${dataNode.leaves.id}']".toString() ).collect(Collectors.toSet())
- and: 'a single get is executed to get all the parent objects and their descendants'
- cpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsXpaths, OMIT_DESCENDANTS)
- resourceMeter.stop()
- def durationInSeconds = resourceMeter.getTotalTimeInSeconds()
- print 'Total time in seconds to query ch handle by alternate id: ' + durationInSeconds
- then: 'the required operations are performed within required time and memory limit'
- recordAndAssertResourceUsage('Look up cm-handle by longest match alternate-id', 1, durationInSeconds, 300, resourceMeter.getTotalMemoryUsageInMB())
- }
-}
import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest
import org.onap.cps.ncmp.api.datajobs.models.WriteOperation
import org.springframework.beans.factory.annotation.Autowired
+import spock.lang.Ignore
/**
* This test does not depend on common performance test data. Hence it just extends the integration spec base.
return new DataJobWriteRequest(writeOperations)
}
+ @Ignore // CPS-2691
def 'Performance test for writeDataJob method'() {
- given: 'register 1,000 cm handles (with alternative ids)'
- registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, 'tagA', 1000, 1, ModuleNameStrategy.UNIQUE, { it -> "/SubNetwork=Europe/SubNetwork=Ireland/MeContext=MyRadioNode${it}/ManagedElement=MyManagedElement${it}" })
- def authorization = 'my authorization header'
- def numberOfWriteOperations = 1000
- def dataJobWriteRequest = populateDataJobWriteRequests(numberOfWriteOperations)
- def myDataJobMetadata = new DataJobMetadata('d1', '', '')
- def dataJobId = 'my-data-job-id'
+ given: 'register 10_000 cm handles (with alternative ids)'
+ registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, 'tagA', 10_000, 1, ModuleNameStrategy.UNIQUE, { it -> "/SubNetwork=Europe/SubNetwork=Ireland/MeContext=MyRadioNode${it}/ManagedElement=MyManagedElement${it}" })
+ def dataJobWriteRequest = populateDataJobWriteRequests(10_000)
when: 'sending a write job to NCMP with dynamically generated write operations'
resourceMeter.start()
- dataJobService.writeDataJob(authorization, dataJobId, myDataJobMetadata, dataJobWriteRequest)
+ dataJobService.writeDataJob('', '', new DataJobMetadata('d1', '', ''), dataJobWriteRequest)
resourceMeter.stop()
then: 'record the result. Not asserted, just recorded in See https://lf-onap.atlassian.net/browse/CPS-2691'
println "*** CPS-2691 Execution time: ${resourceMeter.totalTimeInSeconds} seconds"
cleanup: 'deregister test cm handles'
- deregisterSequenceOfCmHandles(DMI1_URL, 1000, 1)
+ deregisterSequenceOfCmHandles(DMI1_URL, 10_000, 1)
}
}