Set CPS Project Status to 'Mature'
[cps.git] / integration-test / src / test / groovy / org / onap / cps / integration / functional / NcmpCmHandleCreateSpec.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2024 Nordix Foundation
4  *  ================================================================================
5  *  Licensed under the Apache License, Version 2.0 (the 'License');
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an 'AS IS' BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *  SPDX-License-Identifier: Apache-2.0
18  *  ============LICENSE_END=========================================================
19  */
20
21 package org.onap.cps.integration.functional
22
23 import java.time.Duration
24 import java.time.OffsetDateTime
25 import org.apache.kafka.common.TopicPartition
26 import org.apache.kafka.common.serialization.StringDeserializer
27 import org.onap.cps.integration.KafkaTestContainer
28 import org.onap.cps.integration.base.CpsIntegrationSpecBase
29 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
30 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
31 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
32 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
33 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
34 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
35 import org.onap.cps.ncmp.events.lcm.v1.LcmEvent
36 import spock.util.concurrent.PollingConditions
37
38 class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase {
39
40     NetworkCmProxyDataService objectUnderTest
41
42     def kafkaConsumer = KafkaTestContainer.getConsumer('ncmp-group', StringDeserializer.class)
43
44     def setup() {
45         objectUnderTest = networkCmProxyDataService
46     }
47
48     def 'CM Handle registration is successful.'() {
49         given: 'DMI will return modules when requested'
50             dmiDispatcher.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
51
52         and: 'consumer subscribed to topic'
53             kafkaConsumer.subscribe(['ncmp-events'])
54
55         when: 'a CM-handle is registered for creation'
56             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
57             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
58             def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
59
60         then: 'registration gives successful response'
61             assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')]
62
63         and: 'CM-handle is initially in ADVISED state'
64             assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
65
66         when: 'module sync runs'
67             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
68
69         then: 'CM-handle goes to READY state'
70             new PollingConditions().within(3, () -> {
71                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
72             })
73
74         and: 'the messages is polled'
75             def message = kafkaConsumer.poll(Duration.ofMillis(10000))
76             def records = message.records(new TopicPartition('ncmp-events', 0))
77
78         and: 'the newest lcm event notification is received with READY state'
79             def notificationMessage = jsonObjectMapper.convertJsonString(records.last().value().toString(), LcmEvent)
80             assert notificationMessage.event.newValues.cmHandleState.value() == 'READY'
81
82         and: 'the CM-handle has expected modules'
83             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
84
85         cleanup: 'deregister CM handle'
86             deregisterCmHandle(DMI_URL, 'ch-1')
87     }
88
89     def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() {
90         given: 'DMI is not available to handle requests'
91             dmiDispatcher.isAvailable = false
92
93         when: 'a CM-handle is registered for creation'
94             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
95             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
96             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
97
98         and: 'module sync runs'
99             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
100
101         then: 'CM-handle goes to LOCKED state with reason MODULE_SYNC_FAILED'
102             new PollingConditions().within(3, () -> {
103                 def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState('ch-1')
104                 assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED
105                 assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_SYNC_FAILED
106             })
107
108         and: 'CM-handle has no modules'
109             assert objectUnderTest.getYangResourcesModuleReferences('ch-1').empty
110
111         cleanup: 'deregister CM handle'
112             deregisterCmHandle(DMI_URL, 'ch-1')
113     }
114
115     def 'Create a CM-handle with existing moduleSetTag.'() {
116         given: 'DMI will return modules when requested'
117             dmiDispatcher.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M3']]
118         and: 'existing CM-handles cm-1 with moduleSetTag "A", and cm-2 with moduleSetTag "B"'
119             registerCmHandle(DMI_URL, 'ch-1', 'A')
120             registerCmHandle(DMI_URL, 'ch-2', 'B')
121
122         when: 'a CM-handle is registered for creation with moduleSetTag "B"'
123             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B')
124             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]))
125
126         then: 'the CM-handle goes to READY state after module sync'
127             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
128             new PollingConditions().within(3, () -> {
129                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState
130             })
131
132         and: 'the CM-handle has expected moduleSetTag'
133             assert objectUnderTest.getNcmpServiceCmHandle('ch-3').moduleSetTag == 'B'
134
135         and: 'the CM-handle has expected modules from module set "B": M1 and M3'
136             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
137
138         cleanup: 'deregister CM handles'
139             deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3'])
140     }
141
142     def 'CM Handle retry after failed module sync.'() {
143         given: 'DMI is not initially available to handle requests'
144             dmiDispatcher.isAvailable = false
145
146         when: 'CM-handles are registered for creation'
147             def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')]
148             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate)
149             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
150         and: 'module sync runs'
151             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
152         then: 'CM-handles go to LOCKED state'
153             new PollingConditions().within(3, () -> {
154                 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.LOCKED
155                 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.LOCKED
156             })
157
158         when: 'we wait for LOCKED CM handle retry time (actually just subtract 3 minutes from handles lastUpdateTime)'
159             overrideCmHandleLastUpdateTime('ch-1', OffsetDateTime.now().minusMinutes(3))
160             overrideCmHandleLastUpdateTime('ch-2', OffsetDateTime.now().minusMinutes(3))
161         and: 'failed CM handles are reset'
162             moduleSyncWatchdog.resetPreviouslyFailedCmHandles()
163         then: 'CM-handles are ADVISED state'
164             assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.ADVISED
165             assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.ADVISED
166
167         when: 'DMI is available for retry'
168             dmiDispatcher.isAvailable = true
169         and: 'DMI will return expected modules'
170             dmiDispatcher.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M3']]
171         and: 'module sync runs'
172             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
173         then: 'CM-handles go to READY state'
174             new PollingConditions().within(3, () -> {
175                 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.READY
176                 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.READY
177             })
178         and: 'CM-handles have expected modules'
179             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
180             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort()
181         and: 'CM-handles have expected module set tags (blank)'
182             assert objectUnderTest.getNcmpServiceCmHandle('ch-1').moduleSetTag == ''
183             assert objectUnderTest.getNcmpServiceCmHandle('ch-2').moduleSetTag == ''
184
185         cleanup: 'deregister CM handle'
186             deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2'])
187     }
188 }