From a63d5a2325c8a730a84256d2e68258af9d872a49 Mon Sep 17 00:00:00 2001 From: "ajay.dp001" Date: Wed, 10 Jul 2019 12:23:05 +0100 Subject: [PATCH] Teach RFC8040 restconf about actions This adds the handlers to route action requests through DOMActionService and return results. JIRA: NETCONF-618 Change-Id: I9d72fa5939b73beefbbb2e8030b1fa0a1f4ff935 Signed-off-by: ajay.dp001 Signed-off-by: Robert Varga --- .../nb/rfc8040/Rfc8040RestConfWiring.java | 18 +-- .../handlers/ActionServiceHandler.java | 40 +++++++ .../NormalizedNodeJsonBodyWriter.java | 13 +++ .../NormalizedNodeXmlBodyWriter.java | 9 ++ .../XmlNormalizedNodeBodyReader.java | 28 ++--- .../impl/RestconfDataServiceImpl.java | 89 ++++++++++++-- .../rests/utils/ActionResultFactory.java | 22 ++++ .../rfc8040/rests/utils/FutureCallbackTx.java | 5 + .../utils/RestconfInvokeOperationsUtil.java | 110 ++++++++++++++++++ .../services/wrapper/ServicesWrapper.java | 44 ++++--- .../XmlBodyReaderMountPointTest.java | 14 +++ .../providers/test/JsonBodyReaderTest.java | 21 ++++ .../providers/test/XmlBodyReaderTest.java | 20 ++++ .../JSONRestconfServiceRfc8040ImplTest.java | 9 +- .../impl/RestconfDataServiceImplTest.java | 6 +- .../test/incubate/InMemoryMdsalModule.java | 7 ++ .../json/json_cont_action.json | 5 + .../xml/xml_cont_action.xml | 4 + .../yang/instance-identifier-module.yang | 18 ++- 19 files changed, 419 insertions(+), 63 deletions(-) create mode 100644 restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/ActionServiceHandler.java create mode 100644 restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ActionResultFactory.java create mode 100644 restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/json/json_cont_action.json create mode 100644 restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/xml/xml_cont_action.xml diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/Rfc8040RestConfWiring.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/Rfc8040RestConfWiring.java index d3fe40686a..0f3f5fb29a 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/Rfc8040RestConfWiring.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/Rfc8040RestConfWiring.java @@ -11,6 +11,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import org.apache.aries.blueprint.annotation.service.Reference; import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler; @@ -34,18 +35,19 @@ import org.opendaylight.restconf.nb.rfc8040.web.WebInitializer; */ @Singleton public class Rfc8040RestConfWiring { - private final ServicesWrapper servicesWrapper; @Inject - public Rfc8040RestConfWiring( - SchemaContextHandler schemaCtxHandler, - DOMMountPointServiceHandler domMountPointServiceHandler, TransactionChainHandler transactionChainHandler, - DOMDataBrokerHandler domDataBrokerHandler, RpcServiceHandler rpcServiceHandler, - NotificationServiceHandler notificationServiceHandler, @Reference DOMSchemaService domSchemaService) { + public Rfc8040RestConfWiring(final SchemaContextHandler schemaCtxHandler, + final DOMMountPointServiceHandler domMountPointServiceHandler, + final TransactionChainHandler transactionChainHandler, + final DOMDataBrokerHandler domDataBrokerHandler, final RpcServiceHandler rpcServiceHandler, + final ActionServiceHandler actionServiceHandler, + final NotificationServiceHandler notificationServiceHandler, + @Reference final DOMSchemaService domSchemaService) { servicesWrapper = ServicesWrapper.newInstance(schemaCtxHandler, domMountPointServiceHandler, - transactionChainHandler, domDataBrokerHandler, rpcServiceHandler, notificationServiceHandler, - domSchemaService); + transactionChainHandler, domDataBrokerHandler, rpcServiceHandler, actionServiceHandler, + notificationServiceHandler, domSchemaService); } public ServicesWrapper getServicesWrapper() { diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/ActionServiceHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/ActionServiceHandler.java new file mode 100644 index 0000000000..b6b5eb10ad --- /dev/null +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/ActionServiceHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 Ericsson Software Technology AB. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.restconf.nb.rfc8040.handlers; + +import static java.util.Objects.requireNonNull; + +import javax.inject.Inject; +import javax.inject.Singleton; +import org.apache.aries.blueprint.annotation.service.Reference; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.dom.api.DOMActionService; + +/** + * Implementation of {@link ActionServiceHandler}. + */ +@Singleton +public class ActionServiceHandler implements Handler { + private final @NonNull DOMActionService actionService; + + /** + * Set DOMActionService. + * + * @param actionService + * DOMActionService + */ + @Inject + public ActionServiceHandler(final @Reference DOMActionService actionService) { + this.actionService = requireNonNull(actionService); + } + + @Override + public @NonNull DOMActionService get() { + return this.actionService; + } +} diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeJsonBodyWriter.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeJsonBodyWriter.java index 2f586909f8..1272a0fee4 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeJsonBodyWriter.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeJsonBodyWriter.java @@ -41,6 +41,7 @@ import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier; import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; @@ -119,6 +120,18 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter iiToDataList = new ArrayList<>(); - if (isPost() && !isRpc) { + if (isPost() && !isOperation) { final Deque foundSchemaNodes = findPathToSchemaNodeByName(schemaNode, docRootElm, docRootNamespace); if (foundSchemaNodes.isEmpty()) { throw new IllegalStateException(String.format("Child \"%s\" was not found in parent schema node \"%s\"", @@ -122,13 +124,11 @@ public class XmlNormalizedNodeBodyReader extends AbstractNormalizedNodeBodyReade } } // PUT - } else if (!isRpc) { + } else if (!isOperation) { final QName scQName = schemaNode.getQName(); - Preconditions.checkState( - docRootElm.equals(scQName.getLocalName()) - && docRootNamespace.equals(scQName.getNamespace().toASCIIString()), - String.format("Not correct message root element \"%s\", should be \"%s\"", - docRootElm, scQName)); + checkState(docRootElm.equals(scQName.getLocalName()) + && docRootNamespace.equals(scQName.getNamespace().toASCIIString()), + "Not correct message root element \"%s\", should be \"%s\"", docRootElm, scQName); } NormalizedNode parsed; diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java index 7c55e3486f..2b81a48f6e 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java @@ -18,12 +18,13 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map.Entry; -import java.util.Objects; import java.util.Optional; import javax.ws.rs.Path; +import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.dom.api.DOMActionResult; import org.opendaylight.mdsal.dom.api.DOMDataBroker; import org.opendaylight.mdsal.dom.api.DOMMountPoint; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; @@ -31,8 +32,11 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext; import org.opendaylight.restconf.common.context.WriterParameters; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag; +import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; import org.opendaylight.restconf.common.patch.PatchContext; import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler; @@ -46,12 +50,17 @@ import org.opendaylight.restconf.nb.rfc8040.rests.utils.PostDataTransactionUtil; import org.opendaylight.restconf.nb.rfc8040.rests.utils.PutDataTransactionUtil; import org.opendaylight.restconf.nb.rfc8040.rests.utils.ReadDataTransactionUtil; import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant; +import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfInvokeOperationsUtil; import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants; import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,20 +73,24 @@ public class RestconfDataServiceImpl implements RestconfDataService { private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class); private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss"); + private final RestconfStreamsSubscriptionService delegRestconfSubscrService; + + // FIXME: evaluate thread-safety of updates (synchronized) vs. access (mostly unsynchronized) here private SchemaContextHandler schemaContextHandler; private TransactionChainHandler transactionChainHandler; private DOMMountPointServiceHandler mountPointServiceHandler; - - private final RestconfStreamsSubscriptionService delegRestconfSubscrService; + private volatile ActionServiceHandler actionServiceHandler; public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler, - final TransactionChainHandler transactionChainHandler, + final TransactionChainHandler transactionChainHandler, final DOMMountPointServiceHandler mountPointServiceHandler, - final RestconfStreamsSubscriptionService delegRestconfSubscrService) { - this.schemaContextHandler = Objects.requireNonNull(schemaContextHandler); - this.transactionChainHandler = Objects.requireNonNull(transactionChainHandler); - this.mountPointServiceHandler = Objects.requireNonNull(mountPointServiceHandler); - this.delegRestconfSubscrService = Objects.requireNonNull(delegRestconfSubscrService); + final RestconfStreamsSubscriptionService delegRestconfSubscrService, + final ActionServiceHandler actionServiceHandler) { + this.actionServiceHandler = requireNonNull(actionServiceHandler); + this.schemaContextHandler = requireNonNull(schemaContextHandler); + this.transactionChainHandler = requireNonNull(transactionChainHandler); + this.mountPointServiceHandler = requireNonNull(mountPointServiceHandler); + this.delegRestconfSubscrService = requireNonNull(delegRestconfSubscrService); } @Override @@ -85,6 +98,8 @@ public class RestconfDataServiceImpl implements RestconfDataService { for (final Object object : handlers) { if (object instanceof SchemaContextHandler) { schemaContextHandler = (SchemaContextHandler) object; + } else if (object instanceof ActionServiceHandler) { + actionServiceHandler = (ActionServiceHandler) object; } else if (object instanceof DOMMountPointServiceHandler) { mountPointServiceHandler = (DOMMountPointServiceHandler) object; } else if (object instanceof TransactionChainHandler) { @@ -218,7 +233,9 @@ public class RestconfDataServiceImpl implements RestconfDataService { @Override public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) { requireNonNull(payload); - + if (payload.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition) { + return invokeAction(payload, uriInfo); + } boolean insertUsed = false; boolean pointUsed = false; String insert = null; @@ -316,4 +333,56 @@ public class RestconfDataServiceImpl implements RestconfDataService { LOG.warn(errMsg); throw new RestconfDocumentedException(errMsg); } + + /** + * Invoke Action operation. + * + * @param payload + * {@link NormalizedNodeContext} - the body of the operation + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} wrapped in {@link Response} + */ + public Response invokeAction(final NormalizedNodeContext payload, final UriInfo uriInfo) { + final InstanceIdentifierContext context = payload.getInstanceIdentifierContext(); + final DOMMountPoint mountPoint = context.getMountPoint(); + final SchemaPath schemaPath = context.getSchemaNode().getPath(); + final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier(); + final NormalizedNode data = payload.getData(); + + if (yangIIdContext.isEmpty() && !RestconfDataServiceConstant.NETCONF_BASE_QNAME.equals(data.getNodeType())) { + throw new RestconfDocumentedException("Instance identifier need to contain at least one path argument", + ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + + final DOMActionResult response; + final SchemaContextRef schemaContextRef; + if (mountPoint != null) { + response = RestconfInvokeOperationsUtil.invokeActionViaMountPoint(mountPoint, (ContainerNode) data, + schemaPath, yangIIdContext); + schemaContextRef = new SchemaContextRef(mountPoint.getSchemaContext()); + } else { + response = RestconfInvokeOperationsUtil.invokeAction((ContainerNode) data, schemaPath, + this.actionServiceHandler, yangIIdContext); + schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get()); + } + final DOMActionResult result = RestconfInvokeOperationsUtil.checkActionResponse(response); + + ActionDefinition resultNodeSchema = null; + ContainerNode resultData = null; + if (result != null) { + final Optional optOutput = result.getOutput(); + if (optOutput.isPresent()) { + resultData = optOutput.get(); + resultNodeSchema = (ActionDefinition) context.getSchemaNode(); + } + } + + if (resultData != null && resultData.getValue().isEmpty()) { + throw new WebApplicationException(Response.Status.NO_CONTENT); + } + + return Response.status(200).entity(new NormalizedNodeContext(new InstanceIdentifierContext<>(yangIIdContext, + resultNodeSchema, mountPoint, schemaContextRef.get()), resultData)).build(); + } } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ActionResultFactory.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ActionResultFactory.java new file mode 100644 index 0000000000..eda8181871 --- /dev/null +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ActionResultFactory.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 Ericsson Software Technology AB. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.restconf.nb.rfc8040.rests.utils; + +import org.opendaylight.mdsal.dom.api.DOMActionResult; +import org.opendaylight.yangtools.concepts.Builder; + +/** + * Implementation of {@link ActionResultFactory}. + */ +public class ActionResultFactory extends FutureDataFactory implements Builder { + + @Override + public DOMActionResult build() throws IllegalArgumentException { + return this.result; + } +} diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java index e925612e63..58178b6c41 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java @@ -13,8 +13,10 @@ import com.google.common.util.concurrent.ListenableFuture; import java.util.List; import java.util.concurrent.ExecutionException; import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; +import org.opendaylight.mdsal.dom.api.DOMActionException; import org.opendaylight.mdsal.dom.api.DOMRpcException; import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult; import org.opendaylight.netconf.api.NetconfDocumentedException; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.errors.RestconfError; @@ -66,6 +68,9 @@ final class FutureCallbackTx { if (cause instanceof DOMRpcException) { dataFactory.setResult((T) new DefaultDOMRpcResult(ImmutableList.of( RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage())))); + } else if (cause instanceof DOMActionException) { + dataFactory.setResult((T) new SimpleDOMActionResult(ImmutableList.of( + RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage())))); } else if (cause instanceof TransactionCommitFailedException) { /* If device send some error message we want this message to get to client and not just to throw it away or override it with new generic message. diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java index fcc7d3c39c..8e1304ea5b 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java @@ -8,16 +8,26 @@ package org.opendaylight.restconf.nb.rfc8040.rests.utils; import com.google.common.util.concurrent.ListenableFuture; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.concurrent.CancellationException; import javax.ws.rs.core.Response.Status; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.dom.api.DOMActionResult; +import org.opendaylight.mdsal.dom.api.DOMActionService; +import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; import org.opendaylight.mdsal.dom.api.DOMMountPoint; import org.opendaylight.mdsal.dom.api.DOMRpcResult; import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag; import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; +import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.slf4j.Logger; @@ -109,4 +119,104 @@ public final class RestconfInvokeOperationsUtil { FutureCallbackTx.addCallback(rpc, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory); return dataFactory.build(); } + + /** + * Invoking Action via mount point. + * + * @param mountPoint + * mount point + * @param data + * input data + * @param schemaPath + * schema path of data + * @return {@link DOMActionResult} + */ + public static DOMActionResult invokeActionViaMountPoint(final DOMMountPoint mountPoint, final ContainerNode data, + final SchemaPath schemaPath, final YangInstanceIdentifier yangIId) { + final Optional mountPointService = mountPoint.getService(DOMActionService.class); + if (!mountPointService.isPresent()) { + throw new RestconfDocumentedException("DomAction service is missing."); + } + + return prepareActionResult(mountPointService.get().invokeAction(schemaPath, + prepareDataTreeId(yangIId, schemaPath), data)); + } + + /** + * Invoke Action via ActionServiceHandler. + * + * @param data + * input data + * @param schemaPath + * schema path of data + * @param actionServiceHandler + * action service handler to invoke action + * @return {@link DOMActionResult} + */ + public static DOMActionResult invokeAction(final ContainerNode data, final SchemaPath schemaPath, + final ActionServiceHandler actionServiceHandler, final YangInstanceIdentifier yangIId) { + return prepareActionResult(actionServiceHandler.get().invokeAction(schemaPath, + prepareDataTreeId(yangIId, schemaPath), data)); + } + + /** + * Check the validity of the result. + * + * @param response + * response of Action + * @return {@link DOMActionResult} result + */ + public static DOMActionResult checkActionResponse(final DOMActionResult response) { + if (response != null) { + try { + if (response.getErrors().isEmpty()) { + return response; + } + LOG.debug("InvokeAction Error Message {}", response.getErrors()); + throw new RestconfDocumentedException("InvokeAction Error Message ", null, response.getErrors()); + } catch (final CancellationException e) { + final String errMsg = "The Action Operation was cancelled while executing."; + LOG.debug("Cancel Execution: {}", errMsg, e); + throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION, e); + } + } + return null; + } + + /** + * Prepare Action Result. + * + * @param actionResult + * {@link DOMActionResult} - action result + * @return {@link DOMActionResult} result + */ + private static DOMActionResult prepareActionResult(final ListenableFuture actionResult) { + final ActionResultFactory dataFactory = new ActionResultFactory(); + FutureCallbackTx.addCallback(actionResult, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory); + return dataFactory.build(); + } + + /** + * Prepare DOMDataTree Identifier. + * + * @param yangIId + * {@link YangInstanceIdentifier} + * @param schemaPath + * {@link SchemaPath} + * @return {@link DOMDataTreeIdentifier} domDataTreeIdentifier + */ + private static DOMDataTreeIdentifier prepareDataTreeId(final YangInstanceIdentifier yangIId, + final SchemaPath schemaPath) { + final List pathArg = new ArrayList<>(); + for (PathArgument path : yangIId.getPathArguments()) { + if (path.getNodeType().getLocalName().equals(schemaPath.getLastComponent().getLocalName())) { + break; + } + pathArg.add(path); + } + YangInstanceIdentifier yangInstanceIdentifier = YangInstanceIdentifier.builder().append(pathArg).build(); + DOMDataTreeIdentifier domDataTreeIdentifier = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, + yangInstanceIdentifier); + return domDataTreeIdentifier; + } } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapper.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapper.java index aabce8a544..0c60f0ee77 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapper.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapper.java @@ -16,6 +16,7 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext; import org.opendaylight.restconf.common.patch.PatchContext; import org.opendaylight.restconf.common.patch.PatchStatusContext; import org.opendaylight.restconf.common.schema.SchemaExportContext; +import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler; @@ -54,11 +55,11 @@ public final class ServicesWrapper implements BaseServicesWrapper, TransactionSe private final RestconfSchemaService delegRestSchService; private final RestconfService delegRestService; - private ServicesWrapper(RestconfDataService delegRestconfDataService, - RestconfInvokeOperationsService delegRestconfInvokeOpsService, - RestconfStreamsSubscriptionService delegRestconfSubscrService, - RestconfOperationsService delegRestOpsService, RestconfSchemaService delegRestSchService, - RestconfService delegRestService) { + private ServicesWrapper(final RestconfDataService delegRestconfDataService, + final RestconfInvokeOperationsService delegRestconfInvokeOpsService, + final RestconfStreamsSubscriptionService delegRestconfSubscrService, + final RestconfOperationsService delegRestOpsService, final RestconfSchemaService delegRestSchService, + final RestconfService delegRestService) { this.delegRestconfDataService = delegRestconfDataService; this.delegRestconfInvokeOpsService = delegRestconfInvokeOpsService; this.delegRestconfSubscrService = delegRestconfSubscrService; @@ -70,26 +71,23 @@ public final class ServicesWrapper implements BaseServicesWrapper, TransactionSe public static ServicesWrapper newInstance(final SchemaContextHandler schemaCtxHandler, final DOMMountPointServiceHandler domMountPointServiceHandler, final TransactionChainHandler transactionChainHandler, final DOMDataBrokerHandler domDataBrokerHandler, - final RpcServiceHandler rpcServiceHandler, final NotificationServiceHandler notificationServiceHandler, - final DOMSchemaService domSchemaService) { - RestconfOperationsService restconfOpsService = - new RestconfOperationsServiceImpl(schemaCtxHandler, domMountPointServiceHandler); + final RpcServiceHandler rpcServiceHandler, final ActionServiceHandler actionServiceHandler, + final NotificationServiceHandler notificationServiceHandler, final DOMSchemaService domSchemaService) { + RestconfOperationsService restconfOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler, + domMountPointServiceHandler); final DOMYangTextSourceProvider yangTextSourceProvider = domSchemaService.getExtensions() - .getInstance(DOMYangTextSourceProvider.class); - RestconfSchemaService restconfSchemaService = - new RestconfSchemaServiceImpl(schemaCtxHandler, domMountPointServiceHandler, - yangTextSourceProvider); - RestconfStreamsSubscriptionService restconfSubscrService = - new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler, - notificationServiceHandler, schemaCtxHandler, transactionChainHandler); - RestconfDataService restconfDataService = - new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler, domMountPointServiceHandler, - restconfSubscrService); - RestconfInvokeOperationsService restconfInvokeOpsService = - new RestconfInvokeOperationsServiceImpl(rpcServiceHandler, schemaCtxHandler); + .getInstance(DOMYangTextSourceProvider.class); + RestconfSchemaService restconfSchemaService = new RestconfSchemaServiceImpl(schemaCtxHandler, + domMountPointServiceHandler, yangTextSourceProvider); + RestconfStreamsSubscriptionService restconfSubscrService = new RestconfStreamsSubscriptionServiceImpl( + domDataBrokerHandler, notificationServiceHandler, schemaCtxHandler, transactionChainHandler); + RestconfDataService restconfDataService = new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler, + domMountPointServiceHandler, restconfSubscrService, actionServiceHandler); + RestconfInvokeOperationsService restconfInvokeOpsService = new RestconfInvokeOperationsServiceImpl( + rpcServiceHandler, schemaCtxHandler); RestconfService restconfService = new RestconfImpl(schemaCtxHandler); - return new ServicesWrapper(restconfDataService, restconfInvokeOpsService, - restconfSubscrService, restconfOpsService, restconfSchemaService, restconfService); + return new ServicesWrapper(restconfDataService, restconfInvokeOpsService, restconfSubscrService, + restconfOpsService, restconfSchemaService, restconfService); } @Override diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java index 51a82a39bc..7bc0937822 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java @@ -107,6 +107,20 @@ public class XmlBodyReaderMountPointTest extends AbstractBodyReaderTest { checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue); } + @Test + public void moduleSubContainerDataPostActionTest() throws Exception { + final Optional dataSchemaNode = schemaContext + .findDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont/cont1/reset"; + mockBodyReader(uri, this.xmlBodyReader, true); + final InputStream inputStream = XmlBodyReaderMountPointTest.class + .getResourceAsStream("/instanceidentifier/xml/xml_cont_action.xml"); + final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, + null, null, this.mediaType, null, inputStream); + checkMountPointNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode.get(), returnValue); + } + @Test public void rpcModuleInputTest() throws Exception { final String uri = "instance-identifier-module:cont/yang-ext:mount/invoke-rpc-module:rpc-test"; diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/JsonBodyReaderTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/JsonBodyReaderTest.java index 1c3050e33e..2e02284470 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/JsonBodyReaderTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/JsonBodyReaderTest.java @@ -10,6 +10,7 @@ package org.opendaylight.restconf.nb.rfc8040.jersey.providers.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import com.google.common.collect.Sets; import java.io.File; @@ -17,6 +18,7 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.net.URI; import java.util.Collection; +import java.util.Optional; import javax.ws.rs.core.MediaType; import org.junit.BeforeClass; import org.junit.Test; @@ -28,6 +30,7 @@ import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; @@ -110,6 +113,24 @@ public class JsonBodyReaderTest extends AbstractBodyReaderTest { checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); } + @Test + public void moduleSubContainerDataPostActionTest() throws Exception { + final Optional dataSchemaNode = schemaContext + .findDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final QName cont1QName = QName.create(dataSchemaNode.get().getQName(), "cont1"); + final QName actionQName = QName.create(dataSchemaNode.get().getQName(), "reset"); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.get().getQName()) + .node(cont1QName).node(actionQName); + final String uri = "instance-identifier-module:cont/cont1/reset"; + mockBodyReader(uri, this.jsonBodyReader, true); + final InputStream inputStream = JsonBodyReaderTest.class + .getResourceAsStream("/instanceidentifier/json/json_cont_action.json"); + final NormalizedNodeContext returnValue = this.jsonBodyReader + .readFrom(null, null, null, this.mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + assertTrue(returnValue.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition); + } + @Test public void moduleSubContainerAugmentDataPostTest() throws Exception { final DataSchemaNode dataSchemaNode = schemaContext diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/XmlBodyReaderTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/XmlBodyReaderTest.java index 7f8d621cf9..fef810d0e4 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/XmlBodyReaderTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/XmlBodyReaderTest.java @@ -18,6 +18,7 @@ import java.io.File; import java.io.InputStream; import java.net.URI; import java.util.Collection; +import java.util.Optional; import javax.ws.rs.core.MediaType; import org.junit.Assert; import org.junit.BeforeClass; @@ -35,6 +36,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; @@ -148,6 +150,24 @@ public class XmlBodyReaderTest extends AbstractBodyReaderTest { checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); } + @Test + public void moduleSubContainerDataPostActionTest() throws Exception { + final Optional dataSchemaNode = schemaContext + .findDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final QName cont1QName = QName.create(dataSchemaNode.get().getQName(), "cont1"); + final QName actionQName = QName.create(dataSchemaNode.get().getQName(), "reset"); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.get().getQName()) + .node(cont1QName).node(actionQName); + final String uri = "instance-identifier-module:cont/cont1/reset"; + mockBodyReader(uri, this.xmlBodyReader, true); + final InputStream inputStream = XmlBodyReaderTest.class + .getResourceAsStream("/instanceidentifier/xml/xml_cont_action.xml"); + final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null, + inputStream); + checkNormalizedNodeContext(returnValue); + assertTrue(returnValue.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition); + } + @Test public void moduleSubContainerAugmentDataPostTest() throws Exception { final DataSchemaNode dataSchemaNode = schemaContext diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java index 523bee0811..c15d545c89 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java @@ -43,6 +43,7 @@ import org.mockito.MockitoAnnotations; import org.opendaylight.mdsal.common.api.CommitInfo; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; +import org.opendaylight.mdsal.dom.api.DOMActionService; import org.opendaylight.mdsal.dom.api.DOMDataBroker; import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction; import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction; @@ -58,6 +59,7 @@ import org.opendaylight.mdsal.dom.api.DOMSchemaService; import org.opendaylight.mdsal.dom.api.DOMTransactionChain; import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.restconf.nb.rfc8040.TestUtils; +import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler; @@ -139,6 +141,9 @@ public class JSONRestconfServiceRfc8040ImplTest { @Mock private DOMRpcService mockRpcService; + @Mock + private DOMActionService mockActionService; + @Mock private DOMSchemaService domSchemaService; @@ -190,8 +195,8 @@ public class JSONRestconfServiceRfc8040ImplTest { final DOMNotificationService mockNotificationService = mock(DOMNotificationService.class); final ServicesWrapper servicesWrapper = ServicesWrapper.newInstance(schemaContextHandler, mountPointServiceHandler, txChainHandler, new DOMDataBrokerHandler(mockDOMDataBroker), - new RpcServiceHandler(mockRpcService), new NotificationServiceHandler(mockNotificationService), - domSchemaService); + new RpcServiceHandler(mockRpcService), new ActionServiceHandler(mockActionService), + new NotificationServiceHandler(mockNotificationService), domSchemaService); service = new JSONRestconfServiceRfc8040Impl(servicesWrapper, mountPointServiceHandler, schemaContextHandler); diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java index c42d0dc531..101213e5e3 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java @@ -57,6 +57,7 @@ import org.opendaylight.restconf.common.patch.PatchContext; import org.opendaylight.restconf.common.patch.PatchEntity; import org.opendaylight.restconf.common.patch.PatchStatusContext; import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils; +import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler; @@ -117,6 +118,8 @@ public class RestconfDataServiceImplTest { @Mock private DOMDataBroker mountDataBroker; @Mock + private ActionServiceHandler actionServiceHandler; + @Mock private DOMTransactionChain mountTransactionChain; @Mock private RestconfStreamsSubscriptionService delegRestconfSubscrService; @@ -198,7 +201,8 @@ public class RestconfDataServiceImplTest { schemaContextHandler.onGlobalContextUpdated(this.contextRef.get()); this.dataService = new RestconfDataServiceImpl(schemaContextHandler, this.transactionChainHandler, - DOMMountPointServiceHandler.newInstance(mountPointService), this.delegRestconfSubscrService); + DOMMountPointServiceHandler.newInstance(mountPointService), this.delegRestconfSubscrService, + this.actionServiceHandler); doReturn(Optional.of(this.mountPoint)).when(this.mountPointService) .getMountPoint(any(YangInstanceIdentifier.class)); doReturn(this.contextRef.get()).when(this.mountPoint).getSchemaContext(); diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/test/incubate/InMemoryMdsalModule.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/test/incubate/InMemoryMdsalModule.java index 986575cc48..3a23203c78 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/test/incubate/InMemoryMdsalModule.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/test/incubate/InMemoryMdsalModule.java @@ -14,6 +14,7 @@ import javax.inject.Singleton; import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractBaseDataBrokerTest; import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest; +import org.opendaylight.mdsal.dom.api.DOMActionService; import org.opendaylight.mdsal.dom.api.DOMDataBroker; import org.opendaylight.mdsal.dom.api.DOMMountPointService; import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService; @@ -106,6 +107,12 @@ public class InMemoryMdsalModule extends AbstractModule { return DOMRpcRouter.newInstance(schemaService).getRpcService(); } + @Provides + @Singleton + DOMActionService getDOMActionService(DOMSchemaService schemaService) { + return DOMRpcRouter.newInstance(schemaService).getActionService(); + } + @PreDestroy public void close() { } diff --git a/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/json/json_cont_action.json b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/json/json_cont_action.json new file mode 100644 index 0000000000..82da66d905 --- /dev/null +++ b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/json/json_cont_action.json @@ -0,0 +1,5 @@ +{ + "instance-identifier-module:input": { + "delay": 600 + } +} \ No newline at end of file diff --git a/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/xml/xml_cont_action.xml b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/xml/xml_cont_action.xml new file mode 100644 index 0000000000..9f39ed44e4 --- /dev/null +++ b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/xml/xml_cont_action.xml @@ -0,0 +1,4 @@ + + + 600 + \ No newline at end of file diff --git a/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang index ffa5db62ad..6779d00de1 100644 --- a/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang +++ b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang @@ -1,12 +1,20 @@ module instance-identifier-module { + yang-version 1.1; namespace "instance:identifier:module"; prefix "iimodule"; - revision 2014-01-17 { - } + revision 2014-01-17; - container cont { - container cont1 { + container cont { + container cont1 { + action reset { + input { + leaf delay { + type uint32; + default 0; + } } + } } -} \ No newline at end of file + } +} -- 2.36.6