2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 Nordix Foundation
4 * Modifications Copyright (C) 2021 Pantheon.tech
5 * Modifications Copyright (C) 2021-2022 Bell Canada
6 * Modifications Copyright (C) 2023 TechMahindra Ltd.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
20 * SPDX-License-Identifier: Apache-2.0
21 * ============LICENSE_END=========================================================
24 package org.onap.cps.ncmp.api.impl
26 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
27 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
28 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
29 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
30 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
31 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
32 import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
33 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
35 import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
36 import com.hazelcast.map.IMap
37 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
38 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
39 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
40 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
41 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
42 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
43 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
44 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
45 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
46 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
47 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
48 import org.onap.cps.ncmp.api.models.DataOperationDefinition
49 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
50 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
51 import org.onap.cps.ncmp.api.models.ConditionApiProperties
52 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
53 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
54 import org.onap.cps.ncmp.api.models.DataOperationRequest
55 import org.onap.cps.spi.exceptions.CpsException
56 import org.onap.cps.spi.model.ConditionProperties
58 import java.util.stream.Collectors
59 import org.onap.cps.utils.JsonObjectMapper
60 import com.fasterxml.jackson.databind.ObjectMapper
61 import org.onap.cps.api.CpsDataService
62 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
63 import org.onap.cps.spi.FetchDescendantsOption
64 import org.onap.cps.spi.model.DataNode
65 import org.springframework.http.HttpStatus
66 import org.springframework.http.ResponseEntity
67 import spock.lang.Specification
69 class NetworkCmProxyDataServiceImplSpec extends Specification {
71 def mockCpsDataService = Mock(CpsDataService)
72 def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
73 def mockDmiDataOperations = Mock(DmiDataOperations)
74 def nullNetworkCmProxyDataServicePropertyHandler = null
75 def mockInventoryPersistence = Mock(InventoryPersistence)
76 def mockCmHandleQueries = Mock(CmHandleQueries)
77 def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
78 def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
79 def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
80 def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
81 def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
82 def mockTrustLevelManager = Mock(TrustLevelManager)
83 def mockAlternateIdChecker = Mock(AlternateIdChecker)
84 def mockModuleSetTagCache = [:]
87 def NO_REQUEST_ID = null
88 def OPTIONS_PARAM = '(a=1,b=2)'
89 def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id')
91 def objectUnderTest = new NetworkCmProxyDataServiceImpl(
92 spiedJsonObjectMapper,
93 mockDmiDataOperations,
94 nullNetworkCmProxyDataServicePropertyHandler,
95 mockInventoryPersistence,
97 mockCpsCmHandlerQueryService,
98 mockLcmEventsCmHandleStateHandler,
100 stubModuleSyncStartedOnCmHandles,
101 stubTrustLevelPerDmiPlugin,
102 mockTrustLevelManager,
103 mockAlternateIdChecker,
104 mockModuleSetTagCache)
106 def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
108 def dataNode = [new DataNode(leaves: ['id': 'some-cm-handle', 'dmi-service-name': 'testDmiService'])]
110 def 'Write resource data for pass-through running from DMI using POST.'() {
111 given: 'cpsDataService returns valid datanode'
113 when: 'write resource data is called'
114 objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
115 'testResourceId', CREATE,
116 '{some-json}', 'application/json')
117 then: 'DMI called with correct data'
118 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
119 CREATE, '{some-json}', 'application/json')
120 >> { new ResponseEntity<>(HttpStatus.CREATED) }
123 def 'Get resource data for pass-through operational from DMI.'() {
124 given: 'cpsDataService returns valid data node'
126 and: 'get resource data from DMI is called'
127 mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.datastoreName,'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
128 new ResponseEntity<>('dmi-response', HttpStatus.OK)
129 when: 'get resource data operational for cm-handle is called'
130 def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
131 then: 'DMI returns a json response'
132 assert response == 'dmi-response'
135 def 'Get resource data for pass-through running from DMI.'() {
136 given: 'cpsDataService returns valid data node'
138 and: 'DMI returns valid response and data'
139 mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
140 new ResponseEntity<>('{dmi-response}', HttpStatus.OK)
141 when: 'get resource data is called'
142 def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
143 then: 'get resource data returns expected response'
144 assert response == '{dmi-response}'
147 def 'Get resource data for operational (cached) data.'() {
148 given: 'CPS Data service returns some object(s)'
149 mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object']
150 when: 'get resource data is called'
151 def response = objectUnderTest.getResourceDataForCmHandle(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS)
152 then: 'get resource data returns teh first object from the data service'
153 assert response == 'First Object'
156 def 'Execute (async) data operation for #datastoreName from DMI.'() {
157 given: 'cpsDataService returns valid data node'
158 def dataOperationRequest = getDataOperationRequest(datastoreName)
159 when: 'request resource data for data operation is called'
160 objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId')
161 then: 'request resource data for data operation returns expected response'
162 1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId')
163 where: 'the following data stores are used'
164 datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
167 def 'Getting Yang Resources.'() {
168 when: 'yang resources is called'
169 objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
170 then: 'CPS module services is invoked for the correct dataspace and cm handle'
171 1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
174 def 'Get a cm handle.'() {
175 given: 'the system returns a yang modelled cm handle'
176 def dmiServiceName = 'some service name'
177 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
178 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
179 lastUpdateTime: 'some-timestamp',
180 dataSyncEnabled: false,
181 dataStores: dataStores())
182 def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
183 def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
184 def moduleSetTag = 'some-module-set-tag'
185 def alternateId = 'some-alternate-id'
186 def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
187 dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState,
188 moduleSetTag: moduleSetTag, alternateId: alternateId)
189 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
190 when: 'getting cm handle details for a given cm handle id from ncmp service'
191 def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
192 then: 'the result is a ncmpServiceCmHandle'
193 result.class == NcmpServiceCmHandle.class
194 and: 'the cm handle contains the cm handle id'
195 result.cmHandleId == 'some-cm-handle'
196 and: 'the cm handle contains the alternate id'
197 result.alternateId == 'some-alternate-id'
198 and: 'the cm handle contains the module-set-tag'
199 result.moduleSetTag == 'some-module-set-tag'
200 and: 'the cm handle contains the DMI Properties'
201 result.dmiProperties ==[ Book:'Romance Novel' ]
202 and: 'the cm handle contains the public Properties'
203 result.publicProperties == [ "Public Book":'Public Romance Novel' ]
204 and: 'the cm handle contains the cm handle composite state'
205 result.compositeState == compositeState
208 def 'Get cm handle public properties'() {
209 given: 'a yang modelled cm handle'
210 def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
211 def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
212 def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
213 and: 'the system returns this yang modelled cm handle'
214 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
215 when: 'getting cm handle public properties for a given cm handle id from ncmp service'
216 def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
217 then: 'the result returns the correct data'
218 result == [ 'public prop' : 'some public prop' ]
221 def 'Execute cm handle id search for inventory'() {
222 given: 'a ConditionApiProperties object'
223 def conditionProperties = new ConditionProperties()
224 conditionProperties.conditionName = 'hasAllProperties'
225 conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ]
226 def conditionServiceProps = new CmHandleQueryServiceParameters()
227 conditionServiceProps.cmHandleQueryParameters = [conditionProperties] as List<ConditionProperties>
228 and: 'the system returns an set of cmHandle ids'
229 mockCpsCmHandlerQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ]
230 when: 'getting cm handle id set for a given dmi property'
231 def result = objectUnderTest.executeCmHandleIdSearchForInventory(conditionServiceProps)
232 then: 'the result returns the correct 2 elements'
233 assert result.size() == 2
234 assert result.contains('cmHandle1')
235 assert result.contains('cmHandle2')
238 def 'Get cm handle composite state'() {
239 given: 'a yang modelled cm handle'
240 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
241 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
242 lastUpdateTime: 'some-timestamp',
243 dataSyncEnabled: false,
244 dataStores: dataStores())
245 def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
246 def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
247 def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
248 and: 'the system returns this yang modelled cm handle'
249 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
250 when: 'getting cm handle composite state for a given cm handle id from ncmp service'
251 def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle')
252 then: 'the result returns the correct data'
253 result == compositeState
256 def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
257 given: 'cpsDataService returns valid datanode'
258 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
259 when: 'get resource data is called'
260 objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
261 'testResourceId', UPDATE,
262 '{some-json}', 'application/json')
263 then: 'DMI called with correct data'
264 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
265 UPDATE, '{some-json}', 'application/json')
266 >> { new ResponseEntity<>(HttpStatus.OK) }
269 def 'Verify modules and create anchor params.'() {
270 given: 'dmi plugin registration return created cm handles'
271 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
272 dmiDataPlugin: 'service2')
273 dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
274 mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
275 when: 'parse and create cm handle in dmi registration then sync module'
276 objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration, ['test-cm-handle-id'])
277 then: 'system persists the cm handle state'
278 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
280 def cmHandleStatePerCmHandle = (args[0] as Collection)
281 cmHandleStatePerCmHandle.each {
282 assert it.id == 'test-cm-handle-id'
288 def 'Execute cm handle id search'() {
289 given: 'valid CmHandleQueryApiParameters input'
290 def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
291 def conditionApiProperties = new ConditionApiProperties()
292 conditionApiProperties.conditionName = 'hasAllModules'
293 conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
294 cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
295 and: 'query cm handle method return with a data node list'
296 mockCpsCmHandlerQueryService.queryCmHandleIds(
297 spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
298 >> ['cm-handle-id-1']
299 when: 'execute cm handle search is called'
300 def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
301 then: 'result is the same collection as returned by the CPS Data Service'
302 assert result == ['cm-handle-id-1']
305 def 'Getting module definitions by module'() {
306 when: 'get module definitions is performed with module name'
307 objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
308 then: 'ncmp inventory persistence service is invoked once with correct parameters'
309 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
312 def 'Getting module definitions by cm handle id'() {
313 when: 'get module definitions is performed with cm handle id'
314 objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
315 then: 'ncmp inventory persistence service is invoked once with correct parameter'
316 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
319 def 'Execute cm handle search'() {
320 given: 'valid CmHandleQueryApiParameters input'
321 def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
322 def conditionApiProperties = new ConditionApiProperties()
323 conditionApiProperties.conditionName = 'hasAllModules'
324 conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
325 cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
326 and: 'query cm handle method return with a data node list'
327 mockCpsCmHandlerQueryService.queryCmHandles(
328 spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
329 >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
330 when: 'execute cm handle search is called'
331 def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
332 then: 'result is the same collection as returned by the CPS Data Service'
333 assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
336 def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is #scenario'() {
337 given: 'an existing cm handle composite state'
338 def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
339 dataStores: CompositeState.DataStores.builder()
340 .operationalDataStore(CompositeState.Operational.builder()
341 .dataStoreSyncState(initialDataSyncState)
343 and: 'get cm handle state returns the composite state for the given cm handle id'
344 mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
345 when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
346 objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
347 then: 'the data sync enabled flag is set to #dataSyncEnabled'
348 compositeState.dataSyncEnabled == dataSyncEnabledFlag
349 and: 'the data store sync state is set to #expectedDataStoreSyncState'
350 compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
351 and: 'the cps data service to delete data nodes is invoked the expected number of times'
352 deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
353 and: 'the inventory persistence service to update node leaves is called with the correct values'
354 saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
355 where: 'the following data sync enabled flag is used'
356 scenario | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState || expectedDataStoreSyncState | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
357 'enabled' | true | false | DataStoreSyncState.NONE_REQUESTED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 1
358 'disabled' | false | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 0 | 1
359 'disabled where sync-state is currently SYNCHRONIZED' | false | true | DataStoreSyncState.SYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 1 | 1
360 'is set to existing flag state' | true | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 0
363 def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
364 given: 'a cm handle composite state'
365 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
366 and: 'get cm handle state returns the composite state for the given cm handle id'
367 mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
368 when: 'set data sync enabled is called with the data sync enabled flag set to true'
369 objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
370 then: 'the expected exception is thrown'
372 and: 'the inventory persistence service to update node leaves is not invoked'
373 0 * mockInventoryPersistence.saveCmHandleState(_, _)
376 def 'Get all cm handle IDs by DMI plugin identifier.' () {
377 given: 'cm handle queries service returns cm handles'
378 1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
379 when: 'cm handle Ids are requested with dmi plugin identifier'
380 def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
381 then: 'the result size is correct'
382 assert result.size() == 2
383 and: 'the result returns the correct details'
384 assert result.containsAll('cm-handle-1','cm-handle-2')
388 CompositeState.DataStores.builder()
389 .operationalDataStore(CompositeState.Operational.builder()
390 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
391 .lastSyncTime('some-timestamp').build()).build()
395 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
398 def getDataOperationRequest(datastore) {
399 def dataOperationRequest = new DataOperationRequest()
400 def dataOperationDefinitions = new ArrayList()
401 dataOperationDefinitions.add(getDataOperationDefinition(datastore))
402 dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
403 return dataOperationRequest
406 def getDataOperationDefinition(datastore) {
407 def dataOperationDefinition = new DataOperationDefinition()
408 dataOperationDefinition.setOperation("read")
409 dataOperationDefinition.setOperationId("operational-12")
410 dataOperationDefinition.setDatastore(datastore)
411 def targetIds = new ArrayList()
412 targetIds.add("some-cm-handle")
413 dataOperationDefinition.setCmHandleIds(targetIds)
414 return dataOperationDefinition