Prepare netconf to support YANG 1.1 actions 45/74745/17
authorJakub Tóth <jakub.toth@pantheon.tech>
Mon, 6 Aug 2018 14:11:29 +0000 (16:11 +0200)
committerJakubToth <jakub.toth@pantheon.tech>
Wed, 15 Aug 2018 17:08:55 +0000 (17:08 +0000)
This allows implementation of a factory for creating of DOMActionService
of a connected device. This service is going to be part of services provided
by the device's mountpoint.

Change-Id: I1c79b4bc8f1b1c63f7ffb2306f07ea9872f15882
Signed-off-by: Jakub Tóth <jakub.toth@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
18 files changed:
netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java
netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java
netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/DeviceActionFactory.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/MessageTransformer.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/RemoteDeviceHandler.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceBuilder.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacade.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalProvider.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfMessageTransformUtil.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacadeTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java
netconf/sal-netconf-connector/src/test/resources/schemas/example-server-farm.yang [new file with mode: 0644]

index d4f9e132b8ed0429de94999b077db6dde98fab7c..2d174ea0ba3c115c58acb9137af3babc6203dd64 100644 (file)
@@ -15,6 +15,7 @@ import org.opendaylight.controller.config.threadpool.ThreadPool;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
 import org.opendaylight.netconf.topology.AbstractNetconfTopology;
 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
 
 import org.opendaylight.netconf.topology.AbstractNetconfTopology;
 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
 
@@ -26,9 +27,10 @@ abstract class BaseCallHomeTopology extends AbstractNetconfTopology {
                          final SchemaRepositoryProvider schemaRepositoryProvider,
                          final DataBroker dataBroker,
                          final DOMMountPointService mountPointService,
                          final SchemaRepositoryProvider schemaRepositoryProvider,
                          final DataBroker dataBroker,
                          final DOMMountPointService mountPointService,
-                         final AAAEncryptionService encryptionService) {
+                         final AAAEncryptionService encryptionService,
+                         final DeviceActionFactory deviceActionFactory) {
         super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor,
               processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService,
         super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor,
               processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService,
-              encryptionService);
+              encryptionService, deviceActionFactory);
     }
 }
     }
 }
index d661a9e5a3f379d744fa09964a0014f82dbac75e..95d4b7be7bbdaa9792b28303c8f356a45334bedd 100644 (file)
@@ -25,6 +25,7 @@ import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.netconf.client.NetconfClientSession;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
 import org.opendaylight.netconf.client.NetconfClientSession;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
@@ -55,16 +56,28 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom
         }
     };
 
         }
     };
 
+    private DeviceActionFactory deviceActionFactory;
+
     public CallHomeMountDispatcher(final String topologyId, final EventExecutor eventExecutor,
                                    final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
                                    final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker,
                                    final DOMMountPointService mountService,
                                    final AAAEncryptionService encryptionService) {
     public CallHomeMountDispatcher(final String topologyId, final EventExecutor eventExecutor,
                                    final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
                                    final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker,
                                    final DOMMountPointService mountService,
                                    final AAAEncryptionService encryptionService) {
+        this(topologyId, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker,
+                mountService, encryptionService, null);
+    }
+
+    public CallHomeMountDispatcher(final String topologyId, final EventExecutor eventExecutor,
+            final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
+            final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker,
+            final DOMMountPointService mountService,
+            final AAAEncryptionService encryptionService, DeviceActionFactory deviceActionFactory) {
         this.topologyId = topologyId;
         this.eventExecutor = eventExecutor;
         this.keepaliveExecutor = keepaliveExecutor;
         this.processingExecutor = processingExecutor;
         this.schemaRepositoryProvider = schemaRepositoryProvider;
         this.topologyId = topologyId;
         this.eventExecutor = eventExecutor;
         this.keepaliveExecutor = keepaliveExecutor;
         this.processingExecutor = processingExecutor;
         this.schemaRepositoryProvider = schemaRepositoryProvider;
+        this.deviceActionFactory = deviceActionFactory;
         this.sessionManager = new CallHomeMountSessionManager();
         this.dataBroker = dataBroker;
         this.mountService = mountService;
         this.sessionManager = new CallHomeMountSessionManager();
         this.dataBroker = dataBroker;
         this.mountService = mountService;
@@ -93,7 +106,7 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom
 
     void createTopology() {
         this.topology = new CallHomeTopology(topologyId, this, eventExecutor, keepaliveExecutor, processingExecutor,
 
     void createTopology() {
         this.topology = new CallHomeTopology(topologyId, this, eventExecutor, keepaliveExecutor, processingExecutor,
-                schemaRepositoryProvider, dataBroker, mountService, encryptionService);
+                schemaRepositoryProvider, dataBroker, mountService, encryptionService, deviceActionFactory);
     }
 
     @Override
     }
 
     @Override
index 41d121eac324074f11cc1159e43e5802021cdbe5..d918def01b13e2d4bc00a263cdde2c842593c797 100644 (file)
@@ -15,6 +15,7 @@ import org.opendaylight.controller.config.threadpool.ThreadPool;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
@@ -23,15 +24,27 @@ import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
 
 public class CallHomeTopology extends BaseCallHomeTopology {
 
 
 public class CallHomeTopology extends BaseCallHomeTopology {
 
+    public CallHomeTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
+            final EventExecutor eventExecutor,
+            final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
+            final SchemaRepositoryProvider schemaRepositoryProvider,
+            final DataBroker dataBroker, final DOMMountPointService mountPointService,
+            final AAAEncryptionService encryptionService) {
+        this(topologyId, clientDispatcher, eventExecutor,
+                keepaliveExecutor, processingExecutor, schemaRepositoryProvider,
+                dataBroker, mountPointService, encryptionService, null);
+    }
+
     public CallHomeTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
                             final EventExecutor eventExecutor,
                             final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
                             final SchemaRepositoryProvider schemaRepositoryProvider,
                             final DataBroker dataBroker, final DOMMountPointService mountPointService,
     public CallHomeTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
                             final EventExecutor eventExecutor,
                             final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
                             final SchemaRepositoryProvider schemaRepositoryProvider,
                             final DataBroker dataBroker, final DOMMountPointService mountPointService,
-                            final AAAEncryptionService encryptionService) {
+                            final AAAEncryptionService encryptionService,
+                            final DeviceActionFactory deviceActionFactory) {
         super(topologyId, clientDispatcher, eventExecutor,
                 keepaliveExecutor, processingExecutor, schemaRepositoryProvider,
         super(topologyId, clientDispatcher, eventExecutor,
                 keepaliveExecutor, processingExecutor, schemaRepositoryProvider,
-                dataBroker, mountPointService, encryptionService);
+                dataBroker, mountPointService, encryptionService, deviceActionFactory);
     }
 
     @Override
     }
 
     @Override
index 342d3e9cdaaa2a5661463dee7071aadc025e203a..818a3895dc0fe2944d3c35a1cfaf47f7393c6922 100644 (file)
@@ -51,6 +51,7 @@ import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurati
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
@@ -200,15 +201,16 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
         }
     }
 
         }
     }
 
-    protected final String topologyId;
     private final NetconfClientDispatcher clientDispatcher;
     private final EventExecutor eventExecutor;
     private final NetconfClientDispatcher clientDispatcher;
     private final EventExecutor eventExecutor;
+    private final DeviceActionFactory deviceActionFactory;
+    private final NetconfKeystoreAdapter keystoreAdapter;
     protected final ScheduledThreadPool keepaliveExecutor;
     protected final ThreadPool processingExecutor;
     protected final SharedSchemaRepository sharedSchemaRepository;
     protected final DataBroker dataBroker;
     protected final DOMMountPointService mountPointService;
     protected final ScheduledThreadPool keepaliveExecutor;
     protected final ThreadPool processingExecutor;
     protected final SharedSchemaRepository sharedSchemaRepository;
     protected final DataBroker dataBroker;
     protected final DOMMountPointService mountPointService;
-    private final NetconfKeystoreAdapter keystoreAdapter;
+    protected final String topologyId;
     protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
     protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
     protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
     protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
     protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
     protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
@@ -222,12 +224,14 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
                                       final ThreadPool processingExecutor,
                                       final SchemaRepositoryProvider schemaRepositoryProvider,
                                       final DataBroker dataBroker, final DOMMountPointService mountPointService,
                                       final ThreadPool processingExecutor,
                                       final SchemaRepositoryProvider schemaRepositoryProvider,
                                       final DataBroker dataBroker, final DOMMountPointService mountPointService,
-                                      final AAAEncryptionService encryptionService) {
+                                      final AAAEncryptionService encryptionService,
+                                      final DeviceActionFactory deviceActionFactory) {
         this.topologyId = topologyId;
         this.clientDispatcher = clientDispatcher;
         this.eventExecutor = eventExecutor;
         this.keepaliveExecutor = keepaliveExecutor;
         this.processingExecutor = processingExecutor;
         this.topologyId = topologyId;
         this.clientDispatcher = clientDispatcher;
         this.eventExecutor = eventExecutor;
         this.keepaliveExecutor = keepaliveExecutor;
         this.processingExecutor = processingExecutor;
+        this.deviceActionFactory = deviceActionFactory;
         this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
         this.dataBroker = dataBroker;
         this.mountPointService = mountPointService;
         this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
         this.dataBroker = dataBroker;
         this.mountPointService = mountPointService;
@@ -320,7 +324,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
 
         if (keepaliveDelay > 0) {
             LOG.warn("Adding keepalive facade, for device {}", nodeId);
 
         if (keepaliveDelay > 0) {
             LOG.warn("Adding keepalive facade, for device {}", nodeId);
-            salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
+            salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
                     keepaliveDelay, defaultRequestTimeoutMillis);
         }
 
                     keepaliveDelay, defaultRequestTimeoutMillis);
         }
 
@@ -356,13 +360,16 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
         if (node.isSchemaless()) {
             device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
         } else {
         if (node.isSchemaless()) {
             device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
         } else {
-            device = new NetconfDeviceBuilder()
+            NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder()
                     .setReconnectOnSchemasChange(reconnectOnChangedSchema)
                     .setSchemaResourcesDTO(schemaResourcesDTO)
                     .setReconnectOnSchemasChange(reconnectOnChangedSchema)
                     .setSchemaResourcesDTO(schemaResourcesDTO)
-                    .setGlobalProcessingExecutor(processingExecutor.getExecutor())
+                    .setGlobalProcessingExecutor(this.processingExecutor.getExecutor())
                     .setId(remoteDeviceId)
                     .setId(remoteDeviceId)
-                    .setSalFacade(salFacade)
-                    .build();
+                    .setSalFacade(salFacade);
+            if (this.deviceActionFactory != null) {
+                netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory);
+            }
+            device = netconfDeviceBuilder.build();
         }
 
         final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
         }
 
         final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
index 98c45544e2d2f070bf570ba4c89b42186acae548..ebfd283e762da1bc3de2ef3aaf83a0e4b3e3a07b 100644 (file)
@@ -26,6 +26,7 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
@@ -52,13 +53,24 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology
     private ListenerRegistration<NetconfTopologyImpl> datastoreListenerRegistration = null;
 
     public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
     private ListenerRegistration<NetconfTopologyImpl> datastoreListenerRegistration = null;
 
     public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
-                               final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
-                               final ThreadPool processingExecutor,
-                               final SchemaRepositoryProvider schemaRepositoryProvider,
-                               final DataBroker dataBroker, final DOMMountPointService mountPointService,
-                               final AAAEncryptionService encryptionService) {
+            final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
+            final ThreadPool processingExecutor,
+            final SchemaRepositoryProvider schemaRepositoryProvider,
+            final DataBroker dataBroker, final DOMMountPointService mountPointService,
+            final AAAEncryptionService encryptionService) {
+        this(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor,
+                schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, null);
+    }
+
+    public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
+            final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
+            final ThreadPool processingExecutor,
+            final SchemaRepositoryProvider schemaRepositoryProvider,
+            final DataBroker dataBroker, final DOMMountPointService mountPointService,
+            final AAAEncryptionService encryptionService,
+            final DeviceActionFactory deviceActionFactory) {
         super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor,
         super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor,
-                schemaRepositoryProvider, dataBroker, mountPointService, encryptionService);
+                schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, deviceActionFactory);
     }
 
     @Override
     }
 
     @Override
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/DeviceActionFactory.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/DeviceActionFactory.java
new file mode 100644 (file)
index 0000000..e8df4d4
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies s.r.o. 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.netconf.sal.connect.api;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
+import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public interface DeviceActionFactory {
+
+    /**
+     * Allows user to create DOMActionService for specific device.
+     *
+     * @param messageTransformer - message transformer (for action in this case)
+     * @param listener - allows specific service to send and receive messages to/from device
+     * @param schemaContext - schema context of device
+     * @return {@link DOMActionService} of specific device
+     */
+    default DOMActionService createDeviceAction(MessageTransformer<NetconfMessage> messageTransformer,
+            RemoteDeviceCommunicator<NetconfMessage> listener, SchemaContext schemaContext) {
+        return null;
+    }
+}
+
index 1d1acaa0ff7764fc34cf176bb011c03864312f40..0cbc2fd5569b35258948fedd15fccda8ee3af6b5 100644 (file)
@@ -9,6 +9,8 @@ package org.opendaylight.netconf.sal.connect.api;
 
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
@@ -20,4 +22,27 @@ public interface MessageTransformer<M> {
 
     DOMRpcResult toRpcResult(M message, SchemaPath rpc);
 
 
     DOMRpcResult toRpcResult(M message, SchemaPath rpc);
 
+    /**
+     * Parse action into message for request.
+     *
+     * @param action - action schema path
+     * @param domDataTreeIdentifier - identifier of action
+     * @param payload - input of action
+     * @return message
+     */
+    default M toActionRequest(SchemaPath action, DOMDataTreeIdentifier domDataTreeIdentifier, NormalizedNode<?,
+            ?> payload) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Parse result of invoking action into DOM result.
+     *
+     * @param action - action schema path
+     * @param message - message to parsing
+     * @return {@link DOMActionResult}
+     */
+    default DOMActionResult toActionResult(SchemaPath action, M message) {
+        throw new UnsupportedOperationException();
+    }
 }
 }
index 699c39945bd2788eb3151af022b3d940d736bba2..5db885e77e6665b7af713cdac53ac9383bbe6b0c 100644 (file)
@@ -7,14 +7,37 @@
  */
 package org.opendaylight.netconf.sal.connect.api;
 
  */
 package org.opendaylight.netconf.sal.connect.api;
 
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public interface RemoteDeviceHandler<PREF> extends AutoCloseable {
 
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public interface RemoteDeviceHandler<PREF> extends AutoCloseable {
 
-    void onDeviceConnected(SchemaContext remoteSchemaContext,
-                           PREF netconfSessionPreferences, DOMRpcService deviceRpc);
+    /**
+     * When device connected, init new mount point with specific schema context and DOM services.
+     *
+     * @param remoteSchemaContext - schema context of connected device
+     * @param netconfSessionPreferences - session of device
+     * @param deviceRpc - {@link DOMRpcService} of device
+     */
+    default void onDeviceConnected(SchemaContext remoteSchemaContext, PREF netconfSessionPreferences,
+            DOMRpcService deviceRpc) {
+        // DO NOTHING
+    }
+
+    /**
+     * When device connected, init new mount point with specific schema context and DOM services.
+     *
+     * @param remoteSchemaContext - schema context of connected device
+     * @param netconfSessionPreferences - session of device
+     * @param deviceRpc - {@link DOMRpcService} of device
+     * @param deviceAction - {@link DOMActionService} of device
+     */
+    default void onDeviceConnected(SchemaContext remoteSchemaContext, PREF netconfSessionPreferences,
+            DOMRpcService deviceRpc, DOMActionService deviceAction) {
+        // DO NOTHING
+    }
 
     void onDeviceDisconnected();
 
 
     void onDeviceDisconnected();
 
index fc100b3db80a2ec232db0ad3e15ceb8fdb0f7e42..993b1230bda2f51e48598ec8de68cc7dcb2a2fcc 100644 (file)
@@ -36,6 +36,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
 import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
 import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas;
 import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver;
 import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
 import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas;
 import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver;
@@ -81,17 +82,20 @@ public class NetconfDevice
     private static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class);
 
     protected final RemoteDeviceId id;
     private static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class);
 
     protected final RemoteDeviceId id;
-    private final boolean reconnectOnSchemasChange;
-
     protected final SchemaContextFactory schemaContextFactory;
     protected final SchemaContextFactory schemaContextFactory;
-    private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
-    private final ListeningExecutorService processingExecutor;
     protected final SchemaSourceRegistry schemaRegistry;
     protected final SchemaRepository schemaRepository;
     protected final SchemaSourceRegistry schemaRegistry;
     protected final SchemaRepository schemaRepository;
-    private final NetconfDeviceSchemasResolver stateSchemasResolver;
-    private final NotificationHandler notificationHandler;
+
     protected final List<SchemaSourceRegistration<? extends SchemaSourceRepresentation>> sourceRegistrations =
             new ArrayList<>();
     protected final List<SchemaSourceRegistration<? extends SchemaSourceRepresentation>> sourceRegistrations =
             new ArrayList<>();
+
+    private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
+    private final ListeningExecutorService processingExecutor;
+    private final DeviceActionFactory deviceActionFactory;
+    private final NetconfDeviceSchemasResolver stateSchemasResolver;
+    private final NotificationHandler notificationHandler;
+    private final boolean reconnectOnSchemasChange;
+
     @GuardedBy("this")
     private boolean connected = false;
 
     @GuardedBy("this")
     private boolean connected = false;
 
@@ -115,8 +119,16 @@ public class NetconfDevice
     public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id,
                          final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
                          final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) {
     public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id,
                          final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
                          final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) {
+        this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null);
+    }
+
+    public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id,
+            final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
+            final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange,
+            final DeviceActionFactory deviceActionFactory) {
         this.id = id;
         this.reconnectOnSchemasChange = reconnectOnSchemasChange;
         this.id = id;
         this.reconnectOnSchemasChange = reconnectOnSchemasChange;
+        this.deviceActionFactory = deviceActionFactory;
         this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry();
         this.schemaRepository = schemaResourcesDTO.getSchemaRepository();
         this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory();
         this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry();
         this.schemaRepository = schemaResourcesDTO.getSchemaRepository();
         this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory();
@@ -215,19 +227,22 @@ public class NetconfDevice
 
     private synchronized void handleSalInitializationSuccess(final SchemaContext result,
                                         final NetconfSessionPreferences remoteSessionCapabilities,
 
     private synchronized void handleSalInitializationSuccess(final SchemaContext result,
                                         final NetconfSessionPreferences remoteSessionCapabilities,
-                                        final DOMRpcService deviceRpc) {
+                                        final DOMRpcService deviceRpc,
+                                        final RemoteDeviceCommunicator<NetconfMessage> listener) {
         //NetconfDevice.SchemaSetup can complete after NetconfDeviceCommunicator was closed. In that case do nothing,
         //since salFacade.onDeviceDisconnected was already called.
         if (connected) {
             final BaseSchema baseSchema =
                 remoteSessionCapabilities.isNotificationsSupported()
                         ? BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS : BaseSchema.BASE_NETCONF_CTX;
         //NetconfDevice.SchemaSetup can complete after NetconfDeviceCommunicator was closed. In that case do nothing,
         //since salFacade.onDeviceDisconnected was already called.
         if (connected) {
             final BaseSchema baseSchema =
                 remoteSessionCapabilities.isNotificationsSupported()
                         ? BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS : BaseSchema.BASE_NETCONF_CTX;
-            messageTransformer = new NetconfMessageTransformer(result, true, baseSchema);
+            this.messageTransformer = new NetconfMessageTransformer(result, true, baseSchema);
 
 
-            updateTransformer(messageTransformer);
+            updateTransformer(this.messageTransformer);
             // salFacade.onDeviceConnected has to be called before the notification handler is initialized
             // salFacade.onDeviceConnected has to be called before the notification handler is initialized
-            salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc);
-            notificationHandler.onRemoteSchemaUp(messageTransformer);
+            this.salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc,
+                    this.deviceActionFactory == null ? null : this.deviceActionFactory.createDeviceAction(
+                            this.messageTransformer, listener, result));
+            this.notificationHandler.onRemoteSchemaUp(this.messageTransformer);
 
             LOG.info("{}: Netconf connector initialized successfully", id);
         } else {
 
             LOG.info("{}: Netconf connector initialized successfully", id);
         } else {
@@ -478,6 +493,8 @@ public class NetconfDevice
 
         /**
          * Build schema context, in case of success or final failure notify device.
 
         /**
          * Build schema context, in case of success or final failure notify device.
+         *
+         * @param requiredSources required sources
          */
         @SuppressWarnings("checkstyle:IllegalCatch")
         private void setUpSchema(Collection<SourceIdentifier> requiredSources) {
          */
         @SuppressWarnings("checkstyle:IllegalCatch")
         private void setUpSchema(Collection<SourceIdentifier> requiredSources) {
@@ -501,7 +518,8 @@ public class NetconfDevice
                                             remoteSessionCapabilities.getNonModuleBasedCapsOrigin().get(entry)).build())
                             .collect(Collectors.toList()));
 
                                             remoteSessionCapabilities.getNonModuleBasedCapsOrigin().get(entry)).build())
                             .collect(Collectors.toList()));
 
-                    handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result));
+                    handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result),
+                            listener);
                     return;
                 } catch (final ExecutionException e) {
                     // schemaBuilderFuture.checkedGet() throws only SchemaResolutionException
                     return;
                 } catch (final ExecutionException e) {
                     // schemaBuilderFuture.checkedGet() throws only SchemaResolutionException
index f5521e61f4acca2f876d1fa5aba457de6ebb6212..199f0c09f768ec5b952ef2d6d987e02ae7adb450 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.netconf.sal.connect.netconf;
 
 import com.google.common.base.Preconditions;
 import java.util.concurrent.ExecutorService;
 
 import com.google.common.base.Preconditions;
 import java.util.concurrent.ExecutorService;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
@@ -21,6 +22,7 @@ public class NetconfDeviceBuilder {
     private RemoteDeviceId id;
     private RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
     private ExecutorService globalProcessingExecutor;
     private RemoteDeviceId id;
     private RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
     private ExecutorService globalProcessingExecutor;
+    private DeviceActionFactory deviceActionFactory;
 
     public NetconfDeviceBuilder() {
     }
 
     public NetconfDeviceBuilder() {
     }
@@ -50,15 +52,21 @@ public class NetconfDeviceBuilder {
         return this;
     }
 
         return this;
     }
 
+    public NetconfDeviceBuilder setDeviceActionFactory(DeviceActionFactory deviceActionFactory) {
+        this.deviceActionFactory = deviceActionFactory;
+        return this;
+    }
+
     public NetconfDevice build() {
         validation();
     public NetconfDevice build() {
         validation();
-        return new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange);
+        return new NetconfDevice(this.schemaResourcesDTO, this.id, this.salFacade, this.globalProcessingExecutor,
+                this.reconnectOnSchemasChange, this.deviceActionFactory);
     }
 
     private void validation() {
     }
 
     private void validation() {
-        Preconditions.checkNotNull(id, "RemoteDeviceId is not initialized");
-        Preconditions.checkNotNull(salFacade, "RemoteDeviceHandler is not initialized");
-        Preconditions.checkNotNull(globalProcessingExecutor, "ExecutorService is not initialized");
-        Preconditions.checkNotNull(schemaResourcesDTO, "SchemaResourceDTO is not initialized");
+        Preconditions.checkNotNull(this.id, "RemoteDeviceId is not initialized");
+        Preconditions.checkNotNull(this.salFacade, "RemoteDeviceHandler is not initialized");
+        Preconditions.checkNotNull(this.globalProcessingExecutor, "ExecutorService is not initialized");
+        Preconditions.checkNotNull(this.schemaResourcesDTO, "SchemaResourceDTO is not initialized");
     }
 }
     }
 }
index 5d42d3de4df17b8830ea89820285312720a72147..41e282ff88e39c8768b820df49c9b7783902d3df 100644 (file)
@@ -11,6 +11,7 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 import java.util.List;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import com.google.common.collect.Lists;
 import java.util.List;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
@@ -32,7 +33,7 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
     private final List<AutoCloseable> salRegistrations = Lists.newArrayList();
 
     public NetconfDeviceSalFacade(final RemoteDeviceId id, final DOMMountPointService mountPointService,
     private final List<AutoCloseable> salRegistrations = Lists.newArrayList();
 
     public NetconfDeviceSalFacade(final RemoteDeviceId id, final DOMMountPointService mountPointService,
-                                  final DataBroker dataBroker) {
+            final DataBroker dataBroker) {
         this.id = id;
         this.salProvider = new NetconfDeviceSalProvider(id, mountPointService, dataBroker);
     }
         this.id = id;
         this.salProvider = new NetconfDeviceSalProvider(id, mountPointService, dataBroker);
     }
@@ -51,7 +52,7 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
     @Override
     public synchronized void onDeviceConnected(final SchemaContext schemaContext,
                                                final NetconfSessionPreferences netconfSessionPreferences,
     @Override
     public synchronized void onDeviceConnected(final SchemaContext schemaContext,
                                                final NetconfSessionPreferences netconfSessionPreferences,
-                                               final DOMRpcService deviceRpc) {
+                                               final DOMRpcService deviceRpc, DOMActionService deviceAction) {
 
         final DOMDataBroker domBroker =
                 new NetconfDeviceDataBroker(id, schemaContext, deviceRpc, netconfSessionPreferences);
 
         final DOMDataBroker domBroker =
                 new NetconfDeviceDataBroker(id, schemaContext, deviceRpc, netconfSessionPreferences);
@@ -59,7 +60,7 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
         final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
 
         salProvider.getMountInstance()
         final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
 
         salProvider.getMountInstance()
-                .onTopologyDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService);
+                .onTopologyDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService, deviceAction);
         salProvider.getTopologyDatastoreAdapter()
                 .updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
     }
         salProvider.getTopologyDatastoreAdapter()
                 .updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
     }
index b24eb89c9927bcc3da6d7dcd1b8bcc4405de9e75..b215f882d23bf212e811f0fd56495df679c21458 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
@@ -53,20 +54,19 @@ public class NetconfDeviceSalProvider implements AutoCloseable {
         }
     };
 
         }
     };
 
-    public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final DOMMountPointService mountService,
-                                    final DataBroker dataBroker) {
-        this.id = deviceId;
-        mountInstance = new MountInstance(mountService, id);
-        this.dataBroker = dataBroker;
-        txChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(transactionChainListener);
-
-        topologyDatastoreAdapter = new NetconfDeviceTopologyAdapter(id, txChain);
+    public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final DOMMountPointService mountService) {
+        this(deviceId, mountService, null);
     }
 
     }
 
-    public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final DOMMountPointService mountService) {
+    public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final DOMMountPointService mountService,
+            final DataBroker dataBroker) {
         this.id = deviceId;
         mountInstance = new MountInstance(mountService, id);
         this.id = deviceId;
         mountInstance = new MountInstance(mountService, id);
-        this.dataBroker = null;
+        this.dataBroker = dataBroker;
+        if (dataBroker != null) {
+            txChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(transactionChainListener);
+            topologyDatastoreAdapter = new NetconfDeviceTopologyAdapter(id, txChain);
+        }
     }
 
     public MountInstance getMountInstance() {
     }
 
     public MountInstance getMountInstance() {
@@ -106,8 +106,8 @@ public class NetconfDeviceSalProvider implements AutoCloseable {
 
         private final DOMMountPointService mountService;
         private final RemoteDeviceId id;
 
         private final DOMMountPointService mountService;
         private final RemoteDeviceId id;
-        private NetconfDeviceNotificationService notificationService;
 
 
+        private NetconfDeviceNotificationService notificationService;
         private ObjectRegistration<DOMMountPoint> topologyRegistration;
 
         MountInstance(final DOMMountPointService mountService, final RemoteDeviceId id) {
         private ObjectRegistration<DOMMountPoint> topologyRegistration;
 
         MountInstance(final DOMMountPointService mountService, final RemoteDeviceId id) {
@@ -115,9 +115,15 @@ public class NetconfDeviceSalProvider implements AutoCloseable {
             this.id = Preconditions.checkNotNull(id);
         }
 
             this.id = Preconditions.checkNotNull(id);
         }
 
-        public synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
+        public void onTopologyDeviceConnected(final SchemaContext initialCtx,
                 final DOMDataBroker broker, final DOMRpcService rpc,
                 final NetconfDeviceNotificationService newNotificationService) {
                 final DOMDataBroker broker, final DOMRpcService rpc,
                 final NetconfDeviceNotificationService newNotificationService) {
+            onTopologyDeviceConnected(initialCtx, broker, rpc, newNotificationService, null);
+        }
+
+        public synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
+                final DOMDataBroker broker, final DOMRpcService rpc,
+                final NetconfDeviceNotificationService newNotificationService, DOMActionService deviceAction) {
             Preconditions.checkNotNull(mountService, "Closed");
             Preconditions.checkState(topologyRegistration == null, "Already initialized");
 
             Preconditions.checkNotNull(mountService, "Closed");
             Preconditions.checkState(topologyRegistration == null, "Already initialized");
 
@@ -128,11 +134,13 @@ public class NetconfDeviceSalProvider implements AutoCloseable {
             mountBuilder.addService(DOMDataBroker.class, broker);
             mountBuilder.addService(DOMRpcService.class, rpc);
             mountBuilder.addService(DOMNotificationService.class, newNotificationService);
             mountBuilder.addService(DOMDataBroker.class, broker);
             mountBuilder.addService(DOMRpcService.class, rpc);
             mountBuilder.addService(DOMNotificationService.class, newNotificationService);
+            if (deviceAction != null) {
+                mountBuilder.addService(DOMActionService.class, deviceAction);
+            }
             this.notificationService = newNotificationService;
 
             topologyRegistration = mountBuilder.register();
             LOG.debug("{}: TOPOLOGY Mountpoint exposed into MD-SAL {}", id, topologyRegistration);
             this.notificationService = newNotificationService;
 
             topologyRegistration = mountBuilder.register();
             LOG.debug("{}: TOPOLOGY Mountpoint exposed into MD-SAL {}", id, topologyRegistration);
-
         }
 
         @SuppressWarnings("checkstyle:IllegalCatch")
         }
 
         @SuppressWarnings("checkstyle:IllegalCatch")
index 8b68b22b2d4f3f117592d5ff52622753a5572f26..2bd1ce4b4afb594d18b0158d92bd997596b890f3 100644 (file)
@@ -11,7 +11,10 @@ import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTr
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
@@ -21,6 +24,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Map;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Map;
+import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.XMLStreamException;
 import javax.annotation.Nonnull;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.XMLStreamException;
@@ -30,6 +34,9 @@ import org.opendaylight.controller.md.sal.dom.api.DOMEvent;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
 import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.api.xml.MissingNameSpaceException;
 import org.opendaylight.netconf.api.xml.XmlElement;
 import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.api.xml.MissingNameSpaceException;
 import org.opendaylight.netconf.api.xml.XmlElement;
@@ -38,6 +45,7 @@ import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransform
 import org.opendaylight.netconf.sal.connect.util.MessageCounter;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.netconf.sal.connect.util.MessageCounter;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.RpcError;
 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.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -46,8 +54,13 @@ import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
@@ -67,8 +80,8 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
     private final MessageCounter counter;
     private final Map<QName, RpcDefinition> mappedRpcs;
     private final Multimap<QName, NotificationDefinition> mappedNotifications;
     private final MessageCounter counter;
     private final Map<QName, RpcDefinition> mappedRpcs;
     private final Multimap<QName, NotificationDefinition> mappedNotifications;
-
     private final boolean strictParsing;
     private final boolean strictParsing;
+    private final Set<ActionDefinition> actions;
 
     public NetconfMessageTransformer(final SchemaContext schemaContext, final boolean strictParsing) {
         this(schemaContext, strictParsing, BaseSchema.BASE_NETCONF_CTX);
 
     public NetconfMessageTransformer(final SchemaContext schemaContext, final boolean strictParsing) {
         this(schemaContext, strictParsing, BaseSchema.BASE_NETCONF_CTX);
@@ -78,13 +91,39 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
                                      final BaseSchema baseSchema) {
         this.counter = new MessageCounter();
         this.schemaContext = schemaContext;
                                      final BaseSchema baseSchema) {
         this.counter = new MessageCounter();
         this.schemaContext = schemaContext;
-        mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), SchemaNode::getQName);
-        mappedNotifications = Multimaps.index(schemaContext.getNotifications(),
+        this.mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), SchemaNode::getQName);
+        this.actions = getActions();
+        this.mappedNotifications = Multimaps.index(schemaContext.getNotifications(),
             node -> node.getQName().withoutRevision());
         this.baseSchema = baseSchema;
         this.strictParsing = strictParsing;
     }
 
             node -> node.getQName().withoutRevision());
         this.baseSchema = baseSchema;
         this.strictParsing = strictParsing;
     }
 
+    @VisibleForTesting
+    Set<ActionDefinition> getActions() {
+        Builder<ActionDefinition> builder = ImmutableSet.builder();
+        for (DataSchemaNode dataSchemaNode : schemaContext.getChildNodes()) {
+            if (dataSchemaNode instanceof ActionNodeContainer) {
+                findAction(dataSchemaNode, builder);
+            }
+        }
+        return builder.build();
+    }
+
+    private void findAction(DataSchemaNode dataSchemaNode, Builder<ActionDefinition> builder) {
+        if (dataSchemaNode instanceof ActionNodeContainer) {
+            final ActionNodeContainer containerSchemaNode = (ActionNodeContainer) dataSchemaNode;
+            for (ActionDefinition actionDefinition : containerSchemaNode.getActions()) {
+                builder.add(actionDefinition);
+            }
+        }
+        if (dataSchemaNode instanceof DataNodeContainer) {
+            for (DataSchemaNode innerDataSchemaNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+                findAction(innerDataSchemaNode, builder);
+            }
+        }
+    }
+
     @Override
     public synchronized DOMNotification toNotification(final NetconfMessage message) {
         final Map.Entry<Date, XmlElement> stripped = NetconfMessageTransformUtil.stripNotification(message);
     @Override
     public synchronized DOMNotification toNotification(final NetconfMessage message) {
         final Map.Entry<Date, XmlElement> stripped = NetconfMessageTransformUtil.stripNotification(message);
@@ -150,9 +189,9 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
         }
 
         Preconditions.checkNotNull(payload, "Transforming an rpc with input: %s, payload cannot be null", rpcQName);
         }
 
         Preconditions.checkNotNull(payload, "Transforming an rpc with input: %s, payload cannot be null", rpcQName);
+
         Preconditions.checkArgument(payload instanceof ContainerNode,
                 "Transforming an rpc with input: %s, payload has to be a container, but was: %s", rpcQName, payload);
         Preconditions.checkArgument(payload instanceof ContainerNode,
                 "Transforming an rpc with input: %s, payload has to be a container, but was: %s", rpcQName, payload);
-
         // Set the path to the input of rpc for the node stream writer
         rpc = rpc.createChild(QName.create(rpcQName, "input").intern());
         final DOMResult result = NetconfMessageTransformUtil.prepareDomResultForRpcRequest(rpcQName, counter);
         // Set the path to the input of rpc for the node stream writer
         rpc = rpc.createChild(QName.create(rpcQName, "input").intern());
         final DOMResult result = NetconfMessageTransformUtil.prepareDomResultForRpcRequest(rpcQName, counter);
@@ -172,6 +211,47 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
         return new NetconfMessage(node);
     }
 
         return new NetconfMessage(node);
     }
 
+    @Override
+    public NetconfMessage toActionRequest(SchemaPath action, DOMDataTreeIdentifier domDataTreeIdentifier,
+            final NormalizedNode<?, ?> payload) {
+        ActionDefinition actionDefinition = null;
+        SchemaPath schemaPath = action;
+        for (ActionDefinition actionDef : actions) {
+            if (actionDef.getPath().getLastComponent().equals(action.getLastComponent())) {
+                actionDefinition = actionDef;
+                schemaPath = actionDef.getPath();
+            }
+        }
+        Preconditions.checkNotNull(actionDefinition, "Action does not exist: %s", action.getLastComponent());
+
+        if (actionDefinition.getInput().getChildNodes().isEmpty()) {
+            return new NetconfMessage(NetconfMessageTransformUtil.prepareDomResultForActionRequest(
+                    domDataTreeIdentifier, action, counter, actionDefinition.getQName().getLocalName())
+                    .getNode().getOwnerDocument());
+        }
+
+        Preconditions.checkNotNull(payload, "Transforming an action with input: %s, payload cannot be null",
+                action.getLastComponent());
+        Preconditions.checkArgument(payload instanceof ContainerNode,
+                "Transforming an rpc with input: %s, payload has to be a container, but was: %s",
+                action.getLastComponent(), payload);
+        // Set the path to the input of rpc for the node stream writer
+        action = action.createChild(QName.create(action.getLastComponent(), "input").intern());
+        final DOMResult result = NetconfMessageTransformUtil.prepareDomResultForActionRequest(
+                domDataTreeIdentifier, action, counter, actionDefinition.getQName().getLocalName());
+
+        try {
+            NetconfMessageTransformUtil.writeNormalizedRpc((ContainerNode) payload, result,
+                    schemaPath.createChild(QName.create(action.getLastComponent(), "input").intern()), schemaContext);
+        } catch (final XMLStreamException | IOException | IllegalStateException e) {
+            throw new IllegalStateException("Unable to serialize " + action, e);
+        }
+
+        final Document node = result.getNode().getOwnerDocument();
+
+        return new NetconfMessage(node);
+    }
+
     private static boolean isBaseOrNotificationRpc(final QName rpc) {
         return rpc.getNamespace().equals(NETCONF_URI)
                 || rpc.getNamespace().equals(IETF_NETCONF_NOTIFICATIONS.getNamespace())
     private static boolean isBaseOrNotificationRpc(final QName rpc) {
         return rpc.getNamespace().equals(NETCONF_URI)
                 || rpc.getNamespace().equals(IETF_NETCONF_NOTIFICATIONS.getNamespace())
@@ -221,29 +301,48 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
                     "Unable to parse response of %s, the rpc is unknown", rpcQName);
 
             // In case no input for rpc is defined, we can simply construct the payload here
                     "Unable to parse response of %s, the rpc is unknown", rpcQName);
 
             // In case no input for rpc is defined, we can simply construct the payload here
-            if (rpcDefinition.getOutput().getChildNodes().isEmpty()) {
-                Preconditions.checkArgument(XmlElement.fromDomDocument(
-                    message.getDocument()).getOnlyChildElementWithSameNamespaceOptionally("ok").isPresent(),
-                    "Unexpected content in response of rpc: %s, %s", rpcDefinition.getQName(), message);
-                normalizedNode = null;
-            } else {
-                final Element element = message.getDocument().getDocumentElement();
-                try {
-                    final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
-                    final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
-                    final XmlParserStream xmlParser = XmlParserStream.create(writer, schemaContext,
-                            rpcDefinition.getOutput(), strictParsing);
-                    xmlParser.traverse(new DOMSource(element));
-                    normalizedNode = resultHolder.getResult();
-                } catch (XMLStreamException | URISyntaxException | IOException | ParserConfigurationException
-                        | SAXException e) {
-                    throw new IllegalArgumentException(String.format("Failed to parse RPC response %s", element), e);
-                }
-            }
+            normalizedNode = parseResult(message, rpcDefinition);
         }
         return new DefaultDOMRpcResult(normalizedNode);
     }
 
         }
         return new DefaultDOMRpcResult(normalizedNode);
     }
 
+    @Override
+    public DOMActionResult toActionResult(SchemaPath action, NetconfMessage message) {
+        ActionDefinition actionDefinition = null;
+        for (ActionDefinition actionDef : actions) {
+            if (actionDef.getPath().getLastComponent().equals(action.getLastComponent())) {
+                actionDefinition = actionDef;
+            }
+        }
+        Preconditions.checkNotNull(actionDefinition, "Action does not exist: %s", action);
+        ContainerNode normalizedNode = (ContainerNode) parseResult(message, actionDefinition);
+
+        return new SimpleDOMActionResult(normalizedNode, Collections.<RpcError>emptyList());
+    }
+
+    private NormalizedNode<?, ?> parseResult(final NetconfMessage message,
+            final OperationDefinition operationDefinition) {
+        if (operationDefinition.getOutput().getChildNodes().isEmpty()) {
+            Preconditions.checkArgument(XmlElement.fromDomDocument(
+                message.getDocument()).getOnlyChildElementWithSameNamespaceOptionally("ok").isPresent(),
+                "Unexpected content in response of rpc: %s, %s", operationDefinition.getQName(), message);
+            return null;
+        } else {
+            final Element element = message.getDocument().getDocumentElement();
+            try {
+                final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
+                final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
+                final XmlParserStream xmlParser = XmlParserStream.create(writer, schemaContext,
+                        operationDefinition.getOutput(), strictParsing);
+                xmlParser.traverse(new DOMSource(element));
+                return resultHolder.getResult();
+            } catch (XMLStreamException | URISyntaxException | IOException | ParserConfigurationException
+                    | SAXException e) {
+                throw new IllegalArgumentException(String.format("Failed to parse RPC response %s", element), e);
+            }
+        }
+    }
+
     static class NetconfDeviceNotification implements DOMNotification, DOMEvent {
         private final ContainerNode content;
         private final SchemaPath schemaPath;
     static class NetconfDeviceNotification implements DOMNotification, DOMEvent {
         private final ContainerNode content;
         private final SchemaPath schemaPath;
index b7f9dfb96476643e1fce4f48d144d5235333a1c2..7f5e31d14000f191cc8288ba11e3fd5a6d9f7fbd 100644 (file)
@@ -18,6 +18,7 @@ import java.util.AbstractMap;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -25,6 +26,7 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.stream.XMLStreamWriter;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.netconf.api.DocumentedException;
 import org.opendaylight.netconf.api.FailedNetconfMessage;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.netconf.api.DocumentedException;
 import org.opendaylight.netconf.api.FailedNetconfMessage;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
@@ -47,6 +49,8 @@ import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -128,6 +132,9 @@ public final class NetconfMessageTransformUtil {
     public static final QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter").intern();
     public static final QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get").intern();
     public static final QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc").intern();
     public static final QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter").intern();
     public static final QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get").intern();
     public static final QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc").intern();
+    public static final QName YANG_QNAME = null;
+    public static final URI NETCONF_ACTION_NAMESPACE = URI.create("urn:ietf:params:xml:ns:yang:1");
+    public static final String NETCONF_ACTION = "action";
 
     public static final URI NETCONF_ROLLBACK_ON_ERROR_URI = URI
             .create("urn:ietf:params:netconf:capability:rollback-on-error:1.0");
 
     public static final URI NETCONF_ROLLBACK_ON_ERROR_URI = URI
             .create("urn:ietf:params:netconf:capability:rollback-on-error:1.0");
@@ -384,6 +391,51 @@ public final class NetconfMessageTransformUtil {
         return new DOMResult(elementNS);
     }
 
         return new DOMResult(elementNS);
     }
 
+    public static DOMResult prepareDomResultForActionRequest(DOMDataTreeIdentifier domDataTreeIdentifier,
+            final SchemaPath actionSchemaPath, final MessageCounter counter, String action) {
+        final Document document = XmlUtil.newDocument();
+        final Element rpcNS =
+                document.createElementNS(NETCONF_RPC_QNAME.getNamespace().toString(), NETCONF_RPC_QNAME.getLocalName());
+        // set msg id
+        rpcNS.setAttribute(MESSAGE_ID_ATTR, counter.getNewMessageId(MESSAGE_ID_PREFIX));
+
+        final Element actionNS = document.createElementNS(NETCONF_ACTION_NAMESPACE.toString(), NETCONF_ACTION);
+
+        final Element actionData = prepareActionData(actionNS,
+                domDataTreeIdentifier.getRootIdentifier().getPathArguments().iterator(), document);
+
+        Element specificActionElement = document.createElement(action);
+        actionData.appendChild(specificActionElement);
+        rpcNS.appendChild(actionNS);
+        document.appendChild(rpcNS);
+        return new DOMResult(specificActionElement);
+    }
+
+    private static Element prepareActionData(Element actionNS, Iterator<PathArgument> iterator, Document document) {
+        if (iterator.hasNext()) {
+            PathArgument next = iterator.next();
+            final QName actualNS = next.getNodeType();
+
+            final Element actualElement = document.createElementNS(actualNS.getNamespace().toString(),
+                    actualNS.getLocalName());
+            if (next instanceof NodeWithValue) {
+                actualElement.setNodeValue(((NodeWithValue) next).getValue().toString());
+            } else if (next instanceof NodeIdentifierWithPredicates) {
+                for (Entry<QName, Object> entry : ((NodeIdentifierWithPredicates) next).getKeyValues().entrySet()) {
+                    final Element entryElement = document.createElementNS(entry.getKey().getNamespace().toString(),
+                            entry.getKey().getLocalName());
+                    entryElement.setTextContent(entry.getValue().toString());
+                    entryElement.setNodeValue(entry.getValue().toString());
+                    actualElement.appendChild(entryElement);
+                }
+            }
+            actionNS.appendChild(actualElement);
+            return prepareActionData(actualElement, iterator, document);
+        } else {
+            return actionNS;
+        }
+    }
+
     @SuppressWarnings("checkstyle:IllegalCatch")
     public static void writeNormalizedRpc(final ContainerNode normalized, final DOMResult result,
                                           final SchemaPath schemaPath,
     @SuppressWarnings("checkstyle:IllegalCatch")
     public static void writeNormalizedRpc(final ContainerNode normalized, final DOMResult result,
                                           final SchemaPath schemaPath,
index 3618bf67d341a3f6d5519aaf840d689c1d0cf26e..0d3ca2dab49aeb8b7e6cfc0186d62e328749746f 100644 (file)
@@ -39,6 +39,7 @@ import java.util.concurrent.Executors;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
@@ -160,7 +161,8 @@ public class NetconfDeviceTest {
         device.onRemoteSessionUp(sessionCaps, listener);
 
         Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(
         device.onRemoteSessionUp(sessionCaps, listener);
 
         Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(
-                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
+                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class),
+                any(DOMActionService.class));
         Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
     }
 
         Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
     }
 
@@ -247,7 +249,8 @@ public class NetconfDeviceTest {
         device.onRemoteSessionUp(sessionCaps, listener);
 
         Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(
         device.onRemoteSessionUp(sessionCaps, listener);
 
         Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(
-                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
+                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class),
+                any(DOMActionService.class));
         Mockito.verify(schemaFactory, times(1)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
     }
 
         Mockito.verify(schemaFactory, times(1)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
     }
 
@@ -325,7 +328,8 @@ public class NetconfDeviceTest {
 
         verify(schemaContextProviderFactory, timeout(5000)).createSchemaContext(any(Collection.class));
         verify(facade, timeout(5000)).onDeviceConnected(
 
         verify(schemaContextProviderFactory, timeout(5000)).createSchemaContext(any(Collection.class));
         verify(facade, timeout(5000)).onDeviceConnected(
-                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
+                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class),
+                any(DOMActionService.class));
 
         device.onRemoteSessionDown();
         verify(facade, timeout(5000)).onDeviceDisconnected();
 
         device.onRemoteSessionDown();
         verify(facade, timeout(5000)).onDeviceDisconnected();
@@ -334,7 +338,8 @@ public class NetconfDeviceTest {
 
         verify(schemaContextProviderFactory, timeout(5000).times(2)).createSchemaContext(any(Collection.class));
         verify(facade, timeout(5000).times(2)).onDeviceConnected(
 
         verify(schemaContextProviderFactory, timeout(5000).times(2)).createSchemaContext(any(Collection.class));
         verify(facade, timeout(5000).times(2)).onDeviceConnected(
-                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
+                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class),
+                any(DOMActionService.class));
     }
 
     @Test
     }
 
     @Test
@@ -364,7 +369,7 @@ public class NetconfDeviceTest {
         //complete schema setup
         schemaFuture.set(getSchema());
         //facade.onDeviceDisconnected() was called, so facade.onDeviceConnected() shouldn't be called anymore
         //complete schema setup
         schemaFuture.set(getSchema());
         //facade.onDeviceDisconnected() was called, so facade.onDeviceConnected() shouldn't be called anymore
-        verify(facade, after(1000).never()).onDeviceConnected(any(), any(), any());
+        verify(facade, after(1000).never()).onDeviceConnected(any(), any(), any(), any(DOMActionService.class));
     }
 
     @Test
     }
 
     @Test
@@ -397,7 +402,8 @@ public class NetconfDeviceTest {
         final ArgumentCaptor<NetconfSessionPreferences> argument =
                 ArgumentCaptor.forClass(NetconfSessionPreferences.class);
         verify(facade, timeout(5000))
         final ArgumentCaptor<NetconfSessionPreferences> argument =
                 ArgumentCaptor.forClass(NetconfSessionPreferences.class);
         verify(facade, timeout(5000))
-                .onDeviceConnected(any(SchemaContext.class), argument.capture(), any(DOMRpcService.class));
+                .onDeviceConnected(any(SchemaContext.class), argument.capture(), any(DOMRpcService.class),
+                        any(DOMActionService.class));
         final NetconfDeviceCapabilities netconfDeviceCaps = argument.getValue().getNetconfDeviceCapabilities();
 
         netconfDeviceCaps.getResolvedCapabilities()
         final NetconfDeviceCapabilities netconfDeviceCaps = argument.getValue().getNetconfDeviceCapabilities();
 
         netconfDeviceCaps.getResolvedCapabilities()
@@ -421,7 +427,8 @@ public class NetconfDeviceTest {
         final RemoteDeviceHandler<NetconfSessionPreferences> remoteDeviceHandler =
                 mockCloseableClass(RemoteDeviceHandler.class);
         doNothing().when(remoteDeviceHandler).onDeviceConnected(
         final RemoteDeviceHandler<NetconfSessionPreferences> remoteDeviceHandler =
                 mockCloseableClass(RemoteDeviceHandler.class);
         doNothing().when(remoteDeviceHandler).onDeviceConnected(
-                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
+                any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class),
+                any(DOMActionService.class));
         doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
         doNothing().when(remoteDeviceHandler).onNotification(any(DOMNotification.class));
         return remoteDeviceHandler;
         doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
         doNothing().when(remoteDeviceHandler).onNotification(any(DOMNotification.class));
         return remoteDeviceHandler;
index 05678f14ffa9ff3c3533b72998a6eaf0f7c54aea..c96a6741d16a079839597e90cbff0e88b780c7fd 100644 (file)
@@ -23,6 +23,7 @@ import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.dom.api.DOMActionService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
@@ -92,10 +93,11 @@ public class NetconfDeviceSalFacadeTest {
                 NetconfSessionPreferences.fromStrings(getCapabilities());
 
         final DOMRpcService deviceRpc = mock(DOMRpcService.class);
                 NetconfSessionPreferences.fromStrings(getCapabilities());
 
         final DOMRpcService deviceRpc = mock(DOMRpcService.class);
-        deviceFacade.onDeviceConnected(schemaContext, netconfSessionPreferences, deviceRpc);
+        deviceFacade.onDeviceConnected(schemaContext, netconfSessionPreferences, deviceRpc, null);
 
         verify(mountInstance, times(1)).onTopologyDeviceConnected(eq(schemaContext),
 
         verify(mountInstance, times(1)).onTopologyDeviceConnected(eq(schemaContext),
-                any(DOMDataBroker.class), eq(deviceRpc), any(NetconfDeviceNotificationService.class));
+                any(DOMDataBroker.class), eq(deviceRpc), any(NetconfDeviceNotificationService.class),
+                any(DOMActionService.class));
         verify(netconfDeviceTopologyAdapter,
                 times(1)).updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
     }
         verify(netconfDeviceTopologyAdapter,
                 times(1)).updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
     }
index a884a887f209f8f244dd8daad51bd39f691a6f90..96a59a18bfd1097783aa2f4378edbc41058ce77c 100644 (file)
@@ -35,10 +35,14 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import java.io.IOException;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.dom.DOMSource;
+import org.apache.xerces.dom.TextImpl;
 import org.custommonkey.xmlunit.Diff;
 import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
 import org.custommonkey.xmlunit.XMLUnit;
 import org.custommonkey.xmlunit.Diff;
 import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
 import org.custommonkey.xmlunit.XMLUnit;
@@ -47,6 +51,8 @@ import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.api.xml.XmlUtil;
 import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
 import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.api.xml.XmlUtil;
 import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
@@ -59,17 +65,32 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.mon
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
 
 public class NetconfMessageTransformerTest {
 
 import org.xml.sax.SAXException;
 
 public class NetconfMessageTransformerTest {
 
+    private static final String REVISION_EXAMPLE_SERVER_FARM = "2018-08-07";
+    private static final String URN_EXAMPLE_SERVER_FARM = "urn:example:server-farm";
+
+    private NetconfMessageTransformer actionNetconfMessageTransformer;
     private NetconfMessageTransformer netconfMessageTransformer;
     private SchemaContext schema;
 
     private NetconfMessageTransformer netconfMessageTransformer;
     private SchemaContext schema;
 
@@ -81,7 +102,7 @@ public class NetconfMessageTransformerTest {
 
         schema = getSchema(true);
         netconfMessageTransformer = getTransformer(schema);
 
         schema = getSchema(true);
         netconfMessageTransformer = getTransformer(schema);
-
+        actionNetconfMessageTransformer = getActionMessageTransformer();
     }
 
     @Test
     }
 
     @Test
@@ -131,7 +152,7 @@ public class NetconfMessageTransformerTest {
     }
 
     @Test
     }
 
     @Test
-    public void tesGetSchemaRequest() throws Exception {
+    public void testGetSchemaRequest() throws Exception {
         final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(GET_SCHEMA_QNAME),
                 NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")));
         assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
         final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(GET_SCHEMA_QNAME),
                 NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")));
         assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
@@ -144,7 +165,7 @@ public class NetconfMessageTransformerTest {
     }
 
     @Test
     }
 
     @Test
-    public void tesGetSchemaResponse() throws Exception {
+    public void testGetSchemaResponse() throws Exception {
         final NetconfMessageTransformer transformer = getTransformer(getSchema(true));
         final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
                 "<rpc-reply message-id=\"101\"\n"
         final NetconfMessageTransformer transformer = getTransformer(getSchema(true));
         final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
                 "<rpc-reply message-id=\"101\"\n"
@@ -343,4 +364,176 @@ public class NetconfMessageTransformerTest {
                         .netconf.monitoring.rev101004.$YangModuleInfoImpl.getInstance()));
         return moduleInfoBackedContext.tryToCreateSchemaContext().get();
     }
                         .netconf.monitoring.rev101004.$YangModuleInfoImpl.getInstance()));
         return moduleInfoBackedContext.tryToCreateSchemaContext().get();
     }
+
+    @Test
+    public void getActionsTest() {
+        QName reset = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "reset");
+        QName start = QName.create(reset, "start");
+        QName open = QName.create(start, "open");
+        Set<QName> qnames = new HashSet<>(Arrays.asList(reset, start, open));
+        Set<ActionDefinition> actions = actionNetconfMessageTransformer.getActions();
+        assertTrue(!actions.isEmpty());
+        for (ActionDefinition actionDefinition : actions) {
+            QName qname = actionDefinition.getQName();
+            assertTrue(qnames.contains(qname));
+            qnames.remove(qname);
+        }
+    }
+
+    @Test
+    public void toActionRequestListTopLevelTest() {
+        QName qname = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "server");
+        QName nameQname = QName.create(qname, "name");
+        QName actionResetQName = QName.create(qname, "reset");
+
+        Set<PathArgument> nodeIdentifiers =
+                Collections.singleton(new NodeIdentifierWithPredicates(qname, nameQname, "test"));
+        DOMDataTreeIdentifier domDataTreeIdentifier = prepareDataTreeId(nodeIdentifiers);
+
+        ContainerNode data = initInputAction(QName.create(qname, "reset-at"), "now");
+
+        NetconfMessage actionRequest = actionNetconfMessageTransformer.toActionRequest(
+                SchemaPath.create(true, actionResetQName), domDataTreeIdentifier, data);
+
+        Node childAction = checkBasePartOfActionRequest(actionRequest);
+
+        Node childServer = childAction.getFirstChild();
+        checkNode(childServer, "server", "server", qname.getNamespace().toString());
+
+        Node childName = childServer.getFirstChild();
+        checkNode(childName, "name", "name", qname.getNamespace().toString());
+
+        TextImpl childTest = (TextImpl) childName.getFirstChild();
+        assertEquals(childTest.getData(), "test");
+
+        checkAction(actionResetQName, childName.getNextSibling(), "reset-at", "reset-at", "now");
+    }
+
+    @Test
+    public void toActionRequestContainerTopLevelTest() {
+        QName qname = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "device");
+        QName actionStartQName = QName.create(qname, "start");
+
+        Set<PathArgument> nodeIdentifiers = Collections.singleton(NodeIdentifier.create(qname));
+        DOMDataTreeIdentifier domDataTreeIdentifier = prepareDataTreeId(nodeIdentifiers);
+
+        NormalizedNode<?, ?> payload = initInputAction(QName.create(qname, "start-at"), "now");
+        NetconfMessage actionRequest = actionNetconfMessageTransformer.toActionRequest(
+                SchemaPath.create(true, actionStartQName), domDataTreeIdentifier, payload);
+
+        Node childAction = checkBasePartOfActionRequest(actionRequest);
+
+        Node childDevice = childAction.getFirstChild();
+        checkNode(childDevice, "device", "device", qname.getNamespace().toString());
+
+        checkAction(actionStartQName, childDevice.getFirstChild(), "start-at", "start-at", "now");
+    }
+
+    @Test
+    public void toActionRequestContainerInContainerTest() {
+        QName boxOutQName = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "box-out");
+        QName boxInQName = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "box-in");
+        QName actionOpenQName = QName.create(boxOutQName, "open");
+
+        Set<PathArgument> nodeIdentifiers = new HashSet<>();
+        nodeIdentifiers.add(NodeIdentifier.create(boxOutQName));
+        nodeIdentifiers.add(NodeIdentifier.create(boxInQName));
+
+        DOMDataTreeIdentifier domDataTreeIdentifier = prepareDataTreeId(nodeIdentifiers);
+
+        NormalizedNode<?, ?> payload = initInputAction(QName.create(boxOutQName, "start-at"), "now");
+        NetconfMessage actionRequest = actionNetconfMessageTransformer.toActionRequest(
+                SchemaPath.create(true, actionOpenQName), domDataTreeIdentifier, payload);
+
+        Node childAction = checkBasePartOfActionRequest(actionRequest);
+
+        Node childBoxOut = childAction.getFirstChild();
+        checkNode(childBoxOut, "box-out", "box-out", boxOutQName.getNamespace().toString());
+
+        Node childBoxIn = childBoxOut.getFirstChild();
+        checkNode(childBoxIn, "box-in", "box-in", boxOutQName.getNamespace().toString());
+
+        Node action = childBoxIn.getFirstChild();
+        checkNode(action, null, actionOpenQName.getLocalName(), null);
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Test
+    public void toActionResultTest() throws Exception {
+        QName qname = QName.create(URN_EXAMPLE_SERVER_FARM, REVISION_EXAMPLE_SERVER_FARM, "reset");
+
+        NetconfMessage message = new NetconfMessage(XmlUtil.readXmlToDocument(
+                "<rpc-reply message-id=\"101\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+                + "<reset-finished-at xmlns=\"urn:example:server-farm\">"
+                + "now"
+                + "</reset-finished-at>"
+                + "</rpc-reply>"));
+        DOMActionResult actionResult = actionNetconfMessageTransformer.toActionResult(
+                SchemaPath.create(true, qname), message);
+        assertNotNull(actionResult);
+        ContainerNode containerNode = actionResult.getOutput().get();
+        assertNotNull(containerNode);
+        LeafNode<String> leaf = (LeafNode) containerNode.getValue().iterator().next();
+        assertEquals("now", leaf.getValue());
+    }
+
+    private void checkAction(QName actionQname, Node action , String inputLocalName, String inputNodeName,
+            String inputValue) {
+        checkNode(action, null, actionQname.getLocalName(), null);
+
+        Node childResetAt = action.getFirstChild();
+        checkNode(childResetAt, inputLocalName, inputNodeName, actionQname.getNamespace().toString());
+
+        TextImpl firstChild = (TextImpl) childResetAt.getFirstChild();
+        assertEquals(firstChild.getData(), inputValue);
+    }
+
+    private Node checkBasePartOfActionRequest(NetconfMessage actionRequest) {
+        Node baseRpc = actionRequest.getDocument().getFirstChild();
+        checkNode(baseRpc, "rpc", "rpc", NetconfMessageTransformUtil.NETCONF_QNAME.getNamespace().toString());
+        assertTrue(baseRpc.getLocalName().equals("rpc"));
+        assertTrue(baseRpc.getNodeName().equals("rpc"));
+
+        Node messageId = baseRpc.getAttributes().getNamedItem("message-id");
+        assertNotNull(messageId);
+        assertTrue(messageId.getNodeValue().contains("m-"));
+
+        Node childAction = baseRpc.getFirstChild();
+        checkNode(childAction, "action", "action", NetconfMessageTransformUtil.NETCONF_ACTION_NAMESPACE.toString());
+        return childAction;
+    }
+
+    private DOMDataTreeIdentifier prepareDataTreeId(Set<PathArgument> nodeIdentifiers) {
+        YangInstanceIdentifier yangInstanceIdentifier =
+                YangInstanceIdentifier.builder().append(nodeIdentifiers).build();
+        DOMDataTreeIdentifier domDataTreeIdentifier =
+                new DOMDataTreeIdentifier(org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION,
+                        yangInstanceIdentifier);
+        return domDataTreeIdentifier;
+    }
+
+    private ContainerNode initInputAction(QName qname, String value) {
+        ImmutableLeafNodeBuilder<String> immutableLeafNodeBuilder = new ImmutableLeafNodeBuilder<>();
+        DataContainerChild<NodeIdentifier, String> build = immutableLeafNodeBuilder.withNodeIdentifier(
+                NodeIdentifier.create(qname)).withValue(value).build();
+        ContainerNode data = ImmutableContainerNodeBuilder.create().withNodeIdentifier(NodeIdentifier.create(
+                QName.create(qname, "input"))).withChild(build).build();
+        return data;
+    }
+
+    private void checkNode(Node childServer, String expectedLocalName, String expectedNodeName,
+            String expectedNamespace) {
+        assertNotNull(childServer);
+        assertEquals(childServer.getLocalName(), expectedLocalName);
+        assertEquals(childServer.getNodeName(), expectedNodeName);
+        assertEquals(childServer.getNamespaceURI(), expectedNamespace);
+    }
+
+    private SchemaContext getActionSchema() {
+        return YangParserTestUtils.parseYangResource("/schemas/example-server-farm.yang");
+    }
+
+    private NetconfMessageTransformer getActionMessageTransformer() {
+        return new NetconfMessageTransformer(getActionSchema(), true);
+    }
 }
 }
diff --git a/netconf/sal-netconf-connector/src/test/resources/schemas/example-server-farm.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/example-server-farm.yang
new file mode 100644 (file)
index 0000000..0cc0fef
--- /dev/null
@@ -0,0 +1,51 @@
+module example-server-farm {
+  yang-version 1.1;
+  namespace "urn:example:server-farm";
+  prefix "sfarm";
+  revision 2018-08-07;
+
+  list server {
+    key name;
+    leaf name {
+      type string;
+    }
+    action reset {
+      input {
+        leaf reset-at {
+          type string;
+          mandatory true;
+        }
+      }
+      output {
+        leaf reset-finished-at {
+          type string;
+          mandatory true;
+        }
+      }
+    }
+  }
+
+  container device {
+    action start {
+      input {
+        leaf start-at {
+          type string;
+          mandatory true;
+        }
+      }
+      output {
+        leaf start-finished-at {
+          type string;
+          mandatory true;
+        }
+      }
+    }
+  }
+
+  container box-out {
+    container box-in {
+      action open {
+      }
+    }
+  }
+}
\ No newline at end of file