*/
package org.opendaylight.controller.sal.connect.netconf.util;
+import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
NETCONF_GET_QNAME.getLocalName()));
}
+ public static boolean isGetOperation(final QName rpc) {
+ return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_GET_QNAME.getLocalName());
+ }
+
+ public static boolean isGetConfigOperation(final QName rpc) {
+ return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_GET_CONFIG_QNAME.getLocalName());
+ }
+
public static boolean isDataEditOperation(final QName rpc) {
return NETCONF_URI.equals(rpc.getNamespace())
&& rpc.getLocalName().equals(NETCONF_EDIT_CONFIG_QNAME.getLocalName());
return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(editConfigProxy));
}
+ /**
+ * Creates artificial schema node for edit-config rpc. This artificial schema looks like:
+ * <pre>
+ * {@code
+ * rpc
+ * get
+ * filter
+ * // All schema nodes from remote schema
+ * filter
+ * get
+ * rpc
+ * }
+ * </pre>
+ *
+ * This makes the translation of rpc get request(especially the config node)
+ * to xml use schema which is crucial for some types of nodes e.g. identity-ref.
+ */
+ public static DataNodeContainer createSchemaForGet(final SchemaContext schemaContext) {
+ final QName filter = QName.create(NETCONF_GET_QNAME, "filter");
+ final QName get = QName.create(NETCONF_GET_QNAME, "get");
+ final NodeContainerProxy configProxy = new NodeContainerProxy(filter, schemaContext.getChildNodes());
+ final NodeContainerProxy editConfigProxy = new NodeContainerProxy(get, Sets.<DataSchemaNode>newHashSet(configProxy));
+ return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(editConfigProxy));
+ }
+
+ /**
+ * Creates artificial schema node for get rpc. This artificial schema looks like:
+ * <pre>
+ * {@code
+ * rpc
+ * get-config
+ * filter
+ * // All schema nodes from remote schema
+ * filter
+ * get-config
+ * rpc
+ * }
+ * </pre>
+ *
+ * This makes the translation of rpc get-config request(especially the config node)
+ * to xml use schema which is crucial for some types of nodes e.g. identity-ref.
+ */
+ public static DataNodeContainer createSchemaForGetConfig(final SchemaContext schemaContext) {
+ final QName filter = QName.create(NETCONF_GET_CONFIG_QNAME, "filter");
+ final QName getConfig = QName.create(NETCONF_GET_CONFIG_QNAME, "get-config");
+ final NodeContainerProxy configProxy = new NodeContainerProxy(filter, schemaContext.getChildNodes());
+ final NodeContainerProxy editConfigProxy = new NodeContainerProxy(getConfig, Sets.<DataSchemaNode>newHashSet(configProxy));
+ return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(editConfigProxy));
+ }
+
+ /**
+ * Creates artificial schema node for schema defined rpc. This artificial schema looks like:
+ * <pre>
+ * {@code
+ * rpc
+ * rpc-name
+ * // All schema nodes from remote schema
+ * rpc-name
+ * rpc
+ * }
+ * </pre>
+ *
+ * This makes the translation of schema defined rpc request
+ * to xml use schema which is crucial for some types of nodes e.g. identity-ref.
+ */
+ public static DataNodeContainer createSchemaForRpc(final QName rpcName, final SchemaContext schemaContext) {
+ Preconditions.checkNotNull(rpcName);
+ Preconditions.checkNotNull(schemaContext);
+
+ final NodeContainerProxy rpcBodyProxy = new NodeContainerProxy(rpcName, schemaContext.getChildNodes());
+ return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(rpcBodyProxy));
+
+ }
+
public static CompositeNodeTOImpl wrap(final QName name, final Node<?> node) {
if (node != null) {
return new CompositeNodeTOImpl(name, null, Collections.<Node<?>> singletonList(node));
import java.util.List;
import java.util.Set;
-import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.w3c.dom.Document;
+
/**
* Test case for reported bug 1355
*/
public class NetconfToRpcRequestTest {
- private String TEST_MODEL_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:rpc-test";
- private String REVISION = "2014-07-14";
- private QName INPUT_QNAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "input");
- private QName STREAM_NAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "stream-name");
- private QName RPC_NAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "subscribe");
+ private final static String TEST_MODEL_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:rpc-test";
+ private final static String REVISION = "2014-07-14";
+ private final static QName INPUT_QNAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "input");
+ private final static QName STREAM_NAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "stream-name");
+ private final static QName SUBSCRIBE_RPC_NAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "subscribe");
+
+ private final static String CONFIG_TEST_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:rpc:config:defs";
+ private final static String CONFIG_TEST_REVISION = "2014-07-21";
+ private final static QName EDIT_CONFIG_QNAME = QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "edit-config");
+ private final static QName GET_QNAME = QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "get");
+ private final static QName GET_CONFIG_QNAME = QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "get-config");
- NetconfMessageTransformer messageTransformer;
+ static SchemaContext notifCtx;
+ static SchemaContext cfgCtx;
+ static NetconfMessageTransformer messageTransformer;
@SuppressWarnings("deprecation")
- @Before
- public void setup() throws Exception {
- final List<InputStream> modelsToParse = Collections
- .singletonList(getClass().getResourceAsStream("/schemas/rpc-notification-subscription.yang"));
- final YangContextParser parser = new YangParserImpl();
- final Set<Module> modules = parser.parseYangModelsFromStreams(modelsToParse);
- assertTrue(!modules.isEmpty());
- final SchemaContext schemaContext = parser.resolveSchemaContext(modules);
- assertNotNull(schemaContext);
+ @BeforeClass
+ public static void setup() throws Exception {
+ List<InputStream> modelsToParse = Collections
+ .singletonList(NetconfToRpcRequestTest.class.getResourceAsStream("/schemas/rpc-notification-subscription.yang"));
+ YangContextParser parser = new YangParserImpl();
+ final Set<Module> notifModules = parser.parseYangModelsFromStreams(modelsToParse);
+ assertTrue(!notifModules.isEmpty());
+
+ notifCtx = parser.resolveSchemaContext(notifModules);
+ assertNotNull(notifCtx);
+
+ modelsToParse = Collections
+ .singletonList(NetconfToRpcRequestTest.class.getResourceAsStream("/schemas/config-test-rpc.yang"));
+ parser = new YangParserImpl();
+ final Set<Module> configModules = parser.parseYangModelsFromStreams(modelsToParse);
+ cfgCtx = parser.resolveSchemaContext(configModules);
+ assertNotNull(cfgCtx);
messageTransformer = new NetconfMessageTransformer();
- messageTransformer.onGlobalContextUpdated(schemaContext);
}
@Test
- public void test() throws Exception {
+ public void testIsDataEditOperation() throws Exception {
+ messageTransformer.onGlobalContextUpdated(cfgCtx);
+
final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
- rootBuilder.setQName(RPC_NAME);
+ rootBuilder.setQName(EDIT_CONFIG_QNAME);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
+ inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> targetBuilder = ImmutableCompositeNode.builder();
+ targetBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "target"));
+ targetBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "running"), null);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> configBuilder = ImmutableCompositeNode.builder();
+ configBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "config"));
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlTopBuilder = ImmutableCompositeNode.builder();
+ anyxmlTopBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "top"));
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlInterfBuilder = ImmutableCompositeNode.builder();
+ anyxmlInterfBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "interface"));
+
+ anyxmlInterfBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "name"), "Ethernet0/0");
+ anyxmlInterfBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "mtu"), "1500");
+
+ anyxmlTopBuilder.add(anyxmlInterfBuilder.toInstance());
+ configBuilder.add(anyxmlTopBuilder.toInstance());
+
+ inputBuilder.add(targetBuilder.toInstance());
+ inputBuilder.add(configBuilder.toInstance());
+
+ rootBuilder.add(inputBuilder.toInstance());
+ final ImmutableCompositeNode root = rootBuilder.toInstance();
+
+ final NetconfMessage message = messageTransformer.toRpcRequest(EDIT_CONFIG_QNAME, root);
+ assertNotNull(message);
+
+ final Document xmlDoc = message.getDocument();
+ org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
+ assertEquals(rpcChild.getLocalName(), "rpc");
+
+ final org.w3c.dom.Node editConfigNode = rpcChild.getFirstChild();
+ assertEquals(editConfigNode.getLocalName(), "edit-config");
+
+ final org.w3c.dom.Node targetNode = editConfigNode.getFirstChild();
+ assertEquals(targetNode.getLocalName(), "target");
+
+ final org.w3c.dom.Node runningNode = targetNode.getFirstChild();
+ assertEquals(runningNode.getLocalName(), "running");
+
+ final org.w3c.dom.Node configNode = targetNode.getNextSibling();
+ assertEquals(configNode.getLocalName(), "config");
+
+ final org.w3c.dom.Node topNode = configNode.getFirstChild();
+ assertEquals(topNode.getLocalName(), "top");
+
+ final org.w3c.dom.Node interfaceNode = topNode.getFirstChild();
+ assertEquals(interfaceNode.getLocalName(), "interface");
+
+ final org.w3c.dom.Node nameNode = interfaceNode.getFirstChild();
+ assertEquals(nameNode.getLocalName(), "name");
+
+ final org.w3c.dom.Node mtuNode = nameNode.getNextSibling();
+ assertEquals(mtuNode.getLocalName(), "mtu");
+ }
+
+ @Test
+ public void testIsGetOperation() throws Exception {
+ messageTransformer.onGlobalContextUpdated(cfgCtx);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
+ rootBuilder.setQName(GET_QNAME);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
+ inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
+
+ rootBuilder.add(inputBuilder.toInstance());
+ final ImmutableCompositeNode root = rootBuilder.toInstance();
+
+ final NetconfMessage message = messageTransformer.toRpcRequest(GET_QNAME, root);
+ assertNotNull(message);
+
+ final Document xmlDoc = message.getDocument();
+ final org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
+ assertEquals(rpcChild.getLocalName(), "rpc");
+
+ final org.w3c.dom.Node get = rpcChild.getFirstChild();
+ assertEquals(get.getLocalName(), "get");
+ }
+
+ @Test
+ public void testIsGetConfigOperation() throws Exception {
+ messageTransformer.onGlobalContextUpdated(cfgCtx);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
+ rootBuilder.setQName(GET_CONFIG_QNAME);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
+ inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> sourceBuilder = ImmutableCompositeNode.builder();
+ sourceBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "source"));
+ sourceBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "running"), null);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlFilterBuilder = ImmutableCompositeNode.builder();
+ anyxmlFilterBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "filter"));
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlTopBuilder = ImmutableCompositeNode.builder();
+ anyxmlTopBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "top"));
+ anyxmlTopBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "users"), null);
+
+ anyxmlFilterBuilder.add(anyxmlTopBuilder.toInstance());
+
+ inputBuilder.add(sourceBuilder.toInstance());
+ inputBuilder.add(anyxmlFilterBuilder.toInstance());
+ rootBuilder.add(inputBuilder.toInstance());
+ final ImmutableCompositeNode root = rootBuilder.toInstance();
+
+ final NetconfMessage message = messageTransformer.toRpcRequest(GET_CONFIG_QNAME, root);
+ assertNotNull(message);
+
+ final Document xmlDoc = message.getDocument();
+ final org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
+ assertEquals(rpcChild.getLocalName(), "rpc");
+
+ final org.w3c.dom.Node getConfig = rpcChild.getFirstChild();
+ assertEquals(getConfig.getLocalName(), "get-config");
+
+ final org.w3c.dom.Node sourceNode = getConfig.getFirstChild();
+ assertEquals(sourceNode.getLocalName(), "source");
+
+ final org.w3c.dom.Node runningNode = sourceNode.getFirstChild();
+ assertEquals(runningNode.getLocalName(), "running");
+
+ final org.w3c.dom.Node filterNode = sourceNode.getNextSibling();
+ assertEquals(filterNode.getLocalName(), "filter");
+
+ final org.w3c.dom.Node topNode = filterNode.getFirstChild();
+ assertEquals(topNode.getLocalName(), "top");
+
+ final org.w3c.dom.Node usersNode = topNode.getFirstChild();
+ assertEquals(usersNode.getLocalName(), "users");
+ }
+
+ @Test
+ public void testUserDefinedRpcCall() throws Exception {
+ messageTransformer.onGlobalContextUpdated(notifCtx);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
+ rootBuilder.setQName(SUBSCRIBE_RPC_NAME);
final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
inputBuilder.setQName(INPUT_QNAME);
assertNotNull(inputNode);
assertTrue(inputNode.isEmpty());
- final NetconfMessage message = messageTransformer.toRpcRequest(RPC_NAME, root);
+ final NetconfMessage message = messageTransformer.toRpcRequest(SUBSCRIBE_RPC_NAME, root);
+ assertNotNull(message);
+
+ final Document xmlDoc = message.getDocument();
+ final org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
+ assertEquals(rpcChild.getLocalName(), "rpc");
+
+ final org.w3c.dom.Node subscribeName = rpcChild.getFirstChild();
+ assertEquals(subscribeName.getLocalName(), "subscribe");
+
+ final org.w3c.dom.Node streamName = subscribeName.getFirstChild();
+ assertEquals(streamName.getLocalName(), "stream-name");
+ }
+
+ @Test
+ public void testNoSchemaContextToRpcRequest() throws Exception {
+ final String exampleNamespace = "http://example.net/me/my-own/1.0";
+ final String exampleRevision = "2014-07-22";
+ final QName myOwnMethodRpcQName = QName.create(exampleNamespace, exampleRevision, "my-own-method");
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
+ rootBuilder.setQName(myOwnMethodRpcQName);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
+ inputBuilder.setQName(QName.create(exampleNamespace, exampleRevision, "input"));
+ inputBuilder.addLeaf(QName.create(exampleNamespace, exampleRevision, "my-first-parameter"), "14");
+ inputBuilder.addLeaf(QName.create(exampleNamespace, exampleRevision, "another-parameter"), "fred");
+
+ rootBuilder.add(inputBuilder.toInstance());
+ final ImmutableCompositeNode root = rootBuilder.toInstance();
+
+ final NetconfMessage message = messageTransformer.toRpcRequest(myOwnMethodRpcQName, root);
assertNotNull(message);
+
+ final Document xmlDoc = message.getDocument();
+ final org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
+ assertEquals(rpcChild.getLocalName(), "rpc");
+
+ final org.w3c.dom.Node myOwnMethodNode = rpcChild.getFirstChild();
+ assertEquals(myOwnMethodNode.getLocalName(), "my-own-method");
+
+ final org.w3c.dom.Node firstParamNode = myOwnMethodNode.getFirstChild();
+ assertEquals(firstParamNode.getLocalName(), "my-first-parameter");
+
+ final org.w3c.dom.Node secParamNode = firstParamNode.getNextSibling();
+ assertEquals(secParamNode.getLocalName(), "another-parameter");
}
}
--- /dev/null
+module config-test-rpc {
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:rpc:config:defs";
+ prefix "rpc";
+
+ organization
+ "Cisco Systems, Inc.";
+
+ contact
+ "lsedlak@cisco.com";
+
+ description "Test model containing hacked definition of rpc edit-config and definitions for
+ get and get-config rpc operations.
+ The rpc definition is copied from rfc 6241 Appendix C: http://tools.ietf.org/html/rfc6241#appendix-C";
+
+ revision 2014-07-21 {
+ description "Initial revision.";
+ }
+
+ extension get-filter-element-attributes {
+ description
+ "If this extension is present within an 'anyxml'
+ statement named 'filter', which must be conceptually
+ defined within the RPC input section for the <get>
+ and <get-config> protocol operations, then the
+ following unqualified XML attribute is supported
+ within the <filter> element, within a <get> or
+ <get-config> protocol operation:
+
+ type : optional attribute with allowed
+ value strings 'subtree' and 'xpath'.
+ If missing, the default value is 'subtree'.
+
+ If the 'xpath' feature is supported, then the
+ following unqualified XML attribute is
+ also supported:
+
+ select: optional attribute containing a
+ string representing an XPath expression.
+ The 'type' attribute must be equal to 'xpath'
+ if this attribute is present.";
+ }
+
+ rpc edit-config {
+ description "The <edit-config> operation loads all or part of a specified
+ configuration to the specified target configuration.";
+
+ reference "RFC 6241, Section 7.2";
+
+ input {
+ container target {
+ description "Particular configuration to edit.";
+
+ choice config-target {
+ mandatory true;
+ description "The configuration target.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description "The candidate configuration is the config target.";
+ }
+
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description "The running configuration is the config source.";
+ }
+ }
+ }
+
+ choice edit-content {
+ mandatory true;
+ description "The content for the edit operation.";
+
+ anyxml config {
+ description
+ "Inline Config content.";
+ }
+
+ leaf url {
+ if-feature url;
+ type string;
+ description
+ "URL-based config content.";
+ }
+ }
+ }
+ }
+
+ rpc get-config {
+ description
+ "Retrieve all or part of a specified configuration.";
+
+ reference "RFC 6241, Section 7.1";
+
+ input {
+ container source {
+ description "Particular configuration to retrieve.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration to retrieve.";
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.
+ This is optional-to-implement on the server because
+ not all servers will support filtering for this
+ datastore.";
+ }
+ }
+ }
+
+ anyxml filter {
+ description "Subtree or XPath filter to use.";
+ get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the source datastore subset that matched
+ the filter criteria (if any). An empty data container
+ indicates that the request did not produce any results.";
+ }
+ }
+ }
+
+ rpc get {
+ description "Retrieve running configuration and device state information.";
+
+ reference "RFC 6241, Section 7.7";
+
+ input {
+ anyxml filter {
+ description
+ "This parameter specifies the portion of the system
+ configuration and state data to retrieve.";
+ get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the running datastore subset and/or state
+ data that matched the filter criteria (if any).
+ An empty data container indicates that the request did not
+ produce any results.";
+ }
+ }
+ }
+}