diff options
Diffstat (limited to 'cps-ncmp-service/src/main')
21 files changed, 750 insertions, 230 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java index 128eed3f2c..03737bc51b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java @@ -23,10 +23,10 @@ package org.onap.cps.ncmp.api; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; - import java.util.Collection; +import java.util.List; import java.util.Map; +import org.onap.cps.ncmp.api.impl.operations.OperationEnum; import org.onap.cps.ncmp.api.inventory.CompositeState; import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; @@ -51,50 +51,55 @@ public interface NetworkCmProxyDataService { DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration); /** - * Get resource data for data store pass-through operational - * using dmi. + * Get resource data for given data store using dmi. * - * @param cmHandleId cm handle identifier - * @param resourceIdentifier resource identifier + * @param dataStoreName data store name + * @param cmHandleId cm handle identifier + * @param resourceIdentifier resource identifier * @param optionsParamInQuery options query - * @param topicParamInQuery topic name for (triggering) async responses - * @param requestId unique requestId for async request + * @param topicParamInQuery topic name for (triggering) async responses + * @param requestId unique requestId for async request * @return {@code Object} resource data */ - Object getResourceDataOperationalForCmHandle(String cmHandleId, - String resourceIdentifier, - String optionsParamInQuery, - String topicParamInQuery, - String requestId); + Object getResourceDataForCmHandle(String dataStoreName, + String cmHandleId, + String resourceIdentifier, + String optionsParamInQuery, + String topicParamInQuery, + String requestId); /** * Get resource data for operational. * + * @param dataStoreName data store name * @param cmHandleId cm handle identifier * @param resourceIdentifier resource identifier * @Link FetchDescendantsOption fetch descendants option * @return {@code Object} resource data */ - Object getResourceDataOperational(String cmHandleId, + Object getResourceDataForCmHandle(String dataStoreName, + String cmHandleId, String resourceIdentifier, FetchDescendantsOption fetchDescendantsOption); /** - * Get resource data for data store pass-through running - * using dmi. + * Get resource data for given batch of cm handles using dmi. * - * @param cmHandleId cm handle identifier - * @param resourceIdentifier resource identifier + * @param dataStoreName data store name + * @param cmHandleIds cm handle identifiers + * @param resourceIdentifier resource identifier * @param optionsParamInQuery options query - * @param topicParamInQuery topic name for (triggering) async responses - * @param requestId unique requestId for async request + * @param topicParamInQuery topic name for (triggering) async responses + * @param requestId unique requestId for async request * @return {@code Object} resource data */ - Object getResourceDataPassThroughRunningForCmHandle(String cmHandleId, - String resourceIdentifier, - String optionsParamInQuery, - String topicParamInQuery, - String requestId); + Object getResourceDataForCmHandleBatch(String dataStoreName, + List<String> cmHandleIds, + String resourceIdentifier, + String optionsParamInQuery, + String topicParamInQuery, + String requestId); + /** * Write resource data for data store pass-through running 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 b3904bd0bb..1b1997f23a 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 @@ -25,7 +25,6 @@ package org.onap.cps.ncmp.api.impl; import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters; import com.google.common.collect.Lists; @@ -46,7 +45,7 @@ import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; -import org.onap.cps.ncmp.api.impl.operations.DmiOperations; +import org.onap.cps.ncmp.api.impl.operations.OperationEnum; 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; @@ -115,38 +114,41 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } @Override - public Object getResourceDataOperationalForCmHandle(final String cmHandleId, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId) { - final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId, + public Object getResourceDataForCmHandle(final String dataStoreName, + final String cmHandleId, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId) { + final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(dataStoreName, cmHandleId, resourceIdentifier, optionsParamInQuery, - DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, - requestId, topicParamInQuery); + topicParamInQuery, + requestId); return responseEntity.getBody(); } @Override - public Object getResourceDataOperational(final String cmHandleId, + public Object getResourceDataForCmHandle(final String dataStoreName, + final String cmHandleId, final String resourceIdentifier, final FetchDescendantsOption fetchDescendantsOption) { - return cpsDataService.getDataNodes(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, resourceIdentifier, + return cpsDataService.getDataNodes(dataStoreName, cmHandleId, resourceIdentifier, fetchDescendantsOption).iterator().next(); } @Override - public Object getResourceDataPassThroughRunningForCmHandle(final String cmHandleId, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId) { - final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId, + public Object getResourceDataForCmHandleBatch(final String dataStoreName, + final List<String> cmHandleIds, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId) { + final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(dataStoreName, cmHandleIds, resourceIdentifier, optionsParamInQuery, - DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING, - requestId, topicParamInQuery); + topicParamInQuery, + requestId); return responseEntity.getBody(); } 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 d5b459b025..9d087806c0 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 @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,7 +24,7 @@ package org.onap.cps.ncmp.api.impl.client; import lombok.AllArgsConstructor; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration.DmiProperties; import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException; -import org.onap.cps.ncmp.api.impl.operations.DmiRequestBody; +import org.onap.cps.ncmp.api.impl.operations.OperationEnum; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -43,14 +43,14 @@ public class DmiRestClient { /** * Sends POST operation to DMI with json body containing module references. * @param dmiResourceUrl dmi resource url - * @param jsonData json data body + * @param requestBodyAsJsonString json data body * @param operation the type of operation being executed (for error reporting only) * @return response entity of type String */ public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl, - final String jsonData, - final DmiRequestBody.OperationEnum operation) { - final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(new HttpHeaders())); + final String requestBodyAsJsonString, + final OperationEnum operation) { + final var httpEntity = new HttpEntity<>(requestBodyAsJsonString, configureHttpHeaders(new HttpHeaders())); try { return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class); } catch (final HttpStatusCodeException httpStatusCodeException) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java index ac2bd45969..ff7afc9eb7 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java @@ -20,14 +20,12 @@ package org.onap.cps.ncmp.api.impl.config.embeddedcache; -import com.hazelcast.config.Config; import com.hazelcast.config.MapConfig; -import com.hazelcast.config.NamedConfig; import com.hazelcast.config.QueueConfig; -import com.hazelcast.core.Hazelcast; -import com.hazelcast.core.HazelcastInstance; import com.hazelcast.map.IMap; import java.util.concurrent.BlockingQueue; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.cache.HazelcastCacheConfig; import org.onap.cps.spi.model.DataNode; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -35,13 +33,14 @@ import org.springframework.context.annotation.Configuration; /** * Core infrastructure of the hazelcast distributed caches for Module Sync and Data Sync use cases. */ +@Slf4j @Configuration -public class SynchronizationCacheConfig { +public class SynchronizationCacheConfig extends HazelcastCacheConfig { public static final int MODULE_SYNC_STARTED_TTL_SECS = 600; public static final int DATA_SYNC_SEMAPHORE_TTL_SECS = 1800; - private static final QueueConfig commonQueueConfig = createQueueConfig(); + private static final QueueConfig commonQueueConfig = createQueueConfig("defaultQueueConfig"); private static final MapConfig moduleSyncStartedConfig = createMapConfig("moduleSyncStartedConfig"); private static final MapConfig dataSyncSemaphoresConfig = createMapConfig("dataSyncSemaphoresConfig"); @@ -52,7 +51,8 @@ public class SynchronizationCacheConfig { */ @Bean public BlockingQueue<DataNode> moduleSyncWorkQueue() { - return createHazelcastInstance("moduleSyncWorkQueue", commonQueueConfig) + return createHazelcastInstance("moduleSyncWorkQueue", commonQueueConfig, + "synchronization-caches") .getQueue("moduleSyncWorkQueue"); } @@ -63,7 +63,8 @@ public class SynchronizationCacheConfig { */ @Bean public IMap<String, Object> moduleSyncStartedOnCmHandles() { - return createHazelcastInstance("moduleSyncStartedOnCmHandles", moduleSyncStartedConfig) + return createHazelcastInstance("moduleSyncStartedOnCmHandles", moduleSyncStartedConfig, + "synchronization-caches") .getMap("moduleSyncStartedOnCmHandles"); } @@ -74,39 +75,8 @@ public class SynchronizationCacheConfig { */ @Bean public IMap<String, Boolean> dataSyncSemaphores() { - return createHazelcastInstance("dataSyncSemaphores", dataSyncSemaphoresConfig) + return createHazelcastInstance("dataSyncSemaphores", dataSyncSemaphoresConfig, + "synchronization-caches") .getMap("dataSyncSemaphores"); } - - private HazelcastInstance createHazelcastInstance( - final String hazelcastInstanceName, final NamedConfig namedConfig) { - return Hazelcast.newHazelcastInstance(initializeConfig(hazelcastInstanceName, namedConfig)); - } - - private Config initializeConfig(final String instanceName, final NamedConfig namedConfig) { - final Config config = new Config(instanceName); - if (namedConfig instanceof MapConfig) { - config.addMapConfig((MapConfig) namedConfig); - } - if (namedConfig instanceof QueueConfig) { - config.addQueueConfig((QueueConfig) namedConfig); - } - config.setClusterName("synchronization-caches"); - return config; - } - - private static QueueConfig createQueueConfig() { - final QueueConfig commonQueueConfig = new QueueConfig("defaultQueueConfig"); - commonQueueConfig.setBackupCount(3); - commonQueueConfig.setAsyncBackupCount(3); - return commonQueueConfig; - } - - private static MapConfig createMapConfig(final String configName) { - final MapConfig mapConfig = new MapConfig(configName); - mapConfig.setBackupCount(3); - mapConfig.setAsyncBackupCount(3); - return mapConfig; - } - } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfig.java new file mode 100644 index 0000000000..d2c3dc2599 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfig.java @@ -0,0 +1,51 @@ +/* + * ============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.event.avc; + +import com.hazelcast.config.MapConfig; +import com.hazelcast.map.IMap; +import java.util.Set; +import org.onap.cps.cache.HazelcastCacheConfig; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Core infrastructure of the hazelcast distributed cache for subscription forward config use cases. + */ +@Configuration +public class ForwardedSubscriptionEventCacheConfig extends HazelcastCacheConfig { + + private static final MapConfig forwardedSubscriptionEventCacheMapConfig = + createMapConfig("forwardedSubscriptionEventCacheMapConfig"); + + /** + * Distributed instance of forwarded subscription information cache that contains subscription event + * id by dmi names as properties. + * + * @return configured map of subscription event ids as keys to sets of dmi names for values + */ + @Bean + public IMap<String, Set<String>> forwardedSubscriptionEventCache() { + return createHazelcastInstance("hazelCastInstanceSubscriptionEvents", + forwardedSubscriptionEventCacheMapConfig, "cps-ncmp-service-caches") + .getMap("forwardedSubscriptionEventCache"); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ResponseTimeoutTask.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ResponseTimeoutTask.java new file mode 100644 index 0000000000..e7edecfacc --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ResponseTimeoutTask.java @@ -0,0 +1,51 @@ +/* + * ============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.event.avc; + +import com.hazelcast.map.IMap; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor +public class ResponseTimeoutTask implements Runnable { + + private final IMap<String, Set<String>> forwardedSubscriptionEventCache; + private final String subscriptionEventId; + + @Override + public void run() { + if (forwardedSubscriptionEventCache.containsKey(subscriptionEventId)) { + final Set<String> dmiNames = forwardedSubscriptionEventCache.get(subscriptionEventId); + if (dmiNames.isEmpty()) { + //TODO full outcome response here + log.info("placeholder to create full outcome response for subscriptionEventId: {}.", + subscriptionEventId); + } else { + //TODO partial outcome response here + log.info("placeholder to create partial outcome response for subscriptionEventId: {}.", + subscriptionEventId); + } + forwardedSubscriptionEventCache.remove(subscriptionEventId); + } + } +}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumer.java new file mode 100644 index 0000000000..b332ad1a0e --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumer.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.event.avc; + +import com.hazelcast.map.IMap; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.models.SubscriptionEventResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +public class SubscriptionEventResponseConsumer { + + private final IMap<String, Set<String>> forwardedSubscriptionEventCache; + + @Value("${app.ncmp.avc.subscription-outcome-topic}") + private String subscriptionOutcomeEventTopic; + + @Value("${notification.enabled:true}") + private boolean notificationFeatureEnabled; + + @Value("${ncmp.model-loader.subscription:false}") + private boolean subscriptionModelLoaderEnabled; + + /** + * Consume subscription response event. + * + * @param subscriptionEventResponse the event to be consumed + */ + @KafkaListener(topics = "${app.ncmp.avc.subscription-response-topic}", + properties = {"spring.json.value.default.type=org.onap.cps.ncmp.api.models.SubscriptionEventResponse"}) + public void consumeSubscriptionEventResponse(final SubscriptionEventResponse subscriptionEventResponse) { + log.info("subscription event response of clientId: {} is received.", subscriptionEventResponse.getClientId()); + final String subscriptionEventId = subscriptionEventResponse.getClientId() + + subscriptionEventResponse.getSubscriptionName(); + final boolean createOutcomeResponse; + if (forwardedSubscriptionEventCache.containsKey(subscriptionEventId)) { + forwardedSubscriptionEventCache.get(subscriptionEventId).remove(subscriptionEventResponse.getDmiName()); + createOutcomeResponse = forwardedSubscriptionEventCache.get(subscriptionEventId).isEmpty(); + if (createOutcomeResponse) { + forwardedSubscriptionEventCache.remove(subscriptionEventId); + } + } else { + createOutcomeResponse = true; + } + if (subscriptionModelLoaderEnabled) { + updateSubscriptionEvent(subscriptionEventResponse); + } + if (createOutcomeResponse && notificationFeatureEnabled) { + log.info("placeholder to create full outcome response for subscriptionEventId: {}.", subscriptionEventId); + //TODO Create outcome response + } + } + + private void updateSubscriptionEvent(final SubscriptionEventResponse subscriptionEventResponse) { + log.info("placeholder to update persisted subscription for subscriptionEventId: {}.", + subscriptionEventResponse.getClientId() + subscriptionEventResponse.getSubscriptionName()); + } +}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java index c3624b8005..4afa051d30 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java @@ -20,21 +20,28 @@ package org.onap.cps.ncmp.api.impl.events.avcsubscription; +import com.hazelcast.map.IMap; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.impl.event.avc.ResponseTimeoutTask; import org.onap.cps.ncmp.api.impl.events.EventsPublisher; -import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; +import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.inventory.InventoryPersistence; import org.onap.cps.ncmp.event.model.SubscriptionEvent; import org.onap.cps.spi.exceptions.OperationNotYetSupportedException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -45,9 +52,15 @@ public class SubscriptionEventForwarder { private final InventoryPersistence inventoryPersistence; private final EventsPublisher<SubscriptionEvent> eventsPublisher; + private final IMap<String, Set<String>> forwardedSubscriptionEventCache; + + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); private static final String DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX = "ncmp-dmi-cm-avc-subscription-"; + @Value("${ncmp.timers.subscription-forwarding.dmi-response-timeout-ms:30000}") + private int dmiResponseTimeoutInMs; + /** * Forward subscription event. * @@ -56,38 +69,41 @@ public class SubscriptionEventForwarder { public void forwardCreateSubscriptionEvent(final SubscriptionEvent subscriptionEvent) { final List<Object> cmHandleTargets = subscriptionEvent.getEvent().getPredicates().getTargets(); if (cmHandleTargets == null || cmHandleTargets.isEmpty() - || cmHandleTargets.stream().anyMatch(id -> ((String) id).contains("*"))) { + || cmHandleTargets.stream().anyMatch(id -> ((String) id).contains("*"))) { throw new OperationNotYetSupportedException( - "CMHandle targets are required. \"Wildcard\" operations are not yet supported"); + "CMHandle targets are required. \"Wildcard\" operations are not yet supported"); } final List<String> cmHandleTargetsAsStrings = cmHandleTargets.stream().map( - Objects::toString).collect(Collectors.toList()); + Objects::toString).collect(Collectors.toList()); final Collection<YangModelCmHandle> yangModelCmHandles = - inventoryPersistence.getYangModelCmHandles(cmHandleTargetsAsStrings); - final Map<String, Map<String, Map<String, String>>> dmiNameCmHandleMap = - organizeByDmiName(yangModelCmHandles); + inventoryPersistence.getYangModelCmHandles(cmHandleTargetsAsStrings); + + final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName + = DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles); + + final Set<String> dmisToRespond = new HashSet<>(dmiPropertiesPerCmHandleIdPerServiceName.keySet()); + startResponseTimeout(subscriptionEvent, dmisToRespond); + forwardEventToDmis(dmiPropertiesPerCmHandleIdPerServiceName, subscriptionEvent); + } + + private void forwardEventToDmis(final Map<String, Map<String, Map<String, String>>> dmiNameCmHandleMap, + final SubscriptionEvent subscriptionEvent) { dmiNameCmHandleMap.forEach((dmiName, cmHandlePropertiesMap) -> { subscriptionEvent.getEvent().getPredicates().setTargets(Collections.singletonList(cmHandlePropertiesMap)); final String eventKey = createEventKey(subscriptionEvent, dmiName); - eventsPublisher.publishEvent(DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX + dmiName, eventKey, subscriptionEvent); + eventsPublisher.publishEvent( + DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX + dmiName, eventKey, subscriptionEvent); }); } - private Map<String, Map<String, Map<String, String>>> organizeByDmiName( - final Collection<YangModelCmHandle> yangModelCmHandles) { - final Map<String, Map<String, Map<String, String>>> dmiNameCmHandlePropertiesMap = new HashMap<>(); - yangModelCmHandles.forEach(cmHandle -> { - final String dmiName = cmHandle.resolveDmiServiceName(RequiredDmiService.DATA); - if (!dmiNameCmHandlePropertiesMap.containsKey(dmiName)) { - final Map<String, Map<String, String>> cmHandleDmiPropertiesMap = new HashMap<>(); - cmHandleDmiPropertiesMap.put(cmHandle.getId(), dmiPropertiesAsMap(cmHandle)); - dmiNameCmHandlePropertiesMap.put(cmHandle.getDmiDataServiceName(), cmHandleDmiPropertiesMap); - } else { - dmiNameCmHandlePropertiesMap.get(cmHandle.getDmiDataServiceName()) - .put(cmHandle.getId(), dmiPropertiesAsMap(cmHandle)); - } - }); - return dmiNameCmHandlePropertiesMap; + private void startResponseTimeout(final SubscriptionEvent subscriptionEvent, final Set<String> dmisToRespond) { + final String subscriptionEventId = subscriptionEvent.getEvent().getSubscription().getClientID() + + subscriptionEvent.getEvent().getSubscription().getName(); + + forwardedSubscriptionEventCache.put(subscriptionEventId, dmisToRespond); + final ResponseTimeoutTask responseTimeoutTask = + new ResponseTimeoutTask(forwardedSubscriptionEventCache, subscriptionEventId); + executorService.schedule(responseTimeoutTask, dmiResponseTimeoutInMs, TimeUnit.MILLISECONDS); } private String createEventKey(final SubscriptionEvent subscriptionEvent, final String dmiName) { @@ -98,9 +114,4 @@ public class SubscriptionEventForwarder { + dmiName; } - public Map<String, String> dmiPropertiesAsMap(final YangModelCmHandle yangModelCmHandle) { - return yangModelCmHandle.getDmiProperties().stream().collect( - Collectors.toMap(YangModelCmHandle.Property::getName, YangModelCmHandle.Property::getValue)); - } - } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java new file mode 100644 index 0000000000..192062fde5 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java @@ -0,0 +1,47 @@ +/* + * ============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.executor; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TaskExecutor { + + /** + * Execute task asynchronously. + * + * @param taskSupplier functional method is get() task need to executed asynchronously + * @param timeOutInMillis the timeout value in milliseconds + */ + public static CompletableFuture<Object> executeTask(final Supplier<Object> taskSupplier, + final long timeOutInMillis) { + return CompletableFuture.supplyAsync(taskSupplier::get) + .orTimeout(timeOutInMillis, MILLISECONDS); + } +} + + + diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java new file mode 100644 index 0000000000..24edc73f3c --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java @@ -0,0 +1,34 @@ +/* + * ============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.operations; + +import lombok.Getter; + +@Getter +public enum DataStoreEnum { + PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"), + PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"); + private final String value; + + DataStoreEnum(final String value) { + this.value = value; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index 83faa005f0..d648352f15 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,19 +21,24 @@ package org.onap.cps.ncmp.api.impl.operations; -import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ; +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING; +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ; +import java.util.Collection; +import java.util.List; +import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; +import org.onap.cps.ncmp.api.impl.executor.TaskExecutor; +import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer; import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.inventory.CmHandleState; import org.onap.cps.ncmp.api.inventory.InventoryPersistence; import org.onap.cps.spi.exceptions.CpsException; import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -44,15 +49,14 @@ import org.springframework.stereotype.Component; @Slf4j public class DmiDataOperations extends DmiOperations { - /** - * Constructor for {@code DmiOperations}. This method also manipulates url properties. - * - * @param dmiRestClient {@code DmiRestClient} - */ + private static final long DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS = 30000L; + private static final String NO_CM_HANDLE_ID = ""; + public DmiDataOperations(final InventoryPersistence inventoryPersistence, final JsonObjectMapper jsonObjectMapper, final NcmpConfiguration.DmiProperties dmiProperties, - final DmiRestClient dmiRestClient, final DmiServiceUrlBuilder dmiServiceUrlBuilder) { + final DmiRestClient dmiRestClient, + final DmiServiceUrlBuilder dmiServiceUrlBuilder) { super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder); } @@ -60,48 +64,78 @@ public class DmiDataOperations extends DmiOperations { * This method fetches the resource data from operational data store for given cm handle * identifier on given resource using dmi client. * - * @param cmHandleId network resource identifier - * @param resourceId resource identifier + * @param dataStoreName name of data store + * @param cmHandleId network resource identifier + * @param resourceId resource identifier * @param optionsParamInQuery options query - * @param dataStore data store enum - * @param requestId requestId for async responses * @param topicParamInQuery topic name for (triggering) async responses + * @param requestId requestId for async responses * @return {@code ResponseEntity} response entity */ - public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId, + public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, + final String cmHandleId, final String resourceId, final String optionsParamInQuery, - final DataStoreEnum dataStore, - final String requestId, - final String topicParamInQuery) { + final String topicParamInQuery, + final String requestId) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); - final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); - final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, resourceId, optionsParamInQuery, dataStore, - topicParamInQuery, yangModelCmHandle); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); - isCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, + yangModelCmHandle); + final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, resourceId, optionsParamInQuery, + topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ); + } + + /** + * This method fetches the resource data by data store for given list of cm handles using dmi client. + * + * @param dataStoreName data store name + * @param cmHandleIds list of cm handles + * @param resourceId resource identifier + * @param optionsParamInQuery options query + * @param topicParamInQuery topic name for (triggering) async responses + * @param requestId requestId for async responses + * @return {@code ResponseEntity} response entity + */ + public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, + final List<String> cmHandleIds, + final String resourceId, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId) { + final Collection<YangModelCmHandle> yangModelCmHandles + = inventoryPersistence.getYangModelCmHandles(cmHandleIds); + final Map<String, Map<String, Map<String, String>>> dmiServiceNameCmHandlePropertiesMap = + DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles); + + buildBulkResourceDataRequestAndSend(dataStoreName, resourceId, optionsParamInQuery, + topicParamInQuery, requestId, dmiServiceNameCmHandlePropertiesMap); + return new ResponseEntity<>(HttpStatus.ACCEPTED); } /** * This method fetches all the resource data from operational data store for given cm handle * identifier using dmi client. * + * @param dataStoreName data store name * @param cmHandleId network resource identifier - * @param dataStore data store enum * @param requestId requestId for async responses * @return {@code ResponseEntity} response entity */ - public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId, - final DataStoreEnum dataStore, + public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, + final String cmHandleId, final String requestId) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); - final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); - final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, "/", null, dataStore, - null, yangModelCmHandle); + final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, + yangModelCmHandle); + final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/", null, + null, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); - isCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, + READ); } /** @@ -121,12 +155,14 @@ public class DmiDataOperations extends DmiOperations { final String requestData, final String dataType) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); - final String jsonBody = getDmiRequestBody(operation, null, requestData, dataType, yangModelCmHandle); - final String dmiUrl = getDmiRequestUrl(cmHandleId, resourceId, null, PASSTHROUGH_RUNNING, - null, yangModelCmHandle); + final String jsonRequestBody = getDmiRequestBody(operation, null, requestData, dataType, + yangModelCmHandle); + final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getValue(), cmHandleId, resourceId, + null, null, + yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); - isCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operation); } private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { @@ -145,24 +181,80 @@ public class DmiDataOperations extends DmiOperations { return jsonObjectMapper.asJsonString(dmiRequestBody); } - private String getDmiRequestUrl(final String cmHandleId, - final String resourceId, - final String optionsParamInQuery, - final DataStoreEnum dataStore, - final String topicParamInQuery, - final YangModelCmHandle yangModelCmHandle) { + private String getDmiBulkRequestBody(final OperationEnum operation, + final String requestId, + final String requestData) { + final DmiRequestBody dmiBulkRequestBody = DmiRequestBody.builder() + .operation(operation) + .requestId(requestId) + .data(requestData) + .build(); + return jsonObjectMapper.asJsonString(dmiBulkRequestBody); + } + + private String getDmiRequestUrl(final String dataStoreName, + final String cmHandleId, + final String resourceId, + final String optionsParamInQuery, + final String topicParamInQuery, + final String dmiServiceName) { return dmiServiceUrlBuilder.getDmiDatastoreUrl( dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery, - topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables( - yangModelCmHandle, cmHandleId, dataStore)); + topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName, + cmHandleId)); } - private void isCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, final CmHandleState cmHandleState) { + private String getDmiServiceBulkRequestUrl(final String dataStoreName, + final String resourceId, + final String optionsParamInQuery, + final String topicParamInQuery, + final String dmiServiceName) { + return dmiServiceUrlBuilder.getBulkRequestUrl( + dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery, + topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName, + NO_CM_HANDLE_ID)); + } + + private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, + final CmHandleState cmHandleState) { if (cmHandleState != CmHandleState.READY) { throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. " - + "cm handle state is " - + yangModelCmHandle.getCompositeState().getCmHandleState()); + + "cm handle state is " + + yangModelCmHandle.getCompositeState().getCmHandleState()); } } + private void buildBulkResourceDataRequestAndSend(final String dataStoreName, + final String resourceId, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final Map<String, Map<String, Map<String, String>>> + dmiServiceNameCmHandlePropertiesMap) { + dmiServiceNameCmHandlePropertiesMap.entrySet().parallelStream().forEach( + dmiServiceNameCmHandlePropertiesEntry -> { + final String dmiBulkResourceDataUrl = getDmiServiceBulkRequestUrl(dataStoreName, resourceId, + optionsParamInQuery, topicParamInQuery, dmiServiceNameCmHandlePropertiesEntry.getKey()); + final String jsonRequestBodyAsJsonString = + jsonObjectMapper.asJsonString(dmiServiceNameCmHandlePropertiesEntry.getValue()); + final String jsonRequestBody + = getDmiBulkRequestBody(READ, requestId, jsonRequestBodyAsJsonString); + sendDmiResourceDataRequestToDmiService(dmiBulkResourceDataUrl, jsonRequestBody); + }); + } + + private void sendDmiResourceDataRequestToDmiService(final String dmiBulkResourceDataUrl, + final String dmiResourceDataRequestAsJsonString) { + TaskExecutor.executeTask(() -> + dmiRestClient.postOperationWithJsonData(dmiBulkResourceDataUrl, + dmiResourceDataRequestAsJsonString, READ), + DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS) + .whenCompleteAsync(this::handleTaskCompletion); + } + + private void handleTaskCompletion(final Object response, final Throwable throwable) { + // TODO Need to publish an error response to client given topic. + // Code should be implemented into https://jira.onap.org/browse/CPS-1558 ( + // NCMP : Handle non responding DMI-Plugin) + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java index d8d03041fb..392e9c1a24 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -98,17 +98,18 @@ public class DmiModelOperations extends DmiOperations { * Get resources from DMI for modules. * * @param dmiServiceName dmi service name - * @param jsonData module names and revisions as JSON + * @param jsonRequestBody module names and revisions as JSON * @param cmHandle cmHandle * @param resourceName name of the resource(s) * @return {@code ResponseEntity} response entity */ private ResponseEntity<Object> getResourceFromDmiWithJsonData(final String dmiServiceName, - final String jsonData, + final String jsonRequestBody, final String cmHandle, final String resourceName) { final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, DmiRequestBody.OperationEnum.READ); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, + OperationEnum.READ); } private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java index e26ffef870..7e9079ec94 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,7 +21,6 @@ package org.onap.cps.ncmp.api.impl.operations; -import lombok.Getter; import lombok.RequiredArgsConstructor; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; @@ -34,17 +33,6 @@ import org.springframework.stereotype.Service; @Service public class DmiOperations { - @Getter - public enum DataStoreEnum { - PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"), - PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"); - private final String value; - - DataStoreEnum(final String value) { - this.value = value; - } - } - protected final InventoryPersistence inventoryPersistence; protected final JsonObjectMapper jsonObjectMapper; protected final NcmpConfiguration.DmiProperties dmiProperties; @@ -52,7 +40,7 @@ public class DmiOperations { protected final DmiServiceUrlBuilder dmiServiceUrlBuilder; String getDmiResourceUrl(final String dmiServiceName, final String cmHandle, final String resourceName) { - return dmiServiceUrlBuilder.getCmHandleUrl() + return dmiServiceUrlBuilder.getResourceDataBasePathUriBuilder() .pathSegment("{resourceName}") .buildAndExpand(dmiServiceName, dmiProperties.getDmiBasePath(), cmHandle, resourceName).toUriString(); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java index c84e4cb870..3aa6366155 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java @@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.operations; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonValue; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -34,24 +33,6 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; @Getter @Builder public class DmiRequestBody { - public enum OperationEnum { - READ("read"), - CREATE("create"), - UPDATE("update"), - PATCH("patch"), - DELETE("delete"); - private final String value; - - OperationEnum(final String value) { - this.value = value; - } - - @Override - @JsonValue - public String toString() { - return String.valueOf(value); - } - } private OperationEnum operation; private String dataType; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java new file mode 100644 index 0000000000..796cef23d0 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java @@ -0,0 +1,43 @@ +/* + * ============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.operations; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum OperationEnum { + + READ("read"), + CREATE("create"), + UPDATE("update"), + PATCH("patch"), + DELETE("delete"); + private final String value; + + OperationEnum(final String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java new file mode 100644 index 0000000000..26e94866a1 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java @@ -0,0 +1,64 @@ +/* + * ============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.utils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DmiServiceNameOrganizer { + + /** + * organizes a map with dmi service name as key for cm handle with its properties. + * + * @param yangModelCmHandles list of cm handle model + */ + public static Map<String, Map<String, Map<String, String>>> getDmiPropertiesPerCmHandleIdPerServiceName( + final Collection<YangModelCmHandle> yangModelCmHandles) { + final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName + = new HashMap<>(); + yangModelCmHandles.forEach(yangModelCmHandle -> { + final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA); + if (!dmiPropertiesPerCmHandleIdPerServiceName.containsKey(dmiServiceName)) { + final Map<String, Map<String, String>> cmHandleDmiPropertiesMap = new HashMap<>(); + cmHandleDmiPropertiesMap.put(yangModelCmHandle.getId(), + dmiPropertiesAsMap(yangModelCmHandle.getDmiProperties())); + dmiPropertiesPerCmHandleIdPerServiceName.put(dmiServiceName, cmHandleDmiPropertiesMap); + } else { + dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName) + .put(yangModelCmHandle.getId(), dmiPropertiesAsMap(yangModelCmHandle.getDmiProperties())); + } + }); + return dmiPropertiesPerCmHandleIdPerServiceName; + } + + private static Map<String, String> dmiPropertiesAsMap(final List<YangModelCmHandle.Property> dmiProperties) { + return dmiProperties.stream().collect( + Collectors.toMap(YangModelCmHandle.Property::getName, YangModelCmHandle.Property::getValue)); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java index 5f4a6540c2..bba8f48fbd 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-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. @@ -20,16 +20,12 @@ package org.onap.cps.ncmp.api.impl.utils; -import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA; - import java.util.HashMap; import java.util.Map; import lombok.RequiredArgsConstructor; import org.apache.logging.log4j.util.Strings; import org.apache.logging.log4j.util.TriConsumer; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; -import org.onap.cps.ncmp.api.impl.operations.DmiOperations; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; @@ -52,13 +48,21 @@ public class DmiServiceUrlBuilder { */ public String getDmiDatastoreUrl(final MultiValueMap<String, String> queryParams, final Map<String, Object> uriVariables) { - final UriComponentsBuilder uriComponentsBuilder = getCmHandleUrl() - .pathSegment("data") - .pathSegment("ds") - .pathSegment("{dataStore}") - .queryParams(queryParams) - .uriVariables(uriVariables); - return uriComponentsBuilder.buildAndExpand().toUriString(); + return getUriComponentsBuilder(getResourceDataBasePathUriBuilder(), queryParams, uriVariables) + .buildAndExpand().toUriString(); + } + + /** + * This method creates the dmi service url for bulk request. + * + * @param queryParams query param map as key,value pair + * @param uriVariables uri param map as key (placeholder),value pair + * @return {@code String} dmi service url as string + */ + public String getBulkRequestUrl(final MultiValueMap<String, String> queryParams, + final Map<String, Object> uriVariables) { + return getUriComponentsBuilder(getBulkResourceDataBasePathUriBuilder(), queryParams, uriVariables) + .buildAndExpand().toUriString(); } /** @@ -66,7 +70,7 @@ public class DmiServiceUrlBuilder { * * @return {@code UriComponentsBuilder} dmi service url builder object */ - public UriComponentsBuilder getCmHandleUrl() { + public UriComponentsBuilder getResourceDataBasePathUriBuilder() { return UriComponentsBuilder.newInstance() .path("{dmiServiceName}") .pathSegment("{dmiBasePath}") @@ -76,23 +80,36 @@ public class DmiServiceUrlBuilder { } /** + * This method creates the dmi service url builder object with path variables for batch of cm handles. + * + * @return {@code UriComponentsBuilder} dmi service url builder object + */ + public UriComponentsBuilder getBulkResourceDataBasePathUriBuilder() { + return UriComponentsBuilder.newInstance() + .path("{dmiServiceName}") + .pathSegment("{dmiBasePath}") + .pathSegment("v1") + .pathSegment("batch"); + } + + /** * This method populates uri variables. * - * @param yangModelCmHandle get dmi service name + * @param dataStoreName data store name + * @param dmiServiceName dmi service name * @param cmHandleId cm handle id for dmi registration * @return {@code String} dmi service url as string */ - public Map<String, Object> populateUriVariables(final YangModelCmHandle yangModelCmHandle, - final String cmHandleId, - final DmiOperations.DataStoreEnum dataStore) { + public Map<String, Object> populateUriVariables(final String dataStoreName, + final String dmiServiceName, + final String cmHandleId) { cpsValidator.validateNameCharacters(cmHandleId); final Map<String, Object> uriVariables = new HashMap<>(); final String dmiBasePath = dmiProperties.getDmiBasePath(); - uriVariables.put("dmiServiceName", - yangModelCmHandle.resolveDmiServiceName(DATA)); + uriVariables.put("dmiServiceName", dmiServiceName); uriVariables.put("dmiBasePath", dmiBasePath); uriVariables.put("cmHandleId", cmHandleId); - uriVariables.put("dataStore", dataStore.getValue()); + uriVariables.put("dataStore", dataStoreName); return uriVariables; } @@ -124,4 +141,15 @@ public class DmiServiceUrlBuilder { } }; } + + private UriComponentsBuilder getUriComponentsBuilder(final UriComponentsBuilder uriComponentsBuilder, + final MultiValueMap<String, String> queryParams, + final Map<String, Object> uriVariables) { + return uriComponentsBuilder + .pathSegment("data") + .pathSegment("ds") + .pathSegment("{dataStore}") + .queryParams(queryParams) + .uriVariables(uriVariables); + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java index 537f50122c..b9cecfb3d0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,8 @@ package org.onap.cps.ncmp.api.inventory.sync; +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL; + import com.fasterxml.jackson.databind.JsonNode; import java.time.Duration; import java.time.OffsetDateTime; @@ -37,7 +39,6 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; -import org.onap.cps.ncmp.api.impl.operations.DmiOperations; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.inventory.CmHandleQueries; @@ -167,7 +168,8 @@ public class SyncUtils { */ public String getResourceData(final String cmHandleId) { final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi( - cmHandleId, DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, + PASSTHROUGH_OPERATIONAL.getValue(), + cmHandleId, UUID.randomUUID().toString()); if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) { return getFirstResource(resourceDataResponseEntity.getBody()); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/SubscriptionEventResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/SubscriptionEventResponse.java new file mode 100644 index 0000000000..95e773c8c9 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/SubscriptionEventResponse.java @@ -0,0 +1,37 @@ +/* + * ============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.models; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; +import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@Getter +@Setter +public class SubscriptionEventResponse { + private String clientId; + private String subscriptionName; + private String dmiName; + private Map<String, SubscriptionStatus> cmHandleIdToStatus; +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java index 231ba75b5d..659779acf9 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java @@ -31,6 +31,7 @@ import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException; import org.onap.cps.spi.exceptions.AlreadyDefinedException; +import org.onap.cps.spi.model.Dataspace; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -51,6 +52,12 @@ public class SubscriptionModelLoader implements ModelLoader { private static final String SUBSCRIPTION_SCHEMASET_NAME = "subscriptions"; private static final String SUBSCRIPTION_REGISTRY_DATANODE_NAME = "subscription-registry"; + @Value("${ncmp.model-loader.maximum-attempt-count:20}") + private int maximumAttemptCount; + + @Value("${ncmp.timers.model-loader.retry-time-ms:1000}") + private long retryTimeMs; + @Value("${ncmp.model-loader.subscription:false}") private boolean subscriptionModelLoaderEnabled; @@ -63,6 +70,7 @@ public class SubscriptionModelLoader implements ModelLoader { public void onApplicationEvent(final ApplicationReadyEvent applicationReadyEvent) { try { if (subscriptionModelLoaderEnabled) { + checkNcmpDataspaceExists(); onboardSubscriptionModel(createYangResourceToContentMap()); } else { log.info("Subscription Model Loader is disabled"); @@ -73,6 +81,29 @@ public class SubscriptionModelLoader implements ModelLoader { } } + private void checkNcmpDataspaceExists() { + boolean ncmpDataspaceExists = false; + int attemptCount = 0; + while (!ncmpDataspaceExists) { + final Dataspace ncmpDataspace = cpsAdminService.getDataspace(SUBSCRIPTION_DATASPACE_NAME); + if (ncmpDataspace != null) { + ncmpDataspaceExists = true; + } + if (attemptCount < maximumAttemptCount) { + try { + Thread.sleep(attemptCount * retryTimeMs); + attemptCount++; + log.info("Retrieving NCMP dataspace... {} attempt(s) ", attemptCount); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + } + } else { + throw new NcmpStartUpException("Retrieval of NCMP dataspace fails", + "NCMP dataspace does not exist"); + } + } + } + /** * Method to onboard subscription model for NCMP. */ @@ -108,7 +139,7 @@ public class SubscriptionModelLoader implements ModelLoader { */ @Override public boolean createAnchor(final String dataspaceName, final String schemaSetName, - final String anchorName) { + final String anchorName) { try { cpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorName); } catch (final AlreadyDefinedException exception) { diff --git a/cps-ncmp-service/src/main/resources/model/subscription.yang b/cps-ncmp-service/src/main/resources/model/subscription.yang index 8ae1be6646..e332a2898a 100644 --- a/cps-ncmp-service/src/main/resources/model/subscription.yang +++ b/cps-ncmp-service/src/main/resources/model/subscription.yang @@ -4,7 +4,7 @@ module subscription { prefix subs; - revision "2023-21-03" { + revision "2023-03-21" { description "NCMP subscription model"; } |