Teach RFC8040 restconf about actions 92/82992/18
authorajay.dp001 <ajay.deep.singh@ericsson.com>
Wed, 10 Jul 2019 11:23:05 +0000 (12:23 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 6 Sep 2019 16:15:58 +0000 (18:15 +0200)
This adds the handlers to route action requests through
DOMActionService and return results.

JIRA: NETCONF-618
Change-Id: I9d72fa5939b73beefbbb2e8030b1fa0a1f4ff935
Signed-off-by: ajay.dp001 <ajay.deep.singh@ericsson.com>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
19 files changed:
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/Rfc8040RestConfWiring.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/ActionServiceHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeJsonBodyWriter.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeXmlBodyWriter.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlNormalizedNodeBodyReader.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ActionResultFactory.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapper.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/JsonBodyReaderTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/XmlBodyReaderTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/test/incubate/InMemoryMdsalModule.java
restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/json/json_cont_action.json [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/xml/xml_cont_action.xml [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang

index d3fe40686a55de26491a444f9ac1257496ec6477..0f3f5fb29a210d186e744f4b8607f04057e25377 100644 (file)
@@ -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 (file)
index 0000000..b6b5eb1
--- /dev/null
@@ -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<DOMActionService> {
+    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;
+    }
+}
index 2f586909f835b14fe930a06a90332d8d84dfa8fe..1272a0fee43726a0738b723b293b5460ebb130b5 100644 (file)
@@ -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<Normalize
             jsonWriter.beginObject();
             writeChildren(nnWriter, (ContainerNode) data);
             jsonWriter.endObject();
+        } else if (context.getSchemaNode() instanceof ActionDefinition) {
+            /*
+             *  ActionDefinition is not supported as initial codec in JSONStreamWriter,
+             *  so we need to emit initial output declaration..
+             */
+            nnWriter = createNormalizedNodeWriter(context,
+                ((ActionDefinition) context.getSchemaNode()).getOutput().getPath(), jsonWriter, depth, fields);
+            final Module module = context.getSchemaContext().findModule(data.getNodeType().getModule()).get();
+            jsonWriter.name(module.getName() + ":output");
+            jsonWriter.beginObject();
+            writeChildren(nnWriter, (ContainerNode) data);
+            jsonWriter.endObject();
         } else {
             if (SchemaPath.ROOT.equals(path)) {
                 nnWriter = createNormalizedNodeWriter(context, path, jsonWriter, depth, fields);
index 49af300cf077c216cf9c69b74ea09dcdb9f2c571..9d849ec565001c49b76a495bc53c00cfea3f3ab1 100644 (file)
@@ -39,6 +39,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
@@ -118,6 +119,14 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
                     depth,
                     fields);
             writeElements(xmlWriter, nnWriter, (ContainerNode) data);
+        } else if (pathContext.getSchemaNode() instanceof ActionDefinition) {
+            /*
+             *  ActionDefinition is not supported as initial codec in XMLStreamWriter,
+             *  so we need to emit initial output declaration..
+             */
+            nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx,
+                ((ActionDefinition) pathContext.getSchemaNode()).getOutput().getPath(), depth, fields);
+            writeElements(xmlWriter, nnWriter, (ContainerNode) data);
         } else {
             if (SchemaPath.ROOT.equals(path)) {
                 nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, path, depth, fields);
index 1fc91c43816575d5fe2cf7b02db71613118aeb63..0dfe3a04640e46a110d7c97da6e33acbac8da611 100644 (file)
@@ -7,7 +7,8 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
 
-import com.google.common.base.Preconditions;
+import static com.google.common.base.Preconditions.checkState;
+
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.io.InputStream;
@@ -53,7 +54,7 @@ import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -91,21 +92,22 @@ public class XmlNormalizedNodeBodyReader extends AbstractNormalizedNodeBodyReade
             throws XMLStreamException, IOException, ParserConfigurationException, SAXException, URISyntaxException {
         final SchemaNode schemaNodeContext = pathContext.getSchemaNode();
         DataSchemaNode schemaNode;
-        boolean isRpc = false;
-        if (schemaNodeContext instanceof RpcDefinition) {
-            schemaNode = ((RpcDefinition) schemaNodeContext).getInput();
-            isRpc = true;
+        final boolean isOperation;
+        if (schemaNodeContext instanceof OperationDefinition) {
+            schemaNode = ((OperationDefinition) schemaNodeContext).getInput();
+            isOperation = true;
         } else if (schemaNodeContext instanceof DataSchemaNode) {
             schemaNode = (DataSchemaNode) schemaNodeContext;
+            isOperation = false;
         } else {
-            throw new IllegalStateException("Unknown SchemaNode");
+            throw new IllegalStateException("Unknown SchemaNode " + schemaNodeContext);
         }
 
         final String docRootElm = doc.getDocumentElement().getLocalName();
         final String docRootNamespace = doc.getDocumentElement().getNamespaceURI();
         final List<YangInstanceIdentifier.PathArgument> iiToDataList = new ArrayList<>();
 
-        if (isPost() && !isRpc) {
+        if (isPost() && !isOperation) {
             final Deque<Object> 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;
index 7c55e3486fa298b4784aaabf5f858e6c7fe75604..2b81a48f6e74402e972d767ccd7606053cc9f404 100644 (file)
@@ -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<ContainerNode> 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 (file)
index 0000000..eda8181
--- /dev/null
@@ -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<DOMActionResult> implements Builder<DOMActionResult> {
+
+    @Override
+    public DOMActionResult build() throws IllegalArgumentException {
+        return this.result;
+    }
+}
index e925612e63bc8e00c582b4e6c3e195c3660daab6..58178b6c41efcde7af175f4d08fdf0e7f5816776 100644 (file)
@@ -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.
index fcc7d3c39c75d75cf889835f8229bfadb4fe69d7..8e1304ea5bfb8682b4683746c4dcd2da6f3d4959 100644 (file)
@@ -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<DOMActionService> 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<? extends DOMActionResult> 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<PathArgument> 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;
+    }
 }
index aabce8a544836399691eb3817e1558ab9b840da5..0c60f0ee771be26b65c3862c4dd8d5e723ed3ea3 100644 (file)
@@ -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
index 51a82a39bc73d655d07e1a200a61a016dc370a18..7bc0937822a5e3a696d26daf65072efef04b598b 100644 (file)
@@ -107,6 +107,20 @@ public class XmlBodyReaderMountPointTest extends AbstractBodyReaderTest {
         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
     }
 
+    @Test
+    public void moduleSubContainerDataPostActionTest() throws Exception {
+        final Optional<DataSchemaNode> 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";
index 1c3050e33e09cd113030ab0cde2210570c9de643..2e02284470695e7bc3191707c8fd229085aa0ca9 100644 (file)
@@ -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> 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
index 7f8d621cf921926c77cb5bf735022440cf353448..fef810d0e422bb0b1e7eb9dd3e466cd542dbf316 100644 (file)
@@ -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> 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
index 523bee0811947bd98f39d12c353dc15da9cfc02d..c15d545c890175b04dd6fa7f2d4db33bfe7c1359 100644 (file)
@@ -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);
index c42d0dc5316e9f0f094069797699ec8fc1e9140f..101213e5e33e583638be09abd7d9d3995ae0ea4a 100644 (file)
@@ -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();
index 986575cc4874fc401e571dd05a7111495c00ace3..3a23203c781fe2dbd913b72553638c43be398787 100644 (file)
@@ -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 (file)
index 0000000..82da66d
--- /dev/null
@@ -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 (file)
index 0000000..9f39ed4
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<input xmlns="instance:identifier:module">
+  <delay>600</delay>
+</input>
\ No newline at end of file
index ffa5db62ad329c37a3cd9c669b7106f819bbb3e8..6779d00de1ebb1dea6bc7c72c0f5406addc070b8 100644 (file)
@@ -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
+  }
+}