d20ac337bfa6bfd245023dc4a4ff7c7f74e97b2a
[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
24 import org.apache.kafka.common.TopicPartition
25 import org.apache.kafka.common.serialization.StringDeserializer
26 import org.onap.cps.integration.KafkaTestContainer
27 import org.onap.cps.integration.base.CpsIntegrationSpecBase
28 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
29 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
30 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
31 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
32 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
33 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
34 import spock.util.concurrent.PollingConditions
35
36 import java.time.Duration
37 import java.time.OffsetDateTime
38
39 class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase {
40
41     NetworkCmProxyDataService objectUnderTest
42
43     def kafkaConsumer = KafkaTestContainer.getConsumer("ncmp-group", StringDeserializer.class);
44
45     static final MODULE_REFERENCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json')
46     static final MODULE_RESOURCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json')
47     static final MODULE_REFERENCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_Response.json')
48     static final MODULE_RESOURCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_ResourcesResponse.json')
49
50     def setup() {
51         objectUnderTest = networkCmProxyDataService
52     }
53
54     def 'CM Handle registration is successful.'() {
55         given: 'DMI will return modules when requested'
56             mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
57
58         and: 'consumer subscribed to topic'
59             kafkaConsumer.subscribe(['ncmp-events'])
60
61         when: 'a CM-handle is registered for creation'
62             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
63             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
64             def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
65
66         then: 'registration gives successful response'
67             assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')]
68
69         and: 'CM-handle is initially in ADVISED state'
70             assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
71
72         when: 'module sync runs'
73             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
74
75         then: 'CM-handle goes to READY state'
76             new PollingConditions().within(3, () -> {
77                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
78             })
79
80         and: 'the messages is polled'
81             def message = kafkaConsumer.poll(Duration.ofMillis(10000))
82             def records = message.records(new TopicPartition('ncmp-events', 0))
83
84         and: 'the newest lcm event notification is received with READY state'
85             assert records.last().value().toString().contains('"cmHandleState":"READY"')
86
87         and: 'the CM-handle has expected modules'
88             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
89
90         and: 'DMI received expected requests'
91             mockDmiServer.verify()
92
93         cleanup: 'deregister CM handle'
94             deregisterCmHandle(DMI_URL, 'ch-1')
95     }
96
97     def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() {
98         given: 'DMI is not available to handle requests'
99             mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-1')
100
101         when: 'a CM-handle is registered for creation'
102             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
103             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
104             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
105
106         and: 'module sync runs'
107             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
108
109         then: 'CM-handle goes to LOCKED state with reason MODULE_SYNC_FAILED'
110             new PollingConditions().within(3, () -> {
111                 def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState('ch-1')
112                 assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED
113                 assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_SYNC_FAILED
114             })
115
116         and: 'CM-handle has no modules'
117             assert objectUnderTest.getYangResourcesModuleReferences('ch-1').empty
118
119         cleanup: 'deregister CM handle'
120             deregisterCmHandle(DMI_URL, 'ch-1')
121     }
122
123     def 'Create a CM-handle with existing moduleSetTag.'() {
124         given: 'existing CM-handles cm-1 with moduleSetTag "A", and cm-2 with moduleSetTag "B"'
125             registerCmHandle(DMI_URL, 'ch-1', 'A', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
126             registerCmHandle(DMI_URL, 'ch-2', 'B', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B)
127             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
128             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort()
129
130         when: 'a CM-handle is registered for creation with moduleSetTag "B"'
131             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B')
132             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]))
133
134         then: 'the CM-handle goes to READY state after module sync'
135             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
136             new PollingConditions().within(3, () -> {
137                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState
138             })
139
140         and: 'the CM-handle has expected moduleSetTag'
141             assert objectUnderTest.getNcmpServiceCmHandle('ch-3').moduleSetTag == 'B'
142
143         and: 'the CM-handle has expected modules from module set "B": M1 and M3'
144             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
145
146         cleanup: 'deregister CM handles'
147             deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3'])
148     }
149
150     def 'CM Handle retry after failed module sync.'() {
151         given: 'DMI is not initially available to handle requests'
152             mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-1')
153             mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-2')
154         and: 'DMI will be available for retry'
155             mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
156             mockDmiResponsesForModuleSync(DMI_URL, 'ch-2', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B)
157
158         when: 'CM-handles are registered for creation'
159             def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')]
160             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate)
161             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
162         and: 'module sync runs'
163             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
164         then: 'CM-handles go to LOCKED state'
165             new PollingConditions().within(3, () -> {
166                 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.LOCKED
167                 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.LOCKED
168             })
169
170         when: 'we wait for LOCKED CM handle retry time (actually just subtract 3 minutes from handles lastUpdateTime)'
171             overrideCmHandleLastUpdateTime('ch-1', OffsetDateTime.now().minusMinutes(3))
172             overrideCmHandleLastUpdateTime('ch-2', OffsetDateTime.now().minusMinutes(3))
173         and: 'failed CM handles are reset'
174             moduleSyncWatchdog.resetPreviouslyFailedCmHandles()
175         then: 'CM-handles are ADVISED state'
176             assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.ADVISED
177             assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.ADVISED
178
179         when: 'module sync runs'
180             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
181         then: 'CM-handles go to READY state'
182             new PollingConditions().within(3, () -> {
183                 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.READY
184                 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.READY
185             })
186         and: 'CM-handles have expected modules'
187             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
188             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort()
189         and: 'CM-handles have expected module set tags (blank)'
190             assert objectUnderTest.getNcmpServiceCmHandle('ch-1').moduleSetTag == ''
191             assert objectUnderTest.getNcmpServiceCmHandle('ch-2').moduleSetTag == ''
192         and: 'DMI received expected requests'
193             mockDmiServer.verify()
194
195         cleanup: 'deregister CM handle'
196             deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2'])
197     }
198 }