Merge "Add withTrustLevel condition to CmHandle Query API"
[cps.git] / cps-ncmp-service / src / test / groovy / org / onap / cps / ncmp / api / impl / NetworkCmProxyDataServiceImplSpec.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2021-2023 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
11  *
12  *        http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  *
20  *  SPDX-License-Identifier: Apache-2.0
21  *  ============LICENSE_END=========================================================
22  */
23
24 package org.onap.cps.ncmp.api.impl
25
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
34
35 import com.hazelcast.map.IMap
36 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
37 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
38 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
39 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
40 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
41 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
42 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
43 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
44 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
45 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
46 import org.onap.cps.ncmp.api.models.DataOperationDefinition
47 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
48 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
49 import org.onap.cps.ncmp.api.models.ConditionApiProperties
50 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
51 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
52 import org.onap.cps.ncmp.api.models.DataOperationRequest
53 import org.onap.cps.spi.exceptions.CpsException
54 import org.onap.cps.spi.model.ConditionProperties
55 import spock.lang.Shared
56 import java.util.stream.Collectors
57 import org.onap.cps.utils.JsonObjectMapper
58 import com.fasterxml.jackson.databind.ObjectMapper
59 import org.onap.cps.api.CpsDataService
60 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
61 import org.onap.cps.spi.FetchDescendantsOption
62 import org.onap.cps.spi.model.DataNode
63 import org.springframework.http.HttpStatus
64 import org.springframework.http.ResponseEntity
65 import spock.lang.Specification
66
67 class NetworkCmProxyDataServiceImplSpec extends Specification {
68
69     def mockCpsDataService = Mock(CpsDataService)
70     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
71     def mockDmiDataOperations = Mock(DmiDataOperations)
72     def nullNetworkCmProxyDataServicePropertyHandler = null
73     def mockInventoryPersistence = Mock(InventoryPersistence)
74     def mockCmHandleQueries = Mock(CmHandleQueries)
75     def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
76     def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
77     def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
78     def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
79     def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
80
81     def NO_TOPIC = null
82     def NO_REQUEST_ID = null
83     @Shared
84     def OPTIONS_PARAM = '(a=1,b=2)'
85     @Shared
86     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id')
87
88     def objectUnderTest = new NetworkCmProxyDataServiceImpl(
89             spiedJsonObjectMapper,
90             mockDmiDataOperations,
91             nullNetworkCmProxyDataServicePropertyHandler,
92             mockInventoryPersistence,
93             mockCmHandleQueries,
94             mockCpsCmHandlerQueryService,
95             mockLcmEventsCmHandleStateHandler,
96             mockCpsDataService,
97             stubModuleSyncStartedOnCmHandles,
98             stubTrustLevelPerDmiPlugin)
99
100     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
101
102     def dataNode = [new DataNode(leaves: ['id': 'some-cm-handle', 'dmi-service-name': 'testDmiService'])]
103
104     def 'Write resource data for pass-through running from DMI using POST.'() {
105         given: 'cpsDataService returns valid datanode'
106             mockDataNode()
107         when: 'write resource data is called'
108             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
109                 'testResourceId', CREATE,
110                 '{some-json}', 'application/json')
111         then: 'DMI called with correct data'
112             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
113                     CREATE, '{some-json}', 'application/json')
114                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
115     }
116
117     def 'Get resource data for pass-through operational from DMI.'() {
118         given: 'cpsDataService returns valid data node'
119             mockDataNode()
120         and: 'get resource data from DMI is called'
121             mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.datastoreName,'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
122                     new ResponseEntity<>('dmi-response', HttpStatus.OK)
123         when: 'get resource data operational for cm-handle is called'
124             def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
125         then: 'DMI returns a json response'
126             assert response == 'dmi-response'
127     }
128
129     def 'Get resource data for pass-through running from DMI.'() {
130         given: 'cpsDataService returns valid data node'
131             mockDataNode()
132         and: 'DMI returns valid response and data'
133             mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
134                     new ResponseEntity<>('{dmi-response}', HttpStatus.OK)
135         when: 'get resource data is called'
136             def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
137         then: 'get resource data returns expected response'
138             assert response == '{dmi-response}'
139     }
140
141     def 'Get resource data for operational (cached) data.'() {
142         given: 'CPS Data service returns some object(s)'
143             mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object']
144         when: 'get resource data is called'
145             def response = objectUnderTest.getResourceDataForCmHandle(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS)
146         then: 'get resource data returns teh first object from the data service'
147             assert response == 'First Object'
148     }
149
150     def 'Execute (async) data operation for #datastoreName from DMI.'() {
151         given: 'cpsDataService returns valid data node'
152             def dataOperationRequest = getDataOperationRequest(datastoreName)
153         when: 'request resource data for data operation is called'
154             objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId')
155         then: 'request resource data for data operation returns expected response'
156             1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId')
157         where: 'the following data stores are used'
158             datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
159     }
160
161     def 'Getting Yang Resources.'() {
162         when: 'yang resources is called'
163             objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
164         then: 'CPS module services is invoked for the correct dataspace and cm handle'
165             1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
166     }
167
168     def 'Get a cm handle.'() {
169         given: 'the system returns a yang modelled cm handle'
170             def dmiServiceName = 'some service name'
171             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
172                 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
173                 lastUpdateTime: 'some-timestamp',
174                 dataSyncEnabled: false,
175                 dataStores: dataStores())
176             def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
177             def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
178             def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
179                 dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
180             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
181         when: 'getting cm handle details for a given cm handle id from ncmp service'
182             def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
183         then: 'the result is a ncmpServiceCmHandle'
184             result.class == NcmpServiceCmHandle.class
185         and: 'the cm handle contains the cm handle id'
186             result.cmHandleId == 'some-cm-handle'
187         and: 'the cm handle contains the DMI Properties'
188             result.dmiProperties ==[ Book:'Romance Novel' ]
189         and: 'the cm handle contains the public Properties'
190             result.publicProperties == [ "Public Book":'Public Romance Novel' ]
191         and: 'the cm handle contains the cm handle composite state'
192             result.compositeState == compositeState
193
194     }
195
196     def 'Get cm handle public properties'() {
197         given: 'a yang modelled cm handle'
198             def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
199             def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
200             def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
201         and: 'the system returns this yang modelled cm handle'
202             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
203         when: 'getting cm handle public properties for a given cm handle id from ncmp service'
204             def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
205         then: 'the result returns the correct data'
206             result == [ 'public prop' : 'some public prop' ]
207     }
208
209     def 'Execute cm handle id search for inventory'() {
210         given: 'a ConditionApiProperties object'
211             def conditionProperties = new ConditionProperties()
212             conditionProperties.conditionName = 'hasAllProperties'
213             conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ]
214             def conditionServiceProps = new CmHandleQueryServiceParameters()
215             conditionServiceProps.cmHandleQueryParameters = [conditionProperties] as List<ConditionProperties>
216         and: 'the system returns an set of cmHandle ids'
217             mockCpsCmHandlerQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ]
218         when: 'getting cm handle id set for a given dmi property'
219             def result = objectUnderTest.executeCmHandleIdSearchForInventory(conditionServiceProps)
220         then: 'the result returns the correct 2 elements'
221             assert result.size() == 2
222             assert result.contains('cmHandle1')
223             assert result.contains('cmHandle2')
224     }
225
226     def 'Get cm handle composite state'() {
227         given: 'a yang modelled cm handle'
228             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
229                 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
230                 lastUpdateTime: 'some-timestamp',
231                 dataSyncEnabled: false,
232                 dataStores: dataStores())
233             def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
234             def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
235             def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
236         and: 'the system returns this yang modelled cm handle'
237             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
238         when: 'getting cm handle composite state for a given cm handle id from ncmp service'
239             def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle')
240         then: 'the result returns the correct data'
241             result == compositeState
242     }
243
244     def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
245         given: 'cpsDataService returns valid datanode'
246             mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
247         when: 'get resource data is called'
248             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
249                 'testResourceId', UPDATE,
250                 '{some-json}', 'application/json')
251         then: 'DMI called with correct data'
252             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
253                     UPDATE, '{some-json}', 'application/json')
254                 >> { new ResponseEntity<>(HttpStatus.OK) }
255     }
256
257     def 'Verify modules and create anchor params.'() {
258         given: 'dmi plugin registration return created cm handles'
259             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
260                     dmiDataPlugin: 'service2')
261             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
262             mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
263         when: 'parse and create cm handle in dmi registration then sync module'
264             objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration)
265         then: 'system persists the cm handle state'
266             1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> {
267                 args -> {
268                           def cmHandleStatePerCmHandle = (args[0] as Map)
269                           cmHandleStatePerCmHandle.each {
270                             assert it.key.id == 'test-cm-handle-id' && it.value == CmHandleState.ADVISED
271                           }
272                     }
273             }
274     }
275     
276     def 'Execute cm handle id search'() {
277         given: 'valid CmHandleQueryApiParameters input'
278             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
279             def conditionApiProperties = new ConditionApiProperties()
280             conditionApiProperties.conditionName = 'hasAllModules'
281             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
282             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
283         and: 'query cm handle method return with a data node list'
284             mockCpsCmHandlerQueryService.queryCmHandleIds(
285                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
286                     >> ['cm-handle-id-1']
287         when: 'execute cm handle search is called'
288             def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
289         then: 'result is the same collection as returned by the CPS Data Service'
290             assert result == ['cm-handle-id-1']
291     }
292
293     def 'Getting module definitions.'() {
294         when: 'get module definitions method is called with a valid cm handle ID'
295             objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
296         then: 'CPS module services is invoked once'
297             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
298     }
299
300     def 'Execute cm handle search'() {
301         given: 'valid CmHandleQueryApiParameters input'
302             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
303             def conditionApiProperties = new ConditionApiProperties()
304             conditionApiProperties.conditionName = 'hasAllModules'
305             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
306             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
307         and: 'query cm handle method return with a data node list'
308             mockCpsCmHandlerQueryService.queryCmHandles(
309                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
310                     >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
311         when: 'execute cm handle search is called'
312             def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
313         then: 'result is the same collection as returned by the CPS Data Service'
314             assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
315     }
316
317     def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is  #scenario'() {
318         given: 'an existing cm handle composite state'
319             def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
320                 dataStores: CompositeState.DataStores.builder()
321                     .operationalDataStore(CompositeState.Operational.builder()
322                         .dataStoreSyncState(initialDataSyncState)
323                         .build()).build())
324         and: 'get cm handle state returns the composite state for the given cm handle id'
325             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
326         when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
327             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
328         then: 'the data sync enabled flag is set to #dataSyncEnabled'
329             compositeState.dataSyncEnabled == dataSyncEnabledFlag
330         and: 'the data store sync state is set to #expectedDataStoreSyncState'
331             compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
332         and: 'the cps data service to delete data nodes is invoked the expected number of times'
333             deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
334         and: 'the inventory persistence service to update node leaves is called with the correct values'
335             saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
336         where: 'the following data sync enabled flag is used'
337             scenario                                              | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState               || expectedDataStoreSyncState         | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
338             'enabled'                                             | true                | false                      | DataStoreSyncState.NONE_REQUESTED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 1
339             'disabled'                                            | false               | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.NONE_REQUESTED  | 0                                        | 1
340             'disabled where sync-state is currently SYNCHRONIZED' | false               | true                       | DataStoreSyncState.SYNCHRONIZED    || DataStoreSyncState.NONE_REQUESTED  | 1                                        | 1
341             'is set to existing flag state'                       | true                | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 0
342     }
343
344     def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
345         given: 'a cm handle composite state'
346             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
347         and: 'get cm handle state returns the composite state for the given cm handle id'
348             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
349         when: 'set data sync enabled is called with the data sync enabled flag set to true'
350             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
351         then: 'the expected exception is thrown'
352             thrown(CpsException)
353         and: 'the inventory persistence service to update node leaves is not invoked'
354             0 * mockInventoryPersistence.saveCmHandleState(_, _)
355     }
356
357     def 'Get all cm handle IDs by DMI plugin identifier.' () {
358         given: 'cm handle queries service returns cm handles'
359             1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
360         when: 'cm handle Ids are requested with dmi plugin identifier'
361             def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
362         then: 'the result size is correct'
363             assert result.size() == 2
364         and: 'the result returns the correct details'
365             assert result.containsAll('cm-handle-1','cm-handle-2')
366     }
367
368     def dataStores() {
369         CompositeState.DataStores.builder()
370             .operationalDataStore(CompositeState.Operational.builder()
371                 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
372                 .lastSyncTime('some-timestamp').build()).build()
373     }
374
375     def mockDataNode() {
376         mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
377     }
378
379     def getDataOperationRequest(datastore) {
380         def dataOperationRequest = new DataOperationRequest()
381         def dataOperationDefinitions = new ArrayList()
382         dataOperationDefinitions.add(getDataOperationDefinition(datastore))
383         dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
384         return dataOperationRequest
385     }
386
387     def getDataOperationDefinition(datastore) {
388         def dataOperationDefinition = new DataOperationDefinition()
389         dataOperationDefinition.setOperation("read")
390         dataOperationDefinition.setOperationId("operational-12")
391         dataOperationDefinition.setDatastore(datastore)
392         def targetIds = new ArrayList()
393         targetIds.add("some-cm-handle")
394         dataOperationDefinition.setCmHandleIds(targetIds)
395         return dataOperationDefinition
396     }
397 }