From: emaclee Date: Wed, 24 Jan 2024 15:22:58 +0000 (+0000) Subject: Populate cmSubscription cache X-Git-Tag: 3.4.5~3^2 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;ds=sidebyside;h=a25711de47cb9fa7840f5e67a3d93da9fab8e0a3;p=cps.git Populate cmSubscription cache - Create subscription cache handler - Add unit tests Issue-ID: CPS-2025 Change-Id: I54d505369a8429d6f6a87a2af0b169f9530622c3 Signed-off-by: emaclee --- diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java index d3bde011b..362fbeb9e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java @@ -23,17 +23,23 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription; import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent; import io.cloudevents.CloudEvent; +import java.util.List; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent; +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate; import org.springframework.beans.factory.annotation.Value; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component @Slf4j +@RequiredArgsConstructor public class CmNotificationSubscriptionNcmpInEventConsumer { + private final DmiCmNotificationSubscriptionCacheHandler dmiCmNotificationSubscriptionCacheHandler; + @Value("${notification.enabled:true}") private boolean notificationFeatureEnabled; @@ -50,9 +56,12 @@ public class CmNotificationSubscriptionNcmpInEventConsumer { toTargetEvent(cloudEvent, CmNotificationSubscriptionNcmpInEvent.class); log.info("Subscription with name {} to be mapped to hazelcast object...", cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()); + final String subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId(); + final List predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates(); + dmiCmNotificationSubscriptionCacheHandler.add(subscriptionId, predicates); if ("subscriptionCreated".equals(cloudEvent.getType()) && cmNotificationSubscriptionNcmpInEvent != null) { log.info("Subscription for ClientID {} with name {} ...", cloudEvent.getSource(), cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()); } } -} +} \ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java new file mode 100644 index 000000000..2f10b1ce1 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java @@ -0,0 +1,113 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.events.cmsubscription; + +import static org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus.PENDING; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails; +import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate; +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.impl.operations.DatastoreType; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class DmiCmNotificationSubscriptionCacheHandler { + + private final Map> cmNotificationSubscriptionCache; + private final InventoryPersistence inventoryPersistence; + + /** + * Adds new subscription to the subscription cache. + * + * @param subscriptionId subscription Id + * @param predicates subscription request predicates + */ + public void add(final String subscriptionId, final List predicates) { + cmNotificationSubscriptionCache.put(subscriptionId, createDmiCmNotificationSubscriptionsPerDmi(predicates)); + } + + /** + * Creates map of subscription details per DMI. + * + * @param predicates CM Subscription Create Request Predicates + * @return Map of DmiCmNotificationSubscription per DMI plugin + */ + public Map createDmiCmNotificationSubscriptionsPerDmi( + final List predicates) { + final Map dmiCmNotificationSubscriptionDetailsPerDmi = + new HashMap<>(); + for (final Predicate requestPredicate : predicates) { + final List targetFilter = requestPredicate.getTargetFilter(); + final DatastoreType datastoreType = DatastoreType.fromDatastoreName( + requestPredicate.getScopeFilter().getDatastore().toString()); + final Set xpaths = new HashSet<>(requestPredicate.getScopeFilter().getXpathFilter()); + final Map> targetCmHandlesByDmiMap = groupTargetCmHandleIdsByDmi(targetFilter); + for (final Map.Entry> targetCmHandlesByDmi: targetCmHandlesByDmiMap.entrySet()) { + final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate = + new DmiCmNotificationSubscriptionPredicate(targetCmHandlesByDmi.getValue(), + datastoreType, xpaths); + updateDmiCmNotificationSubscriptionDetailsPerDmi(targetCmHandlesByDmi.getKey(), + dmiCmNotificationSubscriptionPredicate, + dmiCmNotificationSubscriptionDetailsPerDmi); + } + } + return dmiCmNotificationSubscriptionDetailsPerDmi; + } + + private void updateDmiCmNotificationSubscriptionDetailsPerDmi( + final String dmiServiceName, + final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate, + final Map dmiCmNotificationSubscriptionDetailsPerDmi) { + if (dmiCmNotificationSubscriptionDetailsPerDmi.containsKey(dmiServiceName)) { + dmiCmNotificationSubscriptionDetailsPerDmi.get(dmiServiceName) + .getDmiCmNotificationSubscriptionPredicates().add(dmiCmNotificationSubscriptionPredicate); + } else { + dmiCmNotificationSubscriptionDetailsPerDmi.put(dmiServiceName, + new DmiCmNotificationSubscriptionDetails( + new ArrayList<>(List.of(dmiCmNotificationSubscriptionPredicate)), + PENDING)); + } + } + + private Map> groupTargetCmHandleIdsByDmi(final List targetCmHandleIds) { + final Map> targetCmHandlesByDmiServiceNames = new HashMap<>(); + final Collection yangModelCmHandles = + inventoryPersistence.getYangModelCmHandles(targetCmHandleIds); + + for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) { + final String dmiServiceName = yangModelCmHandle.getDmiServiceName(); + targetCmHandlesByDmiServiceNames.putIfAbsent(dmiServiceName, new HashSet<>()); + targetCmHandlesByDmiServiceNames.get(dmiServiceName).add(yangModelCmHandle.getId()); + } + return targetCmHandlesByDmiServiceNames; + } +} \ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionDetails.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionDetails.java index 4f6caefce..95757e724 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionDetails.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionDetails.java @@ -21,11 +21,13 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription.model; import java.util.List; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @Getter @Setter +@AllArgsConstructor public class DmiCmNotificationSubscriptionDetails { private List dmiCmNotificationSubscriptionPredicates; diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy index 86273bbd2..e65011f71 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy @@ -50,7 +50,7 @@ class CmNotificationSubscriptionCacheConfigSpec extends Specification { def subscriptionId = 'sub123' def dmiPluginName = 'dummydmi' def cmSubscriptionPredicate = new DmiCmNotificationSubscriptionPredicate(['cmhandle1', 'cmhandle2'].toSet(), DatastoreType.PASSTHROUGH_RUNNING, ['/a/b/c'].toSet()) - def cmSubscriptionCacheObject = new DmiCmNotificationSubscriptionDetails(dmiCmNotificationSubscriptionPredicates: [cmSubscriptionPredicate], cmNotificationSubscriptionStatus: CmNotificationSubscriptionStatus.PENDING) + def cmSubscriptionCacheObject = new DmiCmNotificationSubscriptionDetails([cmSubscriptionPredicate], CmNotificationSubscriptionStatus.PENDING) when: 'the cache is populated' cmNotificationSubscriptionCache.put(subscriptionId, [(dmiPluginName): cmSubscriptionCacheObject]) then: 'the values are present in memory' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy index 107422948..614cf6729 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy @@ -39,7 +39,8 @@ import org.springframework.boot.test.context.SpringBootTest @SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpec { - def objectUnderTest = new CmNotificationSubscriptionNcmpInEventConsumer() + def mockDmiCmNotificationSubscriptionCacheHandler = Mock(DmiCmNotificationSubscriptionCacheHandler) + def objectUnderTest = new CmNotificationSubscriptionNcmpInEventConsumer(mockDmiCmNotificationSubscriptionCacheHandler) def logger = Spy(ListAppender) @Autowired @@ -60,7 +61,7 @@ class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpe def 'Consume valid CMSubscription create message'() { given: 'a cmsubscription event' - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmSubscriptionNcmpInEvent.json') + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) def testCloudEventSent = CloudEventBuilder.v1() .withData(objectMapper.writeValueAsBytes(testEventSent)) @@ -78,6 +79,8 @@ class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpe assert loggingEvent.level == Level.INFO and: 'the log indicates the task completed successfully' assert loggingEvent.formattedMessage == 'Subscription with name cm-subscription-001 to be mapped to hazelcast object...' + and: 'the cache handler method is called once' + 1 * mockDmiCmNotificationSubscriptionCacheHandler.add('cm-subscription-001',_) } def getLoggingEvent() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy new file mode 100644 index 000000000..132c4bc4a --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy @@ -0,0 +1,141 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.events.cmsubscription + +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import io.cloudevents.core.builder.CloudEventBuilder +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent +import org.onap.cps.ncmp.utils.TestUtils +import org.onap.cps.utils.JsonObjectMapper +import org.spockframework.spring.SpringBean +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest + +import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent + +@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) +class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec { + + @Autowired + JsonObjectMapper jsonObjectMapper + @Autowired + ObjectMapper objectMapper + @SpringBean + InventoryPersistence mockInventoryPersistence = Mock(InventoryPersistence) + + def testCache = [:] + def objectUnderTest = new DmiCmNotificationSubscriptionCacheHandler(testCache, mockInventoryPersistence) + + CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent + def yangModelCmHandle1 = new YangModelCmHandle(id:'ch1',dmiServiceName:'dmi-1') + def yangModelCmHandle2 = new YangModelCmHandle(id:'ch2',dmiServiceName:'dmi-2') + def yangModelCmHandle3 = new YangModelCmHandle(id:'ch3',dmiServiceName:'dmi-1') + def yangModelCmHandle4 = new YangModelCmHandle(id:'ch4',dmiServiceName:'dmi-2') + + def setup() { + setUpTestEvent() + initialiseMockInventoryPersistenceResponses() + } + + def 'Load CM subscription event to cache'() { + given: 'a valid subscription event with Id' + def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId(); + and: 'list of predicates' + def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates(); + when: 'a valid event object loaded in cache' + objectUnderTest.add(subscriptionId, predicates) + then: 'the cache contains the correct entry with #subscriptionId subscription ID' + assert testCache.containsKey(subscriptionId) + } + + def 'Create map for DMI cm notification subscription per DMI service name'() { + given: 'list of predicates from the create subscription event' + def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates() + when: 'method to create map of DMI cm notification subscription per DMI service name is called' + def result = objectUnderTest.createDmiCmNotificationSubscriptionsPerDmi(predicates) + then: 'the result size of resulting map is correct to the number of DMIs' + assert result.size() == 2 + and: 'the cache objects per DMI exists' + def resultMapForDmi1 = result.get('dmi-1') + def resultMapForDmi2 = result.get('dmi-2') + assert resultMapForDmi1 != null + assert resultMapForDmi2 != null + and: 'the size of predicates in each object is correct' + assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates.size() == 2 + assert resultMapForDmi2.dmiCmNotificationSubscriptionPredicates.size() == 2 + and: 'the subscription status in each object is correct' + assert resultMapForDmi1.cmNotificationSubscriptionStatus.toString() == 'PENDING' + assert resultMapForDmi2.cmNotificationSubscriptionStatus.toString() == 'PENDING' + and: 'the target cmHandles for each predicate is correct' + assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[0].targetCmHandleIds == ['ch1'].toSet() + assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[1].targetCmHandleIds == ['ch3'].toSet() + + assert resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[0].targetCmHandleIds == ['ch2'].toSet() + assert resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].targetCmHandleIds == ['ch4'].toSet() + and: 'the list of xpath for each is correct' + assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[0].xpaths + && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[0].xpaths == ['/x1/y1','x2/y2'].toSet() + + assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[1].xpaths + && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].xpaths == ['/x3/y3','x4/y4'].toSet() + } + + def 'Get map for cm handle IDs by DMI service name'() { + given: 'the predicate from the test request CM subscription event' + def targetFilter = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates().get(0).getTargetFilter() + when: 'the method to group all target CM handles by DMI service name is called' + def mapOfCMHandleIDsByDmi = objectUnderTest.groupTargetCmHandleIdsByDmi(targetFilter) + then: 'the size of the resulting map is correct' + assert mapOfCMHandleIDsByDmi.size() == 2 + and: 'the values in the map is as expected' + assert mapOfCMHandleIDsByDmi.get('dmi-1') == ['ch1'].toSet() + assert mapOfCMHandleIDsByDmi.get('dmi-2') == ['ch2'].toSet() + } + + def setUpTestEvent(){ + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) + def testCloudEventSent = CloudEventBuilder.v1() + .withData(objectMapper.writeValueAsBytes(testEventSent)) + .withId('subscriptionCreated') + .withType('subscriptionCreated') + .withSource(URI.create('some-resource')) + .withExtension('correlationid', 'test-cmhandle1').build() + def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) + def cloudEvent = consumerRecord.value() + + cmNotificationSubscriptionNcmpInEvent = toTargetEvent(cloudEvent, CmNotificationSubscriptionNcmpInEvent.class); + } + + def initialiseMockInventoryPersistenceResponses(){ + mockInventoryPersistence.getYangModelCmHandles(['ch1','ch2']) + >> [yangModelCmHandle1, yangModelCmHandle2] + + mockInventoryPersistence.getYangModelCmHandles(['ch3','ch4']) + >> [yangModelCmHandle3, yangModelCmHandle4] + } + +} \ No newline at end of file diff --git a/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionNcmpInEvent.json b/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionNcmpInEvent.json new file mode 100644 index 000000000..09796e2f4 --- /dev/null +++ b/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionNcmpInEvent.json @@ -0,0 +1,21 @@ +{ + "data": { + "subscriptionId": "cm-subscription-001", + "predicates": [ + { + "targetFilter": ["ch1","ch2"], + "scopeFilter": { + "datastore": "ncmp-datastore:passthrough-operational", + "xpath-filter": ["/x1/y1","x2/y2"] + } + }, + { + "targetFilter": ["ch3","ch4"], + "scopeFilter": { + "datastore": "ncmp-datastore:passthrough-operational", + "xpath-filter": ["/x3/y3","x4/y4"] + } + } + ] + } +} \ No newline at end of file diff --git a/cps-ncmp-service/src/test/resources/cmSubscription/cmSubscriptionNcmpInEvent.json b/cps-ncmp-service/src/test/resources/cmSubscription/cmSubscriptionNcmpInEvent.json deleted file mode 100644 index 524661882..000000000 --- a/cps-ncmp-service/src/test/resources/cmSubscription/cmSubscriptionNcmpInEvent.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "data": { - "subscriptionId": "cm-subscription-001", - "predicates": [ - { - "targetFilter": [ - "CMHandle1", - "CMHandle2", - "CMHandle3" - ], - "scopeFilter": { - "datastore": "ncmp-datastore:passthrough-running", - "xpath-filter": [ - "//_3gpp-nr-nrm-gnbdufunction:GNBDUFunction/_3gpp-nr-nrm-nrcelldu:NRCellDU/", - "//_3gpp-nr-nrm-gnbcuupfunction:GNBCUUPFunction//", - "//_3gpp-nr-nrm-gnbcucpfunction:GNBCUCPFunction/_3gpp-nr-nrm-nrcelldu:NRCellCU//", - "//_3gpp-nr-nrm-nrsectorcarrier:NRSectorCarrier//" - ] - } - } - ] - } -} \ No newline at end of file