diff options
Diffstat (limited to 'cps-ncmp-service/src/test')
7 files changed, 184 insertions, 53 deletions
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 3d8e9cb2e8..79f7e50e76 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -33,11 +33,13 @@ import org.onap.cps.ncmp.api.inventory.CompositeState import org.onap.cps.ncmp.api.inventory.InventoryPersistence import org.onap.cps.ncmp.api.inventory.LockReasonCategory import org.onap.cps.ncmp.api.inventory.DataStoreSyncState +import org.onap.cps.ncmp.api.models.BatchOperationDefinition import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters import org.onap.cps.ncmp.api.models.ConditionApiProperties import org.onap.cps.ncmp.api.models.DmiPluginRegistration import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle +import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest import org.onap.cps.spi.exceptions.CpsException import org.onap.cps.spi.model.ConditionProperties import spock.lang.Shared @@ -54,8 +56,8 @@ import spock.lang.Specification import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE +import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE class NetworkCmProxyDataServiceImplSpec extends Specification { @@ -133,21 +135,13 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { response == '{dmi-response}' } - def 'Get bulk resource data for #datastoreName from DMI.'() { + def 'Get batch resource data for #datastoreName from DMI.'() { given: 'cpsDataService returns valid data node' - mockDataNode() - and: 'DMI returns valid response and data' - mockDmiDataOperations.getResourceDataFromDmi(datastoreName, ['testCmHandle'], - 'testResourceId', OPTIONS_PARAM,'some topic','requestId') >> - new ResponseEntity<>('{dmi-bulk-response}', HttpStatus.OK) + def resourceDataBatchRequest = getResourceDataBatchRequest(datastoreName) when: 'get batch resource data is called' - def response = objectUnderTest.getResourceDataForCmHandleBatch(datastoreName, ['testCmHandle'], - 'testResourceId', - OPTIONS_PARAM, - 'some topic', - 'requestId') - then: 'get bulk resource data returns expected response' - response == '{dmi-bulk-response}' + objectUnderTest.requestResourceDataForCmHandleBatch('some topic', resourceDataBatchRequest, 'requestId') + then: 'get batch resource data returns expected response' + 1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', resourceDataBatchRequest, 'requestId') where: 'the following data stores are used' datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName] } @@ -373,4 +367,22 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry', cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode } + + def getResourceDataBatchRequest(datastore) { + def resourceDataBatchRequest = new ResourceDataBatchRequest() + def batchOperationDefinitions = new ArrayList() + batchOperationDefinitions.add(getBatchOperationDefinition(datastore)) + resourceDataBatchRequest.setBatchOperationDefinitions(batchOperationDefinitions) + } + + def getBatchOperationDefinition(datastore) { + def batchOperationDefinition = new BatchOperationDefinition() + batchOperationDefinition.setOperation("read") + batchOperationDefinition.setOperationId("operational-12") + batchOperationDefinition.setDatastore(datastore) + def targetIds = new ArrayList() + targetIds.add("some-cm-handle") + batchOperationDefinition.setCmHandleIds(targetIds) + return batchOperationDefinition + } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy index b38ca10f7b..6b0355eee8 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy @@ -34,9 +34,9 @@ import org.springframework.web.client.HttpServerErrorException import org.springframework.web.client.RestTemplate import spock.lang.Specification -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ +import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH +import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE @SpringBootTest @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiRestClient]) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy index 5fd4fbd43f..9343666260 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy @@ -24,6 +24,8 @@ package org.onap.cps.ncmp.api.impl.operations import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder +import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest +import org.onap.cps.ncmp.utils.TestUtils import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired @@ -35,9 +37,9 @@ import spock.lang.Shared import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE +import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ +import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE @SpringBootTest @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiDataOperations]) @@ -50,8 +52,6 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { def NO_REQUEST_ID = null @Shared def OPTIONS_PARAM = '(a=1,b=2)' - @Shared - def expectedBulkRequestAsJson = '{"operation": "read","data": {"fe1c1f1a070c4ce5bbfda7198592a0d3": {"neType": "RadioNode"},"b8e42eed0d9541ed8d8839e8eb86b3e0": {"neType": "RadioNode"}},"requestId": "bbb67474-f705-410a-93d1-b2844e7f45fd"}' @SpringBean JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) @@ -82,23 +82,26 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { 'datastore running with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-running' | '&options=(a=1,b=2)' } - def 'call get bulk resource data for #dataStore from DMI service with topic #scenario.'() { - given: 'collection of yang model cm Handles' + def 'call get batch resource data from DMI service #scenario.'() { + given: 'collection of yang model cm Handles and resource data batch request' mockYangModelCmHandleCollectionRetrieval([yangModelCmHandleProperty]) - and: 'a positive response from DMI service when it is called with the expected parameters' + def resourceDataBatchRequestJsonData = TestUtils.getResourceFileContent('resourceDataBatchRequest.json') + def resourceDataBatchRequest = spiedJsonObjectMapper.convertJsonString(resourceDataBatchRequestJsonData, ResourceDataBatchRequest.class) + resourceDataBatchRequest.batchOperationDefinitions[0].cmHandleIds = [cmHandleId] + def requestBodyAsJsonStringArg = null + and: 'a positive response from DMI service when it is called with valid request parameters' def responseFromDmi = new ResponseEntity<Object>(HttpStatus.ACCEPTED) - def expectedDmiBulkResourceDataUrl = "ncmp/v1/batch/data/ds/${dataStore}?resourceIdentifier=parent/child%26options=(a=1,b=2)&topic=my-topic-name&options=(fields=schemas/schema)" - mockDmiRestClient.postOperationWithJsonData(expectedDmiBulkResourceDataUrl, expectedBulkRequestAsJson, READ) >> responseFromDmi - dmiServiceUrlBuilder.getBulkRequestUrl(_, _) >> expectedDmiBulkResourceDataUrl - when: 'get resource data for bulk cm handle is invoked' - def result = objectUnderTest.getResourceDataFromDmi( dataStore.datastoreName, [cmHandleId], resourceIdentifier, - OPTIONS_PARAM, 'some-topic','requestId') - then: 'the result is the response from the DMI service' - assert result == responseFromDmi - where: 'the following parameters are used' - scenario | dataStore - 'datastore operational' | PASSTHROUGH_OPERATIONAL - 'datastore running' | PASSTHROUGH_RUNNING + def expectedDmiBatchResourceDataUrl = "ncmp/v1/data/topic=my-topic-name" + def expectedBatchRequestAsJson = '[{"operation":"read","operationId":"operational-14","datastore":"ncmp-datastore:passthrough-operational","options":"some option","resourceIdentifier":"some resource identifier","cmHandles":[{"id":"some-cm-handle","cmHandleProperties":{"prop1":"val1"}}]}]' + mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, _, READ.operationName) >> responseFromDmi + dmiServiceUrlBuilder.getBatchRequestUrl(_, _) >> expectedDmiBatchResourceDataUrl + when: 'get resource data for batch of cm handles are invoked' + objectUnderTest.requestResourceDataFromDmi('my-topic-name', resourceDataBatchRequest, 'requestId') + then: 'wait a little to allow execution of service method by task executor (on separate thread)' + Thread.sleep(100) + then: 'validate ncmp generated dmi request body json args' + 1 * mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, _, READ) >> { args -> requestBodyAsJsonStringArg = args[1] } + assert requestBodyAsJsonStringArg == expectedBatchRequestAsJson } def 'call get all resource data.'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy index ed74ad3342..d1025f9d65 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy @@ -36,7 +36,7 @@ import org.springframework.http.ResponseEntity import org.springframework.test.context.ContextConfiguration import spock.lang.Shared -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ +import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ @SpringBootTest @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiModelOperations]) @@ -103,9 +103,9 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { then: 'the result is the response from DMI service' assert result == [] where: 'the following DMI properties are used' - scenario | dmiProperties || expectedAdditionalPropertiesInRequest - 'with properties' | [yangModelCmHandleProperty] || '{"prop1":"val1"}' - 'without properties' | [] || '{}' + scenario | dmiProperties || expectedAdditionalPropertiesInRequest + 'with properties' | [yangModelCmHandleProperty] || '{"prop1":"val1"}' + 'without properties' | [] || '{}' } def 'Retrieving yang resources.'() { @@ -154,10 +154,10 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { then: 'the result is the response from DMI service' assert result == [mod1:'some yang source'] where: 'the following DMI properties are used' - scenario | dmiProperties | unknownModuleReferences || expectedAdditionalPropertiesInRequest | expectedModuleReferencesInRequest - 'with module references and properties' | [yangModelCmHandleProperty] | newModuleReferences || '{"prop1":"val1"}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' - 'without module references' | [yangModelCmHandleProperty] | [] || '{"prop1":"val1"}' | '' - 'without properties' | [] | newModuleReferences || '{}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' + scenario | dmiProperties | unknownModuleReferences || expectedAdditionalPropertiesInRequest | expectedModuleReferencesInRequest + 'with module references and properties' | [yangModelCmHandleProperty] | newModuleReferences || '{"prop1":"val1"}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' + 'without module references' | [yangModelCmHandleProperty] | [] || '{"prop1":"val1"}' | '' + 'without properties' | [] | newModuleReferences || '{}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' } def 'Retrieving yang resources from DMI with null DMI properties.'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtilsSpec.groovy new file mode 100644 index 0000000000..e65874930b --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtilsSpec.groovy @@ -0,0 +1,80 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.utils + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.inventory.CmHandleState +import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder +import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest +import org.onap.cps.ncmp.utils.TestUtils +import org.onap.cps.utils.JsonObjectMapper +import org.spockframework.spring.SpringBean +import spock.lang.Specification + +class ResourceDataBatchRequestUtilsSpec extends Specification { + + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + + def 'Process per operation in batch request with #serviceName.'() { + given: 'batch request with 3 operations' + def resourceDataBatchRequestJsonData = TestUtils.getResourceFileContent('resourceDataBatchRequest.json') + def resourceDataBatchRequest = jsonObjectMapper.convertJsonString(resourceDataBatchRequestJsonData, ResourceDataBatchRequest.class) + and: '4 known cm handles: ch1-dmi1, ch2-dmi1, ch3-dmi2, ch4-dmi2' + def yangModelCmHandles = getYangModelCmHandles() + when: 'Operation in batch request is processed' + def operationsOutPerDmiServiceName = ResourceDataBatchRequestUtils.processPerOperationInBatchRequest(resourceDataBatchRequest, yangModelCmHandles) + and: 'converted to a json node' + def dmiBatchRequestBody = jsonObjectMapper.asJsonString(operationsOutPerDmiServiceName.get(serviceName)) + def dmiBatchRequestBodyAsJsonNode = jsonObjectMapper.convertToJsonNode(dmiBatchRequestBody).get(operationIndex) + then: 'it contains the correct operation details' + assert dmiBatchRequestBodyAsJsonNode.get('operation').asText() == 'read' + assert dmiBatchRequestBodyAsJsonNode.get('operationId').asText() == expectedOperationId + assert dmiBatchRequestBodyAsJsonNode.get('datastore').asText() == expectedDatastore + and: 'the correct cm handles (just for #serviceName)' + assert dmiBatchRequestBodyAsJsonNode.get('cmHandles').size() == expectedCmHandleIds.size() + expectedCmHandleIds.each { + dmiBatchRequestBodyAsJsonNode.get('cmHandles').toString().contains(it) + } + where: 'the following dmi service and operations are checked' + serviceName | operationIndex || expectedOperationId | expectedDatastore | expectedCmHandleIds + 'dmi1' | 0 || 'operational-14' | 'ncmp-datastore:passthrough-operational' | ['ch6-dmi1'] + 'dmi1' | 1 || 'running-12' | 'ncmp-datastore:passthrough-running' | ['ch1-dmi1', 'ch2-dmi1'] + 'dmi1' | 2 || 'operational-15' | 'ncmp-datastore:passthrough-operational' | ['ch6-dmi1'] + 'dmi2' | 0 || 'operational-14' | 'ncmp-datastore:passthrough-operational' | ['ch3-dmi2'] + 'dmi2' | 1 || 'running-12' | 'ncmp-datastore:passthrough-running' | ['ch7-dmi2'] + 'dmi2' | 2 || 'operational-15' | 'ncmp-datastore:passthrough-operational' | ['ch4-dmi2'] + } + + static def getYangModelCmHandles() { + def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')] + def readyState = new CompositeStateBuilder().withCmHandleState(CmHandleState.READY).withLastUpdatedTimeNow().build() + return [new YangModelCmHandle(id: 'ch1-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState), + new YangModelCmHandle(id: 'ch2-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState), + new YangModelCmHandle(id: 'ch6-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState), + new YangModelCmHandle(id: 'ch8-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState), + new YangModelCmHandle(id: 'ch3-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState), + new YangModelCmHandle(id: 'ch4-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState), + new YangModelCmHandle(id: 'ch7-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState), + ] + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy index e1055bb217..dc471e64fa 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy @@ -64,13 +64,13 @@ class RestQueryParametersValidatorSpec extends Specification { and: 'the exception details contain the correct significant term ' thrown.details.contains(expectedWordInDetails) where: - scenario | conditionName | conditionParameters || expectedWordInDetails - 'unknown condition name' | 'unknownCondition' | [['key':'value']] || 'conditionName' - 'no condition name' | '' | [['key':'value']] || 'conditionName' - 'empty properties' | 'validConditionName' | [[ : ]] || 'conditionsParameter' - 'empty conditions' | 'validConditionName' | [[:]] || 'conditionsParameter' - 'too many properties' | 'validConditionName' | [[key1:'value1', key2:'value2']] || 'conditionsParameter' - 'empty key' | 'validConditionName' | [['':'wrong']] || 'conditionsParameter' + scenario | conditionName | conditionParameters || expectedWordInDetails + 'unknown condition name' | 'unknownCondition' | [['key': 'value']] || 'conditionName' + 'no condition name' | '' | [['key': 'value']] || 'conditionName' + 'empty properties' | 'validConditionName' | [[:]] || 'conditionsParameter' + 'empty conditions' | 'validConditionName' | [[:]] || 'conditionsParameter' + 'too many properties' | 'validConditionName' | [[key1: 'value1', key2: 'value2']] || 'conditionsParameter' + 'empty key' | 'validConditionName' | [['': 'wrong']] || 'conditionsParameter' } def 'CM Handle Query validation: validate module name condition properties - valid query.'() { diff --git a/cps-ncmp-service/src/test/resources/resourceDataBatchRequest.json b/cps-ncmp-service/src/test/resources/resourceDataBatchRequest.json new file mode 100644 index 0000000000..98ed39b9ae --- /dev/null +++ b/cps-ncmp-service/src/test/resources/resourceDataBatchRequest.json @@ -0,0 +1,36 @@ +{ + "operations": [ + { + "operation": "read", + "operationId": "operational-14", + "datastore": "ncmp-datastore:passthrough-operational", + "options": "some option", + "resourceIdentifier": "some resource identifier", + "targetIds": [ + "ch3-dmi2", + "unknown-cm-handle", + "ch6-dmi1" + ] + }, + { + "operation": "read", + "operationId": "running-12", + "datastore": "ncmp-datastore:passthrough-running", + "targetIds": [ + "ch1-dmi1", + "ch7-dmi2", + "ch2-dmi1" + ] + }, + { + "operation": "read", + "operationId": "operational-15", + "datastore": "ncmp-datastore:passthrough-operational", + "options": "some option", + "targetIds": [ + "ch4-dmi2", + "ch6-dmi1" + ] + } + ] +} |