--- /dev/null
+/*
+ * ============LICENSE_START========================================================
+ * Copyright (c) 2025 OpenInfra Foundation Europe. All rights reserved.
+ * ================================================================================
+ * 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.dmi.cmstack.lcm;
+
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.service.DmiService;
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent;
+import org.onap.cps.ncmp.events.lcm.v1.Values;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class LcmEventConsumer {
+
+ private final DmiService dmiService;
+
+ /**
+ * Consume the LCM event to enable the data synchronization for the ready cm handle.
+ *
+ * @param lcmEvent Lifecycle event of a cm handle id
+ */
+ @KafkaListener(topics = "${app.ncmp.lcm.topic}",
+ containerFactory = "legacyEventConcurrentKafkaListenerContainerFactory",
+ properties = {"spring.json.value.default.type=org.onap.cps.ncmp.events.lcm.v1.LcmEvent"})
+ public void consumeLcmEvent(final LcmEvent lcmEvent) {
+
+ final Values newValues = lcmEvent.getEvent().getNewValues();
+
+ if (newValues == null) {
+ return;
+ }
+
+ if (newValues.getCmHandleState() == Values.CmHandleState.READY) {
+ final String cmHandleId = lcmEvent.getEvent().getCmHandleId();
+ log.info("Enabling data sync flag for cmHandleId : {}", cmHandleId);
+ dmiService.enableNcmpDataSyncForCmHandles(List.of(cmHandleId));
+ }
+ }
+
+}
\ No newline at end of file
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021 Nordix Foundation
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
private String baseUrl;
@Value("${cps-core.dmiRegistrationUrl}")
private String dmiRegistrationUrl;
+ @Value("${cps-core.dataSyncEnabledUrl}")
+ private String dataSyncEnabledUrl;
@Value("${cps-core.auth.username}")
private String authUsername;
@Value("${cps-core.auth.password}")
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
*/
void registerCmHandles(List<String> cmHandles);
+ /**
+ * This method is used to enable data synchronization for
+ * the given {@code CmHandles}.
+ *
+ * @param cmHandles list of cm-handles
+ */
+ void enableNcmpDataSyncForCmHandles(List<String> cmHandles);
+
+
/**
* Get module resources for the given cm handle and modules.
*
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
* Modifications Copyright (C) 2021-2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
}
}
+ @Override
+ public void enableNcmpDataSyncForCmHandles(final List<String> cmHandles) {
+ log.info("Enabling NCMP dataSync flag for : {}", cmHandles);
+ cmHandles.forEach(cmHandleId -> ncmpRestClient.enableNcmpDataSync(cmHandleId));
+ }
+
private ModuleSetSchemasInner toModuleSetSchemas(final ModuleSchema moduleSchema) {
final ModuleSetSchemasInner moduleSetSchemas = new ModuleSetSchemasInner();
moduleSetSchemas.setModuleName(moduleSchema.getIdentifier());
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2025 Nordix Foundation
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.cps.ncmp.dmi.service.client;
+import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.dmi.config.DmiConfiguration.CpsProperties;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
+@Slf4j
@Component
public class NcmpRestClient {
/**
* Register a cmHandle with NCMP using a HTTP call.
+ *
* @param jsonData json data
* @return the response entity
*/
+
public ResponseEntity<String> registerCmHandlesWithNcmp(final String jsonData) {
final String ncmpRegistrationUrl = buildNcmpRegistrationUrl();
+ return performNcmpRequest(ncmpRegistrationUrl, HttpMethod.POST, jsonData);
+ }
+
+ /**
+ * Enable NCMP data sync flag to enable data sync for a cmHandle with NCMP using a HTTP call.
+ *
+ * @param cmHandleId cm handle identifier
+ * @return the response entity
+ */
+ public ResponseEntity<String> enableNcmpDataSync(final String cmHandleId) {
+ final String dataSyncEnabledUrl = buildDataSyncEnabledUrl(cmHandleId);
+ return performNcmpRequest(dataSyncEnabledUrl, HttpMethod.PUT, null);
+ }
+
+ private ResponseEntity<String> performNcmpRequest(final String ncmpUrl, final HttpMethod httpMethod,
+ final String requestBody) {
final HttpHeaders httpHeaders = new HttpHeaders();
- httpHeaders.setBasicAuth(cpsProperties.getAuthUsername(), cpsProperties.getAuthPassword());
httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
- final HttpEntity<String> httpEntity = new HttpEntity<>(jsonData, httpHeaders);
- return restTemplate.exchange(ncmpRegistrationUrl, HttpMethod.POST, httpEntity, String.class);
+ final HttpEntity<String> httpEntity =
+ (requestBody != null) ? new HttpEntity<>(requestBody, httpHeaders) : new HttpEntity<>(httpHeaders);
+ return restTemplate.exchange(ncmpUrl, httpMethod, httpEntity, String.class);
}
private String buildNcmpRegistrationUrl() {
.path(cpsProperties.getDmiRegistrationUrl())
.toUriString();
}
+
+ private String buildDataSyncEnabledUrl(final String cmHandleId) {
+ final String dataSyncEnabledUrl = UriComponentsBuilder.fromUriString(cpsProperties.getBaseUrl())
+ .path(cpsProperties.getDataSyncEnabledUrl())
+ .buildAndExpand(cmHandleId)
+ .toUriString();
+ log.debug("dataSyncEnabledUrl : {}", dataSyncEnabledUrl);
+ return dataSyncEnabledUrl;
+ }
}
\ No newline at end of file
# ============LICENSE_START=======================================================
-# Copyright (C) 2021-2024 Nordix Foundation
+# Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
# Modifications Copyright (C) 2021 Bell Canada.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
ncmp:
async:
topic: ${NCMP_ASYNC_M2M_TOPIC:ncmp-async-m2m}
+ lcm:
+ topic: ${LCM_EVENTS_TOPIC:ncmp-events}
dmi:
avc:
cm-subscription-dmi-in: ${CM_SUBSCRIPTION_DMI_IN_TOPIC:ncmp-dmi-cm-avc-subscription}
cps-core:
baseUrl: http://${CPS_CORE_HOST}:${CPS_CORE_PORT}
dmiRegistrationUrl : /ncmpInventory/v1/ch
+ dataSyncEnabledUrl: /ncmp/v1/ch/{cmHandleId}/data-sync?dataSyncEnabled=true
auth:
username: ${CPS_CORE_USERNAME}
password: ${CPS_CORE_PASSWORD}
--- /dev/null
+/*
+ * ============LICENSE_START========================================================
+ * Copyright (c) 2025 OpenInfra Foundation Europe. All rights reserved.
+ * ================================================================================
+ * 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.dmi.cmstack.lcm
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.dmi.TestUtils
+import org.onap.cps.ncmp.dmi.service.DmiService
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent
+import org.spockframework.spring.SpringBean
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.annotation.DirtiesContext
+import org.testcontainers.spock.Testcontainers
+import spock.lang.Specification
+
+import static org.onap.cps.ncmp.events.lcm.v1.Values.CmHandleState.LOCKED
+
+@SpringBootTest(classes = [ObjectMapper])
+@Testcontainers
+@DirtiesContext
+class LcmEventConsumerSpec extends Specification {
+
+ def objectMapper = new ObjectMapper()
+ def dmiService = Mock(DmiService)
+
+ def jsonData
+
+ @SpringBean
+ LcmEventConsumer objectUnderTest = new LcmEventConsumer(dmiService)
+
+ void setup() {
+ jsonData = TestUtils.getResourceFileContent('sampleLcmEvent.json')
+ }
+
+ def 'Consume LCM message when the cm handle is READY( module sync done )'() {
+ given: 'LCM event is created and sent '
+ def lcmEvent = objectMapper.readValue(jsonData, LcmEvent.class)
+ when: 'event is consumed'
+ objectUnderTest.consumeLcmEvent(lcmEvent)
+ then: 'cm handle(s) are enabled for data sync'
+ 1 * dmiService.enableNcmpDataSyncForCmHandles(['ch-1'])
+
+ }
+
+ def 'Consume LCM message when the cm handle is in LOCKED state'() {
+ given: 'LCM event is created and sent '
+ def lcmEvent = objectMapper.readValue(jsonData, LcmEvent.class)
+ and: 'cm handle is in LOCKED state'
+ lcmEvent.event.newValues.cmHandleState = LOCKED
+ when: 'event is consumed'
+ objectUnderTest.consumeLcmEvent(lcmEvent)
+ then: 'data sync flag is not enabled as state is not READY'
+ 0 * dmiService.enableNcmpDataSyncForCmHandles(['ch-1'])
+
+ }
+
+ def 'Consume LCM message with no target state'() {
+ given: 'LCM event is created and sent '
+ def lcmEvent = objectMapper.readValue(jsonData, LcmEvent.class)
+ and: 'the target state ( newValues ) is set to null'
+ lcmEvent.event.newValues = null
+ when: 'event is consumed'
+ objectUnderTest.consumeLcmEvent(lcmEvent)
+ then: 'data sync flag is not enabled as state is not READY'
+ 0 * dmiService.enableNcmpDataSyncForCmHandles(_)
+
+ }
+}
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021 Nordix Foundation
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
cpsProperties.dmiRegistrationUrl == 'some registration url'
cpsProperties.authUsername == 'some cps core user'
cpsProperties.authPassword == 'some cps core password'
+ cpsProperties.dataSyncEnabledUrl == 'some data sync url/{some-cm-handle}?dataSyncFlag=true'
}
def 'SDNC properties configuration.'() {
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
and: 'configuration data'
mockCpsProperties.baseUrl >> 'http://some-uri'
mockCpsProperties.dmiRegistrationUrl >> 'some-url'
- mockCpsProperties.authUsername >> 'some-username'
- mockCpsProperties.authPassword >> 'some-password'
and: 'the rest template returns a valid response entity'
- def mockResponseEntity = Mock(ResponseEntity)
+ def mockResponseEntityFromRestTemplate = Mock(ResponseEntity)
when: 'registering a cm handle'
def result = objectUnderTest.registerCmHandlesWithNcmp(someRequestData)
then: 'the rest template is called with the correct uri and original request data in the body'
1 * mockRestTemplate.exchange({ it.toString() == 'http://some-uri/some-url' },
- HttpMethod.POST, { it.body.contains(someRequestData) }, String.class) >> mockResponseEntity
+ HttpMethod.POST, { it.body.contains(someRequestData) }, String.class) >> mockResponseEntityFromRestTemplate
and: 'the output of the method is equal to the output from the rest template service'
- result == mockResponseEntity
+ result == mockResponseEntityFromRestTemplate
+ }
+
+ def 'Enable data sync for a cm handle identifier.'() {
+ given: 'some cm handle id'
+ def someCmHandleId = 'some-cm-handle-id'
+ and: 'configuring the url to enable data sync'
+ mockCpsProperties.baseUrl >> 'http://my-base-uri'
+ mockCpsProperties.dataSyncEnabledUrl >> 'datasync-url/{test-parameter}/data-sync?dataSyncEnabled=true'
+ and: 'the rest template configured to return a response entity'
+ def mockResponseEntityFromRestTemplate = Mock(ResponseEntity)
+ mockRestTemplate.exchange({ it.toString() == 'http://my-base-uri/datasync-url/some-cm-handle-id/data-sync?dataSyncEnabled=true' },
+ HttpMethod.PUT, _, String.class) >> mockResponseEntityFromRestTemplate
+ when: 'enabling the NCMP data sync flag'
+ def result = objectUnderTest.enableNcmpDataSync(someCmHandleId)
+ then: 'the output of the method is equal to the output from the rest template service'
+ assert result == mockResponseEntityFromRestTemplate
}
}
\ No newline at end of file
cps-core:
baseUrl: some url for cps
dmiRegistrationUrl: some registration url
+ dataSyncEnabledUrl: some data sync url/{some-cm-handle}?dataSyncFlag=true
auth:
username: some cps core user
password: some cps core password
--- /dev/null
+{
+ "eventId": "test-uuid",
+ "eventCorrelationId": "pynts-o-du-o1",
+ "eventTime": "2022-12-31T20:30:40.000+0000",
+ "eventSource": "org.onap.ncmp",
+ "eventType": "org.onap.ncmp.cmhandle.lcm.event",
+ "eventSchema": "org.onap.ncmp.cmhandle.lcm.event",
+ "eventSchemaVersion": "v1",
+ "event": {
+ "cmHandleId": "ch-1",
+ "oldValues": {
+ "cmHandleState": "ADVISED"
+ },
+ "newValues": {
+ "cmHandleState": "READY"
+ }
+ }
+}
\ No newline at end of file