Handle restart case for cps-ncmp gauge metrics 92/139892/5
authoremaclee <lee.anjella.macabuhay@est.tech>
Sun, 12 Jan 2025 12:39:55 +0000 (12:39 +0000)
committeremaclee <lee.anjella.macabuhay@est.tech>
Thu, 16 Jan 2025 15:28:15 +0000 (15:28 +0000)
Issue-ID: CPS-2456
Change-Id: I9ac5d6774fcd745d8141551eeff8a1deb1938f57
Signed-off-by: emaclee <lee.anjella.macabuhay@est.tech>
cps-application/src/main/java/org/onap/cps/config/MicroMeterConfig.java
cps-application/src/test/groovy/org/onap/cps/config/MicroMeterConfigSpec.groovy
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/AdminCacheConfig.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/CmHandleStateMonitor.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/NcmpInventoryModelOnboardingFinishedEvent.java [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cache/AdminCacheConfigSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/CmHandleStateMonitorSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy

index 39ed6ef..de98116 100644 (file)
@@ -32,8 +32,8 @@ import org.springframework.context.annotation.Configuration;
 @RequiredArgsConstructor
 public class MicroMeterConfig {
 
-    private static final String TAG = "state";
-    private static final String CMHANDLE_STATE_GAUGE = "cmHandlesByState";
+    private static final String STATE_TAG = "state";
+    private static final String CM_HANDLE_STATE_GAUGE = "cmHandlesByState";
     final IMap<String, Integer> cmHandlesByState;
 
     @Bean
@@ -49,9 +49,9 @@ public class MicroMeterConfig {
      */
     @Bean
     public Gauge advisedCmHandles(final MeterRegistry meterRegistry) {
-        return Gauge.builder(CMHANDLE_STATE_GAUGE, cmHandlesByState,
+        return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState,
                         value -> cmHandlesByState.get("advisedCmHandlesCount"))
-                .tag(TAG, "ADVISED")
+                .tag(STATE_TAG, "ADVISED")
                 .description("Current number of cmhandles in advised state")
                 .register(meterRegistry);
     }
@@ -64,9 +64,9 @@ public class MicroMeterConfig {
      */
     @Bean
     public Gauge readyCmHandles(final MeterRegistry meterRegistry) {
-        return Gauge.builder(CMHANDLE_STATE_GAUGE, cmHandlesByState,
+        return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState,
                         value -> cmHandlesByState.get("readyCmHandlesCount"))
-                .tag(TAG, "READY")
+                .tag(STATE_TAG, "READY")
                 .description("Current number of cmhandles in ready state")
                 .register(meterRegistry);
     }
@@ -79,9 +79,9 @@ public class MicroMeterConfig {
      */
     @Bean
     public Gauge lockedCmHandles(final MeterRegistry meterRegistry) {
-        return Gauge.builder(CMHANDLE_STATE_GAUGE, cmHandlesByState,
+        return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState,
                 value -> cmHandlesByState.get("lockedCmHandlesCount"))
-                .tag(TAG, "LOCKED")
+                .tag(STATE_TAG, "LOCKED")
                 .description("Current number of cmhandles in locked state")
                 .register(meterRegistry);
     }
@@ -94,26 +94,11 @@ public class MicroMeterConfig {
      */
     @Bean
     public Gauge deletingCmHandles(final MeterRegistry meterRegistry) {
-        return Gauge.builder(CMHANDLE_STATE_GAUGE, cmHandlesByState,
+        return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState,
                         value -> cmHandlesByState.get("deletingCmHandlesCount"))
-                .tag(TAG, "DELETING")
+                .tag(STATE_TAG, "DELETING")
                 .description("Current number of cmhandles in deleting state")
                 .register(meterRegistry);
     }
 
-    /**
-     * Register gauge metric for cm handles with state 'deleted'.
-     *
-     * @param meterRegistry meter registry
-     * @return cm handle state gauge
-     */
-    @Bean
-    public Gauge deletedCmHandles(final MeterRegistry meterRegistry) {
-        return Gauge.builder(CMHANDLE_STATE_GAUGE, cmHandlesByState,
-                        value -> cmHandlesByState.get("deletedCmHandlesCount"))
-                .tag(TAG, "DELETED")
-                .description("Current number of cmhandles in deleted state")
-                .register(meterRegistry);
-    }
-
 }
index 67ca646..da3afc6 100644 (file)
@@ -43,9 +43,8 @@ class MicroMeterConfigSpec extends Specification {
              objectUnderTest.readyCmHandles(simpleMeterRegistry)
              objectUnderTest.lockedCmHandles(simpleMeterRegistry)
              objectUnderTest.deletingCmHandles(simpleMeterRegistry)
-             objectUnderTest.deletedCmHandles(simpleMeterRegistry)
         then: 'each state has the correct value when queried'
-            def states = ["ADVISED", "READY", "LOCKED", "DELETING", "DELETED"]
+            def states = ["ADVISED", "READY", "LOCKED", "DELETING"]
             states.each { state ->
                 def gaugeValue = simpleMeterRegistry.get("cmHandlesByState").tag("state",state).gauge().value()
                 assert gaugeValue == 1
index a29799b..fe933d7 100644 (file)
@@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration;
 @Configuration
 public class AdminCacheConfig extends HazelcastCacheConfig {
 
-    private static final MapConfig adminCacheMapConfig = createMapConfig("adminCacheMapConfig");
+    private static final MapConfig cmHandleStateCacheMapConfig = createMapConfig("cmHandleStateCacheMapConfig");
 
     /**
      * Distributed instance admin cache map for cm handles by state for use of gauge metrics.
@@ -37,13 +37,7 @@ public class AdminCacheConfig extends HazelcastCacheConfig {
      */
     @Bean
     public IMap<String, Integer> cmHandlesByState() {
-        final IMap<String, Integer> cmHandlesByState = getOrCreateHazelcastInstance(adminCacheMapConfig).getMap(
+        return getOrCreateHazelcastInstance(cmHandleStateCacheMapConfig).getMap(
                 "cmHandlesByState");
-        cmHandlesByState.putIfAbsent("advisedCmHandlesCount", 0);
-        cmHandlesByState.putIfAbsent("readyCmHandlesCount", 0);
-        cmHandlesByState.putIfAbsent("lockedCmHandlesCount", 0);
-        cmHandlesByState.putIfAbsent("deletingCmHandlesCount", 0);
-        cmHandlesByState.putIfAbsent("deletedCmHandlesCount", 0);
-        return cmHandlesByState;
     }
 }
index 31270ca..708508e 100644 (file)
@@ -25,18 +25,42 @@ import com.hazelcast.map.IMap;
 import java.util.Collection;
 import java.util.Map;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.inventory.models.CmHandleState;
 import org.onap.cps.ncmp.api.inventory.models.CompositeState;
+import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService;
 import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsCmHandleStateHandlerImpl.CmHandleTransitionPair;
+import org.onap.cps.ncmp.utils.events.NcmpInventoryModelOnboardingFinishedEvent;
+import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
 @Component
 @RequiredArgsConstructor
+@Slf4j
 public class CmHandleStateMonitor {
-
     private static final String METRIC_POSTFIX = "CmHandlesCount";
-    final IMap<String, Integer> cmHandlesByState;
+
+    private final CmHandleQueryService cmHandleQueryService;
+    private final IMap<String, Integer> cmHandlesByState;
+
+    /**
+     * Method to initialise cm handle state monitor  by querying the current state counts
+     * and storing them in the provided map. This method is triggered by NcmpInventoryModelOnboardingFinishedEvent.
+     *
+     * @param ncmpInventoryModelOnboardingFinishedEvent the event that triggers the initialization
+     */
+    @EventListener
+    public void initialiseCmHandleStateMonitor(
+            final NcmpInventoryModelOnboardingFinishedEvent ncmpInventoryModelOnboardingFinishedEvent) {
+        for (final CmHandleState cmHandleState : CmHandleState.values()) {
+            final String cmHandleStateAsString = cmHandleState.name().toLowerCase();
+            final String stateMetricKey = cmHandleStateAsString + METRIC_POSTFIX;
+            final int cmHandleCountForState =  cmHandleQueryService.queryCmHandleIdsByState(cmHandleState).size();
+            cmHandlesByState.putIfAbsent(stateMetricKey, cmHandleCountForState);
+            log.info("Cm handle state monitor has set " + stateMetricKey + " to " + cmHandleCountForState);
+        }
+    }
 
     /**
      * Asynchronously update the cm handle state metrics.
index 58a5f55..514d9b8 100644 (file)
@@ -30,11 +30,15 @@ import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsDataspaceService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.init.AbstractModelLoader;
+import org.onap.cps.ncmp.utils.events.NcmpInventoryModelOnboardingFinishedEvent;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Service;
 
 @Slf4j
 @Service
 public class InventoryModelLoader extends AbstractModelLoader {
+
+    private final ApplicationEventPublisher applicationEventPublisher;
     private static final String NEW_MODEL_FILE_NAME = "dmi-registry@2024-02-23.yang";
     private static final String NEW_SCHEMA_SET_NAME = "dmi-registry-2024-02-23";
     private static final String REGISTRY_DATANODE_NAME = "dmi-registry";
@@ -42,14 +46,17 @@ public class InventoryModelLoader extends AbstractModelLoader {
     public InventoryModelLoader(final CpsDataspaceService cpsDataspaceService,
                                 final CpsModuleService cpsModuleService,
                                 final CpsAnchorService cpsAnchorService,
-                                final CpsDataService cpsDataService) {
+                                final CpsDataService cpsDataService,
+                                final ApplicationEventPublisher applicationEventPublisher) {
         super(cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService);
+        this.applicationEventPublisher = applicationEventPublisher;
     }
 
     @Override
     public void onboardOrUpgradeModel() {
         updateInventoryModel();
         log.info("Inventory Model updated successfully");
+        applicationEventPublisher.publishEvent(new NcmpInventoryModelOnboardingFinishedEvent(this));
     }
 
     private void updateInventoryModel() {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/NcmpInventoryModelOnboardingFinishedEvent.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/NcmpInventoryModelOnboardingFinishedEvent.java
new file mode 100644 (file)
index 0000000..92d5e82
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 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.
+ *  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.utils.events;
+
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * Custom event triggered when the NCMP inventory model onboarding process is completed.
+ * Extends Spring's {@link ApplicationEvent} to be used within Spring's event-driven architecture.
+ */
+public class NcmpInventoryModelOnboardingFinishedEvent extends ApplicationEvent {
+
+    /**
+     * Creates a new instance of NcmpModelOnboardingFinishedEvent.
+     *
+     * @param source the object that is the source of the event (i.e. the source that completed the onboarding process)
+     */
+    public NcmpInventoryModelOnboardingFinishedEvent(final Object source) {
+        super(source);
+    }
+}
index 9b96033..a576865 100644 (file)
@@ -45,13 +45,5 @@ class AdminCacheConfigSpec extends Specification {
             assert Hazelcast.allHazelcastInstances.size() > 0
         and: 'Hazelcast cache instance for cm handle by state is present'
             assert Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').getMap('cmHandlesByState') != null
-        and: 'the cache already contains 5 entries, an entry for each state'
-            def cmHandleByState =  Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').getMap('cmHandlesByState')
-            assert cmHandleByState.size() == 5
-            assert cmHandleByState.containsKey('advisedCmHandlesCount')
-            assert cmHandleByState.containsKey('lockedCmHandlesCount')
-            assert cmHandleByState.containsKey('readyCmHandlesCount')
-            assert cmHandleByState.containsKey('deletingCmHandlesCount')
-            assert cmHandleByState.containsKey('deletedCmHandlesCount')
     }
 }
index 9fd40b9..4d7c22a 100644 (file)
@@ -26,16 +26,19 @@ import static org.onap.cps.ncmp.api.inventory.models.CmHandleState.READY
 import com.hazelcast.core.Hazelcast
 import com.hazelcast.map.IMap
 import org.onap.cps.ncmp.api.inventory.models.CompositeState
+import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService
 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
 import org.onap.cps.ncmp.impl.inventory.sync.lcm.CmHandleStateMonitor.DecreasingEntryProcessor
 import org.onap.cps.ncmp.impl.inventory.sync.lcm.CmHandleStateMonitor.IncreasingEntryProcessor
+import org.onap.cps.ncmp.utils.events.NcmpInventoryModelOnboardingFinishedEvent
 import spock.lang.Shared
 import spock.lang.Specification;
 
 class CmHandleStateMonitorSpec extends Specification {
 
-    def cmHandlesByState = Mock(IMap)
-    def objectUnderTest = new CmHandleStateMonitor(cmHandlesByState)
+    def mockCmHandlesByState = Mock(IMap)
+    def mockCmHandleQueryService = Mock(CmHandleQueryService)
+    def objectUnderTest = new CmHandleStateMonitor(mockCmHandleQueryService, mockCmHandlesByState)
 
     @Shared
     def entryProcessingMap =  Hazelcast.newHazelcastInstance().getMap('entryProcessingMap')
@@ -49,6 +52,25 @@ class CmHandleStateMonitorSpec extends Specification {
         Hazelcast.shutdownAll()
     }
 
+    def 'Initialise cm handle state monitor: #scenario'() {
+        given: 'the query service returns a list of cm-handle ids for the given state'
+            mockCmHandleQueryService.queryCmHandleIdsByState(_) >> queryResult
+        and: 'a mocked NcmpModelOnboardingFinishedEvent is triggered'
+            def mockNcmpModelOnboardingFinishedEvent = Mock(NcmpInventoryModelOnboardingFinishedEvent)
+        when: 'the method to initialise cm handle state monitor is triggered by onboarding event'
+            objectUnderTest.initialiseCmHandleStateMonitor(mockNcmpModelOnboardingFinishedEvent)
+        then: 'metrics map is called correct number of times for each state except DELETED, with expected value'
+            1 * mockCmHandlesByState.putIfAbsent("advisedCmHandlesCount", expectedValue)
+            1 * mockCmHandlesByState.putIfAbsent("readyCmHandlesCount", expectedValue)
+            1 * mockCmHandlesByState.putIfAbsent("lockedCmHandlesCount", expectedValue)
+            1 * mockCmHandlesByState.putIfAbsent("deletingCmHandlesCount", expectedValue)
+        where:
+            scenario                                 | queryResult                 || expectedValue
+            'query service returns zero cm handle id'| []                          || 0
+            'query service returns 1 cm handle id'   | ['someId']                  || 1
+    }
+
+
     def 'Update cm handle state metric'() {
         given: 'a collection of cm handle state pair'
             def cmHandleTransitionPair = new LcmEventsCmHandleStateHandlerImpl.CmHandleTransitionPair()
@@ -57,19 +79,19 @@ class CmHandleStateMonitorSpec extends Specification {
         when: 'method to update cm handle state metrics is called'
             objectUnderTest.updateCmHandleStateMetrics([cmHandleTransitionPair])
         then: 'cm handle by state cache map is called once for current and target state for entry processing'
-            1 * cmHandlesByState.executeOnKey('advisedCmHandlesCount', _)
-            1 * cmHandlesByState.executeOnKey('readyCmHandlesCount', _)
+            1 * mockCmHandlesByState.executeOnKey('advisedCmHandlesCount', _)
+            1 * mockCmHandlesByState.executeOnKey('readyCmHandlesCount', _)
     }
 
-    def 'Updating cm handle state metric with no previous state'() {
+    def 'Update cm handle state metric with no previous state'() {
         given: 'a collection of cm handle state pair wherein current state is null'
             def cmHandleTransitionPair = new LcmEventsCmHandleStateHandlerImpl.CmHandleTransitionPair()
             cmHandleTransitionPair.currentYangModelCmHandle = new YangModelCmHandle(compositeState: null)
             cmHandleTransitionPair.targetYangModelCmHandle =  new YangModelCmHandle(compositeState: new CompositeState(cmHandleState: ADVISED))
-        when: 'method to update cm handle state metrics is called'
+        when: 'updating cm handle state metrics'
             objectUnderTest.updateCmHandleStateMetrics([cmHandleTransitionPair])
         then: 'cm handle by state cache map is called only once'
-            1 * cmHandlesByState.executeOnKey(_, _)
+            1 * mockCmHandlesByState.executeOnKey(_, _)
     }
 
     def 'Applying decreasing entry processor to a key on map where #scenario'() {
index ffd1d89..dc6ec41 100644 (file)
@@ -30,6 +30,7 @@ import org.onap.cps.api.CpsModuleService
 import org.onap.cps.api.model.Dataspace
 import org.slf4j.LoggerFactory
 import org.springframework.boot.context.event.ApplicationStartedEvent
+import org.springframework.context.ApplicationEventPublisher
 import org.springframework.context.annotation.AnnotationConfigApplicationContext
 import spock.lang.Specification
 
@@ -42,7 +43,8 @@ class InventoryModelLoaderSpec extends Specification {
     def mockCpsModuleService = Mock(CpsModuleService)
     def mockCpsDataService = Mock(CpsDataService)
     def mockCpsAnchorService = Mock(CpsAnchorService)
-    def objectUnderTest = new InventoryModelLoader(mockCpsAdminService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService)
+    def mockApplicationEventPublisher = Mock(ApplicationEventPublisher)
+    def objectUnderTest = new InventoryModelLoader(mockCpsAdminService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockApplicationEventPublisher)
 
     def applicationContext = new AnnotationConfigApplicationContext()
 
@@ -75,6 +77,8 @@ class InventoryModelLoaderSpec extends Specification {
             1 * mockCpsAnchorService.updateAnchorSchemaSet(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, 'dmi-registry-2024-02-23')
         and: 'No schema sets are being removed by the module service (yet)'
             0 * mockCpsModuleService.deleteSchemaSet(NCMP_DATASPACE_NAME, _, _)
+        and: 'application event publisher is called once'
+            1 * mockApplicationEventPublisher.publishEvent(_)
     }
 
 }