Merge "Update comment and remove unwanted FIXME in SchemaSetup"
authorTomas Cere <tcere@cisco.com>
Fri, 6 May 2016 10:26:49 +0000 (10:26 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 6 May 2016 10:26:49 +0000 (10:26 +0000)
84 files changed:
features/netconf/pom.xml
features/netconf/src/main/features/features.xml
features/pom.xml
features/restconf/src/main/features/features.xml
features/yanglib/pom.xml [new file with mode: 0644]
features/yanglib/src/main/features/features.xml [new file with mode: 0644]
karaf/pom.xml
netconf/mdsal-netconf-notification/src/test/java/org/opendaylight/controller/config/yang/netconf/mdsal/notification/CapabilityChangeNotificationProducerTest.java [moved from netconf/mdsal-netconf-notification/src/test/java/org.opendaylight.controller.config.yang.netconf.mdsal.notification/CapabilityChangeNotificationProducerTest.java with 100% similarity]
netconf/mdsal-netconf-notification/src/test/java/org/opendaylight/controller/config/yang/netconf/mdsal/notification/NotificationToMdsalWriterTest.java [moved from netconf/mdsal-netconf-notification/src/test/java/org.opendaylight.controller.config.yang.netconf.mdsal.notification/NotificationToMdsalWriterTest.java with 100% similarity]
netconf/mdsal-netconf-notification/src/test/java/org/opendaylight/controller/config/yang/netconf/mdsal/notification/SessionNotificationProducerTest.java [moved from netconf/mdsal-netconf-notification/src/test/java/org.opendaylight.controller.config.yang.netconf.mdsal.notification/SessionNotificationProducerTest.java with 100% similarity]
netconf/models/ietf-netconf-yang-library/pom.xml [new file with mode: 0644]
netconf/models/ietf-netconf-yang-library/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/yang/library/rev160201/OptionalRevisionBuilder.java [new file with mode: 0644]
netconf/models/ietf-netconf-yang-library/src/main/yang/ietf-netconf-yang-library.yang [new file with mode: 0644]
netconf/models/pom.xml
netconf/netconf-artifacts/pom.xml
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/pipeline/ClusteredNetconfDevice.java
netconf/pom.xml
netconf/sal-netconf-connector/pom.xml
netconf/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemas.java [new file with mode: 0644]
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 [new file with mode: 0644]
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/YangLibrarySchemaYangSourceProvider.java [new file with mode: 0644]
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/main/yang/netconf-node-topology.yang
netconf/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemasTest.java [new file with mode: 0644]
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/NetconfToNotificationTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/schema/YangLibrarySchemaYangSourceProviderTest.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/resources/schemas/user-notification2.yang [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/resources/schemas/user-notification3.yang [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/resources/schemas/user-notification4.yang [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/resources/yang-library-fail-completely.xml [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/resources/yang-library-fail.xml [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/resources/yang-library.json [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/resources/yang-library.xml [new file with mode: 0644]
netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/NetconfDeviceConnectionManager.java
netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/commands/input/Input.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/Execution.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/Main.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/ScaleUtil.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/TesttoolParameters.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/Parameters.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/stress/Parameters.java
netconf/tools/netconf-testtool/src/main/resources/config-template.json [new file with mode: 0644]
netconf/yanglib/pom.xml [new file with mode: 0644]
netconf/yanglib/src/main/java/org/opendaylight/controller/config/yang/yanglib/impl/YanglibModule.java [new file with mode: 0644]
netconf/yanglib/src/main/java/org/opendaylight/controller/config/yang/yanglib/impl/YanglibModuleFactory.java [new file with mode: 0644]
netconf/yanglib/src/main/java/org/opendaylight/yanglib/api/YangLibRestAppService.java [new file with mode: 0644]
netconf/yanglib/src/main/java/org/opendaylight/yanglib/api/YangLibService.java [new file with mode: 0644]
netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibProvider.java [new file with mode: 0644]
netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibRestApp.java [new file with mode: 0644]
netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibServiceImpl.java [new file with mode: 0644]
netconf/yanglib/src/main/resources/WEB-INF/web.xml [new file with mode: 0644]
netconf/yanglib/src/main/yang/yanglib.yang [new file with mode: 0644]
netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibProviderTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/RestconfWrapperProviders.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/rest/connector/RestConnectorModule.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYangBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYinBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaRetrievalService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeJsonBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeXmlBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/RestConnectorProvider.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/schema/context/SchemaContextHandlerImpl.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/Draft11ServicesWrapperImpl.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfModulesServiceImpl.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfOperationsServiceImpl.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfSchemaServiceImpl.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfStreamsServiceImpl.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/SchemaContextRef.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/RestconfConstants.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeConstants.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingStreamConstants.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/validation/RestconfValidation.java [new file with mode: 0644]

index c82dc1c23a602530d8632f164a58a2cdfa38155c..c8c36a262909226dfb7c50fd4b79a8b58d21da56 100644 (file)
       <groupId>${project.groupId}</groupId>
       <artifactId>ietf-netconf-monitoring</artifactId>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>ietf-netconf-yang-library</artifactId>
+    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>ietf-netconf-monitoring-extension</artifactId>
index ac0ad75e53a982c79f2e4be8140d91c04a38bfd2..493d6d97773b36d0f45e01d5ea82ad6cabd845ec 100644 (file)
@@ -31,6 +31,7 @@
     <bundle>mvn:org.opendaylight.netconf/ietf-netconf/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.netconf/ietf-netconf-notifications/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.netconf/ietf-netconf-monitoring-extension/{{VERSION}}</bundle>
+    <bundle>mvn:org.opendaylight.netconf/ietf-netconf-yang-library/{{VERSION}}</bundle>
     <feature version='${mdsal.model.version}'>odl-mdsal-models</feature>
   </feature>
 
index e63065ed5fb79ca6d05cc9036ec3cfbd4716ad8e..dff44388302282a1bd59c22d29e29d72ac961e53 100644 (file)
@@ -14,6 +14,7 @@
     <module>netconf</module>
     <module>netconf-connector</module>
     <module>restconf</module>
+    <module>yanglib</module>
   </modules>
 
 </project>
index 77366b825e3b834b8d5873c387c0fd252166e2cb..2855b6176c9b83964253a4907b5bff3b0305e45d 100644 (file)
@@ -37,9 +37,6 @@
         <bundle>mvn:com.google.code.gson/gson/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.yangtools/yang-data-codec-gson/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.yangtools/yang-model-export/{{VERSION}}</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-core/{{VERSION}}</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-server/{{VERSION}}</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-servlet/{{VERSION}}</bundle>
         <configfile finalname="${config.configfile.directory}/${config.restconf.configfile}">mvn:org.opendaylight.netconf/sal-rest-connector-config/{{VERSION}}/xml/config</configfile>
         <configfile finalname="${config.configfile.directory}/${config.restconf.service.configfile}">mvn:org.opendaylight.netconf/sal-rest-connector-config/{{VERSION}}/xml/configrestconfservice</configfile>
     </feature>
diff --git a/features/yanglib/pom.xml b/features/yanglib/pom.xml
new file mode 100644 (file)
index 0000000..bd65f1f
--- /dev/null
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2016 Cisco Systems, Inc. and others.  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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>features-parent</artifactId>
+        <version>1.7.0-SNAPSHOT</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.netconf</groupId>
+    <artifactId>features-yanglib</artifactId>
+    <version>1.1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <properties>
+        <controller.mdsal.version>1.4.0-SNAPSHOT</controller.mdsal.version>
+        <netconf.version>1.1.0-SNAPSHOT</netconf.version>
+        <mdsal.version>2.1.0-SNAPSHOT</mdsal.version>
+        <mdsal.model.version>0.9.0-SNAPSHOT</mdsal.model.version>
+        <restconf.version>1.4.0-SNAPSHOT</restconf.version>
+        <yangtools.version>1.0.0-SNAPSHOT</yangtools.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.odlparent</groupId>
+                <artifactId>odlparent-artifacts</artifactId>
+                <version>1.7.0-SNAPSHOT</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>mdsal-artifacts</artifactId>
+                <version>${controller.mdsal.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.mdsal.model</groupId>
+                <artifactId>mdsal-model-artifacts</artifactId>
+                <version>${mdsal.model.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.netconf</groupId>
+                <artifactId>netconf-artifacts</artifactId>
+                <version>${netconf.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.netconf</groupId>
+                <artifactId>restconf-artifacts</artifactId>
+                <version>${restconf.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${yangtools.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.odlparent</groupId>
+            <artifactId>features-odlparent</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>features-mdsal-model</artifactId>
+            <version>${mdsal.model.version}</version>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>features-mdsal</artifactId>
+            <version>${mdsal.version}</version>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.netconf</groupId>
+            <artifactId>features-restconf</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>features-yangtools</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ietf-netconf-yang-library</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yanglib</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-servlet</artifactId>
+        </dependency>
+    </dependencies>
+
+    <scm>
+        <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+        <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+        <tag>HEAD</tag>
+        <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+    </scm>
+</project>
\ No newline at end of file
diff --git a/features/yanglib/src/main/features/features.xml b/features/yanglib/src/main/features/features.xml
new file mode 100644 (file)
index 0000000..e08af12
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2016 Cisco Systems, Inc. and others.  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
+-->
+<features name="odl-yanglib-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+    <repository>mvn:org.opendaylight.mdsal.model/features-mdsal-model/{{VERSION}}/xml/features</repository>
+    <repository>mvn:org.opendaylight.controller/features-mdsal/{{VERSION}}/xml/features</repository>
+    <repository>mvn:org.opendaylight.yangtools/features-yangtools/{{VERSION}}/xml/features</repository>
+    <repository>mvn:org.opendaylight.netconf/features-restconf/{{VERSION}}/xml/features</repository>
+
+    <feature name='odl-yanglib' description='OpenDaylight :: Yanglib' version='${project.version}'>
+        <bundle>mvn:org.opendaylight.netconf/yanglib/{{VERSION}}</bundle>
+        <feature version='${yangtools.version}'>odl-yangtools-yang-data</feature>
+        <feature version='${restconf.version}'>odl-restconf</feature>
+        <feature version='${mdsal.model.version}'>odl-mdsal-models</feature>
+        <feature version='${yangtools.version}'>odl-yangtools-yang-data</feature>
+        <bundle>mvn:org.opendaylight.netconf/ietf-netconf-yang-library/{{VERSION}}</bundle>
+        <feature>war</feature>
+        <bundle>mvn:org.opendaylight.netconf/ietf-netconf-yang-library/{{VERSION}}</bundle>
+        <bundle>mvn:com.sun.jersey/jersey-core/{{VERSION}}</bundle>
+        <bundle>mvn:com.sun.jersey/jersey-server/{{VERSION}}</bundle>
+        <bundle>mvn:com.sun.jersey/jersey-servlet/{{VERSION}}</bundle>
+    </feature>
+</features>
index 68c6e132a504990645d0d8a748b0a234631f72c5..18710a6c647e3ebcb137ad248f9e7ba8cda81246 100644 (file)
       <type>xml</type>
       <scope>runtime</scope>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>features-yanglib</artifactId>
+      <version>${netconf.version}</version>
+      <classifier>features</classifier>
+      <type>xml</type>
+      <scope>runtime</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/netconf/models/ietf-netconf-yang-library/pom.xml b/netconf/models/ietf-netconf-yang-library/pom.xml
new file mode 100644 (file)
index 0000000..5c3fb74
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2016 Cisco Systems, Inc. and others.  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
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.opendaylight.mdsal</groupId>
+        <artifactId>binding-parent</artifactId>
+        <version>0.9.0-SNAPSHOT</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.netconf</groupId>
+    <artifactId>ietf-netconf-yang-library</artifactId>
+    <version>1.1.0-SNAPSHOT</version>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-yang-types-20130715</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-inet-types</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/netconf/models/ietf-netconf-yang-library/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/yang/library/rev160201/OptionalRevisionBuilder.java b/netconf/models/ietf-netconf-yang-library/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/yang/library/rev160201/OptionalRevisionBuilder.java
new file mode 100644 (file)
index 0000000..653dc03
--- /dev/null
@@ -0,0 +1,19 @@
+package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201;
+
+
+/**
+ * The purpose of generated class in src/main/java for Union types is to create new instances of unions from a string representation.
+ * In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).
+ *
+ * The reason behind putting it under src/main/java is:
+ * This class is generated in form of a stub and needs to be finished by the user. This class is generated only once to prevent
+ * loss of user code.
+ *
+ */
+public class OptionalRevisionBuilder {
+
+    public static OptionalRevision getDefaultInstance(java.lang.String defaultValue) {
+        throw new java.lang.UnsupportedOperationException("Not yet implemented");
+    }
+
+}
diff --git a/netconf/models/ietf-netconf-yang-library/src/main/yang/ietf-netconf-yang-library.yang b/netconf/models/ietf-netconf-yang-library/src/main/yang/ietf-netconf-yang-library.yang
new file mode 100644 (file)
index 0000000..ed6b2e3
--- /dev/null
@@ -0,0 +1,269 @@
+module ietf-yang-library {
+
+    yang-version 1;
+    namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library";
+    prefix "yanglib";
+
+    import ietf-yang-types {
+      prefix yang;
+      revision-date "2013-07-15";
+    }
+
+    import ietf-inet-types {
+      prefix inet;
+      revision-date "2010-09-24";
+    }
+
+
+    organization
+      "IETF NETCONF (Network Configuration) Working Group";
+
+    contact
+      "WG Web:   <http://tools.ietf.org/wg/netconf/>
+      WG List:  <mailto:netconf@ietf.org>
+
+      WG Chair: Mehmet Ersue
+                <mailto:mehmet.ersue@nsn.com>
+
+      WG Chair: Mahesh Jethanandani
+                <mailto:mjethanandani@gmail.com>
+
+      Editor:   Andy Bierman
+                <mailto:andy@yumaworks.com>
+
+      Editor:   Martin Bjorklund
+                <mailto:mbj@tail-f.com>
+
+      Editor:   Kent Watsen
+                <mailto:kwatsen@juniper.net>";
+
+    description
+      "This module contains monitoring information about the YANG
+       modules and submodules that are used within a YANG-based
+       server.
+
+       Copyright (c) 2016 IETF Trust and the persons identified as
+       authors of the code.  All rights reserved.
+
+       Redistribution and use in source and binary forms, with or
+       without modification, is permitted pursuant to, and subject
+       to the license terms contained in, the Simplified BSD License
+       set forth in Section 4.c of the IETF Trust's Legal Provisions
+       Relating to IETF Documents
+       (http://trustee.ietf.org/license-info).
+
+       This version of this YANG module is part of RFC XXXX; see
+       the RFC itself for full legal notices.";
+
+       // RFC Ed.: replace XXXX with actual RFC number and remove this
+       // note.
+
+       // RFC Ed.: remove this note
+       // Note: extracted from draft-ietf-netconf-yang-library-04.txt
+
+       // RFC Ed.: update the date below with the date of RFC publication
+       // and remove this note.
+
+    revision 2016-02-01 {
+      description
+        "Initial revision.";
+      reference
+        "RFC XXXX: YANG Module Library.";
+    }
+
+    /*
+     * Typedefs
+     */
+    // FIXME inline this union after https://bugs.opendaylight.org/show_bug.cgi?id=5826 is fixed
+    typedef optional-revision {
+      type union {
+        type revision-identifier;
+        type string { length 0; }
+      }
+    }
+
+    typedef revision-identifier {
+      type string {
+        pattern '\d{4}-\d{2}-\d{2}';
+      }
+      description
+        "Represents a specific date in YYYY-MM-DD format.";
+    }
+
+    /*
+     * Groupings
+     */
+    grouping module-list {
+      description
+        "The module data structure is represented as a grouping
+         so it can be reused in configuration or another monitoring
+         data structure.";
+
+
+      grouping common-leafs {
+        description
+          "Common parameters for YANG modules and submodules.";
+
+        leaf name {
+          type yang:yang-identifier;
+          description
+            "The YANG module or submodule name.";
+        }
+        leaf revision {
+          type optional-revision;
+            description
+              "The YANG module or submodule revision date.
+               A zero-length string is used if no revision statement
+               is present in the YANG module or submodule.";
+        }
+      }
+
+      grouping schema-leaf {
+        description
+          "Common schema leaf parameter for modules and submodules.";
+
+        leaf schema {
+          type inet:uri;
+          description
+            "Contains a URL that represents the YANG schema
+             resource for this module or submodule.
+
+             This leaf will only be present if there is a URL
+             available for retrieval of the schema for this entry.";
+        }
+      }
+
+      list module {
+        key "name revision";
+        description
+          "Each entry represents one module currently
+           supported by the server.";
+
+        uses common-leafs;
+        uses schema-leaf;
+
+        leaf namespace {
+          type inet:uri;
+          mandatory true;
+          description
+            "The XML namespace identifier for this module.";
+        }
+        leaf-list feature {
+          type yang:yang-identifier;
+          description
+            "List of YANG feature names from this module that are
+             supported by the server, regardless whether they are
+             defined in the module or any included submodule.";
+        }
+        list deviation {
+          key "name revision";
+          description
+            "List of YANG deviation module names and revisions
+             used by this server to modify the conformance of
+             the module associated with this entry.  Note that
+             the same module can be used for deviations for
+             multiple modules, so the same entry MAY appear
+             within multiple 'module' entries.
+
+             The deviation module MUST be present in the 'module'
+             list, with the same name and revision values.
+             The 'conformance-type' value will be 'implement' for
+             the deviation module.";
+          uses common-leafs;
+        }
+        leaf conformance-type {
+          type enumeration {
+            enum implement {
+              description
+                "Indicates that the server implements one or more
+                 protocol-accessible objects defined in the YANG module
+                 identified in this entry.  This includes deviation
+                 statements defined in the module.
+
+                 For YANG version 1.1 modules, there is at most one
+                 module entry with conformance type 'implement' for a
+                 particular module name, since YANG 1.1 requires that
+                 at most one revision of a module is implemented.
+
+                 For YANG version 1 modules, there SHOULD NOT be more
+                 than one module entry for a particular module name.";
+            }
+            enum import {
+              description
+                "Indicates that the server imports reusable definitions
+                 from the specified revision of the module, but does
+                 not implement any protocol accessible objects from
+                 this revision.
+
+                 Multiple module entries for the same module name MAY
+                 exist. This can occur if multiple modules import the
+                 same module, but specify different revision-dates in
+                 the import statements.";
+            }
+          }
+          mandatory true;
+          description
+            "Indicates the type of conformance the server is claiming
+             for the YANG module identified by this entry.";
+        }
+        container submodules {
+          description
+            "Contains information about all the submodules used
+             by the parent module entry";
+
+          list submodule {
+            key "name revision";
+            description
+              "Each entry represents one submodule within the
+               parent module.";
+             uses common-leafs;
+             uses schema-leaf;
+          }
+        }
+      }
+    }
+
+    /*
+     * Operational state data nodes
+     */
+
+    container modules-state {
+      config false;
+      description
+        "Contains YANG module monitoring information.";
+
+      leaf module-set-id {
+        type string;
+        mandatory true;
+        description
+          "Contains a server-specific identifier representing
+           the current set of modules and submodules.  The
+           server MUST change the value of this leaf if the
+           information represented by the 'module' list instances
+           has changed.";
+      }
+
+      uses module-list;
+    }
+
+    /*
+     * Notifications
+     */
+    notification yang-library-change {
+      description
+        "Generated when the set of modules and submodules supported
+         by the server has changed.";
+      leaf module-set-id {
+        type leafref {
+          path "/yanglib:modules-state/yanglib:module-set-id";
+        }
+        mandatory true;
+        description
+          "Contains the module-set-id value representing the
+           set of modules and submodules supported at the server at
+           the time the notification is generated.";
+      }
+    }
+
+}
+
index a25a78a9b573f8d8045eaf27cf3df5d55eeb29e6..456d7cc6af88e2d2dee3a153cc8defd1e10b7274 100644 (file)
@@ -17,5 +17,6 @@
     <module>ietf-netconf-monitoring</module>
     <module>ietf-netconf-notifications</module>
     <module>ietf-netconf-monitoring-extension</module>
+    <module>ietf-netconf-yang-library</module>
   </modules>
 </project>
index f8dcad625bc820f24b38c778d17cb9f1cf625a4d..f6c9a77d778be06bc535bdb5797fbb29f414e9f2 100644 (file)
                 <artifactId>ietf-netconf-notifications</artifactId>
                 <version>${project.version}</version>
             </dependency>
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ietf-netconf-yang-library</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.netconf</groupId>
+                <artifactId>yanglib</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-notifications-api</artifactId>
                 <classifier>clustered-config</classifier>
                 <type>xml</type>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>yanglib-config</artifactId>
+                <version>${project.version}</version>
+                <classifier>config</classifier>
+                <type>xml</type>
+            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-client</artifactId>
index 7a0d0f7dbb9527e060e304e0739b13e61d1440d4..c09dca9162846b272b48845bd36a25502fc9c769 100644 (file)
@@ -11,6 +11,7 @@ package org.opendaylight.netconf.topology;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -18,6 +19,7 @@ import io.netty.util.concurrent.EventExecutor;
 import java.io.File;
 import java.math.BigDecimal;
 import java.net.InetSocketAddress;
+import java.net.URL;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -41,13 +43,16 @@ import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurati
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
+import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.netconf.topology.pipeline.TopologyMountPointFacade.ConnectionStatusListenerRegistration;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
@@ -62,7 +67,10 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
@@ -273,10 +281,41 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
             salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay, defaultRequestTimeoutMillis);
         }
 
+        // pre register yang library sources as fallback schemas to schema registry
+        List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
+        if (node.getYangLibrary() != null) {
+            final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
+            final String yangLibUsername = node.getYangLibrary().getUsername();
+            final String yangLigPassword = node.getYangLibrary().getPassword();
+
+            LibraryModulesSchemas libraryModulesSchemas;
+            if(yangLibURL != null) {
+                if(yangLibUsername != null && yangLigPassword != null) {
+                    libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
+                } else {
+                    libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
+                }
+
+                for (Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry : libraryModulesSchemas.getAvailableModels().entrySet()) {
+                    registeredYangLibSources.
+                            add(schemaRegistry.registerSchemaSource(
+                                    new YangLibrarySchemaYangSourceProvider(remoteDeviceId, libraryModulesSchemas.getAvailableModels()),
+                                    PotentialSchemaSource
+                                            .create(sourceIdentifierURLEntry.getKey(), YangTextSchemaSource.class,
+                                            PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
+                }
+            }
+        }
+
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
 
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, remoteDeviceId, salFacade,
-                processingExecutor.getExecutor(), reconnectOnChangedSchema);
+        final NetconfDevice device = new NetconfDeviceBuilder()
+                .setReconnectOnSchemasChange(reconnectOnChangedSchema)
+                .setSchemaResourcesDTO(schemaResourcesDTO)
+                .setGlobalProcessingExecutor(processingExecutor.getExecutor())
+                .setId(remoteDeviceId)
+                .setSalFacade(salFacade)
+                .build();
 
         final Optional<NetconfSessionPreferences> userCapabilities = getUserCapabilities(node);
 
index 058204b9f5d390677e8612105d8400dd5edc87b3..a94bb90cd7927f5eed670a953b0cd59a427297a5 100644 (file)
@@ -27,6 +27,7 @@ import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceCommunicator;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
@@ -62,7 +63,7 @@ public class ClusteredNetconfDevice extends NetconfDevice implements EntityOwner
     public ClusteredNetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
                                   final ExecutorService globalProcessingExecutor, final ActorSystem actorSystem, final String topologyId, final String nodeId,
                                   ActorContext cachedContext) {
-        super(schemaResourcesDTO, id, salFacade, globalProcessingExecutor);
+        super(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, false);
         this.schemaRepo = (SchemaRepository) schemaResourcesDTO.getSchemaRegistry();
         this.actorSystem = actorSystem;
         this.topologyId = topologyId;
index 1d95c7f182d18d8370469597cf842aaa32a529f2..78571c6c1826008dfcf3af6e317f57205a59f903 100644 (file)
@@ -1,4 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2016 Cisco Systems, Inc. and others.  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
+-->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
@@ -42,6 +50,7 @@
     <module>netconf-topology-config</module>
     <module>sal-netconf-connector</module>
     <module>messagebus-netconf</module>
+    <module>yanglib</module>
     <module>models</module>
     <module>tools</module>
 
index 40fe3b2f70a17303d8138ac24e84a130ecde168f..b4c915f44cdcbb1436899e9c066d39c9012901bd 100644 (file)
       <groupId>${project.groupId}</groupId>
       <artifactId>ietf-netconf-notifications</artifactId>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>ietf-netconf-yang-library</artifactId>
+    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>netconf-client</artifactId>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-parser-impl</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-codec-gson</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.mdsal.model</groupId>
       <artifactId>ietf-inet-types</artifactId>
       <groupId>org.hamcrest</groupId>
       <artifactId>hamcrest-core</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+    </dependency>
   </dependencies>
 
   <scm>
index 4538857184f42f7befc4c9034dd42fd6e9f85e25..f6c6f02443c90938cbc2da88acde99bffb1a8a9f 100644 (file)
@@ -12,10 +12,12 @@ import static org.opendaylight.controller.config.api.JmxAttributeValidationExcep
 
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
 import io.netty.util.concurrent.EventExecutor;
 import java.io.File;
 import java.math.BigDecimal;
 import java.net.InetSocketAddress;
+import java.net.URL;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -34,7 +36,9 @@ import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurati
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
@@ -42,6 +46,7 @@ import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
@@ -54,6 +59,7 @@ import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
@@ -211,7 +217,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
         final String moduleSchemaCacheDirectory = getSchemaCacheDirectory();
         // Only checks to ensure the String is not empty or null;  further checks related to directory accessibility and file permissions
-        // are handled during the FilesystemScehamSourceCache initialization.
+        // are handled during the FilesystemSchemaSourceCache initialization.
         if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
             // If a custom schema cache directory is specified, create the backing DTO; otherwise, the SchemaRegistry and
             // SchemaContextFactory remain the default values.
@@ -246,13 +252,44 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
                     instanceName, QUALIFIED_DEFAULT_CACHE_DIRECTORY);
         }
 
+        // pre register yang library sources as fallback schemas to schema registry
+        List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
+        if (getYangLibrary() != null) {
+            final String yangLibURL = getYangLibrary().getYangLibraryUrl().getValue();
+            final String yangLibUsername = getYangLibrary().getUsername();
+            final String yangLigPassword = getYangLibrary().getPassword();
+
+            LibraryModulesSchemas libraryModulesSchemas;
+            if(yangLibURL != null) {
+                if(yangLibUsername != null && yangLigPassword != null) {
+                    libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
+                } else {
+                    libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
+                }
+
+                for (Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry : libraryModulesSchemas.getAvailableModels().entrySet()) {
+                    registeredYangLibSources.
+                            add(schemaRegistry.registerSchemaSource(
+                                    new YangLibrarySchemaYangSourceProvider(id, libraryModulesSchemas.getAvailableModels()),
+                                    PotentialSchemaSource
+                                            .create(sourceIdentifierURLEntry.getKey(), YangTextSchemaSource.class,
+                                                    PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
+                }
+            }
+        }
+
         if (schemaResourcesDTO == null) {
             schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory,
                     new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
         }
 
-        final NetconfDevice device =
-                new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, getReconnectOnChangedSchema());
+        final NetconfDevice device = new NetconfDeviceBuilder()
+                .setReconnectOnSchemasChange(getReconnectOnChangedSchema())
+                .setSchemaResourcesDTO(schemaResourcesDTO)
+                .setGlobalProcessingExecutor(globalProcessingExecutor)
+                .setId(id)
+                .setSalFacade(salFacade)
+                .build();
 
         final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
                 new NetconfDeviceCommunicator(id, device,
@@ -266,7 +303,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
         listener.initializeRemoteConnection(clientDispatcher, clientConfig);
 
-        return new SalConnectorCloseable(listener, salFacade);
+        return new SalConnectorCloseable(listener, salFacade, registeredYangLibSources);
     }
 
     /**
@@ -373,16 +410,22 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
 
     private static final class SalConnectorCloseable implements AutoCloseable {
         private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
+        private final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources;
         private final NetconfDeviceCommunicator listener;
 
         public SalConnectorCloseable(final NetconfDeviceCommunicator listener,
-                                     final RemoteDeviceHandler<NetconfSessionPreferences> salFacade) {
+                                     final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
+                                     final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources) {
             this.listener = listener;
             this.salFacade = salFacade;
+            this.registeredYangLibSources = registeredYangLibSources;
         }
 
         @Override
         public void close() {
+            for (SchemaSourceRegistration<YangTextSchemaSource> registeredYangLibSource : registeredYangLibSources) {
+                registeredYangLibSource.close();
+            }
             listener.close();
             salFacade.close();
         }
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemas.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemas.java
new file mode 100644 (file)
index 0000000..020b443
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  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.netconf;
+
+import static javax.xml.bind.DatatypeConverter.printBase64Binary;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.Map;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.ModulesState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.module.list.Module;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.common.QName;
+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.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+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.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils;
+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.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+/**
+ * Holds URLs with YANG schema resources for all yang modules reported in
+ * ietf-netconf-yang-library/modules-state/modules node
+ */
+public class LibraryModulesSchemas {
+
+    private static final Logger LOG = LoggerFactory.getLogger(LibraryModulesSchemas.class);
+
+    private static SchemaContext libraryContext;
+
+    private final Map<SourceIdentifier, URL> availableModels;
+
+    static {
+        final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+        moduleInfoBackedContext.registerModuleInfo(
+                org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.
+                        $YangModuleInfoImpl.getInstance());
+        libraryContext = moduleInfoBackedContext.tryToCreateSchemaContext().get();
+    }
+
+    private LibraryModulesSchemas(final Map<SourceIdentifier, URL> availableModels) {
+        this.availableModels = availableModels;
+    }
+
+    public Map<SourceIdentifier, URL> getAvailableModels() {
+        return availableModels;
+    }
+
+
+    /**
+     * Resolves URLs with YANG schema resources from modules-state. Uses basic http authenticaiton
+     * @param url URL pointing to yang library
+     * @return Resolved URLs with YANG schema resources for all yang modules from yang library
+     */
+    public static LibraryModulesSchemas create(final String url, final String username, final String password) {
+        Preconditions.checkNotNull(url);
+        try {
+            final URL urlConnection = new URL(url);
+            final URLConnection connection = urlConnection.openConnection();
+
+            if(connection instanceof HttpURLConnection) {
+                connection.setRequestProperty("Accept", "application/xml");
+                String userpass = username + ":" + password;
+                String basicAuth = "Basic " + printBase64Binary(userpass.getBytes());
+
+                connection.setRequestProperty("Authorization", basicAuth);
+            }
+
+            return createFromURLConnection(connection);
+
+        } catch (IOException e) {
+            LOG.warn("Unable to download yang library from {}", url, e);
+            return new LibraryModulesSchemas(Collections.<SourceIdentifier, URL>emptyMap());
+        }
+    }
+
+
+    private static LibraryModulesSchemas createFromURLConnection(URLConnection connection) {
+
+        String contentType = connection.getContentType();
+
+        // TODO try to guess Json also from intput stream
+        if (guessJsonFromFileName(connection.getURL().getFile())) {
+            contentType = "application/json";
+        }
+
+        Preconditions.checkNotNull(contentType, "Content type unknown");
+        Preconditions.checkState(contentType.equals("application/json") || contentType.equals("application/xml"),
+                "Only XML and JSON types are supported.");
+        try (final InputStream in = connection.getInputStream()) {
+            final Optional<NormalizedNode<?, ?>> optionalModulesStateNode =
+                    contentType.equals("application/json") ? readJson(in) : readXml(in);
+
+            if (!optionalModulesStateNode.isPresent()) {
+                return new LibraryModulesSchemas(Collections.<SourceIdentifier, URL>emptyMap());
+            }
+
+            final NormalizedNode<?, ?> modulesStateNode = optionalModulesStateNode.get();
+            Preconditions.checkState(modulesStateNode.getNodeType().equals(ModulesState.QNAME),
+                    "Wrong QName %s", modulesStateNode.getNodeType());
+            Preconditions.checkState(modulesStateNode instanceof ContainerNode,
+                    "Expecting container containing module list, but was %s", modulesStateNode);
+
+            final YangInstanceIdentifier.NodeIdentifier moduleListNodeId =
+                    new YangInstanceIdentifier.NodeIdentifier(Module.QNAME);
+            final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> moduleListNode =
+                    ((ContainerNode) modulesStateNode).getChild(moduleListNodeId);
+            Preconditions.checkState(moduleListNode.isPresent(),
+                    "Unable to find list: %s in %s", moduleListNodeId, modulesStateNode);
+            Preconditions.checkState(moduleListNode.get() instanceof MapNode,
+                    "Unexpected structure for container: %s in : %s. Expecting a list",
+                    moduleListNodeId, modulesStateNode);
+
+            final ImmutableMap.Builder<SourceIdentifier, URL> schemasMapping = new ImmutableMap.Builder<>();
+            for (final MapEntryNode moduleNode : ((MapNode) moduleListNode.get()).getValue()) {
+                final Optional<Map.Entry<SourceIdentifier, URL>> schemaMappingEntry = createFromEntry(moduleNode);
+                if (schemaMappingEntry.isPresent()) {
+                    schemasMapping.put(createFromEntry(moduleNode).get());
+                }
+            }
+
+            return new LibraryModulesSchemas(schemasMapping.build());
+        } catch (IOException e) {
+            LOG.warn("Unable to download yang library from {}", connection.getURL(), e);
+            return new LibraryModulesSchemas(Collections.<SourceIdentifier, URL>emptyMap());
+        }
+    }
+
+    /**
+     * Resolves URLs with YANG schema resources from modules-state
+     * @param url URL pointing to yang library
+     * @return Resolved URLs with YANG schema resources for all yang modules from yang library
+     */
+    public static LibraryModulesSchemas create(final String url) {
+        Preconditions.checkNotNull(url);
+        try {
+            final URL urlConnection = new URL(url);
+            final URLConnection connection = urlConnection.openConnection();
+
+            if(connection instanceof HttpURLConnection) {
+                connection.setRequestProperty("Accept", "application/xml");
+            }
+
+            return createFromURLConnection(connection);
+
+        } catch (IOException e) {
+            LOG.warn("Unable to download yang library from {}", url, e);
+            return new LibraryModulesSchemas(Collections.<SourceIdentifier, URL>emptyMap());
+        }
+    }
+
+    private static boolean guessJsonFromFileName(final String fileName) {
+        String extension = "";
+        final int i = fileName.lastIndexOf(46);
+        if(i != -1) {
+            extension = fileName.substring(i).toLowerCase();
+        }
+
+        return extension.equals(".json");
+    }
+
+    private static Optional<NormalizedNode<?, ?>> readJson(final InputStream in) {
+        final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
+
+        final JsonParserStream jsonParser = JsonParserStream.create(writer, libraryContext);
+        final JsonReader reader = new JsonReader(new InputStreamReader(in));
+
+        jsonParser.parse(reader);
+
+        return resultHolder.isFinished() ?
+                Optional.of(resultHolder.getResult()) :
+                Optional.<NormalizedNode<?, ?>>absent();
+    }
+
+    private static Optional<NormalizedNode<?, ?>> readXml(final InputStream in) {
+        final DomToNormalizedNodeParserFactory parserFactory =
+                DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, libraryContext);
+
+        try {
+            final NormalizedNode<?, ?> parsed =
+                    parserFactory.getContainerNodeParser().parse(Collections.singleton(XmlUtil.readXmlToElement(in)),
+                            (ContainerSchemaNode) libraryContext.getDataChildByName(ModulesState.QNAME));
+            return Optional.of(parsed);
+        } catch (IOException|SAXException e) {
+            LOG.warn("Unable to parse yang library xml content", e);
+        }
+
+        return Optional.<NormalizedNode<?, ?>>absent();
+    }
+
+    private static Optional<Map.Entry<SourceIdentifier, URL>> createFromEntry(final MapEntryNode moduleNode) {
+        Preconditions.checkArgument(
+                moduleNode.getNodeType().equals(Module.QNAME), "Wrong QName %s", moduleNode.getNodeType());
+
+        YangInstanceIdentifier.NodeIdentifier childNodeId =
+                new YangInstanceIdentifier.NodeIdentifier(QName.create(Module.QNAME, "name"));
+        final String moduleName = getSingleChildNodeValue(moduleNode, childNodeId).get();
+
+        childNodeId = new YangInstanceIdentifier.NodeIdentifier(QName.create(Module.QNAME, "revision"));
+        final Optional<String> revision = getSingleChildNodeValue(moduleNode, childNodeId);
+        if(revision.isPresent()) {
+            if(!SourceIdentifier.REVISION_PATTERN.matcher(revision.get()).matches()) {
+                LOG.warn("Skipping library schema for {}. Revision {} is in wrong format.", moduleNode, revision.get());
+                return Optional.<Map.Entry<SourceIdentifier, URL>>absent();
+            }
+        }
+
+        // FIXME leaf schema with url that represents the yang schema resource for this module is not mandatory
+        // don't fail if schema node is not present, just skip the entry or add some default URL
+        childNodeId = new YangInstanceIdentifier.NodeIdentifier(QName.create(Module.QNAME, "schema"));
+        final Optional<String> schemaUriAsString = getSingleChildNodeValue(moduleNode, childNodeId);
+
+
+        final SourceIdentifier sourceId = revision.isPresent()
+                ? new SourceIdentifier(moduleName, revision.get())
+                : new SourceIdentifier(moduleName);
+
+        try {
+            return Optional.<Map.Entry<SourceIdentifier, URL>>of(new AbstractMap.SimpleImmutableEntry<>(
+                    sourceId, new URL(schemaUriAsString.get())));
+        } catch (MalformedURLException e) {
+            LOG.warn("Skipping library schema for {}. URL {} representing yang schema resource is not valid",
+                    moduleNode, schemaUriAsString.get());
+            return Optional.<Map.Entry<SourceIdentifier, URL>>absent();
+        }
+    }
+
+    private static Optional<String> getSingleChildNodeValue(final DataContainerNode<?> schemaNode,
+                                                            final YangInstanceIdentifier.NodeIdentifier childNodeId) {
+        final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> node =
+                schemaNode.getChild(childNodeId);
+        Preconditions.checkArgument(node.isPresent(), "Child node %s not present", childNodeId.getNodeType());
+        return getValueOfSimpleNode(node.get());
+    }
+
+    private static Optional<String> getValueOfSimpleNode(
+            final NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?> node) {
+        final Object value = node.getValue();
+        return value == null || Strings.isNullOrEmpty(value.toString())
+                ? Optional.<String>absent() : Optional.of(value.toString().trim());
+    }
+
+}
index a3234e59d74f73cd058d5b7137eaf8839b9e9010..a590c10850643ca26e7ba3596922fc475cd7943d 100644 (file)
@@ -87,11 +87,6 @@ public class NetconfDevice implements RemoteDevice<NetconfSessionPreferences, Ne
     // Message transformer is constructed once the schemas are available
     private MessageTransformer<NetconfMessage> messageTransformer;
 
-    public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
-                         final ExecutorService globalProcessingExecutor) {
-        this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, false);
-    }
-
     /**
      * Create rpc implementation capable of handling RPC for monitoring and notifications even before the schemas of remote device are downloaded
      */
@@ -103,9 +98,7 @@ public class NetconfDevice implements RemoteDevice<NetconfSessionPreferences, Ne
         return new NetconfDeviceRpc(baseSchema.getSchemaContext(), listener, new NetconfMessageTransformer(baseSchema.getSchemaContext(), false, baseSchema));
     }
 
-
-    // FIXME reduce parameters
-    public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
+    protected NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
                          final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) {
         this.id = id;
         this.reconnectOnSchemasChange = reconnectOnSchemasChange;
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceBuilder.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceBuilder.java
new file mode 100644 (file)
index 0000000..f5521e6
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  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.netconf;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.ExecutorService;
+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;
+
+public class NetconfDeviceBuilder {
+
+    private boolean reconnectOnSchemasChange;
+    private NetconfDevice.SchemaResourcesDTO schemaResourcesDTO;
+    private RemoteDeviceId id;
+    private RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
+    private ExecutorService globalProcessingExecutor;
+
+    public NetconfDeviceBuilder() {
+    }
+
+    public NetconfDeviceBuilder setReconnectOnSchemasChange(boolean reconnectOnSchemasChange) {
+        this.reconnectOnSchemasChange = reconnectOnSchemasChange;
+        return this;
+    }
+
+    public NetconfDeviceBuilder setId(RemoteDeviceId id) {
+        this.id = id;
+        return this;
+    }
+
+    public NetconfDeviceBuilder setSchemaResourcesDTO(NetconfDevice.SchemaResourcesDTO schemaResourcesDTO) {
+        this.schemaResourcesDTO = schemaResourcesDTO;
+        return this;
+    }
+
+    public NetconfDeviceBuilder setSalFacade(RemoteDeviceHandler<NetconfSessionPreferences> salFacade) {
+        this.salFacade = salFacade;
+        return this;
+    }
+
+    public NetconfDeviceBuilder setGlobalProcessingExecutor(ExecutorService globalProcessingExecutor) {
+        this.globalProcessingExecutor = globalProcessingExecutor;
+        return this;
+    }
+
+    public NetconfDevice build() {
+        validation();
+        return new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange);
+    }
+
+    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");
+    }
+}
index 72d2b391a2c32276768789774c9bddde053c455e..3a65ce74324f2b7c80e277085e2a86f4279e991a 100644 (file)
@@ -177,7 +177,7 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
 
     }
 
-    private static class NetconfYangTextSchemaSource extends YangTextSchemaSource {
+    static class NetconfYangTextSchemaSource extends YangTextSchemaSource {
         private final RemoteDeviceId id;
         private final Optional<String> schemaString;
 
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/YangLibrarySchemaYangSourceProvider.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/YangLibrarySchemaYangSourceProvider.java
new file mode 100644 (file)
index 0000000..0f27126
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  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.netconf.schema;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Map;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides YANG schema sources from yang library
+ */
+public final class YangLibrarySchemaYangSourceProvider implements SchemaSourceProvider<YangTextSchemaSource> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(YangLibrarySchemaYangSourceProvider.class);
+
+    private final Map<SourceIdentifier, URL> availableSources;
+    private final RemoteDeviceId id;
+
+    public YangLibrarySchemaYangSourceProvider(
+            final RemoteDeviceId id, final Map<SourceIdentifier, URL> availableSources) {
+        this.id = id;
+        this.availableSources = Preconditions.checkNotNull(availableSources);
+    }
+
+    @Override
+    public CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> getSource(
+            final SourceIdentifier sourceIdentifier) {
+        Preconditions.checkNotNull(sourceIdentifier);
+        Preconditions.checkArgument(availableSources.containsKey(sourceIdentifier));
+        return download(sourceIdentifier);
+    }
+
+    private CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> download(final SourceIdentifier sId) {
+        final URL url = availableSources.get(sId);
+        try(final InputStream in = url.openStream()) {
+            final String schemaContent = new String(ByteStreams.toByteArray(in));
+            final NetconfRemoteSchemaYangSourceProvider.NetconfYangTextSchemaSource yangSource =
+                    new NetconfRemoteSchemaYangSourceProvider.
+                            NetconfYangTextSchemaSource(id, sId, Optional.of(schemaContent));
+            LOG.debug("Source {} downloaded from a yang library's url {}", sId, url);
+            return Futures.immediateCheckedFuture(yangSource);
+        } catch (IOException e) {
+            LOG.warn("Unable to download source {} from a yang library's url {}", sId, url, e);
+            return Futures.immediateFailedCheckedFuture(
+                    new SchemaSourceException("Unable to download remote schema for " + sId + " from " + url, e));
+        }
+    }
+}
index 668415aa6b1a3387b3d88a80e44fa634637e3ce0..2a9b53e6c29dc0a7aa4d2dbeb03ea63785f0558a 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.netconf.sal.connect.netconf.schema.mapping;
 
-import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.EVENT_TIME;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.IETF_NETCONF_NOTIFICATIONS;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RPC_QNAME;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI;
@@ -20,11 +19,9 @@ import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
 import java.io.IOException;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.AbstractMap;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -32,7 +29,6 @@ import javax.annotation.Nonnull;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import javax.xml.transform.dom.DOMResult;
-import org.opendaylight.controller.config.util.xml.DocumentedException;
 import org.opendaylight.controller.config.util.xml.MissingNameSpaceException;
 import org.opendaylight.controller.config.util.xml.XmlElement;
 import org.opendaylight.controller.config.util.xml.XmlUtil;
@@ -41,7 +37,6 @@ 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.netconf.api.NetconfMessage;
-import org.opendaylight.netconf.notifications.NetconfNotification;
 import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.netconf.sal.connect.util.MessageCounter;
@@ -154,24 +149,20 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
 
     @Override
     public synchronized DOMNotification toNotification(final NetconfMessage message) {
-        final Map.Entry<Date, XmlElement> stripped = stripNotification(message);
+        final Map.Entry<Date, XmlElement> stripped = NetconfMessageTransformUtil.stripNotification(message);
         final QName notificationNoRev;
         try {
             notificationNoRev = QName.create(stripped.getValue().getNamespace(), stripped.getValue().getName()).withoutRevision();
         } catch (final MissingNameSpaceException e) {
             throw new IllegalArgumentException("Unable to parse notification " + message + ", cannot find namespace", e);
         }
-
         final Collection<NotificationDefinition> notificationDefinitions = mappedNotifications.get(notificationNoRev);
         Preconditions.checkArgument(notificationDefinitions.size() > 0,
                 "Unable to parse notification %s, unknown notification. Available notifications: %s", notificationDefinitions, mappedNotifications.keySet());
 
-        // FIXME if multiple revisions for same notifications are present, we should pick the most recent. Or ?
-        // We should probably just put the most recent notification versions into our map. We can expect that the device sends the data according to the latest available revision of a model.
-        final NotificationDefinition next = notificationDefinitions.iterator().next();
+        final NotificationDefinition mostRecentNotification = getMostRecentNotification(notificationDefinitions);
 
-        // We wrap the notification as a container node in order to reuse the parsers and builders for container node
-        final ContainerSchemaNode notificationAsContainerSchemaNode = NetconfMessageTransformUtil.createSchemaForNotification(next);
+        final ContainerSchemaNode notificationAsContainerSchemaNode = NetconfMessageTransformUtil.createSchemaForNotification(mostRecentNotification);
 
         final Element element = stripped.getValue().getDomElement();
         final ContainerNode content;
@@ -184,61 +175,10 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
         return new NetconfDeviceNotification(content, stripped.getKey());
     }
 
-    private static final ThreadLocal<SimpleDateFormat> EVENT_TIME_FORMAT = new ThreadLocal<SimpleDateFormat>() {
-        @Override
-        protected SimpleDateFormat initialValue() {
-
-            final SimpleDateFormat withMillis = new SimpleDateFormat(
-                NetconfNotification.RFC3339_DATE_FORMAT_WITH_MILLIS_BLUEPRINT);
-
-            return new SimpleDateFormat(NetconfNotification.RFC3339_DATE_FORMAT_BLUEPRINT) {
-                private static final long serialVersionUID = 1L;
-
-                @Override public Date parse(final String source) throws ParseException {
-                    try {
-                        return super.parse(source);
-                    } catch (ParseException e) {
-                        // In case of failure, try to parse with milliseconds
-                        return withMillis.parse(source);
-                    }
-                }
-            };
-        }
+    private static NotificationDefinition getMostRecentNotification(final Collection<NotificationDefinition> notificationDefinitions) {
+        Comparator<NotificationDefinition> cmp = (o1, o2) -> o1.getQName().getRevision().compareTo(o2.getQName().getRevision());
 
-        @Override
-        public void set(final SimpleDateFormat value) {
-            throw new UnsupportedOperationException();
-        }
-    };
-
-    // FIXME move somewhere to util
-    private static Map.Entry<Date, XmlElement> stripNotification(final NetconfMessage message) {
-        final XmlElement xmlElement = XmlElement.fromDomDocument(message.getDocument());
-        final List<XmlElement> childElements = xmlElement.getChildElements();
-        Preconditions.checkArgument(childElements.size() == 2, "Unable to parse notification %s, unexpected format", message);
-
-        final XmlElement eventTimeElement;
-        final XmlElement notificationElement;
-
-        if (childElements.get(0).getName().equals(EVENT_TIME)) {
-            eventTimeElement = childElements.get(0);
-            notificationElement = childElements.get(1);
-        }
-        else if(childElements.get(1).getName().equals(EVENT_TIME)) {
-            eventTimeElement = childElements.get(1);
-            notificationElement = childElements.get(0);
-        } else {
-            throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message);
-        }
-
-        try {
-            return new AbstractMap.SimpleEntry<>(EVENT_TIME_FORMAT.get().parse(eventTimeElement.getTextContent()), notificationElement);
-        } catch (DocumentedException e) {
-            throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message);
-        } catch (ParseException e) {
-            LOG.warn("Unable to parse event time from {}. Setting time to {}", eventTimeElement, NetconfNotification.UNKNOWN_EVENT_TIME, e);
-            return new AbstractMap.SimpleEntry<>(NetconfNotification.UNKNOWN_EVENT_TIME, notificationElement);
-        }
+        return Collections.max(notificationDefinitions, cmp);
     }
 
     @Override
index a0622d719c41fa60a0ed1c4d5823dc5a9a0ef802..aa60efe08b8bea9bd6b7166698e619c7c3c0b08e 100644 (file)
@@ -13,16 +13,23 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
 import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.AbstractMap;
 import java.util.Collections;
+import java.util.Date;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.controller.config.util.xml.DocumentedException;
+import org.opendaylight.controller.config.util.xml.XmlElement;
 import org.opendaylight.controller.config.util.xml.XmlUtil;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.notifications.NetconfNotification;
 import org.opendaylight.netconf.util.NetconfUtil;
 import org.opendaylight.netconf.util.messages.NetconfMessageUtil;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.edit.config.input.EditContent;
@@ -295,4 +302,60 @@ public class NetconfMessageTransformUtil {
         return SchemaPath.create(true, rpc);
     }
 
+    private static final ThreadLocal<SimpleDateFormat> EVENT_TIME_FORMAT = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+
+            final SimpleDateFormat withMillis = new SimpleDateFormat(
+                    NetconfNotification.RFC3339_DATE_FORMAT_WITH_MILLIS_BLUEPRINT);
+
+            return new SimpleDateFormat(NetconfNotification.RFC3339_DATE_FORMAT_BLUEPRINT) {
+                private static final long serialVersionUID = 1L;
+
+                @Override public Date parse(final String source) throws ParseException {
+                    try {
+                        return super.parse(source);
+                    } catch (ParseException e) {
+                        // In case of failure, try to parse with milliseconds
+                        return withMillis.parse(source);
+                    }
+                }
+            };
+        }
+
+        @Override
+        public void set(final SimpleDateFormat value) {
+            throw new UnsupportedOperationException();
+        }
+    };
+
+    public static Map.Entry<Date, XmlElement> stripNotification(final NetconfMessage message) {
+        final XmlElement xmlElement = XmlElement.fromDomDocument(message.getDocument());
+        final List<XmlElement> childElements = xmlElement.getChildElements();
+        Preconditions.checkArgument(childElements.size() == 2, "Unable to parse notification %s, unexpected format.\nExpected 2 childElements," +
+                " actual childElements size is %s", message, childElements.size());
+
+        final XmlElement eventTimeElement;
+        final XmlElement notificationElement;
+
+        if (childElements.get(0).getName().equals(EVENT_TIME)) {
+            eventTimeElement = childElements.get(0);
+            notificationElement = childElements.get(1);
+        }
+        else if(childElements.get(1).getName().equals(EVENT_TIME)) {
+            eventTimeElement = childElements.get(1);
+            notificationElement = childElements.get(0);
+        } else {
+            throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message);
+        }
+
+        try {
+            return new AbstractMap.SimpleEntry<>(EVENT_TIME_FORMAT.get().parse(eventTimeElement.getTextContent()), notificationElement);
+        } catch (DocumentedException e) {
+            throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message);
+        } catch (ParseException e) {
+            LOG.warn("Unable to parse event time from {}. Setting time to {}", eventTimeElement, NetconfNotification.UNKNOWN_EVENT_TIME, e);
+            return new AbstractMap.SimpleEntry<>(NetconfNotification.UNKNOWN_EVENT_TIME, notificationElement);
+        }
+    }
 }
index 141ff813b9446d995c0e4ee9ee03257dde9cf4cc..6dab3d6e1ae9afe0845ae44ae36230d7b5f10234 100644 (file)
@@ -188,6 +188,25 @@ module netconf-node-topology {
             description "The destination schema repository for yang files relative to the cache directory.  This may be specified per netconf mount
                          so that the loaded yang files are stored to a distinct directory to avoid potential conflict.";
         }
+
+        container yang-library {
+            leaf yang-library-url {
+                config true;
+                type inet:uri;
+                description "Yang library to be plugged as additional source provider into the shared schema repository";
+            }
+
+            // credentials for basic http authentication
+            leaf username {
+                config true;
+                type string;
+            }
+
+            leaf password {
+                config true;
+                type string;
+            }
+        }
     }
 
     grouping netconf-node-fields {
index 28c504f09315b9c9f7ccd66dc97885b8eb2e2d33..a7bb0827148efc97b99e6b96469c2086c382baa7 100644 (file)
@@ -190,6 +190,25 @@ module odl-sal-netconf-connector-cfg {
 
                 description "Dedicated solely to keepalive execution";
             }
+
+            container yang-library {
+                leaf yang-library-url {
+                    config true;
+                    type inet:uri;
+                    description "Yang library to be plugged as additional source provider into the shared schema repository";
+                }
+
+                // credentials for basic http authentication for get request for yanglib data
+                leaf username {
+                    config true;
+                    type string;
+                }
+
+                leaf password {
+                    config true;
+                    type string;
+                }
+            }
         }
     }
 }
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemasTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemasTest.java
new file mode 100644 (file)
index 0000000..26e48c6
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  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.netconf;
+
+import static org.hamcrest.CoreMatchers.is;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Map;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+public class LibraryModulesSchemasTest {
+
+    @Test
+    public void testCreate() throws Exception {
+        // test create from xml
+        LibraryModulesSchemas libraryModulesSchemas =
+                LibraryModulesSchemas.create(getClass().getResource("/yang-library.xml").toString());
+
+        verifySchemas(libraryModulesSchemas);
+
+        // test create from json
+        LibraryModulesSchemas libraryModuleSchemas =
+                LibraryModulesSchemas.create(getClass().getResource("/yang-library.json").toString());
+
+        verifySchemas(libraryModulesSchemas);
+    }
+
+
+    private void verifySchemas(final LibraryModulesSchemas libraryModulesSchemas) throws MalformedURLException {
+        final Map<SourceIdentifier, URL> resolvedModulesSchema = libraryModulesSchemas.getAvailableModels();
+        Assert.assertThat(resolvedModulesSchema.size(), is(3));
+
+        Assert.assertTrue(resolvedModulesSchema.containsKey(new SourceIdentifier("module-with-revision", "2014-04-08")));
+        Assert.assertThat(resolvedModulesSchema.get(
+                new SourceIdentifier("module-with-revision", "2014-04-08")),
+                is(new URL("http://localhost:8181/yanglib/schemas/module-with-revision/2014-04-08")));
+
+        Assert.assertTrue(resolvedModulesSchema.containsKey(
+                new SourceIdentifier("another-module-with-revision", "2013-10-21")));
+        Assert.assertThat(resolvedModulesSchema.get(
+                new SourceIdentifier("another-module-with-revision", "2013-10-21")),
+                is(new URL("http://localhost:8181/yanglib/schemas/another-module-with-revision/2013-10-21")));
+
+        Assert.assertTrue(resolvedModulesSchema.containsKey(new SourceIdentifier("module-without-revision")));
+        Assert.assertThat(resolvedModulesSchema.get(
+                new SourceIdentifier("module-without-revision")),
+                is(new URL("http://localhost:8181/yanglib/schemas/module-without-revision/")));
+    }
+
+    @Test
+    public void testCreateInvalidModulesEntries() throws Exception {
+        LibraryModulesSchemas libraryModulesSchemas =
+                LibraryModulesSchemas.create(getClass().getResource("/yang-library-fail.xml").toString());
+
+        final Map<SourceIdentifier, URL> resolvedModulesSchema = libraryModulesSchemas.getAvailableModels();
+        Assert.assertThat(resolvedModulesSchema.size(), is(1));
+
+        Assert.assertFalse(resolvedModulesSchema.containsKey(new SourceIdentifier("module-with-bad-url")));
+        Assert.assertFalse(resolvedModulesSchema.containsKey(
+                new SourceIdentifier("module-with-bad-revision", "bad-revision")));
+        Assert.assertTrue(resolvedModulesSchema.containsKey(new SourceIdentifier("good-ol-module")));
+    }
+
+
+    @Test
+    public void testCreateFromInvalidAll() throws Exception {
+        // test bad yang lib url
+        LibraryModulesSchemas libraryModulesSchemas = LibraryModulesSchemas.create("ObviouslyBadUrl");
+        Assert.assertThat(libraryModulesSchemas.getAvailableModels(), is(Collections.emptyMap()));
+
+        // TODO test also fail on json and xml parsing. But can we fail not on runtime exceptions?
+    }
+}
\ No newline at end of file
index 7e5bce2332c32b09c379989a0e2896b774d99350..df46f96913a8d093e80d5d2b7efb024889a39bc1 100644 (file)
@@ -124,7 +124,14 @@ public class NetconfDeviceTest {
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(),true);
+        final NetconfDevice device = new NetconfDeviceBuilder()
+                .setReconnectOnSchemasChange(true)
+                .setSchemaResourcesDTO(schemaResourcesDTO)
+                .setGlobalProcessingExecutor(getExecutor())
+                .setId(getId())
+                .setSalFacade(facade)
+                .build();
+
         // Monitoring not supported
         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, capList);
         device.onRemoteSessionUp(sessionCaps, listener);
@@ -169,7 +176,13 @@ public class NetconfDeviceTest {
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
 
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
+        final NetconfDevice device = new NetconfDeviceBuilder()
+                .setReconnectOnSchemasChange(true)
+                .setSchemaResourcesDTO(schemaResourcesDTO)
+                .setGlobalProcessingExecutor(getExecutor())
+                .setId(getId())
+                .setSalFacade(facade)
+                .build();
         // Monitoring supported
         final NetconfSessionPreferences sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_CAPABILITY, TEST_CAPABILITY2));
         device.onRemoteSessionUp(sessionCaps, listener);
@@ -193,7 +206,13 @@ public class NetconfDeviceTest {
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), getSchemaFactory(), stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
+        final NetconfDevice device = new NetconfDeviceBuilder()
+                .setReconnectOnSchemasChange(true)
+                .setSchemaResourcesDTO(schemaResourcesDTO)
+                .setGlobalProcessingExecutor(getExecutor())
+                .setId(getId())
+                .setSalFacade(facade)
+                .build();
 
         device.onNotification(notification);
         device.onNotification(notification);
@@ -205,7 +224,7 @@ public class NetconfDeviceTest {
 
         final DOMRpcService deviceRpc = mock(DOMRpcService.class);
 
-        device.handleSalInitializationSuccess(NetconfToNotificationTest.getNotificationSchemaContext(getClass()), sessionCaps, deviceRpc);
+        device.handleSalInitializationSuccess(NetconfToNotificationTest.getNotificationSchemaContext(getClass(), false), sessionCaps, deviceRpc);
 
         verify(facade, timeout(10000).times(2)).onNotification(any(DOMNotification.class));
 
@@ -222,7 +241,13 @@ public class NetconfDeviceTest {
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaContextProviderFactory, stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
+        final NetconfDevice device = new NetconfDeviceBuilder()
+                .setReconnectOnSchemasChange(true)
+                .setSchemaResourcesDTO(schemaResourcesDTO)
+                .setGlobalProcessingExecutor(getExecutor())
+                .setId(getId())
+                .setSalFacade(facade)
+                .build();
         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
                 Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
         device.onRemoteSessionUp(sessionCaps, listener);
index b87ec2b7db6644eb1651167b51d76d256e8fdc00..1b4c19098d3b6f15d6df8da3cf2167387a52bdec 100644 (file)
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
 import com.google.common.collect.Iterables;
 import java.io.InputStream;
 import java.text.SimpleDateFormat;
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -43,10 +43,6 @@ public class NetconfToNotificationTest {
     @SuppressWarnings("deprecation")
     @Before
     public void setup() throws Exception {
-        final SchemaContext schemaContext = getNotificationSchemaContext(getClass());
-
-        messageTransformer = new NetconfMessageTransformer(schemaContext, true);
-
         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         InputStream notifyPayloadStream = getClass().getResourceAsStream("/notification-payload.xml");
@@ -57,8 +53,17 @@ public class NetconfToNotificationTest {
         userNotification = new NetconfMessage(doc);
     }
 
-    static SchemaContext getNotificationSchemaContext(Class<?> loadClass) {
-        final List<InputStream> modelsToParse = Collections.singletonList(loadClass.getResourceAsStream("/schemas/user-notification.yang"));
+    static SchemaContext getNotificationSchemaContext(Class<?> loadClass, boolean getExceptionTest) {
+        final List<InputStream> modelsToParse = new ArrayList<>();
+
+        if (getExceptionTest) {
+            modelsToParse.add(loadClass.getResourceAsStream("/schemas/user-notification4.yang"));
+            modelsToParse.add(loadClass.getResourceAsStream("/schemas/user-notification3.yang"));
+        } else {
+            modelsToParse.add(loadClass.getResourceAsStream("/schemas/user-notification.yang"));
+            modelsToParse.add(loadClass.getResourceAsStream("/schemas/user-notification2.yang"));
+        }
+
         final YangContextParser parser = new YangParserImpl();
         final Set<Module> modules = parser.parseYangModelsFromStreams(modelsToParse);
         assertTrue(!modules.isEmpty());
@@ -67,8 +72,17 @@ public class NetconfToNotificationTest {
         return schemaContext;
     }
 
+    @Test(expected =  IllegalStateException.class)
+    public void testMostRecentWrongYangModel() throws Exception {
+        final SchemaContext schemaContext = getNotificationSchemaContext(getClass(), true);
+        messageTransformer = new NetconfMessageTransformer(schemaContext, true);
+        messageTransformer.toNotification(userNotification);
+    }
+
     @Test
-    public void test() throws Exception {
+    public void testToNotificationFunction() throws Exception {
+        final SchemaContext schemaContext = getNotificationSchemaContext(getClass(), false);
+        messageTransformer = new NetconfMessageTransformer(schemaContext, true);
         final DOMNotification domNotification = messageTransformer.toNotification(userNotification);
         final ContainerNode root = domNotification.getBody();
         assertNotNull(root);
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/schema/YangLibrarySchemaYangSourceProviderTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/schema/YangLibrarySchemaYangSourceProviderTest.java
new file mode 100644 (file)
index 0000000..15f0ab3
--- /dev/null
@@ -0,0 +1,58 @@
+package org.opendaylight.netconf.sal.connect.netconf.schema;
+
+import com.google.common.base.Optional;
+import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Map;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public class YangLibrarySchemaYangSourceProviderTest {
+
+    private SourceIdentifier workingSid;
+    private YangLibrarySchemaYangSourceProvider yangLibrarySchemaYangSourceProvider;
+
+    @Before
+    public void setUp() throws Exception {
+        final URL url = getClass().getResource("/schemas/config-test-rpc.yang");
+        workingSid = new SourceIdentifier("abc", Optional.<String>absent());
+        final Map<SourceIdentifier, URL> sourceIdentifierURLMap = Collections.singletonMap(workingSid, url);
+        final RemoteDeviceId id = new RemoteDeviceId("id", new InetSocketAddress("localhost", 22));
+        yangLibrarySchemaYangSourceProvider = new YangLibrarySchemaYangSourceProvider(id, sourceIdentifierURLMap);
+    }
+
+    @Test
+    public void testGetSource() throws Exception {
+        CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> source =
+                yangLibrarySchemaYangSourceProvider.getSource(workingSid);
+        final String x = new String(ByteStreams.toByteArray(source.checkedGet().openStream()));
+        Assert.assertThat(x, CoreMatchers.containsString("module config-test-rpc"));
+    }
+
+    @Test(expected = SchemaSourceException.class)
+    public void testGetSourceFailure() throws Exception {
+        final URL url = new URL("http://non-existing-entity.yang");
+        final Map<SourceIdentifier, URL> sourceIdentifierURLMap = Collections.singletonMap(workingSid, url);
+        final RemoteDeviceId id = new RemoteDeviceId("id", new InetSocketAddress("localhost", 22));
+        final YangLibrarySchemaYangSourceProvider failingYangLibrarySchemaYangSourceProvider =
+                new YangLibrarySchemaYangSourceProvider(id, sourceIdentifierURLMap);
+
+        CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> source =
+                failingYangLibrarySchemaYangSourceProvider.getSource(workingSid);
+        source.checkedGet();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetSourceNotAvailable() throws Exception {
+        yangLibrarySchemaYangSourceProvider.getSource(new SourceIdentifier("aaaaa", "0000-00-00"));
+    }
+}
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/resources/schemas/user-notification2.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/user-notification2.yang
new file mode 100644 (file)
index 0000000..385d9d5
--- /dev/null
@@ -0,0 +1,46 @@
+module user-notification {
+    yang-version 1;
+    namespace "org:opendaylight:notification:test:ns:yang:user-notification";
+    prefix "user";
+
+    organization "Cisco Systems";
+    contact "Lukas Sedlak";
+    description "Test model for testing notifications";
+
+    revision "2013-07-08" {
+        description "Initial revision";
+    }
+
+    identity user-identity {
+        description "Identity of user incoming to Web Page";
+    }
+
+    identity public-user {
+        base user-identity;
+        description "Identity of random public non-registered user";
+    }
+
+    notification user-visited-page {
+        leaf incoming-user {
+            type identityref {
+                base "user-identity";
+            }
+        }
+
+        leaf ip-address {
+            type string;
+        }
+
+        leaf mac {
+            type string;
+        }
+
+        leaf browser-id {
+            type string;
+        }
+
+        leaf visiting-date {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/resources/schemas/user-notification3.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/user-notification3.yang
new file mode 100644 (file)
index 0000000..f04b3f5
--- /dev/null
@@ -0,0 +1,46 @@
+module user-notification {
+    yang-version 1;
+    namespace "org:opendaylight:notification:test:ns:yang:user-notification";
+    prefix "user";
+
+    organization "Cisco Systems";
+    contact "Lukas Sedlak";
+    description "Test model for testing notifications";
+
+    revision "2016-07-08" {
+        description "Initial revision";
+    }
+
+    identity user-identity {
+        description "Identity of user incoming to Web Page";
+    }
+
+    identity public-user {
+        base user-identity;
+        description "Identity of random public non-registered user";
+    }
+
+    notification user-visited-page {
+        leaf incoming-user {
+            type identityref {
+                base "user-identity";
+            }
+        }
+
+        leaf ip-address {
+            type string;
+        }
+
+        leaf mac {
+            type string;
+        }
+
+        leaf browser-id {
+            type string;
+        }
+
+        leaf visiting-date {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/resources/schemas/user-notification4.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/user-notification4.yang
new file mode 100644 (file)
index 0000000..ca8b737
--- /dev/null
@@ -0,0 +1,56 @@
+module user-notification {
+    yang-version 1;
+    namespace "org:opendaylight:notification:test:ns:yang:user-notification";
+    prefix "user";
+
+    organization "Cisco Systems";
+    contact "Lukas Sedlak";
+    description "Test model for testing notifications";
+
+    revision "2015-07-08" {
+        description "Initial revision";
+    }
+
+    identity user-identity {
+        description "Identity of user incoming to Web Page";
+    }
+
+    identity public-user {
+        base user-identity;
+        description "Identity of random public non-registered user";
+    }
+
+    notification user-visited-page {
+        leaf incoming-user {
+            type identityref {
+                base "user-identity";
+            }
+        }
+
+        leaf ip-address {
+            type string;
+        }
+
+        leaf mac {
+            type string;
+        }
+
+        leaf browser-id {
+            type string;
+        }
+
+        container region {
+            leaf name {
+                type string;
+            }
+
+            leaf time-zone {
+                type string;
+            }
+        }
+
+        leaf visiting-date {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/resources/yang-library-fail-completely.xml b/netconf/sal-netconf-connector/src/test/resources/yang-library-fail-completely.xml
new file mode 100644 (file)
index 0000000..5a6fe21
--- /dev/null
@@ -0,0 +1,3 @@
+<modules-are-note-here xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+    <module-set-id>21429371628890</module-set-id>
+</modules-are-note-here>
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/resources/yang-library-fail.xml b/netconf/sal-netconf-connector/src/test/resources/yang-library-fail.xml
new file mode 100644 (file)
index 0000000..ef1feea
--- /dev/null
@@ -0,0 +1,18 @@
+<modules xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+    <module>
+        <name>module-with-bad-url</name>
+        <revision></revision>
+        <schema>
+            badurl
+        </schema>
+    </module>
+    <module>
+        <name>module-bad-revision</name>
+        <revision>bad-revision</revision>
+    </module>
+    <module>
+        <name>good-ol-module</name>
+        <revision></revision>
+        <schema>http://www.example.com</schema>
+    </module>
+</modules>
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/resources/yang-library.json b/netconf/sal-netconf-connector/src/test/resources/yang-library.json
new file mode 100644 (file)
index 0000000..d0f5552
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "ietf-yang-library:modules-state": {
+    "module": [
+      {
+        "name": "module-with-revision",
+        "revision": "2014-04-08",
+        "schema": "http://localhost:8181/yanglib/schemas/module-with-revision/2014-04-08"
+      },
+      {
+        "name": "another-module-with-revision",
+        "revision": "2014-06-08",
+        "schema": "http://localhost:8181/yanglib/schemas/another-module-with-revision/2013-10-21"
+      },
+      {
+        "name": "module-without-revision",
+        "revision": "",
+        "schema": "http://localhost:8181/yanglib/schemas/module-without-revision/2013-10-21"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/resources/yang-library.xml b/netconf/sal-netconf-connector/src/test/resources/yang-library.xml
new file mode 100644 (file)
index 0000000..704ec67
--- /dev/null
@@ -0,0 +1,23 @@
+<modules xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+    <module>
+        <name>module-with-revision</name>
+        <revision>2014-04-08</revision>
+        <schema>
+            http://localhost:8181/yanglib/schemas/module-with-revision/2014-04-08
+        </schema>
+    </module>
+    <module>
+        <name>another-module-with-revision</name>
+        <revision>2013-10-21</revision>
+        <schema>
+            http://localhost:8181/yanglib/schemas/another-module-with-revision/2013-10-21
+        </schema>
+    </module>
+    <module>
+        <name>module-without-revision</name>
+        <revision></revision>
+        <schema>
+            http://localhost:8181/yanglib/schemas/module-without-revision/
+        </schema>
+    </module>
+</modules>
\ No newline at end of file
index 9ec9464d93908613d6e8a677af167a3940a352d3..2a73412d2c20166e51166f1cbb2701761aeeaef0 100644 (file)
@@ -23,6 +23,7 @@ import org.opendaylight.netconf.client.NetconfClientDispatcherImpl;
 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO;
+import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas.NetconfStateSchemasResolverImpl;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
@@ -82,9 +83,15 @@ public class NetconfDeviceConnectionManager implements Closeable {
         final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File(CACHE));
         repository.registerSchemaSourceListener(cache);
         repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository));
-
-        device = new NetconfDevice(new SchemaResourcesDTO(repository, schemaContextFactory, new NetconfStateSchemasResolverImpl()),
-                deviceId, handler, executor, true);
+        final SchemaResourcesDTO schemaResourcesDTO = new SchemaResourcesDTO(repository, schemaContextFactory, new NetconfStateSchemasResolverImpl());
+
+        device = new NetconfDeviceBuilder()
+                .setReconnectOnSchemasChange(true)
+                .setSchemaResourcesDTO(schemaResourcesDTO)
+                .setGlobalProcessingExecutor(executor)
+                .setId(deviceId)
+                .setSalFacade(handler)
+                .build();
         listener = new NetconfDeviceCommunicator(deviceId, device);
         configBuilder.withSessionListener(listener);
         listener.initializeRemoteConnection(netconfClientDispatcher, configBuilder.build());
index 79ae79d648b198ff7139e53b4c724bab930b0ddf..cc86d7b58cca5ce750d49ce7bbdb64e59a916404 100644 (file)
@@ -37,9 +37,8 @@ public class Input {
         }
 
         final NormalizedNode<?, ?> input = args.iterator().next();
-        Preconditions
-                .checkArgument(input instanceof DataContainerChild<?, ?>, "Input container has to be of type Data Container Child.");
-        this.args = new ArrayList<>((Collection) input.getValue());
+        Preconditions.checkArgument(input instanceof DataContainerChild<?, ?>, "Input container has to be of type Data Container Child.");
+        this.args = new ArrayList<>((Collection<NormalizedNode<?, ?>>) input.getValue());
 
         for (final NormalizedNode<?, ?> arg : this.args) {
             nameToArg.put(arg.getNodeType().getLocalName(), arg);
index 5fc8d9ba9f18ee4c36335ae23950a9183b5f22a3..e2799d86614888bb2668117e46026d8b5d2c717b 100644 (file)
@@ -64,9 +64,9 @@ public class Execution implements Callable<Void> {
 
         this.payloads = new ArrayList<>();
         for (DestToPayload payload : payloads) {
-            AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.preparePut(payload.getDestination())
-                    .addHeader("Content-Type", "application/xml")
-                    .addHeader("Accept", "application/xml")
+            AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.preparePost(payload.getDestination())
+                    .addHeader("Content-Type", "application/json")
+                    .addHeader("Accept", "application/json")
                     .setBody(payload.getPayload())
                     .setRequestTimeout(Integer.MAX_VALUE);
 
@@ -75,7 +75,7 @@ public class Execution implements Callable<Void> {
                         .setScheme(Realm.AuthScheme.BASIC)
                         .setPrincipal(params.auth.get(0))
                         .setPassword(params.auth.get(1))
-                        .setMethodName("PUT")
+                        .setMethodName("POST")
                         .setUsePreemptiveAuth(true)
                         .build());
             }
@@ -89,9 +89,14 @@ public class Execution implements Callable<Void> {
             try {
                 Response response = asyncHttpClient.executeRequest(request).get();
                 if (response.getStatusCode() != 200 && response.getStatusCode() != 204) {
-                    LOG.warn("Status code: {}", response.getStatusCode());
-                    LOG.warn("url: {}", request.getUrl());
-                    LOG.warn(response.getResponseBody());
+                    if (response.getStatusCode() == 409) {
+                        LOG.warn("Request failed, status code: {} - one or more of the devices" +
+                                " is already configured, skipping the whole batch", response.getStatusCode());
+                    } else {
+                        LOG.warn("Status code: {}", response.getStatusCode());
+                        LOG.warn("url: {}", request.getUrl());
+                        LOG.warn(response.getResponseBody());
+                    }
                 }
             } catch (InterruptedException | ExecutionException | IOException e) {
                 LOG.warn(e.toString());
@@ -115,8 +120,13 @@ public class Execution implements Callable<Void> {
                 public STATE onStatusReceived(HttpResponseStatus status) throws Exception {
                     super.onStatusReceived(status);
                     if (status.getStatusCode() != 200 && status.getStatusCode() != 204) {
-                        LOG.warn("Request failed, status code: {}", status.getStatusCode() + status.getStatusText());
-                        LOG.warn("request: {}", request.toString());
+                        if (status.getStatusCode() == 409) {
+                            LOG.warn("Request failed, status code: {} - one or more of the devices" +
+                                    " is already configured, skipping the whole batch", status.getStatusCode());
+                        } else {
+                            LOG.warn("Request failed, status code: {}", status.getStatusCode() + status.getStatusText());
+                            LOG.warn("request: {}", request.toString());
+                        }
                     }
                     return STATE.CONTINUE;
                 }
index bffb6f87e0fbc2e5813b04201d2345b797b47fca..e7c772521cabfe69b8e149bca7397b82cd93c361 100644 (file)
@@ -54,7 +54,7 @@ public final class Main {
         final ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
         root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
 
-        final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator();
+        final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator(params.threadPoolSize);
         try {
             final List<Integer> openDevices = netconfDeviceSimulator.start(params);
             if (openDevices.size() == 0) {
index 23d6750896140857b0dc10b9e6b3d36939edc0e3..50cbf2d647785438947860b8604de2126f707570 100644 (file)
@@ -88,11 +88,10 @@ public class NetconfDeviceSimulator implements Closeable {
 
     private boolean sendFakeSchema = false;
 
-    public NetconfDeviceSimulator() {
-        // TODO make pool size configurable
+    public NetconfDeviceSimulator(final int ThreadPoolSize) {
         this(new NioEventLoopGroup(), new HashedWheelTimer(),
-                Executors.newScheduledThreadPool(8, new ThreadFactoryBuilder().setNameFormat("netconf-ssh-server-mina-timers-%d").build()),
-                ThreadUtils.newFixedThreadPool("netconf-ssh-server-nio-group", 8));
+                Executors.newScheduledThreadPool(ThreadPoolSize, new ThreadFactoryBuilder().setNameFormat("netconf-ssh-server-mina-timers-%d").build()),
+                ThreadUtils.newFixedThreadPool("netconf-ssh-server-nio-group", ThreadPoolSize));
     }
 
     private NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer, final ScheduledExecutorService minaTimerExecutor, final ExecutorService nioExecutor) {
@@ -122,7 +121,7 @@ public class NetconfDeviceSimulator implements Closeable {
 
         final AggregatedNetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = new AggregatedNetconfOperationServiceFactory();
         final NetconfOperationServiceFactory operationProvider = mdSal ? new MdsalOperationProvider(idProvider, transformedCapabilities, schemaContext, sourceProvider) :
-            new SimulatedOperationProvider(idProvider, transformedCapabilities, notificationsFile, initialConfigXMLFile);
+                new SimulatedOperationProvider(idProvider, transformedCapabilities, notificationsFile, initialConfigXMLFile);
 
         transformedCapabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
 
@@ -173,7 +172,7 @@ public class NetconfDeviceSimulator implements Closeable {
                 LOG.warn("Port cannot be greater than 65535, stopping further attempts.");
                 break;
             }
-            final InetSocketAddress address = getAddress(currentPort);
+            final InetSocketAddress address = getAddress(params.ip, currentPort);
 
             final ChannelFuture server;
             if(params.ssh) {
@@ -361,10 +360,9 @@ public class NetconfDeviceSimulator implements Closeable {
         }, PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue()));
     }
 
-    private static InetSocketAddress getAddress(final int port) {
+    private static InetSocketAddress getAddress(final String ip, final int port) {
         try {
-            // TODO make address configurable
-            return new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port);
+            return new InetSocketAddress(Inet4Address.getByName(ip), port);
         } catch (final UnknownHostException e) {
             throw new RuntimeException(e);
         }
index a8040f95eedb4f90c1ba3e0cef5062130cf84486..555f6833b2b38eb10a96c1d2bc7e6c7225fa5361 100644 (file)
@@ -65,7 +65,7 @@ public class ScaleUtil {
         while (true) {
             root.warn("Starting scale test with {} devices", params.deviceCount);
             timeoutGuardFuture = executor.schedule(new TimeoutGuard(), timeout, TimeUnit.MINUTES);
-            final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator();
+            final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator(params.threadAmount);
             try {
                 final List<Integer> openDevices = netconfDeviceSimulator.start(params);
                 if (openDevices.size() == 0) {
index eb6363227d8d40d08165f86eace8ac72933667b9..23ed222c37c1153acc41a1c815e179403879b8d3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  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,
@@ -19,6 +19,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
@@ -33,73 +34,58 @@ public class TesttoolParameters {
     private static final String PORT_KEY = "{PORT}";
     private static final String SSH = "{SSH}";
     private static final String ADDRESS_PORT = "{ADDRESS:PORT}";
-    private static final String dest = "http://{ADDRESS:PORT}/restconf/config/network-topology:network-topology/topology/topology-netconf/node/{PORT}-sim-device";
-
-    private static final String RESOURCE = "/config-template.xml";
-    private InputStream stream;
+    private static final String dest = "http://{ADDRESS:PORT}/restconf/config/network-topology:network-topology/topology/topology-netconf/";
 
+    private static final String RESOURCE = "/config-template.json";
     @Arg(dest = "edit-content")
     public File editContent;
-
     @Arg(dest = "async")
     public boolean async;
-
     @Arg(dest = "thread-amount")
     public int threadAmount;
-
     @Arg(dest = "throttle")
     public int throttle;
-
     @Arg(dest = "auth")
     public ArrayList<String> auth;
-
     @Arg(dest = "controller-destination")
     public String controllerDestination;
-
     @Arg(dest = "schemas-dir")
     public File schemasDir;
-
     @Arg(dest = "devices-count")
     public int deviceCount;
-
     @Arg(dest = "devices-per-port")
     public int devicesPerPort;
-
     @Arg(dest = "starting-port")
     public int startingPort;
-
     @Arg(dest = "generate-config-connection-timeout")
     public int generateConfigsTimeout;
-
     @Arg(dest = "generate-config-address")
     public String generateConfigsAddress;
-
     @Arg(dest = "distro-folder")
     public File distroFolder;
-
     @Arg(dest = "generate-configs-batch-size")
     public int generateConfigBatchSize;
-
     @Arg(dest = "ssh")
     public boolean ssh;
-
     @Arg(dest = "exi")
     public boolean exi;
-
     @Arg(dest = "debug")
     public boolean debug;
-
     @Arg(dest = "notification-file")
     public File notificationFile;
-
     @Arg(dest = "md-sal")
     public boolean mdSal;
-
     @Arg(dest = "initial-config-xml-file")
     public File initialConfigXMLFile;
-
     @Arg(dest = "time-out")
     public long timeOut;
+    private InputStream stream;
+
+    @Arg(dest = "ip")
+    public String ip;
+
+    @Arg(dest = "thread-pool-size")
+    public int threadPoolSize;
 
     static ArgumentParser getParser() {
         final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testtool");
@@ -118,7 +104,8 @@ public class TesttoolParameters {
         parser.addArgument("--thread-amount")
                 .type(Integer.class)
                 .setDefault(1)
-                .dest("thread-amount");
+                .dest("thread-amount")
+                .help("The number of threads to use for configuring devices.");
 
         parser.addArgument("--throttle")
                 .type(Integer.class)
@@ -134,10 +121,10 @@ public class TesttoolParameters {
 
         parser.addArgument("--controller-destination")
                 .type(String.class)
-                .help("Ip address and port of controller. Must be in following format <ip>:<port> "+
-                      "if available it will be used for spawning netconf connectors via topology configuration as "+
-                      "a part of URI. Example (http://<controller destination>/restconf/config/network-topology:network-topology/topology/topology-netconf/node/<node-id>)"+
-                      "otherwise it will just start simulated devices and skip the execution of PUT requests")
+                .help("Ip address and port of controller. Must be in following format <ip>:<port> " +
+                        "if available it will be used for spawning netconf connectors via topology configuration as " +
+                        "a part of URI. Example (http://<controller destination>/restconf/config/network-topology:network-topology/topology/topology-netconf/node/<node-id>)" +
+                        "otherwise it will just start simulated devices and skip the execution of PUT requests")
                 .dest("controller-destination");
 
         parser.addArgument("--device-count")
@@ -187,7 +174,7 @@ public class TesttoolParameters {
 
         parser.addArgument("--generate-configs-batch-size")
                 .type(Integer.class)
-                .setDefault(4000)
+                .setDefault(1)
                 .help("Number of connector configs per generated file")
                 .dest("generate-configs-batch-size");
 
@@ -226,6 +213,20 @@ public class TesttoolParameters {
                 .help("the maximum time in seconds for executing each PUT request")
                 .dest("time-out");
 
+        parser.addArgument("-ip")
+                .type(String.class)
+                .setDefault("0.0.0.0")
+                .help("Ip address which will be used for creating a socket address." +
+                        "It can either be a machine name, such as " +
+                        "java.sun.com, or a textual representation of its IP address.")
+                .dest("ip");
+
+        parser.addArgument("--thread-pool-size")
+                .type(Integer.class)
+                .setDefault(8)
+                .help("The number of threads to keep in the pool, when creating a device simulator. Even if they are idle.")
+                .dest("thread-pool-size");
+
         return parser;
     }
 
@@ -242,6 +243,25 @@ public class TesttoolParameters {
         return null;
     }
 
+    private static String modifyMessage(final StringBuilder payloadBuilder, final int payloadPosition, final int size) {
+        if (size == 1) {
+            return payloadBuilder.toString();
+        }
+
+        if (payloadPosition == 0) {
+            payloadBuilder.insert(payloadBuilder.toString().indexOf('{', 2), "[");
+            payloadBuilder.replace(payloadBuilder.length() - 1, payloadBuilder.length(), ",");
+        } else if (payloadPosition + 1 == size) {
+            payloadBuilder.delete(0, payloadBuilder.toString().indexOf(':') + 1);
+            payloadBuilder.insert(payloadBuilder.toString().indexOf('}', 2) + 1, "]");
+        } else {
+            payloadBuilder.delete(0, payloadBuilder.toString().indexOf(':') + 1);
+            payloadBuilder.replace(payloadBuilder.length() - 2, payloadBuilder.length() - 1, ",");
+            payloadBuilder.deleteCharAt(payloadBuilder.toString().lastIndexOf('}'));
+        }
+        return payloadBuilder.toString();
+    }
+
     void validate() {
         if (editContent == null) {
             stream = TesttoolParameters.class.getResourceAsStream(RESOURCE);
@@ -267,11 +287,10 @@ public class TesttoolParameters {
         }
     }
 
-    public ArrayList<ArrayList<Execution.DestToPayload>> getThreadsPayloads(List<Integer> openDevices) {
+    public ArrayList<ArrayList<Execution.DestToPayload>> getThreadsPayloads(final List<Integer> openDevices) {
         final String editContentString;
         try {
-            if(stream == null)
-            {
+            if (stream == null) {
                 editContentString = Files.toString(editContent, Charsets.UTF_8);
             } else {
                 editContentString = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
@@ -280,18 +299,67 @@ public class TesttoolParameters {
             throw new IllegalArgumentException("Cannot read content of " + editContent);
         }
 
+        int from, to;
+        Iterator<Integer> iterator;
+
         final ArrayList<ArrayList<Execution.DestToPayload>> allThreadsPayloads = new ArrayList<>();
-        for (int i = 0; i < threadAmount; i++) {
-            final ArrayList<Execution.DestToPayload> payloads = new ArrayList<>();
-            for (int j = 0; j < openDevices.size(); j++) {
-                final StringBuilder destBuilder = new StringBuilder(dest);
-                destBuilder.replace(destBuilder.indexOf(ADDRESS_PORT), destBuilder.indexOf(ADDRESS_PORT) + ADDRESS_PORT.length(), controllerDestination)
-                        .replace(destBuilder.indexOf(PORT_KEY), destBuilder.indexOf(PORT_KEY) + PORT_KEY.length(), Integer.toString(openDevices.get(j)));
-                payloads.add(new Execution.DestToPayload(destBuilder.toString(), prepareMessage(openDevices.get(j), editContentString)));
+        if (generateConfigBatchSize > 1) {
+
+            final int batchedRequests = openDevices.size() / generateConfigBatchSize;
+            final int batchedRequestsPerThread = batchedRequests / threadAmount;
+            final int leftoverBatchedRequests = (batchedRequests) % threadAmount;
+            final int leftoverRequests = openDevices.size() - (batchedRequests * generateConfigBatchSize);
+
+            final StringBuilder destBuilder = new StringBuilder(dest);
+            destBuilder.replace(destBuilder.indexOf(ADDRESS_PORT), destBuilder.indexOf(ADDRESS_PORT) + ADDRESS_PORT.length(), controllerDestination);
+
+            for (int l = 0; l < threadAmount; l++) {
+                from = l * (batchedRequests * batchedRequestsPerThread);
+                to = from + (batchedRequests * batchedRequestsPerThread);
+                iterator = openDevices.subList(from, to).iterator();
+                allThreadsPayloads.add(createBatchedPayloads(batchedRequestsPerThread, iterator, editContentString, destBuilder.toString()));
             }
-            allThreadsPayloads.add(payloads);
-        }
+            ArrayList<Execution.DestToPayload> payloads = null;
+            if (leftoverBatchedRequests > 0) {
+                from = threadAmount * (batchedRequests * batchedRequestsPerThread);
+                to = from + (batchedRequests * batchedRequestsPerThread);
+                iterator = openDevices.subList(from, to).iterator();
+                payloads = createBatchedPayloads(leftoverBatchedRequests, iterator, editContentString, destBuilder.toString());
+            }
+            String payload = "";
+
+            for (int j = 0; j < leftoverRequests; j++) {
+                from = openDevices.size() - leftoverRequests;
+                to = openDevices.size();
+                iterator = openDevices.subList(from, to).iterator();
+                final StringBuilder payloadBuilder = new StringBuilder(prepareMessage(iterator.next(), editContentString));
+                payload += modifyMessage(payloadBuilder, j, leftoverRequests);
+            }
+            if (leftoverRequests > 0 || leftoverBatchedRequests > 0) {
 
+                if (payloads != null) {
+                    payloads.add(new Execution.DestToPayload(destBuilder.toString(), payload));
+                }
+                allThreadsPayloads.add(payloads);
+            }
+        } else {
+            final int requestPerThreads = openDevices.size() / threadAmount;
+            final int leftoverRequests = openDevices.size() % threadAmount;
+
+            for (int i = 0; i < threadAmount; i++) {
+                from = i * requestPerThreads;
+                to = from + requestPerThreads;
+                iterator = openDevices.subList(from, to).iterator();
+                allThreadsPayloads.add(createPayloads(iterator, editContentString));
+            }
+
+            if (leftoverRequests > 0) {
+                from = (threadAmount) * requestPerThreads;
+                to = from + leftoverRequests;
+                iterator = openDevices.subList(from, to).iterator();
+                allThreadsPayloads.add(createPayloads(iterator, editContentString));
+            }
+        }
         return allThreadsPayloads;
     }
 
@@ -310,4 +378,30 @@ public class TesttoolParameters {
         }
         return messageBuilder.toString();
     }
+
+    private ArrayList<Execution.DestToPayload> createPayloads(final Iterator<Integer> openDevices, final String editContentString) {
+        final ArrayList<Execution.DestToPayload> payloads = new ArrayList<>();
+
+        while (openDevices.hasNext()) {
+            final StringBuilder destBuilder = new StringBuilder(dest);
+            destBuilder.replace(destBuilder.indexOf(ADDRESS_PORT), destBuilder.indexOf(ADDRESS_PORT) + ADDRESS_PORT.length(), controllerDestination);
+            payloads.add(new Execution.DestToPayload(destBuilder.toString(), prepareMessage(openDevices.next(), editContentString)));
+        }
+        return payloads;
+    }
+
+    private ArrayList<Execution.DestToPayload> createBatchedPayloads(final int batchedRequestsCount, final Iterator<Integer> openDevices, final String editContentString,
+                                                                     final String destination) {
+        final ArrayList<Execution.DestToPayload> payloads = new ArrayList<>();
+
+        for (int i = 0; i < batchedRequestsCount; i++) {
+            String payload = "";
+            for (int j = 0; j < generateConfigBatchSize; j++) {
+                final StringBuilder payloadBuilder = new StringBuilder(prepareMessage(openDevices.next(), editContentString));
+                payload += modifyMessage(payloadBuilder, j, generateConfigBatchSize);
+            }
+            payloads.add(new Execution.DestToPayload(destination, payload));
+        }
+        return payloads;
+    }
 }
index 6f5736f0112b7cd67e3b24f0f18eeb848c1fab66..53296d408036a57431fefb6a40bdef336ff24848 100644 (file)
@@ -144,8 +144,7 @@ public class Parameters {
         Preconditions.checkArgument(editContent.canRead(), "Edit content file is unreadable");
 
         Preconditions.checkArgument(destination.startsWith("/"), "Destination should start with a '/'");
-
-        // TODO validate
+        Preconditions.checkArgument(threadAmount > 0, "Parameter thread-amount must be greater than 0");
     }
 
     public InetSocketAddress getInetAddress() {
index c7e3dbfdab5c362d3920c8130ca861fd69d0e0f2..77a0a60a301fd66128cf9a31dcf0b6cf4e0f4a68 100644 (file)
@@ -163,9 +163,6 @@ public class Parameters {
                 .setDefault(1)
                 .dest("thread-amount");
 
-        // TODO add get-config option instead of edit + commit
-        // TODO different edit config content
-
         return parser;
     }
 
@@ -181,7 +178,8 @@ public class Parameters {
         Preconditions.checkArgument(editContent.exists(), "Edit content file missing");
         Preconditions.checkArgument(editContent.isDirectory() == false, "Edit content file is a dir");
         Preconditions.checkArgument(editContent.canRead(), "Edit content file is unreadable");
-        // TODO validate
+        Preconditions.checkArgument(threadAmount > 0, "Parameter thread-amount must be greater than 0");
+        Preconditions.checkArgument(msgTimeout >= 0, "Parameter msg-timeout must be greater than 0");
     }
 
     public InetSocketAddress getInetAddress() {
diff --git a/netconf/tools/netconf-testtool/src/main/resources/config-template.json b/netconf/tools/netconf-testtool/src/main/resources/config-template.json
new file mode 100644 (file)
index 0000000..479f20d
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "node": {
+    "node-id": "{PORT}-sim-device",
+    "host": "{HOST}",
+    "port": "{PORT}",
+    "username": "admin",
+    "password": "admin",
+    "tcp-only": "{SSH}",
+    "keepalive-delay": "0"
+  }
+}
\ No newline at end of file
diff --git a/netconf/yanglib/pom.xml b/netconf/yanglib/pom.xml
new file mode 100644 (file)
index 0000000..232a2fe
--- /dev/null
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Cisco Systems, Inc. and others.  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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>config-parent</artifactId>
+        <version>0.5.0-SNAPSHOT</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.netconf</groupId>
+    <artifactId>yanglib</artifactId>
+    <version>1.1.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-Name>YangLib</Bundle-Name>
+                        <Import-Package>*,
+                            com.sun.jersey.spi.container.servlet, org.eclipse.jetty.servlets</Import-Package>
+                        <Web-ContextPath>/yanglib</Web-ContextPath>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.netconf</groupId>
+                <artifactId>netconf-artifacts</artifactId>
+                <version>${project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.netconf</groupId>
+            <artifactId>ietf-netconf-yang-library</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jaxrs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/netconf/yanglib/src/main/java/org/opendaylight/controller/config/yang/yanglib/impl/YanglibModule.java b/netconf/yanglib/src/main/java/org/opendaylight/controller/config/yang/yanglib/impl/YanglibModule.java
new file mode 100644 (file)
index 0000000..23a73a0
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  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.controller.config.yang.yanglib.impl;
+
+import java.io.File;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.yanglib.impl.YangLibProvider;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class YanglibModule extends org.opendaylight.controller.config.yang.yanglib.impl.AbstractYanglibModule {
+    private static final Logger LOG = LoggerFactory.getLogger(YanglibModule.class);
+
+    public YanglibModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public YanglibModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.yanglib.impl.YanglibModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        JmxAttributeValidationException.checkNotNull(getCacheFolder(), cacheFolderJmxAttribute);
+        final File file = new File(getCacheFolder());
+        JmxAttributeValidationException
+                .checkCondition(file.exists(), "Non existing cache file", cacheFolderJmxAttribute);
+        JmxAttributeValidationException
+                .checkCondition(file.isDirectory(), "Non directory cache file", cacheFolderJmxAttribute);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        // Start cache and Text to AST transformer
+        final SharedSchemaRepository repository = new SharedSchemaRepository("yang-library");
+        YangLibProvider provider = new YangLibProvider(repository, getBindingAddr(), getBindingPort());
+
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache =
+                new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File(getCacheFolder()));
+        repository.registerSchemaSourceListener(cache);
+        LOG.info("Starting yang library with sources from {}", getCacheFolder());
+        getBrokerDependency().registerProvider(provider);
+        return provider;
+    }
+}
diff --git a/netconf/yanglib/src/main/java/org/opendaylight/controller/config/yang/yanglib/impl/YanglibModuleFactory.java b/netconf/yanglib/src/main/java/org/opendaylight/controller/config/yang/yanglib/impl/YanglibModuleFactory.java
new file mode 100644 (file)
index 0000000..7273f32
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  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
+ */
+/*
+* Generated file
+*
+* Generated from: yang module name: yanglib yang module local name: yanglib
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Tue Apr 26 17:33:41 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.yanglib.impl;
+public class YanglibModuleFactory extends org.opendaylight.controller.config.yang.yanglib.impl.AbstractYanglibModuleFactory {
+
+}
diff --git a/netconf/yanglib/src/main/java/org/opendaylight/yanglib/api/YangLibRestAppService.java b/netconf/yanglib/src/main/java/org/opendaylight/yanglib/api/YangLibRestAppService.java
new file mode 100644 (file)
index 0000000..2f57145
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.yanglib.api;
+
+import org.opendaylight.yanglib.impl.YangLibServiceImpl;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Interface for register YangLibRestApp service via {@link BundleContext}.
+ */
+public interface YangLibRestAppService {
+
+    /**
+     * Get {@link YangLibServiceImpl} via service.
+     * @return
+     */
+    YangLibServiceImpl getYangLibService();
+}
diff --git a/netconf/yanglib/src/main/java/org/opendaylight/yanglib/api/YangLibService.java b/netconf/yanglib/src/main/java/org/opendaylight/yanglib/api/YangLibService.java
new file mode 100644 (file)
index 0000000..d578813
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.yanglib.api;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+/**
+ * Service provides YANG schema sources for modules from yang library.
+ */
+@Path("/")
+public interface YangLibService {
+
+    /**
+     * Get module's source for each module from yang library
+     * @param name Module's name
+     * @param revision Module's revision
+     * @return Module's source
+     */
+    @GET
+    @Produces("text/plain")
+    @Path("/schemas/{modelName}/{revision:([0-9\\-]*)}")
+    String getSchema(@PathParam("modelName") String name, @PathParam("revision") String revision);
+}
diff --git a/netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibProvider.java b/netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibProvider.java
new file mode 100644 (file)
index 0000000..aeab04a
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.yanglib.impl;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.ModulesState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.ModulesStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.OptionalRevision;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.RevisionIdentifier;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.module.list.Module;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.module.list.ModuleBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.module.list.ModuleKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier;
+import org.opendaylight.yanglib.api.YangLibRestAppService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Listens on new schema sources registered event. For each new source
+ * registered generates URL representing its schema source and write this URL
+ * along with source identifier to
+ * ietf-netconf-yang-library/modules-state/module list.
+ */
+public class YangLibProvider implements BindingAwareProvider, AutoCloseable, SchemaSourceListener{
+    private static final Logger LOG = LoggerFactory.getLogger(YangLibProvider.class);
+
+    private static final Predicate<PotentialSchemaSource<?>> YANG_SCHEMA_SOURCE = new Predicate<PotentialSchemaSource<?>>() {
+        @Override
+        public boolean apply(final PotentialSchemaSource<?> input) {
+            // filter out non yang sources
+            return YangTextSchemaSource.class.isAssignableFrom(input.getRepresentation());
+        }
+    };
+
+    protected DataBroker dataBroker;
+    protected SchemaListenerRegistration schemaListenerRegistration;
+    protected final SharedSchemaRepository schemaRepository;
+    private final String bindingAddress;
+    private final long bindingPort;
+
+    public YangLibProvider(final SharedSchemaRepository schemaRepository,
+                           final String bindingAddress, final long bindingPort) {
+        this.schemaRepository = schemaRepository;
+        this.bindingAddress = bindingAddress;
+        this.bindingPort = bindingPort;
+    }
+
+    @Override
+    public void close() throws Exception {
+        dataBroker = null;
+        schemaListenerRegistration.close();
+    }
+
+    @Override
+    public void onSessionInitiated(final BindingAwareBroker.ProviderContext providerContext) {
+        this.dataBroker = providerContext.getSALService(DataBroker.class);
+        schemaListenerRegistration = schemaRepository.registerSchemaSourceListener(this);
+        getObjectFromBundleContext(YangLibRestAppService.class, YangLibRestAppService.class.getName())
+        .getYangLibService().setSchemaRepository(schemaRepository);
+    }
+
+    @Override
+    public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+        // NOOP
+    }
+
+    @Override
+    public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+        final List<Module> newModules = new ArrayList<>();
+
+        for(PotentialSchemaSource<?> potentialYangSource : Iterables.filter(sources, YANG_SCHEMA_SOURCE)) {
+            final YangIdentifier moduleName = new YangIdentifier(potentialYangSource.getSourceIdentifier().getName());
+
+            final OptionalRevision moduleRevision = getRevisionForModule(potentialYangSource.getSourceIdentifier());
+
+            final Module newModule = new ModuleBuilder()
+                    .setName(moduleName)
+                    .setRevision(moduleRevision)
+                    .setSchema(getUrlForModule(potentialYangSource.getSourceIdentifier()))
+                    .build();
+
+            newModules.add(newModule);
+        }
+
+        if(newModules.isEmpty()) {
+            // If no new yang modules then do nothing
+            return;
+        }
+
+        WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+        tx.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(ModulesState.class),
+                new ModulesStateBuilder().setModule(newModules).build());
+
+        Futures.addCallback(tx.submit(), new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(@Nullable Void result) {
+                LOG.debug("Modules state successfully populated with new modules");
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.warn("Unable to update modules state", t);
+            }
+        });
+    }
+
+    @Override
+    public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+        if(!YANG_SCHEMA_SOURCE.apply(source)) {
+            // if representation of potential schema source is not yang text schema source do nothing
+            // we do not want to delete this module entry from module list
+            return;
+        }
+
+        WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+        tx.delete(LogicalDatastoreType.OPERATIONAL,
+                InstanceIdentifier.create(ModulesState.class)
+                        .child(Module.class,
+                                new ModuleKey(
+                                        new YangIdentifier(source.getSourceIdentifier().getName()),
+                                        getRevisionForModule(source.getSourceIdentifier()))));
+
+        Futures.addCallback(tx.submit(), new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(@Nullable Void result) {
+                LOG.debug("Modules state successfully updated.");
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.warn("Unable to update modules state", t);
+            }
+        });
+    }
+
+    private Uri getUrlForModule(final SourceIdentifier sourceIdentifier) {
+        final String revision = sourceIdentifier.getRevision().equals(SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION)
+                ? "" : sourceIdentifier.getRevision();
+        return new Uri("http://" + bindingAddress + ":" + bindingPort
+                + "/yanglib/schemas/" + sourceIdentifier.getName() + "/" + revision);
+    }
+
+    private OptionalRevision getRevisionForModule(final SourceIdentifier sourceIdentifier) {
+        return sourceIdentifier.getRevision().equals(SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION)
+                ? new OptionalRevision("")
+                : new OptionalRevision(new RevisionIdentifier(sourceIdentifier.getRevision()));
+    }
+
+    private <T> T getObjectFromBundleContext(final Class<T> type, final String serviceRefName) {
+        final BundleContext bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext();
+        final ServiceReference<?> serviceReference = bundleContext.getServiceReference(serviceRefName);
+        return (T) bundleContext.getService(serviceReference);
+    }
+}
diff --git a/netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibRestApp.java b/netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibRestApp.java
new file mode 100644 (file)
index 0000000..6a11645
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.yanglib.impl;
+
+import java.util.Collections;
+import java.util.Set;
+import javax.ws.rs.core.Application;
+import org.opendaylight.yanglib.api.YangLibRestAppService;
+import org.opendaylight.yanglib.api.YangLibService;
+import org.osgi.framework.FrameworkUtil;
+
+public class YangLibRestApp extends Application implements YangLibRestAppService {
+
+    private final YangLibServiceImpl yangLibService;
+
+    public YangLibRestApp() {
+        this.yangLibService = new YangLibServiceImpl();
+        FrameworkUtil.getBundle(getClass()).getBundleContext().registerService(YangLibRestAppService.class.getName(),
+                this, null);
+    }
+
+    @Override
+    public Set<Object> getSingletons() {
+        return Collections.<Object>singleton(this.yangLibService);
+    }
+
+    @Override
+    public YangLibServiceImpl getYangLibService() {
+        return yangLibService;
+    }
+}
diff --git a/netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibServiceImpl.java b/netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibServiceImpl.java
new file mode 100644 (file)
index 0000000..5038b0c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.yanglib.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.io.IOException;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import org.opendaylight.yanglib.api.YangLibService;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides schema sources from yang library
+ */
+public class YangLibServiceImpl implements YangLibService {
+    private static final Logger LOG = LoggerFactory.getLogger(YangLibServiceImpl.class);
+
+    private SharedSchemaRepository schemaRepository;
+
+    public YangLibServiceImpl() {
+
+    }
+
+    public void setSchemaRepository(final SharedSchemaRepository schemaRepository) {
+        LOG.debug("Setting schema repository {}", schemaRepository);
+        this.schemaRepository = schemaRepository;
+    }
+
+    @Override
+    public String getSchema(final String name, final String revision) {
+        Preconditions.checkNotNull(schemaRepository, "Schema repository is not initialized");
+        LOG.debug("Attempting load for schema source {}:{}", name, revision);
+        final SourceIdentifier sourceId =
+                new SourceIdentifier(name, Optional.fromNullable(revision.equals("") ? null : revision));
+
+        final CheckedFuture<YangTextSchemaSource, SchemaSourceException> sourceFuture =
+                schemaRepository.getSchemaSource(sourceId, YangTextSchemaSource.class);
+
+        try {
+            final YangTextSchemaSource source = sourceFuture.checkedGet();
+            return new String(ByteStreams.toByteArray(source.openStream()));
+        } catch (SchemaSourceException|IOException e) {
+            throw new IllegalStateException("Unable to get schema" + sourceId, e);
+        }
+    }
+}
diff --git a/netconf/yanglib/src/main/resources/WEB-INF/web.xml b/netconf/yanglib/src/main/resources/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..ec758fb
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <servlet>
+        <servlet-name>JAXRSYanglib</servlet-name>
+        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.opendaylight.yanglib.impl.YangLibRestApp</param-value>
+        </init-param>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>JAXRSYanglib</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
\ No newline at end of file
diff --git a/netconf/yanglib/src/main/yang/yanglib.yang b/netconf/yanglib/src/main/yang/yanglib.yang
new file mode 100644 (file)
index 0000000..0267abf
--- /dev/null
@@ -0,0 +1,56 @@
+module yanglib {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:yanglib:impl";
+    prefix "yanglib";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;}
+
+    description
+        "Service definition for yanglib project";
+
+    revision "2014-12-10" {
+        description
+            "Initial revision";
+    }
+
+    identity yanglib {
+        base config:module-type;
+        config:java-name-prefix Yanglib;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case yanglib {
+            when "/config:modules/config:module/config:type = 'yanglib'";
+            container broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity md-sal-binding:binding-broker-osgi-registry;
+                    }
+                }
+            }
+
+            // TODO extracting the schema repositories
+            leaf cache-folder {
+                type string;
+                description "local filesystem folder to use as cache + to load yang models from";
+            }
+
+            // TODO it would be better if the binding arguments could be located by the app automatically
+            leaf binding-addr {
+                type string;
+                // TODO make this uri
+                description "binding address is necessary for generating proper URLS (accessible from the outside world)
+                             for models present directly in the library";
+            }
+
+            leaf binding-port {
+                type uint32;
+                // TODO proper type
+                description "binding port is necessary for generating proper URLS (accessible from the outside world)
+                             for models present directly in the library";
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibProviderTest.java b/netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibProviderTest.java
new file mode 100644 (file)
index 0000000..ab3dffa
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. 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.yanglib.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.util.concurrent.Futures;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.ModulesState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.ModulesStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.OptionalRevision;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.RevisionIdentifier;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.module.list.Module;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.module.list.ModuleBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160201.module.list.ModuleKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinSchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
+
+public class YangLibProviderTest {
+
+    @Mock
+    private BindingAwareBroker.ProviderContext context;
+
+    @Mock
+    private DataBroker dataBroker;
+
+    @Mock
+    private WriteTransaction writeTransaction;
+
+    private TestingYangLibProvider yangLibProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        yangLibProvider = new TestingYangLibProvider(new SharedSchemaRepository("test"), "www.fake.com", 300);
+        when(context.getSALService(eq(DataBroker.class))).thenReturn(dataBroker);
+    }
+
+    @Test
+    public void testSchemaSourceRegistered() {
+        yangLibProvider.onSessionInitiated(context);
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(writeTransaction);
+        doNothing().when(writeTransaction)
+                .merge(eq(LogicalDatastoreType.OPERATIONAL), eq(InstanceIdentifier.create(ModulesState.class)), any());
+
+        List<PotentialSchemaSource<?>> list = new ArrayList<>();
+        list.add(
+                PotentialSchemaSource.create(new SourceIdentifier("no-revision"),
+                        YangTextSchemaSource.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue()));
+
+        list.add(
+                PotentialSchemaSource.create(new SourceIdentifier("with-revision", "2016-04-28"),
+                        YangTextSchemaSource.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue()));
+
+        when(writeTransaction.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+        yangLibProvider.schemaSourceRegistered(list);
+
+        List<Module> newModulesList = new ArrayList<>();
+
+        Module newModule = new ModuleBuilder()
+                .setName(new YangIdentifier("no-revision"))
+                .setRevision(new OptionalRevision(""))
+                .setSchema(new Uri("http://www.fake.com:300/yanglib/schemas/no-revision/"))
+                .build();
+
+        newModulesList.add(newModule);
+
+        newModule = new ModuleBuilder()
+                .setName(new YangIdentifier("with-revision"))
+                .setRevision(new OptionalRevision(new RevisionIdentifier("2016-04-28")))
+                .setSchema(new Uri("http://www.fake.com:300/yanglib/schemas/with-revision/2016-04-28"))
+                .build();
+
+        newModulesList.add(newModule);
+
+        verify(dataBroker).newWriteOnlyTransaction();
+        verify(writeTransaction).merge(eq(LogicalDatastoreType.OPERATIONAL),
+                eq(InstanceIdentifier.create(ModulesState.class)),
+                eq(new ModulesStateBuilder().setModule(newModulesList).build()));
+        verify(writeTransaction).submit();
+    }
+
+    @Test
+    public void testFilteringNonYangSchemaSourceRegistered() {
+        yangLibProvider.onSessionInitiated(context);
+
+        // test empty list of schema sources registered
+        List<PotentialSchemaSource<?>> potentialSources = Collections.emptyList();
+        yangLibProvider.schemaSourceRegistered(potentialSources);
+
+        verifyZeroInteractions(dataBroker, writeTransaction);
+
+        // test list of non yang schema sources registered
+        // expected behavior is to do nothing
+        potentialSources = new ArrayList<>();
+        potentialSources.add(
+                PotentialSchemaSource.create(new SourceIdentifier("yin-source-representation"),
+                        YinSchemaSourceRepresentation.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue()));
+
+        potentialSources.add(
+                PotentialSchemaSource.create(new SourceIdentifier("asts-schema-source"),
+                        ASTSchemaSource.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue()));
+
+        yangLibProvider.schemaSourceRegistered(potentialSources);
+        verifyZeroInteractions(dataBroker, writeTransaction);
+
+        // add yang schema source to list
+        potentialSources.add(
+                PotentialSchemaSource.create(new SourceIdentifier("yang-schema-source"),
+                        YangTextSchemaSource.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue()));
+
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(writeTransaction);
+        when(writeTransaction.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+        yangLibProvider.schemaSourceRegistered(potentialSources);
+        verify(dataBroker).newWriteOnlyTransaction();
+
+        ArgumentCaptor<ModulesState> modulesStateCaptor = ArgumentCaptor.forClass(ModulesState.class);
+        verify(writeTransaction).merge(eq(LogicalDatastoreType.OPERATIONAL),
+                eq(InstanceIdentifier.create(ModulesState.class)), modulesStateCaptor.capture());
+        assertEquals(modulesStateCaptor.getValue().getModule().size(), 1);
+        verify(writeTransaction).submit();
+    }
+
+    @Test
+    public void testNonYangSchemaSourceUnregistered() {
+        yangLibProvider.onSessionInitiated(context);
+
+        final PotentialSchemaSource<YinSchemaSourceRepresentation> nonYangSource =
+                PotentialSchemaSource.create(new SourceIdentifier("yin-source-representation"),
+                YinSchemaSourceRepresentation.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue());
+
+        yangLibProvider.schemaSourceUnregistered(nonYangSource);
+
+        // expected behaviour is to do nothing if non yang based source is unregistered
+        verifyZeroInteractions(dataBroker, writeTransaction);
+    }
+
+    @Test
+    public void testSchemaSourceUnregistered() {
+        yangLibProvider.onSessionInitiated(context);
+
+        when(dataBroker.newWriteOnlyTransaction()).thenReturn(writeTransaction);
+        doNothing().when(writeTransaction)
+                .delete(eq(LogicalDatastoreType.OPERATIONAL), any(InstanceIdentifier.class));
+
+        when(writeTransaction.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+
+        PotentialSchemaSource<YangTextSchemaSource> yangUnregistererSource =
+                PotentialSchemaSource.create(new SourceIdentifier("unregistered-yang-schema-without-revision"),
+                        YangTextSchemaSource.class, PotentialSchemaSource.Costs.LOCAL_IO.getValue());
+
+        yangLibProvider.schemaSourceUnregistered(yangUnregistererSource);
+
+        verify(dataBroker).newWriteOnlyTransaction();
+        verify(writeTransaction).delete(eq(LogicalDatastoreType.OPERATIONAL),
+                eq(InstanceIdentifier.create(ModulesState.class)
+                        .child(Module.class,
+                                new ModuleKey(new YangIdentifier("unregistered-yang-schema-without-revision"),
+                                        new OptionalRevision("")))));
+
+        verify(writeTransaction).submit();
+
+        yangUnregistererSource =
+                PotentialSchemaSource.create(new SourceIdentifier("unregistered-yang-with-revision", "2016-04-28"),
+                        YangTextSchemaSource.class, PotentialSchemaSource.Costs.LOCAL_IO.getValue());
+
+        yangLibProvider.schemaSourceUnregistered(yangUnregistererSource);
+
+        verify(dataBroker, times(2)).newWriteOnlyTransaction();
+        verify(writeTransaction).delete(eq(LogicalDatastoreType.OPERATIONAL),
+                eq(InstanceIdentifier.create(ModulesState.class)
+                        .child(Module.class,
+                                new ModuleKey(new YangIdentifier("unregistered-yang-with-revision"),
+                                        new OptionalRevision(new RevisionIdentifier("2016-04-28"))))));
+
+        verify(writeTransaction, times(2)).submit();
+    }
+
+    private static class TestingYangLibProvider extends YangLibProvider {
+
+        public TestingYangLibProvider(SharedSchemaRepository schemaRepository, String bindingAddress, long bindingPort) {
+            super(schemaRepository, bindingAddress, bindingPort);
+        }
+
+        @Override
+        public void onSessionInitiated(BindingAwareBroker.ProviderContext providerContext) {
+            this.dataBroker = providerContext.getSALService(DataBroker.class);
+            schemaListenerRegistration = schemaRepository.registerSchemaSourceListener(this);
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/RestconfWrapperProviders.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/RestconfWrapperProviders.java
new file mode 100644 (file)
index 0000000..33b826b
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  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;
+
+import org.opendaylight.controller.config.yang.md.sal.rest.connector.RestConnectorRuntimeRegistration;
+import org.opendaylight.controller.config.yang.md.sal.rest.connector.RestConnectorRuntimeRegistrator;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.netconf.sal.rest.api.RestConnector;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfProviderImpl;
+import org.opendaylight.restconf.rest.RestConnectorProvider;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+
+/**
+ * Wrapping providers from restconf draft02 and draft11.
+ *
+ */
+public class RestconfWrapperProviders implements AutoCloseable, RestConnector {
+
+    // DRAFT02
+    private final RestconfProviderImpl providerDraft02;
+    // DRAFT11
+    private final RestConnectorProvider providerDraft11;
+
+    /**
+     * Init both providers:
+     * <ul>
+     * <li>draft02 - {@link RestconfProviderImpl}
+     * <li>draft11 - {@link RestConnectorProvider}
+     * </ul>
+     *
+     * @param port
+     *            - port for web sockets in provider for draft02
+     */
+    public RestconfWrapperProviders(final PortNumber port) {
+        // Init draft02 provider
+        this.providerDraft02 = new RestconfProviderImpl();
+        this.providerDraft02.setWebsocketPort(port);
+
+        // Init draft11 provider
+        this.providerDraft11 = new RestConnectorProvider();
+    }
+
+    /**
+     * Register both providers, which will use the SAL layer:
+     * <ul>
+     * <li>draft02 - {@link RestconfProviderImpl}
+     * <li>draft11 - {@link RestConnectorProvider}
+     * </ul>
+     *
+     * @param broker
+     *            - {@link Broker}
+     */
+    public void registerProviders(final Broker broker) {
+        // Register draft02 provider
+        broker.registerProvider(this.providerDraft02);
+
+        // Register draft11 provider
+        broker.registerProvider(this.providerDraft11);
+    }
+
+    /**
+     * Register runtime beans from restconf draft02 {@link RestconfProviderImpl}
+     *
+     * @param runtimeRegistration
+     *            - for register runtime beans
+     * @return {@link RestConnectorRuntimeRegistration}
+     */
+    public RestConnectorRuntimeRegistration runtimeRegistration(
+            final RestConnectorRuntimeRegistrator runtimeRegistration) {
+        return runtimeRegistration.register(this.providerDraft02);
+    }
+
+    @Override
+    public void close() throws Exception {
+        this.providerDraft02.close();
+        this.providerDraft11.close();
+    }
+
+}
index 6b2aedcce459f7796831b175e06675eb46bb8157..0b80458b639c82b4e618fbe12e2599b7fa08e699 100644 (file)
@@ -8,18 +8,18 @@
 
 package org.opendaylight.controller.config.yang.md.sal.rest.connector;
 
-import org.opendaylight.netconf.sal.restconf.impl.RestconfProviderImpl;
+import org.opendaylight.RestconfWrapperProviders;
 
 
 public class RestConnectorModule extends org.opendaylight.controller.config.yang.md.sal.rest.connector.AbstractRestConnectorModule {
 
     private static RestConnectorRuntimeRegistration runtimeRegistration;
 
-    public RestConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+    public RestConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
 
-    public RestConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.md.sal.rest.connector.RestConnectorModule oldModule, java.lang.AutoCloseable oldInstance) {
+    public RestConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.md.sal.rest.connector.RestConnectorModule oldModule, final java.lang.AutoCloseable oldInstance) {
         super(identifier, dependencyResolver, oldModule, oldInstance);
     }
 
@@ -30,21 +30,16 @@ public class RestConnectorModule extends org.opendaylight.controller.config.yang
 
     @Override
     public java.lang.AutoCloseable createInstance() {
-        // Create an instance of our provider
-        RestconfProviderImpl instance = new RestconfProviderImpl();
-        // Set its port
-        instance.setWebsocketPort(getWebsocketPort());
-        // Register it with the Broker
-        getDomBrokerDependency().registerProvider(instance);
+        final RestconfWrapperProviders wrapperProviders = new RestconfWrapperProviders(getWebsocketPort());
+        wrapperProviders.registerProviders(getDomBrokerDependency());
 
         if(runtimeRegistration != null){
             runtimeRegistration.close();
         }
 
-        runtimeRegistration =
-            getRootRuntimeBeanRegistratorWrapper().register(instance);
+        runtimeRegistration = wrapperProviders.runtimeRegistration(getRootRuntimeBeanRegistratorWrapper());
 
-        return instance;
+        return wrapperProviders;
     }
 }
 
index 27c132f28bf2ee40c44c483a04c64b3d415df21f..d6fb46010018a5ca3414410d360a50b13a97698c 100644 (file)
@@ -18,9 +18,10 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyWriter;
 import javax.ws.rs.ext.Provider;
+import org.opendaylight.restconf.Draft11;
 
 @Provider
-@Produces(SchemaRetrievalService.YANG_MEDIA_TYPE)
+@Produces({ SchemaRetrievalService.YANG_MEDIA_TYPE, Draft11.MediaTypes.YANG })
 public class SchemaExportContentYangBodyWriter implements MessageBodyWriter<SchemaExportContext> {
 
     @Override
index 98d4f5476c51aaf04b1f62ab45e5456316d03130..2dabce3b718f7d3fbf768ed3b4c104b1a615f5e0 100644 (file)
@@ -18,10 +18,12 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyWriter;
 import javax.ws.rs.ext.Provider;
 import javax.xml.stream.XMLStreamException;
+import org.opendaylight.restconf.Draft11;
+import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
 
 @Provider
-@Produces(SchemaRetrievalService.YIN_MEDIA_TYPE)
+@Produces({ SchemaRetrievalService.YIN_MEDIA_TYPE, Draft11.MediaTypes.YIN + RestconfConstants.XML })
 public class SchemaExportContentYinBodyWriter implements MessageBodyWriter<SchemaExportContext> {
 
     @Override
index 3020ab08edb2d0c6150399b6774ce14960175851..52999a5cd2019ed580d3a9dce9315b0b132cbaec 100644 (file)
@@ -12,7 +12,13 @@ import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import org.opendaylight.restconf.rest.api.services.schema.RestconfSchemaService;
 
+/**
+ * @deprecated do not use this api. It is replaced by
+ *             {@link RestconfSchemaService}
+ */
+@Deprecated
 @Beta
 public interface SchemaRetrievalService {
 
index 17731de775d582fa4c21e94c998c0acd3d43d8ed..5bb80f7d2efe92e10ec51175fb249e4e8bcebdae 100644 (file)
@@ -26,6 +26,9 @@ import org.opendaylight.netconf.sal.rest.impl.PATCH;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
+import org.opendaylight.restconf.rest.api.services.RestconfModulesService;
+import org.opendaylight.restconf.rest.api.services.RestconfOperationsService;
+import org.opendaylight.restconf.rest.api.services.RestconfStreamsService;
 
 /**
  * The URI hierarchy for the RESTCONF resources consists of an entry point container, 4 top-level resources, and 1
@@ -58,30 +61,55 @@ public interface RestconfService {
     @GET
     public Object getRoot();
 
+    /**
+     * @deprecated do not use this method. It is replaced by
+     *             {@link RestconfModulesService#getModules(UriInfo)}
+     */
+    @Deprecated
     @GET
     @Path("/modules")
     @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getModules(@Context UriInfo uriInfo);
 
+    /**
+     * @deprecated do not use this method. It is replaced by
+     *             {@link RestconfModulesService#getModules(String, UriInfo)}
+     */
+    @Deprecated
     @GET
     @Path("/modules/{identifier:.+}")
     @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getModules(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
 
+    /**
+     * @deprecated do not use this method. It is replaced by
+     *             {@link RestconfModulesService#getModule(String, UriInfo)}
+     */
+    @Deprecated
     @GET
     @Path("/modules/module/{identifier:.+}")
     @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getModule(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
 
+    /**
+     * @deprecated do not use this method. It is replaced by
+     *             {@link RestconfOperationsService#getOperations(UriInfo)}
+     */
+    @Deprecated
     @GET
     @Path("/operations")
     @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getOperations(@Context UriInfo uriInfo);
 
+    /**
+     * @deprecated do not use this method. It is replaced by
+     *             {@link RestconfOperationsService#getOperations(String, UriInfo)}
+     */
+    @Deprecated
     @GET
     @Path("/operations/{identifier:.+}")
     @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON,
@@ -149,6 +177,11 @@ public interface RestconfService {
     @Path("/streams/stream/{identifier:.+}")
     public Response subscribeToStream(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo);
 
+    /**
+     * @deprecated do not use this method. It is replaced by
+     *             {@link RestconfStreamsService#getAvailableStreams(UriInfo)}
+     */
+    @Deprecated
     @GET
     @Path("/streams")
     @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON,
index 594a115bd451bb0bdbc5ebbb6e5191bef6a3829d..3b65083c0512a71663904584d944556c36d48826 100644 (file)
@@ -27,6 +27,8 @@ import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.restconf.Draft11;
+import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
@@ -45,7 +47,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 @Provider
 @Produces({ Draft02.MediaTypes.API + RestconfService.JSON, Draft02.MediaTypes.DATA + RestconfService.JSON,
-    Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON })
+        Draft02.MediaTypes.OPERATION + RestconfService.JSON,
+        Draft11.MediaTypes.API + RestconfConstants.JSON, Draft11.MediaTypes.DATA + RestconfConstants.JSON,
+        Draft11.MediaTypes.OPERATION + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
 public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
 
     private static final int DEFAULT_INDENT_SPACES_NUM = 2;
@@ -64,7 +68,7 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
     public void writeTo(final NormalizedNodeContext t, final Class<?> type, final Type genericType, final Annotation[] annotations,
             final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
                     throws IOException, WebApplicationException {
-        NormalizedNode<?, ?> data = t.getData();
+        final NormalizedNode<?, ?> data = t.getData();
         if (data == null) {
             return;
         }
@@ -72,7 +76,7 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
         @SuppressWarnings("unchecked")
         final InstanceIdentifierContext<SchemaNode> context = (InstanceIdentifierContext<SchemaNode>) t.getInstanceIdentifierContext();
 
-        SchemaPath path = context.getSchemaNode().getPath();
+        final SchemaPath path = context.getSchemaNode().getPath();
         final JsonWriter jsonWriter = createJsonWriter(entityStream, t.getWriterParameters().isPrettyPrint());
         jsonWriter.beginObject();
         writeNormalizedNode(jsonWriter,path,context,data, t.getWriterParameters().getDepth());
@@ -80,8 +84,8 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
         jsonWriter.flush();
     }
 
-    private void writeNormalizedNode(JsonWriter jsonWriter, SchemaPath path,
-            InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data, Optional<Integer> depth) throws
+    private void writeNormalizedNode(final JsonWriter jsonWriter, SchemaPath path,
+            final InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data, final Optional<Integer> depth) throws
             IOException {
         final RestconfNormalizedNodeWriter nnWriter;
         if (SchemaPath.ROOT.equals(path)) {
@@ -121,7 +125,7 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
     }
 
     private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final InstanceIdentifierContext<SchemaNode> context,
-            final SchemaPath path, final JsonWriter jsonWriter, Optional<Integer> depth) {
+            final SchemaPath path, final JsonWriter jsonWriter, final Optional<Integer> depth) {
 
         final SchemaNode schema = context.getSchemaNode();
         final JSONCodecFactory codecs = getCodecFactory(context);
@@ -144,7 +148,7 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
         }
     }
 
-    private JsonWriter createJsonWriter(final OutputStream entityStream, boolean prettyPrint) {
+    private JsonWriter createJsonWriter(final OutputStream entityStream, final boolean prettyPrint) {
         if (prettyPrint) {
             return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8),
                     DEFAULT_INDENT_SPACES_NUM);
index 90784794751d17237a2325ec6671c471d7bbe948..41417f93d0697f03bed455303546fd716f9c237b 100644 (file)
@@ -30,6 +30,8 @@ import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.restconf.Draft11;
+import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
@@ -43,7 +45,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 @Provider
 @Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML,
-        Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+        Draft02.MediaTypes.OPERATION + RestconfService.XML,
+        Draft11.MediaTypes.API + RestconfConstants.XML, Draft11.MediaTypes.DATA + RestconfConstants.XML,
+        Draft11.MediaTypes.OPERATION + RestconfConstants.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
 public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
 
     private static final XMLOutputFactory XML_FACTORY;
@@ -86,8 +90,8 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
         } catch (final FactoryConfigurationError e) {
             throw new IllegalStateException(e);
         }
-        NormalizedNode<?, ?> data = t.getData();
-        SchemaPath schemaPath = pathContext.getSchemaNode().getPath();
+        final NormalizedNode<?, ?> data = t.getData();
+        final SchemaPath schemaPath = pathContext.getSchemaNode().getPath();
 
 
 
@@ -95,8 +99,8 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
 
     }
 
-    private void writeNormalizedNode(XMLStreamWriter xmlWriter, SchemaPath schemaPath, InstanceIdentifierContext<?>
-            pathContext, NormalizedNode<?, ?> data, Optional<Integer> depth) throws IOException {
+    private void writeNormalizedNode(final XMLStreamWriter xmlWriter, final SchemaPath schemaPath, final InstanceIdentifierContext<?>
+            pathContext, NormalizedNode<?, ?> data, final Optional<Integer> depth) throws IOException {
         final RestconfNormalizedNodeWriter nnWriter;
         final SchemaContext schemaCtx = pathContext.getSchemaContext();
         if (SchemaPath.ROOT.equals(schemaPath)) {
@@ -118,9 +122,9 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
         nnWriter.flush();
     }
 
-    private RestconfNormalizedNodeWriter createNormalizedNodeWriter(XMLStreamWriter xmlWriter,
-                                                                        SchemaContext schemaContext, SchemaPath schemaPath, Optional<Integer> depth) {
-        NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, schemaContext, schemaPath);
+    private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final XMLStreamWriter xmlWriter,
+                                                                        final SchemaContext schemaContext, final SchemaPath schemaPath, final Optional<Integer> depth) {
+        final NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, schemaContext, schemaPath);
         if (depth.isPresent()) {
             return DepthAwareNormalizedNodeWriter.forStreamWriter(xmlStreamWriter, depth.get());
         } else {
@@ -135,7 +139,7 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
             final QName name = data.getNodeType();
             xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getLocalName(), name.getNamespace().toString());
             xmlWriter.writeDefaultNamespace(name.getNamespace().toString());
-            for(NormalizedNode<?,?> child : data.getValue()) {
+            for(final NormalizedNode<?,?> child : data.getValue()) {
                 nnWriter.write(child);
             }
             nnWriter.flush();
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/RestConnectorProvider.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/RestConnectorProvider.java
new file mode 100644 (file)
index 0000000..7b2a3b6
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.rest;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Collections;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.netconf.sal.rest.api.RestConnector;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+
+/**
+ * Provider for restconf draft11.
+ *
+ */
+public class RestConnectorProvider implements Provider, RestConnector, AutoCloseable {
+
+    private ListenerRegistration<SchemaContextListener> listenerRegistration;
+
+    @Override
+    public void onSessionInitiated(final ProviderSession session) {
+        final SchemaService schemaService = Preconditions.checkNotNull(session.getService(SchemaService.class));
+    }
+
+    @Override
+    public Collection<ProviderFunctionality> getProviderFunctionality() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (this.listenerRegistration != null) {
+            this.listenerRegistration.close();
+        }
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/schema/context/SchemaContextHandlerImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/schema/context/SchemaContextHandlerImpl.java
new file mode 100644 (file)
index 0000000..b2ac93e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.rest.impl.schema.context;
+
+import org.opendaylight.restconf.rest.api.schema.context.SchemaContextHandler;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Implementation of {@link SchemaContextHandler}
+ *
+ */
+public class SchemaContextHandlerImpl implements SchemaContextHandler {
+
+    private SchemaContext context;
+
+    @Override
+    public void onGlobalContextUpdated(final SchemaContext context) {
+        this.context = null;
+        this.context = context;
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return this.context;
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/Draft11ServicesWrapperImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/Draft11ServicesWrapperImpl.java
new file mode 100644 (file)
index 0000000..375788a
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.rest.impl.services;
+
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.restconf.rest.api.schema.context.SchemaContextHandler;
+import org.opendaylight.restconf.rest.api.services.Draft11ServicesWrapper;
+import org.opendaylight.restconf.rest.api.services.RestconfModulesService;
+import org.opendaylight.restconf.rest.api.services.RestconfOperationsService;
+import org.opendaylight.restconf.rest.api.services.RestconfStreamsService;
+import org.opendaylight.restconf.rest.api.services.schema.RestconfSchemaService;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Implementation of {@link Draft11ServicesWrapper}
+ *
+ */
+public class Draft11ServicesWrapperImpl implements Draft11ServicesWrapper {
+
+    private final RestconfModulesService delegRestModSer;
+    private final RestconfOperationsService delegRestOpsSer;
+    private final RestconfStreamsService delegRestStrsSer;
+    private final RestconfSchemaService delegRestSchSer;
+
+    /**
+     * Creating delegates to all implemented services
+     *
+     * @param schemaContextHandler
+     *            - for handling {@link SchemaContext}
+     */
+    public Draft11ServicesWrapperImpl(final SchemaContextHandler schemaContextHandler) {
+        this.delegRestModSer = new RestconfModulesServiceImpl(schemaContextHandler);
+        this.delegRestOpsSer = new RestconfOperationsServiceImpl(schemaContextHandler);
+        this.delegRestStrsSer = new RestconfStreamsServiceImpl(schemaContextHandler);
+        this.delegRestSchSer = new RestconfSchemaServiceImpl(schemaContextHandler);
+    }
+
+    @Override
+    public NormalizedNodeContext getModules(final UriInfo uriInfo) {
+        return this.delegRestModSer.getModules(uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
+        return this.delegRestModSer.getModules(identifier, uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
+        return this.delegRestModSer.getModules(identifier, uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
+        return this.delegRestOpsSer.getOperations(uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
+        return this.delegRestOpsSer.getOperations(identifier, uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
+        return this.delegRestStrsSer.getAvailableStreams(uriInfo);
+    }
+
+    @Override
+    public SchemaExportContext getSchema(final String mountAndModuleId) {
+        return this.delegRestSchSer.getSchema(mountAndModuleId);
+    }
+
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfModulesServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfModulesServiceImpl.java
new file mode 100644 (file)
index 0000000..8ff0b02
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.rest.impl.services;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.Set;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.restconf.Draft11;
+import org.opendaylight.restconf.rest.api.schema.context.SchemaContextHandler;
+import org.opendaylight.restconf.rest.api.services.RestconfModulesService;
+import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeUtil;
+import org.opendaylight.restconf.utils.parser.ParserIdentifier;
+import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link RestconfModulesService}
+ */
+public class RestconfModulesServiceImpl implements RestconfModulesService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestconfModulesServiceImpl.class);
+    private final SchemaContextHandler schemaContextHandler;
+
+    /**
+     * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext}
+     *
+     * @param schemaContextHandler
+     *            - handling schema context
+     */
+    public RestconfModulesServiceImpl(final SchemaContextHandler schemaContextHandler) {
+        this.schemaContextHandler = schemaContextHandler;
+    }
+
+    @Override
+    public NormalizedNodeContext getModules(final UriInfo uriInfo) {
+        final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.getSchemaContext());
+        return getModules(schemaContextRef.getModules(), schemaContextRef, null);
+    }
+
+    @Override
+    public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
+        Preconditions.checkNotNull(identifier);
+        if (!identifier.contains(RestconfConstants.MOUNT)) {
+            final String errMsg = "URI has bad format. If modules behind mount point should be showed,"
+                    + " URI has to end with " + RestconfConstants.MOUNT;
+            LOG.debug(errMsg + " for " + identifier);
+            throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+        final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.getSchemaContext());
+        final DOMMountPoint mountPoint = ParserIdentifier.toInstanceIdentifier(identifier).getMountPoint();
+        return getModules(schemaContextRef.getModules(mountPoint), schemaContextRef, mountPoint);
+    }
+
+
+    @Override
+    public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
+        Preconditions.checkNotNull(identifier);
+        final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.getSchemaContext());
+        final QName moduleQname = ParserIdentifier.makeQNameFromIdentifier(identifier);
+        Module module = null;
+        DOMMountPoint mountPoint = null;
+        if (identifier.contains(RestconfConstants.MOUNT)) {
+            mountPoint = ParserIdentifier.toInstanceIdentifier(identifier).getMountPoint();
+            module = schemaContextRef.findModuleInMountPointByQName(mountPoint, moduleQname);
+        } else {
+            module = schemaContextRef.findModuleByQName(moduleQname);
+        }
+
+        if (module == null) {
+            final String errMsg = "Module with name '" + moduleQname.getLocalName() + "' and revision '"
+                    + moduleQname.getRevision() + "' was not found.";
+            LOG.debug(errMsg);
+            throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
+        }
+
+        final Set<Module> modules = Collections.singleton(module);
+        final MapNode moduleMap = RestconfMappingNodeUtil
+                .restconfMappingNode(schemaContextRef.getRestconfModule(), modules);
+        final DataSchemaNode moduleSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
+                schemaContextRef.getRestconfModule(), Draft11.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+        Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
+        return new NormalizedNodeContext(
+                new InstanceIdentifierContext<>(null, moduleSchemaNode, mountPoint, schemaContextRef.get()), moduleMap);
+        }
+
+    /**
+     * Get {@link NormalizedNodeContext} from set of modules. Used by
+     * {@link #getModules(UriInfo)} and {@link #getModules(String, UriInfo)}
+     *
+     * @param modules
+     *            - all modules
+     * @param schemaContextRef
+     *            - schema context reference
+     * @param mountPoint
+     *            - mount point
+     * @return {@link NormalizedNodeContext}
+     */
+    private NormalizedNodeContext getModules(final Set<Module> modules, final SchemaContextRef schemaContextRef,
+            final DOMMountPoint mountPoint) {
+        final Module restconfModule = schemaContextRef.getRestconfModule();
+        Preconditions.checkNotNull(restconfModule);
+
+        final MapNode mapNodes = RestconfMappingNodeUtil.restconfMappingNode(restconfModule, modules);
+        final DataSchemaNode schemaNode = RestconfSchemaUtil.getRestconfSchemaNode(restconfModule,
+                Draft11.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
+        Preconditions.checkState(schemaNode instanceof ContainerSchemaNode);
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> modulContainerSchemaNodeBuilder = Builders
+                .containerBuilder((ContainerSchemaNode) schemaNode);
+        modulContainerSchemaNodeBuilder.withChild(mapNodes);
+
+        return new NormalizedNodeContext(
+                new InstanceIdentifierContext<>(null, schemaNode, mountPoint, schemaContextRef.get()),
+                modulContainerSchemaNodeBuilder.build());
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfOperationsServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfOperationsServiceImpl.java
new file mode 100644 (file)
index 0000000..09a3530
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.rest.impl.services;
+
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.restconf.rest.api.schema.context.SchemaContextHandler;
+import org.opendaylight.restconf.rest.api.services.RestconfOperationsService;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Implementation of {@link RestconfOperationsService}
+ *
+ */
+public class RestconfOperationsServiceImpl implements RestconfOperationsService {
+
+    private final SchemaContextHandler schemaContextHandler;
+
+    /**
+     * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext}
+     *
+     * @param schemaContextHandler
+     *            - handling schema context
+     */
+    public RestconfOperationsServiceImpl(final SchemaContextHandler schemaContextHandler) {
+        this.schemaContextHandler = schemaContextHandler;
+    }
+
+    @Override
+    public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
+        throw new RestconfDocumentedException("Not yet implemented.", new UnsupportedOperationException());
+    }
+
+
+    @Override
+    public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
+        throw new RestconfDocumentedException("Not yet implemented.", new UnsupportedOperationException());
+    }
+
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfSchemaServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfSchemaServiceImpl.java
new file mode 100644 (file)
index 0000000..bf30b76
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.rest.impl.services;
+
+import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
+import org.opendaylight.restconf.rest.api.schema.context.SchemaContextHandler;
+import org.opendaylight.restconf.rest.api.services.schema.RestconfSchemaService;
+import org.opendaylight.restconf.utils.parser.ParserIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Implementation of {@link RestconfSchemaService}.
+ *
+ */
+public class RestconfSchemaServiceImpl implements RestconfSchemaService {
+
+    private final SchemaContextHandler schemaContextHandler;
+
+    /**
+     * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext}
+     * .
+     *
+     * @param schemaContextHandler
+     *            - handling schema context
+     */
+    public RestconfSchemaServiceImpl(final SchemaContextHandler schemaContextHandler) {
+        this.schemaContextHandler = schemaContextHandler;
+    }
+
+    @Override
+    public SchemaExportContext getSchema(final String identifier) {
+        final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.getSchemaContext());
+        return ParserIdentifier.toSchemaExportContextFromIdentifier(schemaContextRef.get(), identifier);
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfStreamsServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/RestconfStreamsServiceImpl.java
new file mode 100644 (file)
index 0000000..1b63652
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.rest.impl.services;
+
+import com.google.common.base.Preconditions;
+import java.util.Set;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.restconf.Draft11;
+import org.opendaylight.restconf.rest.api.schema.context.SchemaContextHandler;
+import org.opendaylight.restconf.rest.api.services.RestconfStreamsService;
+import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeUtil;
+import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Implementation of {@link RestconfStreamsService}
+ *
+ */
+public class RestconfStreamsServiceImpl implements RestconfStreamsService {
+
+    private final SchemaContextHandler schemaContextHandler;
+
+    /**
+     * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext}
+     * .
+     *
+     * @param schemaContextHandler
+     *            - handling schema context
+     */
+    public RestconfStreamsServiceImpl(final SchemaContextHandler schemaContextHandler) {
+        this.schemaContextHandler = schemaContextHandler;
+    }
+
+    @Override
+    public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
+        final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.getSchemaContext());
+        final Set<String> availableStreams = Notificator.getStreamNames();
+
+        final DataSchemaNode streamListSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
+                schemaContextRef.getRestconfModule(), Draft11.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
+        Preconditions.checkState(streamListSchemaNode instanceof ListSchemaNode);
+        final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamBuilder = Builders
+                .mapBuilder((ListSchemaNode) streamListSchemaNode);
+
+        for (final String streamValue : availableStreams) {
+            listStreamBuilder.withChild(RestconfMappingNodeUtil.toStreamEntryNode(streamValue, streamListSchemaNode));
+        }
+
+        final DataSchemaNode streamContSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
+                schemaContextRef.getRestconfModule(), Draft11.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
+        Preconditions.checkState(streamContSchemaNode instanceof ContainerSchemaNode);
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder = Builders
+                .containerBuilder((ContainerSchemaNode) streamContSchemaNode);
+
+        streamsContainerBuilder.withChild(listStreamBuilder.build());
+
+        return new NormalizedNodeContext(
+                new InstanceIdentifierContext<>(null, streamContSchemaNode, null, schemaContextRef.get()),
+                streamsContainerBuilder.build());
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/SchemaContextRef.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/impl/services/SchemaContextRef.java
new file mode 100644 (file)
index 0000000..b4a385e
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.rest.impl.services;
+
+import java.lang.ref.SoftReference;
+import java.net.URI;
+import java.util.Date;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.restconf.Draft11;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * This class creates {@link SoftReference} of actual {@link SchemaContext}
+ * object and even if the {@link SchemaContext} changes, this will be sticks
+ * reference to the old {@link SchemaContext} and provides work with the old
+ * {@link SchemaContext}.
+ *
+ */
+final class SchemaContextRef {
+
+    private final SoftReference<SchemaContext> schemaContextRef;
+
+    /**
+     * Create {@link SoftReference} of actual {@link SchemaContext}
+     *
+     * @param schemaContext
+     *            - actual {@link SchemaContext}
+     */
+    public SchemaContextRef(final SchemaContext schemaContext) {
+        this.schemaContextRef = new SoftReference<SchemaContext>(schemaContext);
+    }
+
+    /**
+     * Get {@link SchemaContext} from reference
+     *
+     * @return {@link SchemaContext}
+     */
+    public SchemaContext get() {
+        return this.schemaContextRef.get();
+    }
+
+    /**
+     * Get all modules like {@link Set} of {@link Module} from
+     * {@link SchemaContext}
+     *
+     * @return {@link Set} of {@link Module}
+     */
+    public Set<Module> getModules() {
+        return get().getModules();
+    }
+
+    /**
+     * Get all modules like {@link Set} of {@link Module} from
+     * {@link SchemaContext} of {@link DOMMountPoint}
+     *
+     * @param mountPoint
+     *            - mount point
+     *
+     * @return {@link Set} of {@link Module}
+     */
+    public Set<Module> getModules(final DOMMountPoint mountPoint) {
+        final SchemaContext schemaContext = mountPoint == null ? null : mountPoint.getSchemaContext();
+        return schemaContext == null ? null : schemaContext.getModules();
+    }
+
+    /**
+     * Get {@link Module} by ietf-restconf qname from
+     * {@link Draft09.RestconfModule}
+     *
+     * @return {@link Module}
+     */
+    public Module getRestconfModule() {
+        return this.findModuleByNamespaceAndRevision(Draft11.RestconfModule.IETF_RESTCONF_QNAME.getNamespace(),
+                Draft11.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+    }
+
+    /**
+     * Find {@link Module} in {@link SchemaContext} by {@link URI} and
+     * {@link Date}
+     *
+     * @param namespace
+     *            - namespace of module
+     * @param revision
+     *            - revision of module
+     * @return {@link Module}
+     */
+    public Module findModuleByNamespaceAndRevision(final URI namespace, final Date revision) {
+        return this.get().findModuleByNamespaceAndRevision(namespace, revision);
+    }
+
+
+    /**
+     * Find {@link Module} in {@link SchemaContext} of {@link DOMMountPoint} by
+     * {@link QName} of {@link Module}
+     *
+     * @param mountPoint
+     *            - mount point
+     * @param qname
+     *            - {@link QName} of module
+     * @return {@link Module}
+     */
+    public Module findModuleInMountPointByQName(final DOMMountPoint mountPoint, final QName moduleQname) {
+        final SchemaContext schemaContext = mountPoint == null ? null : mountPoint.getSchemaContext();
+        return schemaContext == null ? null
+                : schemaContext.findModuleByName(moduleQname.getLocalName(), moduleQname.getRevision());
+    }
+
+    /**
+     * Find {@link Module} in {@link SchemaContext} by {@link QName}
+     *
+     * @param moduleQname
+     *            - {@link QName} of module
+     * @return {@link Module}
+     */
+    public Module findModuleByQName(final QName moduleQname) {
+        return this.findModuleByNameAndRevision(moduleQname.getLocalName(), moduleQname.getRevision());
+    }
+
+    /**
+     * Find {@link Module} in {@link SchemaContext} by {@link String} localName
+     * and {@link Date} revision.
+     *
+     * @param localName
+     *            - local name of module
+     * @param revision
+     *            - revision of module
+     * @return {@link Module}
+     */
+    public Module findModuleByNameAndRevision(final String localName, final Date revision) {
+        return this.get().findModuleByName(localName, revision);
+    }
+
+}
index d9888a962bf1007e0f5ae1ce9a9c9ad1cfbddf7a..bef0be9dfccc1d2b379a20c783f0dbf401cc5cb9 100644 (file)
@@ -7,16 +7,21 @@
  */
 package org.opendaylight.restconf.utils;
 
+import com.google.common.base.Splitter;
+import java.text.SimpleDateFormat;
+
 /**
  * Util class for Restconf constants.
  *
  */
-public class RestconfConstants {
+public final class RestconfConstants {
 
     public static final String XML = "+xml";
     public static final String JSON = "+json";
-
+    public static final String MOUNT = "yang-ext:mount";
     public static final String IDENTIFIER = "identifier";
+    public static final SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+    public static final Splitter SLASH_SPLITTER = Splitter.on("/");
 
     private RestconfConstants() {
         throw new UnsupportedOperationException("Util class");
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeConstants.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeConstants.java
new file mode 100644 (file)
index 0000000..6938af4
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.utils.mapping;
+
+/**
+ * Util class for constants of mapping node
+ *
+ */
+public final class RestconfMappingNodeConstants {
+
+    public static final String NAME = "name";
+    public static final String REVISION = "revision";
+    public static final String NAMESPACE = "namespace";
+    public static final String FEATURE = "feature";
+    public static final String DESCRIPTION = "description";
+    public static final String REPLAY_SUPPORT = "replay-support";
+    public static final String REPLAY_LOG = "replay-log-creation-time";
+    public static final String EVENTS = "events";
+
+    private RestconfMappingNodeConstants() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java
new file mode 100644 (file)
index 0000000..939f3ac
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.utils.mapping;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Set;
+import org.opendaylight.restconf.Draft11;
+import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Util class for mapping nodes
+ *
+ */
+public final class RestconfMappingNodeUtil {
+
+    private RestconfMappingNodeUtil() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Mapping {@link Module} from {@link Set} of {@link Module} to
+     * {@link ListSchemaNode} of {@link Module} list.
+     *
+     * @param restconfModule
+     *            - restconf module
+     * @param modules
+     *            - all modules
+     * @return {@link MapNode}
+     */
+    public static MapNode restconfMappingNode(final Module restconfModule, final Set<Module> modules) {
+        final DataSchemaNode modulListSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(restconfModule,
+                Draft11.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+        Preconditions.checkState(modulListSchemaNode instanceof ListSchemaNode);
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
+                .mapBuilder((ListSchemaNode) modulListSchemaNode);
+        for (final Module module : modules) {
+            listModuleBuilder.withChild(toModuleEntryNode(module, modulListSchemaNode));
+        }
+        return listModuleBuilder.build();
+    }
+
+    /**
+     * Mapping {@link MapEntryNode} entries of {@link Module} to
+     * {@link ListSchemaNode}.
+     *
+     * @param module
+     *            - module for mapping
+     * @param modulListSchemaNode
+     *            - mapped {@link DataSchemaNode}
+     * @return {@link MapEntryNode}
+     */
+    private static MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode modulListSchemaNode) {
+        final ListSchemaNode listSchemaNode = (ListSchemaNode) modulListSchemaNode;
+        final Collection<DataSchemaNode> childListSchemaNode = listSchemaNode.getChildNodes();
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
+                .mapEntryBuilder(listSchemaNode);
+
+        // MODULE NAME SCHEMA NODE
+        fillListWithLeaf(listSchemaNode, moduleNodeValues, RestconfMappingNodeConstants.NAME, module.getName());
+
+        // MODULE REVISION SCHEMA NODE
+        fillListWithLeaf(listSchemaNode, moduleNodeValues, RestconfMappingNodeConstants.REVISION,
+                RestconfConstants.REVISION_FORMAT.format(module.getRevision()));
+
+        // MODULE NAMESPACE SCHEMA NODE
+        fillListWithLeaf(listSchemaNode, moduleNodeValues, RestconfMappingNodeConstants.NAMESPACE,
+                module.getNamespace().toString());
+
+        // MODULE FEATURES SCHEMA NODES
+        final DataSchemaNode schemaNode = RestconfSchemaUtil.findSchemaNodeInCollection(childListSchemaNode,
+                RestconfMappingNodeConstants.FEATURE);
+        Preconditions.checkState(schemaNode instanceof LeafListSchemaNode);
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featureBuilder = Builders
+                .leafSetBuilder((LeafListSchemaNode) schemaNode);
+        for (final FeatureDefinition feature : module.getFeatures()) {
+            featureBuilder.withChild(Builders.leafSetEntryBuilder((LeafListSchemaNode) schemaNode)
+                    .withValue(feature.getQName().getLocalName()).build());
+        }
+        moduleNodeValues.withChild(featureBuilder.build());
+
+        return moduleNodeValues.build();
+    }
+
+    /**
+     * Mapping {@link MapEntryNode} stream entries of stream to
+     * {@link ListSchemaNode}
+     *
+     * @param streamName
+     *            - stream name
+     * @param streamListSchemaNode
+     *            - mapped {@link DataSchemaNode}
+     * @return {@link MapEntryNode}
+     */
+    public static MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamListSchemaNode) {
+        Preconditions.checkState(streamListSchemaNode instanceof ListSchemaNode);
+        final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamListSchemaNode;
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
+                .mapEntryBuilder(listStreamSchemaNode);
+
+        // STREAM NAME
+        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.NAME, streamName);
+
+        // STREAM DESCRIPTION
+        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.DESCRIPTION,
+                RestconfMappingStreamConstants.DESCRIPTION);
+
+        // STREAM REPLAY_SUPPORT
+        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.REPLAY_SUPPORT,
+                RestconfMappingStreamConstants.REPLAY_SUPPORT);
+
+        // STREAM REPLAY_LOG
+        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.REPLAY_LOG,
+                RestconfMappingStreamConstants.REPLAY_LOG);
+
+        // STREAM EVENTS
+        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.EVENTS,
+                RestconfMappingStreamConstants.EVENTS);
+
+        return streamNodeValues.build();
+    }
+
+    /**
+     * Method for filling {@link ListSchemaNode} with {@link LeafSchemaNode}
+     *
+     * @param listStreamSchemaNode
+     *            - {@link ListSchemaNode}
+     * @param streamNodeValues
+     *            - filled {@link DataContainerNodeAttrBuilder}
+     * @param nameSchemaNode
+     *            - name of mapped leaf
+     * @param value
+     *            - value for mapped node
+     */
+    private static void fillListWithLeaf(
+            final ListSchemaNode listStreamSchemaNode,
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues,
+            final String nameSchemaNode, final Object value) {
+        final DataSchemaNode schemaNode = RestconfSchemaUtil
+                .findSchemaNodeInCollection(listStreamSchemaNode.getChildNodes(), nameSchemaNode);
+        Preconditions.checkState(schemaNode instanceof LeafSchemaNode);
+        streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) schemaNode).withValue(value).build());
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingStreamConstants.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingStreamConstants.java
new file mode 100644 (file)
index 0000000..912dac4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.utils.mapping;
+
+/**
+ * Util class for mapping entry stream
+ *
+ */
+public final class RestconfMappingStreamConstants {
+
+    public static final String DESCRIPTION = "DESCRIPTION_PLACEHOLDER";
+    public static final Boolean REPLAY_SUPPORT = Boolean.valueOf(true);
+    public static final String REPLAY_LOG = "";
+    public static final String EVENTS = "";
+
+    private RestconfMappingStreamConstants() {
+        throw new UnsupportedOperationException("Util class");
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java
new file mode 100644 (file)
index 0000000..355b9a3
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.utils.parser;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.restconf.utils.validation.RestconfValidation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Util class for parsing identifier
+ *
+ */
+public final class ParserIdentifier {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ParserIdentifier.class);
+
+    public static InstanceIdentifierContext<?> toInstanceIdentifier(final String identifier) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    /**
+     * Make a {@link QName} from identifier
+     *
+     * @param identifier
+     *            - path parameter
+     * @return {@link QName}
+     */
+    public static QName makeQNameFromIdentifier(final String identifier) {
+        final int mountIndex = identifier.indexOf(RestconfConstants.MOUNT);
+        String moduleNameAndRevision = "";
+        if (mountIndex >= 0) {
+            moduleNameAndRevision = identifier.substring(mountIndex + RestconfConstants.MOUNT.length());
+        } else {
+            moduleNameAndRevision = identifier;
+        }
+
+        final Splitter splitter = Splitter.on("/").omitEmptyStrings();
+        final Iterable<String> split = splitter.split(moduleNameAndRevision);
+        final List<String> pathArgs = Lists.<String> newArrayList(split);
+        if (pathArgs.size() < 2) {
+            LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' " + identifier);
+            throw new RestconfDocumentedException(
+                    "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
+                    ErrorTag.INVALID_VALUE);
+        }
+
+        try {
+            final String moduleName = pathArgs.get(0);
+            final String revision = pathArgs.get(1);
+            final Date moduleRevision = RestconfConstants.REVISION_FORMAT.parse(revision);
+
+            return QName.create(null, moduleRevision, moduleName);
+        } catch (final ParseException e) {
+            LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' " + identifier);
+            throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+    }
+
+    /**
+     * Parsing {@link Module} module by {@link String} module name and
+     * {@link Date} revision and from the parsed module create
+     * {@link SchemaExportContext}
+     *
+     * @param schemaContext
+     *            - {@link SchemaContext}
+     * @param identifier
+     *            - path parameter
+     * @return {@link SchemaExportContext}
+     */
+    public static SchemaExportContext toSchemaExportContextFromIdentifier(final SchemaContext schemaContext,
+            final String identifier) {
+        final Iterable<String> pathComponents = RestconfConstants.SLASH_SPLITTER.split(identifier);
+        final Iterator<String> componentIter = pathComponents.iterator();
+        if (!Iterables.contains(pathComponents, RestconfConstants.MOUNT)) {
+            final String moduleName = RestconfValidation.validateAndGetModulName(componentIter);
+            final Date revision = RestconfValidation.validateAndGetRevision(componentIter);
+            final Module module = schemaContext.findModuleByName(moduleName, revision);
+            return new SchemaExportContext(schemaContext, module);
+        } else {
+            final StringBuilder pathBuilder = new StringBuilder();
+            while (componentIter.hasNext()) {
+                final String current = componentIter.next();
+                if (pathBuilder.length() != 0) {
+                    pathBuilder.append("/");
+                }
+                pathBuilder.append(current);
+                if (RestconfConstants.MOUNT.equals(current)) {
+                    break;
+                }
+            }
+            final InstanceIdentifierContext<?> mountPoint = ParserIdentifier
+                    .toInstanceIdentifier(pathBuilder.toString());
+            final String moduleName = RestconfValidation.validateAndGetModulName(componentIter);
+            final Date revision = RestconfValidation.validateAndGetRevision(componentIter);
+            final Module module = mountPoint.getSchemaContext().findModuleByName(moduleName, revision);
+            return new SchemaExportContext(mountPoint.getSchemaContext(), module);
+        }
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java
new file mode 100644 (file)
index 0000000..fbeb7d9
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.utils.schema.context;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Set;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.restconf.Draft11;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+/**
+ * Util class for finding {@link DataSchemaNode}.
+ *
+ */
+public final class RestconfSchemaUtil {
+
+    private RestconfSchemaUtil() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Get {@link DataSchemaNode} from {@link Module} Restconf module by
+     * {@link String} schema node name.
+     *
+     * @param restconfModule
+     *            - restconf module
+     * @param schemaNodeName
+     *            - schema node name
+     * @return {@link DataSchemaNode}
+     */
+    public static DataSchemaNode getRestconfSchemaNode(final Module restconfModule, final String schemaNodeName) {
+
+        final Set<GroupingDefinition> groupings = restconfModule.getGroupings();
+        final GroupingDefinition restGroup = findSchemaNodeInCollection(groupings,
+                Draft11.RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE);
+        final Collection<DataSchemaNode> childNodes = restGroup.getChildNodes();
+        final DataSchemaNode restCont = childNodes.iterator().next();
+
+        return findSchemaNode(restCont, schemaNodeName);
+    }
+
+    /**
+     * Find specific {@link DataSchemaNode} child in {@link DataNodeContainer}
+     * by {@link String} schema node name.
+     *
+     * @param restCont
+     *            - restconf container
+     * @param schemaNodeName
+     *            - schema node name
+     * @return {@link DataSchemaNode}
+     */
+    private static DataSchemaNode findSchemaNode(final DataSchemaNode restCont, final String schemaNodeName) {
+        switch (schemaNodeName) {
+            //MODULES
+            case Draft11.RestconfModule.MODULE_LIST_SCHEMA_NODE:
+                final DataSchemaNode moduleListSchNode = findSchemaNodeInCollection(
+                        ((DataNodeContainer) findSchemaNode(restCont,
+                                Draft11.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE)).getChildNodes(),
+                        Draft11.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+                Preconditions.checkNotNull(moduleListSchNode);
+                return moduleListSchNode;
+            case Draft11.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE:
+                final DataSchemaNode modulesContSchNode = findSchemaNodeInCollection(((DataNodeContainer) restCont).getChildNodes(),
+                        Draft11.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
+                Preconditions.checkNotNull(modulesContSchNode);
+                return modulesContSchNode;
+
+            //STREAMS
+            case Draft11.MonitoringModule.STREAM_LIST_SCHEMA_NODE:
+                final DataSchemaNode streamListSchNode = findSchemaNodeInCollection(
+                        ((DataNodeContainer) findSchemaNode(restCont,
+                                Draft11.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE)).getChildNodes(),
+                        Draft11.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
+                Preconditions.checkNotNull(streamListSchNode);
+                return streamListSchNode;
+            case Draft11.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE:
+                final DataSchemaNode streamsContSchNode = findSchemaNodeInCollection(
+                        ((DataNodeContainer) restCont).getChildNodes(),
+                        Draft11.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
+                Preconditions.checkNotNull(streamsContSchNode);
+                return streamsContSchNode;
+            default:
+                throw new RestconfDocumentedException("Schema node " + schemaNodeName + " does not exist in module.",
+                        ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
+        }
+    }
+
+    /**
+     * Find child of {@link SchemaNode} in {@link Collection} by {@link String}
+     * schema node name.
+     *
+     * @param <T>
+     *            - child of SchemaNode
+     * @param collection
+     *            - child of node
+     * @param schemaNodeName
+     *            - schema node name
+     * @return {@link SchemaNode}
+     */
+    public static <T extends SchemaNode> T findSchemaNodeInCollection(final Collection<T> collection,
+            final String schemaNodeName) {
+        for (final T child : collection) {
+            if (child.getQName().getLocalName().equals(schemaNodeName)) {
+                return child;
+            }
+        }
+        throw new RestconfDocumentedException("Schema node " + schemaNodeName + " does not exist in module.",
+                ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
+    }
+
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/validation/RestconfValidation.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/validation/RestconfValidation.java
new file mode 100644 (file)
index 0000000..d688a8f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.utils.validation;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Iterator;
+import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+
+/**
+ * Util class for validations
+ *
+ */
+public final class RestconfValidation {
+
+    private RestconfValidation() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Validation and parsing of revision.
+     *
+     * @param revisionDate
+     *            - iterator
+     * @return {@link Date}
+     */
+    public static Date validateAndGetRevision(final Iterator<String> revisionDate) {
+        RestconfValidationUtils.checkDocumentedError(revisionDate.hasNext(), ErrorType.PROTOCOL,
+                ErrorTag.INVALID_VALUE, "Revision date must be supplied.");
+        try {
+            return SimpleDateFormatUtil.getRevisionFormat().parse(revisionDate.next());
+        } catch (final ParseException e) {
+            throw new RestconfDocumentedException("Supplied revision is not in expected date format YYYY-mm-dd", e);
+        }
+    }
+
+    /**
+     * Validation of name.
+     *
+     * @param moduleName
+     *            - iterator
+     * @return {@link String}
+     */
+    public static String validateAndGetModulName(final Iterator<String> moduleName) {
+        RestconfValidationUtils.checkDocumentedError(moduleName.hasNext(), ErrorType.PROTOCOL,
+                ErrorTag.INVALID_VALUE, "Module name must be supplied.");
+        return moduleName.next();
+    }
+
+}