From: halil.cakal Date: Wed, 29 Nov 2023 10:03:29 +0000 (+0000) Subject: Fix the algorithm for device trust level X-Git-Tag: 3.4.1~24^2 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=a36b3f28624a14da75eb5ea1cd0bd677ebce325c;p=cps.git Fix the algorithm for device trust level - 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 --- diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java index 45aa631b1..230a370d6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java @@ -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); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java index 4df6bd237..22f18cd24 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java @@ -20,10 +20,14 @@ 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 trustLevelPerCmHandle; - private final AvcEventPublisher avcEventPublisher; - private static final String NO_OLD_VALUE = null; - private static final String CHANGED_ATTRIBUTE_NAME = "trustLevel"; + private final Map 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 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()); + } } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java index 9fd096dae..6a272f1c6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java @@ -38,12 +38,13 @@ public class DmiPluginWatchDog { private final DmiRestClient dmiRestClient; private final NetworkCmProxyDataService networkCmProxyDataService; + private final TrustLevelManager trustLevelManager; private final Map 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 notificationCandidateCmHandleIds = + final Collection cmHandleIds = networkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier(dmiServiceName); - for (final String cmHandleId: notificationCandidateCmHandleIds) { - trustLevelManager.handleUpdateOfTrustLevels(cmHandleId, newDmiTrustLevel.name()); - } + trustLevelManager.handleUpdateOfDmiTrustLevel(dmiServiceName, cmHandleIds, newDmiTrustLevel); } }); } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy index 8886fc189..42a1c5d5a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy @@ -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('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') diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManagerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManagerSpec.groovy index b3559e41e..886648a7e 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManagerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManagerSpec.groovy @@ -21,13 +21,18 @@ 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(*_) } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy index ec1d8e8f3..446126be9 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy @@ -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 } }