Fix the algorithm for device trust level 98/136698/4
authorhalil.cakal <halil.cakal@est.tech>
Wed, 29 Nov 2023 10:03:29 +0000 (10:03 +0000)
committerhalil.cakal <halil.cakal@est.tech>
Wed, 29 Nov 2023 15:01:13 +0000 (15:01 +0000)
- Add new method to handle device trust level changes
  in trust level manager
- Change algorithm to find effective trust level for
  each use case
- Separate responsibilities of each function
- Change device heartbeat consumer accordingly

Issue-ID: CPS-1910
Change-Id: I228f82955b224dc4ebfd1b305082e06aac6008d2
Signed-off-by: halil.cakal <halil.cakal@est.tech>
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManagerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy

index 45aa631..230a370 100644 (file)
@@ -36,7 +36,6 @@ public class DeviceHeartbeatConsumer {
     private static final String CLOUD_EVENT_ID_HEADER_NAME = "ce_id";
     private final TrustLevelManager trustLevelManager;
 
-
     /**
      * Listening the device heartbeats.
      *
@@ -51,8 +50,8 @@ public class DeviceHeartbeatConsumer {
 
         final DeviceTrustLevel deviceTrustLevel =
             CloudEventMapper.toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class);
-
-        trustLevelManager.handleUpdateOfTrustLevels(cmHandleId, deviceTrustLevel.getData().getTrustLevel());
+        final TrustLevel newDeviceTrustLevel = TrustLevel.valueOf(deviceTrustLevel.getData().getTrustLevel());
+        trustLevelManager.handleUpdateOfDeviceTrustLevel(cmHandleId, newDeviceTrustLevel);
 
     }
 
index 4df6bd2..22f18cd 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.trustlevel;
 
+import java.util.Collection;
 import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.impl.events.avc.ncmptoclient.AvcEventPublisher;
+import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 import org.springframework.stereotype.Service;
 
 @Slf4j
@@ -32,10 +36,12 @@ import org.springframework.stereotype.Service;
 public class TrustLevelManager {
 
     private final Map<String, TrustLevel> trustLevelPerCmHandle;
-    private final AvcEventPublisher avcEventPublisher;
-    private static final String NO_OLD_VALUE = null;
-    private static final String CHANGED_ATTRIBUTE_NAME = "trustLevel";
+    private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
 
+    private final InventoryPersistence inventoryPersistence;
+    private final AvcEventPublisher avcEventPublisher;
+    private static final String AVC_CHANGED_ATTRIBUTE_NAME = "trustLevel";
+    private static final String AVC_NO_OLD_VALUE = null;
 
     /**
      * Add cmHandles to the cache and publish notification for initial trust level of cmHandles if it is NONE.
@@ -54,37 +60,72 @@ public class TrustLevelManager {
                 }
                 trustLevelPerCmHandle.put(cmHandleId, initialTrustLevel);
                 if (TrustLevel.NONE.equals(initialTrustLevel)) {
-                    sendAvcNotificationEvent(cmHandleId, NO_OLD_VALUE, initialTrustLevel.name());
+                    avcEventPublisher.publishAvcEvent(cmHandleId,
+                        AVC_CHANGED_ATTRIBUTE_NAME,
+                        AVC_NO_OLD_VALUE,
+                        initialTrustLevel.name());
                 }
             }
         }
     }
 
-
     /**
-     * Update a cmHandle in the cache and publish notification if the trust level is different.
+     * Updates trust level of dmi plugin in the cache and publish notification for trust level of cmHandles if it
+     * has changed.
      *
-     * @param cmHandleId    id of the cmHandle being updated
-     * @param newTrustLevel new trust level of the cmHandle being updated
+     * @param dmiServiceName        dmi service name
+     * @param affectedCmHandleIds   cm handle ids belonging to dmi service name
+     * @param newDmiTrustLevel      new trust level of the dmi plugin
      */
-    public void handleUpdateOfTrustLevels(final String cmHandleId, final String newTrustLevel) {
-        final TrustLevel oldTrustLevel = trustLevelPerCmHandle.get(cmHandleId);
-        if (newTrustLevel.equals(oldTrustLevel.name())) {
-            log.debug("The Cm Handle: {} has already the same trust level: {}", cmHandleId, newTrustLevel);
-        } else {
-            trustLevelPerCmHandle.put(cmHandleId, TrustLevel.valueOf(newTrustLevel));
-            sendAvcNotificationEvent(cmHandleId, oldTrustLevel.name(), newTrustLevel);
-            log.info("The new trust level: {} has been updated for Cm Handle: {}", newTrustLevel, cmHandleId);
+    public void handleUpdateOfDmiTrustLevel(final String dmiServiceName,
+                                            final Collection<String> affectedCmHandleIds,
+                                            final TrustLevel newDmiTrustLevel) {
+        final TrustLevel oldDmiTrustLevel  = trustLevelPerDmiPlugin.get(dmiServiceName);
+        trustLevelPerDmiPlugin.put(dmiServiceName, newDmiTrustLevel);
+        for (final String affectedCmHandleId : affectedCmHandleIds) {
+            final TrustLevel deviceTrustLevel = trustLevelPerCmHandle.get(affectedCmHandleId);
+            final TrustLevel oldEffectiveTrustLevel = deviceTrustLevel.getEffectiveTrustLevel(oldDmiTrustLevel);
+            final TrustLevel newEffectiveTrustLevel = deviceTrustLevel.getEffectiveTrustLevel(newDmiTrustLevel);
+            sendAvcNotificationIfRequired(affectedCmHandleId, oldEffectiveTrustLevel, newEffectiveTrustLevel);
         }
     }
 
-    private void sendAvcNotificationEvent(final String cmHandleId,
-                                          final String oldTrustLevel,
-                                          final String newTrustLevel) {
-        avcEventPublisher.publishAvcEvent(cmHandleId,
-                CHANGED_ATTRIBUTE_NAME,
-                oldTrustLevel,
-                newTrustLevel);
+    /**
+     * Updates trust level of device in the cache and publish notification for trust level of device if it has
+     * changed.
+     *
+     * @param cmHandleId            cm handle id
+     * @param newDeviceTrustLevel   new trust level of the device
+     */
+    public void handleUpdateOfDeviceTrustLevel(final String cmHandleId,
+                                               final TrustLevel newDeviceTrustLevel) {
+        final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
+        final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA);
+
+        final TrustLevel dmiTrustLevel = trustLevelPerDmiPlugin.get(dmiServiceName);
+        final TrustLevel oldDeviceTrustLevel = trustLevelPerCmHandle.get(cmHandleId);
+
+        final TrustLevel oldEffectiveTrustLevel = oldDeviceTrustLevel.getEffectiveTrustLevel(dmiTrustLevel);
+        final TrustLevel newEffectiveTrustLevel = newDeviceTrustLevel.getEffectiveTrustLevel(dmiTrustLevel);
+
+        trustLevelPerCmHandle.put(cmHandleId, newDeviceTrustLevel);
+        sendAvcNotificationIfRequired(cmHandleId, oldEffectiveTrustLevel, newEffectiveTrustLevel);
+    }
+
+    private void sendAvcNotificationIfRequired(final String notificationCandidateCmHandleId,
+                                               final TrustLevel oldEffectiveTrustLevel,
+                                               final TrustLevel newEffectiveTrustLevel) {
+        if (oldEffectiveTrustLevel.equals(newEffectiveTrustLevel)) {
+            log.debug("The Cm Handle: {} has already the same trust level: {}", notificationCandidateCmHandleId,
+                newEffectiveTrustLevel);
+        } else {
+            log.info("The trust level for Cm Handle: {} is now: {} ", notificationCandidateCmHandleId,
+                newEffectiveTrustLevel);
+            avcEventPublisher.publishAvcEvent(notificationCandidateCmHandleId,
+                AVC_CHANGED_ATTRIBUTE_NAME,
+                oldEffectiveTrustLevel.name(),
+                newEffectiveTrustLevel.name());
+        }
     }
 
 }
index 9fd096d..6a272f1 100644 (file)
@@ -38,12 +38,13 @@ public class DmiPluginWatchDog {
 
     private final DmiRestClient dmiRestClient;
     private final NetworkCmProxyDataService networkCmProxyDataService;
+
     private final TrustLevelManager trustLevelManager;
     private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
 
     /**
      * This class monitors the trust level of all DMI plugin by checking the health status
-     * the resulting trustlevel wil be stored in the relevant cache.
+     * the resulting trust level wil be stored in the relevant cache.
      * The @fixedDelayString is the time interval, in milliseconds, between consecutive checks.
      */
     @Scheduled(fixedDelayString = "${ncmp.timers.trust-evel.dmi-availability-watchdog-ms:30000}")
@@ -60,18 +61,12 @@ public class DmiPluginWatchDog {
             } else {
                 newDmiTrustLevel = TrustLevel.NONE;
             }
-
             if (oldDmiTrustLevel.equals(newDmiTrustLevel)) {
-                log.debug("The Dmi Plugin: {} has already the same trust level: {}", dmiServiceName,
-                        newDmiTrustLevel);
+                log.debug("The Dmi Plugin: {} has already the same trust level: {}", dmiServiceName, newDmiTrustLevel);
             } else {
-                trustLevelPerDmiPlugin.put(dmiServiceName, newDmiTrustLevel);
-
-                final Collection<String> notificationCandidateCmHandleIds =
+                final Collection<String> cmHandleIds =
                     networkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier(dmiServiceName);
-                for (final String cmHandleId: notificationCandidateCmHandleIds) {
-                    trustLevelManager.handleUpdateOfTrustLevels(cmHandleId, newDmiTrustLevel.name());
-                }
+                trustLevelManager.handleUpdateOfDmiTrustLevel(dmiServiceName, cmHandleIds, newDmiTrustLevel);
             }
         });
     }
index 8886fc1..42a1c5d 100644 (file)
@@ -26,7 +26,6 @@ import io.cloudevents.core.builder.CloudEventBuilder
 import org.apache.kafka.clients.consumer.ConsumerRecord
 import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel
 import org.onap.cps.utils.JsonObjectMapper
-import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import spock.lang.Specification
 
@@ -37,28 +36,24 @@ class DeviceHeartbeatConsumerSpec extends Specification {
 
     def objectUnderTest = new DeviceHeartbeatConsumer(mockTrustLevelManager)
     def objectMapper = new ObjectMapper()
+    def jsonObjectMapper = new JsonObjectMapper(objectMapper)
 
-    @Autowired
-    JsonObjectMapper jsonObjectMapper
-
-    def static trustLevelString = '{"data":{"trustLevel": "COMPLETE"}}'
-
-    def 'Consume a trustlevel event'() {
+    def 'Consume a trust level event'() {
         given: 'an event from dmi with trust level complete'
-            def payload = jsonObjectMapper.convertJsonString(trustLevelString, DeviceTrustLevel.class)
+            def payload = jsonObjectMapper.convertJsonString('{"data":{"trustLevel": "COMPLETE"}}', DeviceTrustLevel.class)
             def eventFromDmi = createTrustLevelEvent(payload)
         and: 'transformed to a consumer record with a cloud event id (ce_id)'
             def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'sample-message-key', eventFromDmi)
-            consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1'))
+            consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('ch-1'))
         when: 'the event is consumed'
             objectUnderTest.heartbeatListener(consumerRecord)
         then: 'cm handles are stored with correct trust level'
-            1 * mockTrustLevelManager.handleUpdateOfTrustLevels('"cmhandle1"', 'COMPLETE')
+            1 * mockTrustLevelManager.handleUpdateOfDeviceTrustLevel('"ch-1"', TrustLevel.COMPLETE)
     }
 
     def createTrustLevelEvent(eventPayload) {
         return CloudEventBuilder.v1().withData(objectMapper.writeValueAsBytes(eventPayload))
-            .withId("cmhandle1")
+            .withId('ch-1')
             .withSource(URI.create('DMI'))
             .withDataSchema(URI.create('test'))
             .withType('org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel')
index b3559e4..886648a 100644 (file)
 package org.onap.cps.ncmp.api.impl.trustlevel
 
 import org.onap.cps.ncmp.api.impl.events.avc.ncmptoclient.AvcEventPublisher
+import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
 import spock.lang.Specification
 
 class TrustLevelManagerSpec extends Specification {
 
     def trustLevelPerCmHandle = [:]
+    def trustLevelPerDmiPlugin = [:]
+
+    def mockInventoryPersistence = Mock(InventoryPersistence)
     def mockAttributeValueChangeEventPublisher = Mock(AvcEventPublisher)
-    def objectUnderTest = new TrustLevelManager(trustLevelPerCmHandle, mockAttributeValueChangeEventPublisher)
+    def objectUnderTest = new TrustLevelManager(trustLevelPerCmHandle, trustLevelPerDmiPlugin, mockInventoryPersistence, mockAttributeValueChangeEventPublisher)
 
     def 'Initial cm handle registration'() {
         given: 'two cm handles: one with no trust level and one trusted'
@@ -50,24 +55,73 @@ class TrustLevelManagerSpec extends Specification {
             1 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
     }
 
-    def 'Trust level updated'() {
-        given: 'a not trusted cm handle'
-            trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE)
+    def 'Dmi trust level updated'() {
+        given: 'a trusted dmi plugin'
+            trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.COMPLETE)
+        and: 'a trusted cm handle'
+            trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE)
         when: 'the update is handled'
-            objectUnderTest.handleUpdateOfTrustLevels('ch-1', 'COMPLETE')
+            objectUnderTest.handleUpdateOfDmiTrustLevel('my-dmi', ['ch-1'], TrustLevel.NONE)
         then: 'notification is sent'
-            1 * mockAttributeValueChangeEventPublisher.publishAvcEvent('ch-1', 'trustLevel', 'NONE', 'COMPLETE')
-        and: 'the cm handle in the cache is trusted'
-            assert trustLevelPerCmHandle.get('ch-1') == TrustLevel.COMPLETE
+            1 * mockAttributeValueChangeEventPublisher.publishAvcEvent('ch-1', 'trustLevel', 'COMPLETE', 'NONE')
+        and: 'the dmi in the cache is not trusted'
+            assert trustLevelPerDmiPlugin.get('my-dmi') == TrustLevel.NONE
     }
 
-    def 'Trust level updated with same value'() {
-        given: 'a trusted cm handle'
+    def 'Dmi trust level updated with same value'() {
+        given: 'a trusted dmi plugin'
+            trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.COMPLETE)
+        and: 'a trusted cm handle'
             trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE)
         when: 'the update is handled'
-            objectUnderTest.handleUpdateOfTrustLevels('ch-1', 'COMPLETE')
+            objectUnderTest.handleUpdateOfDmiTrustLevel('my-dmi', ['ch-1'], TrustLevel.COMPLETE)
         then: 'no notification is sent'
             0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
+        and: 'the dmi in the cache is trusted'
+            assert trustLevelPerDmiPlugin.get('my-dmi') == TrustLevel.COMPLETE
+    }
+
+    def 'Device trust level updated'() {
+        given: 'a non trusted cm handle'
+            trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE)
+        and: 'a trusted dmi plugin'
+            trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.COMPLETE)
+        and: 'inventory persistence service returns yang model cm handle'
+            mockInventoryPersistence.getYangModelCmHandle('ch-1') >> new YangModelCmHandle(id: 'ch-1', dmiDataServiceName: 'my-dmi')
+        when: 'update of device to COMPLETE trust level handled'
+            objectUnderTest.handleUpdateOfDeviceTrustLevel('ch-1', TrustLevel.COMPLETE)
+        then: 'the cm handle in the cache is trusted'
+            assert trustLevelPerCmHandle.get('ch-1', TrustLevel.COMPLETE)
+        and: 'notification is sent'
+            1 * mockAttributeValueChangeEventPublisher.publishAvcEvent('ch-1', 'trustLevel', 'NONE', 'COMPLETE')
+    }
+
+    def 'Device trust level updated with same value'() {
+        given: 'a non trusted cm handle'
+            trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE)
+        and: 'a trusted dmi plugin'
+            trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.COMPLETE)
+        and: 'inventory persistence service returns yang model cm handle'
+            mockInventoryPersistence.getYangModelCmHandle('ch-1') >> new YangModelCmHandle(id: 'ch-1', dmiDataServiceName: 'my-dmi')
+        when: 'update of device trust to the same level (NONE)'
+            objectUnderTest.handleUpdateOfDeviceTrustLevel('ch-1', TrustLevel.NONE)
+        then: 'the cm handle in the cache is not trusted'
+            assert trustLevelPerCmHandle.get('ch-1', TrustLevel.NONE)
+        and: 'no notification is sent'
+            0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
+    }
+
+    def 'Dmi trust level restored to complete with non trusted device'() {
+        given: 'a non trusted dmi'
+            trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.NONE)
+        and: 'a non trusted device'
+            trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE)
+        when: 'restore the dmi trust level to COMPLETE'
+            objectUnderTest.handleUpdateOfDmiTrustLevel('my-dmi', ['ch-1'], TrustLevel.COMPLETE)
+        then: 'the cm handle in the cache is still NONE'
+            assert trustLevelPerCmHandle.get('ch-1') == TrustLevel.NONE
+        and: 'no notification is sent'
+            0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
     }
 
 }
index ec1d8e8..446126b 100644 (file)
@@ -44,21 +44,17 @@ class DmiPluginWatchDogSpec extends Specification {
             trustLevelPerDmiPlugin.put('dmi-1', dmiOldTrustLevel)
         and: 'dmi client returns health status #dmiHealhStatus'
             mockDmiRestClient.getDmiHealthStatus('dmi-1') >> dmiHealhStatus
-        and: 'network cm proxy data returns a list of all cm handle ids belonging to a dmi'
-            mockNetworkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier('dmi-1') >> ['ch-1']
         when: 'dmi watch dog method runs'
             objectUnderTest.checkDmiAvailability()
-        then: 'the result is as expected'
-            assert trustLevelPerDmiPlugin.get('dmi-1') == newDmiTrustLevel
-        and: 'the update delegated to manager'
-            times * mockTrustLevelManager.handleUpdateOfTrustLevels(*_)
+        then: 'the update delegated to manager'
+            numberOfCalls * mockTrustLevelManager.handleUpdateOfDmiTrustLevel('dmi-1', _, newDmiTrustLevel)
         where: 'the following parameters are used'
-            dmiHealhStatus | dmiOldTrustLevel    || newDmiTrustLevel    || times
-            'UP'           | TrustLevel.COMPLETE || TrustLevel.COMPLETE || 0
-            'DOWN'         | TrustLevel.COMPLETE || TrustLevel.NONE     || 1
-            'DOWN'         | TrustLevel.NONE     || TrustLevel.NONE     || 0
-            'UP'           | TrustLevel.NONE     || TrustLevel.COMPLETE || 1
-            ''             | TrustLevel.COMPLETE || TrustLevel.NONE     || 1
+            dmiHealhStatus | dmiOldTrustLevel    | newDmiTrustLevel    || numberOfCalls
+            'UP'           | TrustLevel.COMPLETE | TrustLevel.COMPLETE || 0
+            'DOWN'         | TrustLevel.COMPLETE | TrustLevel.NONE     || 1
+            'DOWN'         | TrustLevel.NONE     | TrustLevel.NONE     || 0
+            'UP'           | TrustLevel.NONE     | TrustLevel.COMPLETE || 1
+            ''             | TrustLevel.COMPLETE | TrustLevel.NONE     || 1
     }
 
 }