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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.integration.functional
23 import java.time.OffsetDateTime
24 import org.onap.cps.integration.base.CpsIntegrationSpecBase
25 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
26 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
27 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
28 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
29 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
30 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
31 import spock.util.concurrent.PollingConditions
33 class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase {
35 NetworkCmProxyDataService objectUnderTest
37 static final MODULE_REFERENCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json')
38 static final MODULE_RESOURCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json')
39 static final MODULE_REFERENCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_Response.json')
40 static final MODULE_RESOURCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_ResourcesResponse.json')
43 objectUnderTest = networkCmProxyDataService
46 def 'CM Handle registration is successful.'() {
47 given: 'DMI will return modules when requested'
48 mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
50 when: 'a CM-handle is registered for creation'
51 def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
52 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
53 def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
55 then: 'registration gives successful response'
56 assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')]
58 and: 'CM-handle is initially in ADVISED state'
59 assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
61 when: 'module sync runs'
62 moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
64 then: 'CM-handle goes to READY state'
65 new PollingConditions().within(3, () -> {
66 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
69 and: 'the CM-handle has expected modules'
70 assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
72 and: 'DMI received expected requests'
73 mockDmiServer.verify()
75 cleanup: 'deregister CM handle'
76 deregisterCmHandle(DMI_URL, 'ch-1')
79 def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() {
80 given: 'DMI is not available to handle requests'
81 mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-1')
83 when: 'a CM-handle is registered for creation'
84 def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
85 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
86 networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
88 and: 'module sync runs'
89 moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
91 then: 'CM-handle goes to LOCKED state with reason MODULE_SYNC_FAILED'
92 new PollingConditions().within(3, () -> {
93 def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState('ch-1')
94 assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED
95 assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_SYNC_FAILED
98 and: 'CM-handle has no modules'
99 assert objectUnderTest.getYangResourcesModuleReferences('ch-1').empty
101 cleanup: 'deregister CM handle'
102 deregisterCmHandle(DMI_URL, 'ch-1')
105 def 'Create a CM-handle with existing moduleSetTag.'() {
106 given: 'existing CM-handles cm-1 with moduleSetTag "A", and cm-2 with moduleSetTag "B"'
107 registerCmHandle(DMI_URL, 'ch-1', 'A', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
108 registerCmHandle(DMI_URL, 'ch-2', 'B', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B)
109 assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
110 assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort()
112 when: 'a CM-handle is registered for creation with moduleSetTag "B"'
113 def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B')
114 networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]))
116 then: 'the CM-handle goes to READY state after module sync'
117 moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
118 new PollingConditions().within(3, () -> {
119 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState
122 and: 'the CM-handle has expected moduleSetTag'
123 assert objectUnderTest.getNcmpServiceCmHandle('ch-3').moduleSetTag == 'B'
125 and: 'the CM-handle has expected modules from module set "B": M1 and M3'
126 assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
128 cleanup: 'deregister CM handles'
129 deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3'])
132 def 'CM Handle retry after failed module sync.'() {
133 given: 'DMI is not initially available to handle requests'
134 mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-1')
135 mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-2')
136 and: 'DMI will be available for retry'
137 mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
138 mockDmiResponsesForModuleSync(DMI_URL, 'ch-2', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B)
140 when: 'CM-handles are registered for creation'
141 def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')]
142 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate)
143 networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
144 and: 'module sync runs'
145 moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
146 then: 'CM-handles go to LOCKED state'
147 new PollingConditions().within(3, () -> {
148 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.LOCKED
149 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.LOCKED
152 when: 'we wait for LOCKED CM handle retry time (actually just subtract 3 minutes from handles lastUpdateTime)'
153 overrideCmHandleLastUpdateTime('ch-1', OffsetDateTime.now().minusMinutes(3))
154 overrideCmHandleLastUpdateTime('ch-2', OffsetDateTime.now().minusMinutes(3))
155 and: 'failed CM handles are reset'
156 moduleSyncWatchdog.resetPreviouslyFailedCmHandles()
157 then: 'CM-handles are ADVISED state'
158 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.ADVISED
159 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.ADVISED
161 when: 'module sync runs'
162 moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
163 then: 'CM-handles go to READY state'
164 new PollingConditions().within(3, () -> {
165 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.READY
166 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.READY
168 and: 'CM-handles have expected modules'
169 assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
170 assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort()
171 and: 'CM-handles have expected module set tags (blank)'
172 assert objectUnderTest.getNcmpServiceCmHandle('ch-1').moduleSetTag == ''
173 assert objectUnderTest.getNcmpServiceCmHandle('ch-2').moduleSetTag == ''
174 and: 'DMI received expected requests'
175 mockDmiServer.verify()
177 cleanup: 'deregister CM handle'
178 deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2'])