diff options
author | Toine Siebelink <toine.siebelink@est.tech> | 2023-11-29 08:34:03 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2023-11-29 08:34:03 +0000 |
commit | 626547adfd590e5a7cfe98bfaf68fcefb00be9af (patch) | |
tree | 69dcc0b3de4ee0815eb978f6bdb57464e068c9a0 | |
parent | 0fafbf504b52c78d1d09b71234957f7df0696fa1 (diff) | |
parent | 2e71615fa27cefd1aee3fb85d04907f3d95d5d14 (diff) |
Merge "Publish trust level notification event"
19 files changed, 454 insertions, 127 deletions
diff --git a/cps-ncmp-events/src/main/resources/schemas/ncmpdataavc/attribute-value-change-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/ncmpdataavc/avc-event-schema-1.0.0.json index 9dee00e21e..d24ec2c737 100644 --- a/cps-ncmp-events/src/main/resources/schemas/ncmpdataavc/attribute-value-change-event-schema-1.0.0.json +++ b/cps-ncmp-events/src/main/resources/schemas/ncmpdataavc/avc-event-schema-1.0.0.json @@ -1,10 +1,10 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "urn:cps:org.onap.cps.ncmp.events:attribute-value-change-event-schema:1.0.0", - "$ref": "#/definitions/AttributeValueChangeEvent", + "$id": "urn:cps:org.onap.cps.ncmp.events:avc-event-schema:1.0.0", + "$ref": "#/definitions/AvcEvent", "definitions": { - "AttributeValueChangeEvent": { - "javaType": "org.onap.cps.ncmp.events.avc.ncmp_to_client.AttributeValueChangeEvent", + "AvcEvent": { + "javaType": "org.onap.cps.ncmp.events.avc.ncmp_to_client.AvcEvent", "type": "object", "additionalProperties": false, "properties": { @@ -15,7 +15,7 @@ "required": [ "data" ], - "title": "AttributeValueChangeEvent" + "title": "AvcEvent" }, "Data": { "type": "object", @@ -24,16 +24,16 @@ "attributeValueChange": { "type": "array", "items": { - "$ref": "#/definitions/AttributeValueChange" + "$ref": "#/definitions/Avc" } } }, "required": [ - "attributeValueChange" + "avc" ], "title": "Data" }, - "AttributeValueChange": { + "Avc": { "type": "object", "additionalProperties": false, "properties": { @@ -50,7 +50,7 @@ "required": [ "attributeName" ], - "title": "AttributeValueChange" + "title": "avc" } } }
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index f0336900f9..1afe5c7114 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -64,6 +64,7 @@ import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleOperationsUtils; import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; import org.onap.cps.ncmp.api.impl.operations.OperationType; import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager; import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions; import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; @@ -101,8 +102,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler; private final CpsDataService cpsDataService; private final IMap<String, Object> moduleSyncStartedOnCmHandles; - private final Map<String, TrustLevel> trustLevelPerCmHandle; private final Map<String, TrustLevel> trustLevelPerDmiPlugin; + private final TrustLevelManager trustLevelManager; @Override public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( @@ -120,7 +121,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) { dmiPluginRegistrationResponse.setCreatedCmHandles( parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)); - populateTrustLevelPerCmHandleCache(dmiPluginRegistration); } if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) { dmiPluginRegistrationResponse.setUpdatedCmHandles( @@ -318,17 +318,21 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ public List<CmHandleRegistrationResponse> parseAndProcessCreatedCmHandlesInRegistration( final DmiPluginRegistration dmiPluginRegistration) { - final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>(); - dmiPluginRegistration.getCreatedCmHandles().forEach(cmHandle -> { - final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle( - dmiPluginRegistration.getDmiPlugin(), - dmiPluginRegistration.getDmiDataPlugin(), - dmiPluginRegistration.getDmiModelPlugin(), - cmHandle, - cmHandle.getModuleSetTag()); - cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.ADVISED); - }); - return registerNewCmHandles(cmHandleStatePerCmHandle); + final List<NcmpServiceCmHandle> cmHandlesToBeCreated = dmiPluginRegistration.getCreatedCmHandles(); + final Map<String, TrustLevel> initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size()); + final List<YangModelCmHandle> yangModelCmHandles = new ArrayList<>(cmHandlesToBeCreated.size()); + cmHandlesToBeCreated + .forEach(cmHandle -> { + final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle( + dmiPluginRegistration.getDmiPlugin(), + dmiPluginRegistration.getDmiDataPlugin(), + dmiPluginRegistration.getDmiModelPlugin(), + cmHandle, + cmHandle.getModuleSetTag()); + yangModelCmHandles.add(yangModelCmHandle); + initialTrustLevelPerCmHandleId.put(cmHandle.getCmHandleId(), cmHandle.getRegistrationTrustLevel()); + }); + return registerNewCmHandles(yangModelCmHandles, initialTrustLevelPerCmHandleId); } protected List<CmHandleRegistrationResponse> parseAndProcessDeletedCmHandlesInRegistration( @@ -470,11 +474,13 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } - private List<CmHandleRegistrationResponse> registerNewCmHandles(final Map<YangModelCmHandle, CmHandleState> - cmHandleStatePerCmHandle) { - final List<String> cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); + private List<CmHandleRegistrationResponse> registerNewCmHandles(final List<YangModelCmHandle> yangModelCmHandles, + final Map<String, TrustLevel> + initialTrustLevelPerCmHandleId) { + final Set<String> cmHandleIds = initialTrustLevelPerCmHandleId.keySet(); try { - lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); + lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandles); + trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId); return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds); } catch (final AlreadyDefinedException alreadyDefinedException) { return CmHandleRegistrationResponse.createFailureResponses( @@ -512,20 +518,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId).toList(); } - private void populateTrustLevelPerCmHandleCache(final DmiPluginRegistration dmiPluginRegistration) { - for (final NcmpServiceCmHandle cmHandle: dmiPluginRegistration.getCreatedCmHandles()) { - if (cmHandle.getRegistrationTrustLevel() == null) { - if (trustLevelPerCmHandle.containsKey(cmHandle.getCmHandleId())) { - log.warn("CmHandle : {}, Already exists, Initial trustLevel ignored.", cmHandle.getCmHandleId()); - } else { - trustLevelPerCmHandle.put(cmHandle.getCmHandleId(), TrustLevel.COMPLETE); - } - } else { - trustLevelPerCmHandle.put(cmHandle.getCmHandleId(), cmHandle.getRegistrationTrustLevel()); - } - } - } - private void setTrustLevelPerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) { if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) { trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiPlugin(), TrustLevel.COMPLETE); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java index b6eb092182..5b93eb4853 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java @@ -41,7 +41,7 @@ import org.springframework.web.client.RestTemplate; public class DmiRestClient { private static final String HEALTH_CHECK_URL_EXTENSION = "/actuator/health"; - private static final String EMPTY_STRING = ""; + private static final String NOT_SPECIFIED = ""; private final RestTemplate restTemplate; private final DmiProperties dmiProperties; @@ -70,19 +70,19 @@ public class DmiRestClient { * Get DMI plugin health status. * * @param dmiPluginBaseUrl the base URL of the dmi-plugin - * @return plugin health status ("UP" is all OK, EMPTY_STRING in case of any exception) + * @return plugin health status ("UP" is all OK, "" (not-specified) in case of any exception) */ public String getDmiHealthStatus(final String dmiPluginBaseUrl) { final HttpEntity<Object> httpHeaders = new HttpEntity<>(configureHttpHeaders(new HttpHeaders())); try { final JsonNode responseHealthStatus = restTemplate.getForObject(dmiPluginBaseUrl + HEALTH_CHECK_URL_EXTENSION, - JsonNode.class, httpHeaders); - return responseHealthStatus == null ? EMPTY_STRING : + JsonNode.class, httpHeaders); + return responseHealthStatus == null ? NOT_SPECIFIED : responseHealthStatus.get("status").asText(); } catch (final Exception e) { log.warn("Failed to retrieve health status from {}. Error Message: {}", dmiPluginBaseUrl, e.getMessage()); - return EMPTY_STRING; + return NOT_SPECIFIED; } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avc/ncmptoclient/AvcEventPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avc/ncmptoclient/AvcEventPublisher.java new file mode 100644 index 0000000000..cba1f76ff7 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avc/ncmptoclient/AvcEventPublisher.java @@ -0,0 +1,82 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.avc.ncmptoclient; + +import io.cloudevents.CloudEvent; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.impl.events.EventsPublisher; +import org.onap.cps.ncmp.api.impl.events.NcmpCloudEventBuilder; +import org.onap.cps.ncmp.events.avc.ncmp_to_client.Avc; +import org.onap.cps.ncmp.events.avc.ncmp_to_client.AvcEvent; +import org.onap.cps.ncmp.events.avc.ncmp_to_client.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AvcEventPublisher { + + private final EventsPublisher<CloudEvent> eventsPublisher; + + @Value("${app.ncmp.avc.cm-events-topic}") + private String avcTopic; + + /** + * Publish attribute value change event. + * + * @param eventKey id of the cmHandle being registered + */ + public void publishAvcEvent(final String eventKey, final String attributeName, + final String oldAttributeValue, final String newAttributeValue) { + final AvcEvent avcEvent = buildAvcEvent(attributeName, oldAttributeValue, newAttributeValue); + + final Map<String, String> extensions = createAvcEventExtensions(eventKey); + final CloudEvent avcCloudEvent = + NcmpCloudEventBuilder.builder().type(AvcEvent.class.getTypeName()) + .event(avcEvent).extensions(extensions).setCloudEvent().build(); + + eventsPublisher.publishCloudEvent(avcTopic, eventKey, avcCloudEvent); + } + + private AvcEvent buildAvcEvent(final String attributeName, + final String oldAttributeValue, + final String newAttributeValue) { + final Avc avc = new Avc(); + avc.setAttributeName(attributeName); + avc.setOldAttributeValue(oldAttributeValue); + avc.setNewAttributeValue(newAttributeValue); + + final Data payload = new Data(); + payload.setAttributeValueChange(Collections.singletonList(avc)); + final AvcEvent avcEvent = new AvcEvent(); + avcEvent.setData(payload); + return avcEvent; + } + + private Map<String, String> createAvcEventExtensions(final String eventKey) { + final Map<String, String> extensions = new HashMap<>(); + extensions.put("correlationid", eventKey); + return extensions; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandler.java index fdee28e337..8274772d2c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandler.java @@ -20,6 +20,7 @@ package org.onap.cps.ncmp.api.impl.events.lcm; +import java.util.Collection; import java.util.Map; import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; @@ -45,4 +46,10 @@ public interface LcmEventsCmHandleStateHandler { */ void updateCmHandleStateBatch(final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle); + /** + * Sets the initial state of cmHandles to ADVISED. + * + * @param yangModelCmHandles List of Yang Model Cm Handle. + */ + void initiateStateAdvised(Collection<YangModelCmHandle> yangModelCmHandles); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java index 02368b8574..a31332f094 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java @@ -28,6 +28,7 @@ import static org.onap.cps.ncmp.api.impl.inventory.CmHandleState.READY; import io.micrometer.core.annotation.Timed; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -82,6 +83,15 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState lcmEventsCmHandleStateHandlerAsyncHelper.publishLcmEventBatchAsynchronously(cmHandleTransitionPairs); } + @Override + public void initiateStateAdvised(final Collection<YangModelCmHandle> yangModelCmHandles) { + final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>(yangModelCmHandles.size()); + for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) { + cmHandleStatePerCmHandle.put(yangModelCmHandle, ADVISED); + } + updateCmHandleStateBatch(cmHandleStatePerCmHandle); + } + private Collection<CmHandleTransitionPair> prepareCmHandleTransitionBatch( final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle) { final List<CmHandleTransitionPair> cmHandleTransitionPairs = new ArrayList<>(cmHandleStatePerCmHandle.size()); 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 b6d74d980f..45aa631b12 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 @@ -22,22 +22,20 @@ package org.onap.cps.ncmp.api.impl.trustlevel; import io.cloudevents.CloudEvent; import io.cloudevents.kafka.impl.KafkaHeaders; -import java.util.Map; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper; import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; -@Slf4j @Component @RequiredArgsConstructor public class DeviceHeartbeatConsumer { private static final String CLOUD_EVENT_ID_HEADER_NAME = "ce_id"; - private final Map<String, TrustLevel> trustLevelPerCmHandle; + private final TrustLevelManager trustLevelManager; + /** * Listening the device heartbeats. @@ -45,20 +43,17 @@ public class DeviceHeartbeatConsumer { * @param deviceHeartbeatConsumerRecord Device Heartbeat record. */ @KafkaListener(topics = "${app.dmi.device-heartbeat.topic}", - containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") + containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") public void heartbeatListener(final ConsumerRecord<String, CloudEvent> deviceHeartbeatConsumerRecord) { final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(), - CLOUD_EVENT_ID_HEADER_NAME); + CLOUD_EVENT_ID_HEADER_NAME); final DeviceTrustLevel deviceTrustLevel = - CloudEventMapper.toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class); + CloudEventMapper.toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class); + + trustLevelManager.handleUpdateOfTrustLevels(cmHandleId, deviceTrustLevel.getData().getTrustLevel()); - if (cmHandleId != null && deviceTrustLevel != null) { - final String trustLevel = deviceTrustLevel.getData().getTrustLevel(); - trustLevelPerCmHandle.put(cmHandleId, TrustLevel.valueOf(trustLevel)); - log.debug("Added cmHandleId to trustLevelPerCmHandle map as {}:{}", cmHandleId, trustLevel); - } } } 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 new file mode 100644 index 0000000000..4df6bd237a --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java @@ -0,0 +1,90 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.trustlevel; + +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.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +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"; + + + /** + * Add cmHandles to the cache and publish notification for initial trust level of cmHandles if it is NONE. + * + * @param cmHandlesToBeCreated a list of cmHandles being created + */ + public void handleInitialRegistrationOfTrustLevels(final Map<String, TrustLevel> cmHandlesToBeCreated) { + for (final Map.Entry<String, TrustLevel> entry : cmHandlesToBeCreated.entrySet()) { + final String cmHandleId = entry.getKey(); + if (trustLevelPerCmHandle.containsKey(cmHandleId)) { + log.warn("Cm handle: {} already registered", cmHandleId); + } else { + TrustLevel initialTrustLevel = entry.getValue(); + if (initialTrustLevel == null) { + initialTrustLevel = TrustLevel.COMPLETE; + } + trustLevelPerCmHandle.put(cmHandleId, initialTrustLevel); + if (TrustLevel.NONE.equals(initialTrustLevel)) { + sendAvcNotificationEvent(cmHandleId, NO_OLD_VALUE, initialTrustLevel.name()); + } + } + } + } + + + /** + * Update a cmHandle in the cache and publish notification if the trust level is different. + * + * @param cmHandleId id of the cmHandle being updated + * @param newTrustLevel new trust level of the cmHandle being updated + */ + 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); + } + } + + private void sendAvcNotificationEvent(final String cmHandleId, + final String oldTrustLevel, + final String newTrustLevel) { + avcEventPublisher.publishAvcEvent(cmHandleId, + CHANGED_ATTRIBUTE_NAME, + oldTrustLevel, + newTrustLevel); + } + +} 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 b073f1bc3a..9fd096daea 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 @@ -20,11 +20,14 @@ package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability; +import java.util.Collection; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -34,6 +37,8 @@ import org.springframework.stereotype.Service; public class DmiPluginWatchDog { private final DmiRestClient dmiRestClient; + private final NetworkCmProxyDataService networkCmProxyDataService; + private final TrustLevelManager trustLevelManager; private final Map<String, TrustLevel> trustLevelPerDmiPlugin; /** @@ -42,15 +47,36 @@ public class DmiPluginWatchDog { * The @fixedDelayString is the time interval, in milliseconds, between consecutive checks. */ @Scheduled(fixedDelayString = "${ncmp.timers.trust-evel.dmi-availability-watchdog-ms:30000}") - public void watchDmiPluginTrustLevel() { - trustLevelPerDmiPlugin.keySet().forEach(dmiKey -> { - final String dmiHealthStatus = dmiRestClient.getDmiHealthStatus(dmiKey); + public void checkDmiAvailability() { + trustLevelPerDmiPlugin.entrySet().forEach(entry -> { + final TrustLevel newDmiTrustLevel; + final TrustLevel oldDmiTrustLevel = entry.getValue(); + final String dmiServiceName = entry.getKey(); + final String dmiHealthStatus = getDmiHealthStatus(dmiServiceName); + log.debug("The health status for dmi-plugin: {} is {}", dmiServiceName, dmiHealthStatus); + if ("UP".equals(dmiHealthStatus)) { - trustLevelPerDmiPlugin.put(dmiKey, TrustLevel.COMPLETE); + newDmiTrustLevel = TrustLevel.COMPLETE; + } else { + newDmiTrustLevel = TrustLevel.NONE; + } + + if (oldDmiTrustLevel.equals(newDmiTrustLevel)) { + log.debug("The Dmi Plugin: {} has already the same trust level: {}", dmiServiceName, + newDmiTrustLevel); } else { - trustLevelPerDmiPlugin.put(dmiKey, TrustLevel.NONE); + trustLevelPerDmiPlugin.put(dmiServiceName, newDmiTrustLevel); + + final Collection<String> notificationCandidateCmHandleIds = + networkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier(dmiServiceName); + for (final String cmHandleId: notificationCandidateCmHandleIds) { + trustLevelManager.handleUpdateOfTrustLevels(cmHandleId, newDmiTrustLevel.name()); + } } }); } + private String getDmiHealthStatus(final String dmiServiceName) { + return dmiRestClient.getDmiHealthStatus(dmiServiceName); + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java index b0aed57630..82283228a1 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java @@ -116,7 +116,7 @@ public class CmHandleRegistrationResponse { .status(Status.SUCCESS).build(); } - public static List<CmHandleRegistrationResponse> createSuccessResponses(final List<String> cmHandleIds) { + public static List<CmHandleRegistrationResponse> createSuccessResponses(final Collection<String> cmHandleIds) { return cmHandleIds.stream().map(CmHandleRegistrationResponse::createSuccessResponse) .collect(Collectors.toList()); } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy index 9f15e1fa56..97693a4154 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy @@ -21,6 +21,7 @@ package org.onap.cps.ncmp.api.impl +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager import org.onap.cps.ncmp.api.models.UpgradedCmHandles import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND @@ -68,8 +69,8 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler) def mockCpsDataService = Mock(CpsDataService) def mockModuleSyncStartedOnCmHandles = Mock(IMap<String, Object>) - def trustLevelPerCmHandle = [:] def trustLevelPerDmiPlugin = [:] + def mockTrustLevelManager = Mock(TrustLevelManager) def objectUnderTest = getObjectUnderTest() def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() { @@ -208,14 +209,12 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { assert it.cmHandle == 'cmhandle' } and: 'state handler is invoked with the expected parameters' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { + 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> { args -> { def cmHandleStatePerCmHandle = (args[0] as Map) cmHandleStatePerCmHandle.each { - assert (it.key.id == 'cmhandle' - && it.key.dmiServiceName == 'my-server' - && it.value == CmHandleState.ADVISED) + assert (it.id == 'cmhandle' && it.dmiServiceName == 'my-server') } } } @@ -237,12 +236,12 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { then: 'a successful response is received' assert response.createdCmHandles.size() == expectedNumberOfCreatedCmHandles and: 'trustLevel is set for the created cm-handle' - assert trustLevelPerCmHandle.get(cmHandleId) == expectedTrustLevel + 1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(_) where: - scenario | cmHandleId | registrationTrustLevel || expectedNumberOfCreatedCmHandles | expectedTrustLevel - 'new cmHandleId and trustLevel' | 'ch-new' | TrustLevel.COMPLETE || 2 | TrustLevel.COMPLETE - 'existing cmHandleId with null trustLevel' | 'ch-1' | null || 1 | TrustLevel.NONE - 'cmHandleId with null trustLevel' | 'ch-new' | null || 2 | TrustLevel.COMPLETE + scenario | cmHandleId | registrationTrustLevel || expectedNumberOfCreatedCmHandles + 'new trusted cm handle' | 'ch-new' | TrustLevel.COMPLETE || 2 + 'existing cm handle without trust level' | 'ch-1' | null || 1 + 'new cm handle without trust level' | 'ch-new' | null || 2 } def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed with some failures'() { @@ -253,7 +252,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { new NcmpServiceCmHandle(cmHandleId: 'cmhandle3')]) and: 'cm-handle creation is successful for 1st and 3rd; failed for 2nd' def xpath = "somePathWithId[@id='cmhandle2']" - mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(*_) >> { throw AlreadyDefinedException.forDataNodes([xpath], 'some-context') } + mockLcmEventsCmHandleStateHandler.initiateStateAdvised(*_) >> { throw AlreadyDefinedException.forDataNodes([xpath], 'some-context') } when: 'registration is updated to create cm-handles' def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'a response is received for all cm-handles' @@ -272,7 +271,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle')] and: 'cm-handler registration fails: #scenario' - mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(*_) >> { throw exception } + mockLcmEventsCmHandleStateHandler.initiateStateAdvised(*_) >> { throw exception } when: 'registration is updated' def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'a failure response is received' @@ -439,7 +438,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations, mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries, stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService, - mockModuleSyncStartedOnCmHandles, trustLevelPerCmHandle, trustLevelPerDmiPlugin)) + mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager)) } def addPersistedYangModelCmHandles(ids) { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 71511cc161..4f7b726985 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -22,7 +22,6 @@ */ package org.onap.cps.ncmp.api.impl - import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR @@ -36,6 +35,7 @@ import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries import org.onap.cps.ncmp.api.impl.inventory.CmHandleState @@ -76,8 +76,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService) def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler) def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>) - def stubTrustLevelPerCmHandle = Stub(Map<String, TrustLevel>) def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>) + def mockTrustLevelManager = Mock(TrustLevelManager) def NO_TOPIC = null def NO_REQUEST_ID = null @@ -96,8 +96,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { mockLcmEventsCmHandleStateHandler, mockCpsDataService, stubModuleSyncStartedOnCmHandles, - stubTrustLevelPerCmHandle, - stubTrustLevelPerDmiPlugin) + stubTrustLevelPerDmiPlugin, + mockTrustLevelManager) def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']" @@ -265,11 +265,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { when: 'parse and create cm handle in dmi registration then sync module' objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration) then: 'system persists the cm handle state' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { + 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> { args -> { - def cmHandleStatePerCmHandle = (args[0] as Map) + def cmHandleStatePerCmHandle = (args[0] as Collection) cmHandleStatePerCmHandle.each { - assert it.key.id == 'test-cm-handle-id' && it.value == CmHandleState.ADVISED + assert it.id == 'test-cm-handle-id' } } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy index c9ba5645fb..0176de7147 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy @@ -96,7 +96,7 @@ class DmiRestClientSpec extends Specification { mockRestTemplate.getForObject(*_) >> {jsonNode} when: 'get trust level of the dmi plugin' def result = objectUnderTest.getDmiHealthStatus('some url') - then: 'the correct trust level is returned' + then: 'the status value from the json is return' assert result == 'my status' } @@ -105,9 +105,9 @@ class DmiRestClientSpec extends Specification { mockRestTemplate.getForObject(*_) >> healthStatusResponse when: 'attempt to get health status of the dmi plugin' def result = objectUnderTest.getDmiHealthStatus('some url') - then: 'result will be EMPTY_STRING "" ' + then: 'result will be empty' assert result == '' - where: 'the following values are used' + where: 'the following responses are used' scenario | healthStatusResponse 'null' | null 'exception' | {throw new Exception()} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/ncmptoclient/AvcEventPublisherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/ncmptoclient/AvcEventPublisherSpec.groovy new file mode 100644 index 0000000000..a614fd2a1d --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/ncmptoclient/AvcEventPublisherSpec.groovy @@ -0,0 +1,63 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2023 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.avc.ncmptoclient + +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import org.onap.cps.ncmp.api.impl.events.EventsPublisher +import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext +import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec +import org.onap.cps.ncmp.events.avc.ncmp_to_client.Avc +import org.onap.cps.ncmp.events.avc.ncmp_to_client.AvcEvent +import org.onap.cps.utils.JsonObjectMapper +import org.springframework.test.context.ContextConfiguration + +import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent + +@ContextConfiguration(classes = [CpsApplicationContext, ObjectMapper, JsonObjectMapper]) +class AvcEventPublisherSpec extends MessagingBaseSpec { + + def mockEventsPublisher = Mock(EventsPublisher<CloudEvent>) + def objectUnderTest = new AvcEventPublisher(mockEventsPublisher) + + def 'Publish an attribute value change event'() { + given: 'the event key' + def someEventKey = 'someEventKey' + and: 'the name of the attribute being changed' + def someAttributeName = 'someAttributeName' + and: 'the old value of the attribute' + def someOldAttributeValue = 'someOldAttributeValue' + and: 'the new value of the attribute' + def someNewAttributeValue = 'someNewAttributeValue' + when: 'an attribute value change event is published' + objectUnderTest.publishAvcEvent(someEventKey, someAttributeName, someOldAttributeValue, someNewAttributeValue) + then: 'the cloud event publisher is invoked with the correct data' + 1 * mockEventsPublisher.publishCloudEvent(_, someEventKey, + cloudEvent -> { + def actualAvcs = toTargetEvent(cloudEvent, AvcEvent.class).data.attributeValueChange + def expectedAvc = new Avc(attributeName: someAttributeName, + oldAttributeValue: someOldAttributeValue, + newAttributeValue: someNewAttributeValue) + assert actualAvcs == [expectedAvc] + }) + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy index 0ec73a26a4..3ae95209f2 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy @@ -169,9 +169,9 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { def 'Batch of new cm handles provided'() { given: 'A batch of new cm handles' - def cmHandleStateMap = setupBatch('NEW') - when: 'updating a batch of changes' - objectUnderTest.updateCmHandleStateBatch(cmHandleStateMap) + def yangModelCmHandlesToBeCreated = setupBatch('NEW') + when: 'instantiating a batch of new cm handles' + objectUnderTest.initiateStateAdvised(yangModelCmHandlesToBeCreated) then: 'new cm handles are saved using inventory persistence' 1 * mockInventoryPersistence.saveCmHandleBatch(_) >> { args -> { @@ -180,7 +180,6 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { } and: 'event service is called to publish events' 2 * mockLcmEventsService.publishLcmEvent(_, _, _) - } def 'Batch of existing cm handles is updated'() { @@ -219,7 +218,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { def yangModelCmHandle2 = new YangModelCmHandle(id: 'cmhandle2', dmiProperties: [], publicProperties: []) if ('NEW' == type) { - return [(yangModelCmHandle1): ADVISED, (yangModelCmHandle2): ADVISED] + return [yangModelCmHandle1, yangModelCmHandle2] } if ('DELETED' == type) { 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 80778b9c71..8886fc189f 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 @@ -21,7 +21,6 @@ package org.onap.cps.ncmp.api.impl.trustlevel import com.fasterxml.jackson.databind.ObjectMapper -import com.hazelcast.map.IMap import io.cloudevents.CloudEvent import io.cloudevents.core.builder.CloudEventBuilder import org.apache.kafka.clients.consumer.ConsumerRecord @@ -34,9 +33,9 @@ import spock.lang.Specification @SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) class DeviceHeartbeatConsumerSpec extends Specification { - def mockTrustLevelPerCmHandle = Mock(Map<String, TrustLevel>) + def mockTrustLevelManager = Mock(TrustLevelManager) - def objectUnderTest = new DeviceHeartbeatConsumer(mockTrustLevelPerCmHandle) + def objectUnderTest = new DeviceHeartbeatConsumer(mockTrustLevelManager) def objectMapper = new ObjectMapper() @Autowired @@ -54,29 +53,7 @@ class DeviceHeartbeatConsumerSpec extends Specification { when: 'the event is consumed' objectUnderTest.heartbeatListener(consumerRecord) then: 'cm handles are stored with correct trust level' - 1 * mockTrustLevelPerCmHandle.put('"cmhandle1"', TrustLevel.COMPLETE) - } - - def 'Consume trustlevel event without cloud event id'() { - given: 'an event from dmi' - def payload = jsonObjectMapper.convertJsonString(trustLevelString, DeviceTrustLevel.class) - def eventFromDmi = createTrustLevelEvent(payload) - and: 'transformed to a consumer record WITHOUT Cloud event ID (ce_id)' - def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'sample-message-key', eventFromDmi) - when: 'the event is consumed' - objectUnderTest.heartbeatListener(consumerRecord) - then: 'no cm handle has been stored in the map' - 0 * mockTrustLevelPerCmHandle.put(*_) - } - - def 'Consume a trust level event without payload'() { - given: 'a consumer record with ce_id header but without payload' - def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'cmhandle1', createTrustLevelEvent(null)) - consumerRecord.headers().add('some_other_header_value', objectMapper.writeValueAsBytes('cmhandle1')) - when: 'the event is consumed' - objectUnderTest.heartbeatListener(consumerRecord) - then: 'no cm handle has been stored in the map' - 0 * mockTrustLevelPerCmHandle.put(*_) + 1 * mockTrustLevelManager.handleUpdateOfTrustLevels('"cmhandle1"', 'COMPLETE') } def createTrustLevelEvent(eventPayload) { 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 new file mode 100644 index 0000000000..b3559e41ee --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManagerSpec.groovy @@ -0,0 +1,73 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.trustlevel + +import org.onap.cps.ncmp.api.impl.events.avc.ncmptoclient.AvcEventPublisher +import spock.lang.Specification + +class TrustLevelManagerSpec extends Specification { + + def trustLevelPerCmHandle = [:] + def mockAttributeValueChangeEventPublisher = Mock(AvcEventPublisher) + def objectUnderTest = new TrustLevelManager(trustLevelPerCmHandle, mockAttributeValueChangeEventPublisher) + + def 'Initial cm handle registration'() { + given: 'two cm handles: one with no trust level and one trusted' + def cmHandleModelsToBeCreated = ['ch-1': null, 'ch-2': TrustLevel.COMPLETE] + when: 'the initial registration handled' + objectUnderTest.handleInitialRegistrationOfTrustLevels(cmHandleModelsToBeCreated) + then: 'no notification sent' + 0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) + and: 'both cm handles are in the cache and are trusted' + assert trustLevelPerCmHandle.get('ch-1') == TrustLevel.COMPLETE + assert trustLevelPerCmHandle.get('ch-2') == TrustLevel.COMPLETE + } + + def 'Initial cm handle registration with a cm handle that is not trusted'() { + given: 'a not trusted cm handle' + def cmHandleModelsToBeCreated = ['ch-2': TrustLevel.NONE] + when: 'the initial registration handled' + objectUnderTest.handleInitialRegistrationOfTrustLevels(cmHandleModelsToBeCreated) + then: 'notification is sent' + 1 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) + } + + def 'Trust level updated'() { + given: 'a not trusted cm handle' + trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE) + when: 'the update is handled' + objectUnderTest.handleUpdateOfTrustLevels('ch-1', 'COMPLETE') + 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 + } + + def 'Trust level updated with same value'() { + given: 'a trusted cm handle' + trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE) + when: 'the update is handled' + objectUnderTest.handleUpdateOfTrustLevels('ch-1', 'COMPLETE') + then: 'no notification is sent' + 0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelTest.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelSpec.groovy index 9971f6307c..f2521b560c 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelTest.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelSpec.groovy @@ -22,7 +22,7 @@ package org.onap.cps.ncmp.api.impl.trustlevel import spock.lang.Specification -class TrustLevelTest extends Specification { +class TrustLevelSpec extends Specification { def 'Get effective trust level between this and other.'() { expect: 'the lower of two is returned' 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 2771c4df13..ec1d8e8f3d 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 @@ -20,31 +20,45 @@ package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability +import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.onap.cps.ncmp.api.impl.client.DmiRestClient import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager import spock.lang.Specification class DmiPluginWatchDogSpec extends Specification { def mockDmiRestClient = Mock(DmiRestClient) + def mockNetworkCmProxyDataService = Mock(NetworkCmProxyDataService) + def mockTrustLevelManager = Mock(TrustLevelManager) def trustLevelPerDmiPlugin = [:] - def objectUnderTest = new DmiPluginWatchDog(mockDmiRestClient, trustLevelPerDmiPlugin) + + def objectUnderTest = new DmiPluginWatchDog(mockDmiRestClient, + mockNetworkCmProxyDataService, + mockTrustLevelManager, + trustLevelPerDmiPlugin) def 'watch dmi plugin health status for #dmiHealhStatus'() { given: 'the cache has been initialised and "knows" about dmi-1' - trustLevelPerDmiPlugin.put('dmi-1',null) + 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.watchDmiPluginTrustLevel() + objectUnderTest.checkDmiAvailability() then: 'the result is as expected' - assert trustLevelPerDmiPlugin.get('dmi-1') == expectedResult - where: 'the following health status is used' - dmiHealhStatus || expectedResult - 'UP' || TrustLevel.COMPLETE - 'Other' || TrustLevel.NONE - null || TrustLevel.NONE + assert trustLevelPerDmiPlugin.get('dmi-1') == newDmiTrustLevel + and: 'the update delegated to manager' + times * mockTrustLevelManager.handleUpdateOfTrustLevels(*_) + 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 } } |