<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>
<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>
<module>netconf</module>
<module>netconf-connector</module>
<module>restconf</module>
+ <module>yanglib</module>
</modules>
</project>
<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>
--- /dev/null
+<?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
--- /dev/null
+<?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>
<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>
--- /dev/null
+<?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
--- /dev/null
+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");
+ }
+
+}
--- /dev/null
+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.";
+ }
+ }
+
+}
+
<module>ietf-netconf-monitoring</module>
<module>ietf-netconf-notifications</module>
<module>ietf-netconf-monitoring-extension</module>
+ <module>ietf-netconf-yang-library</module>
</modules>
</project>
<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>
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;
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;
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;
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;
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);
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;
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;
<?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>
<module>netconf-topology-config</module>
<module>sal-netconf-connector</module>
<module>messagebus-netconf</module>
+ <module>yanglib</module>
<module>models</module>
<module>tools</module>
<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>
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;
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;
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;
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;
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.
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,
final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
listener.initializeRemoteConnection(clientDispatcher, clientConfig);
- return new SalConnectorCloseable(listener, salFacade);
+ return new SalConnectorCloseable(listener, salFacade, registeredYangLibSources);
}
/**
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();
}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
// 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
*/
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;
--- /dev/null
+/*
+ * 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");
+ }
+}
}
- private static class NetconfYangTextSchemaSource extends YangTextSchemaSource {
+ static class NetconfYangTextSchemaSource extends YangTextSchemaSource {
private final RemoteDeviceId id;
private final Optional<String> schemaString;
--- /dev/null
+/*
+ * 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));
+ }
+ }
+}
*/
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;
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;
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;
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;
@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;
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
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;
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);
+ }
+ }
}
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 {
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;
+ }
+ }
}
}
}
--- /dev/null
+/*
+ * 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
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);
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);
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);
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));
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 + "&revision=" + TEST_REVISION));
device.onRemoteSessionUp(sessionCaps, listener);
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;
@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");
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());
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);
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+{
+ "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
--- /dev/null
+<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
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;
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());
}
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);
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);
.setScheme(Realm.AuthScheme.BASIC)
.setPrincipal(params.auth.get(0))
.setPassword(params.auth.get(1))
- .setMethodName("PUT")
+ .setMethodName("POST")
.setUsePreemptiveAuth(true)
.build());
}
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());
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;
}
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) {
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) {
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"));
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) {
}, 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);
}
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) {
/*
- * 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,
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;
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");
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)
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")
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");
.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;
}
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);
}
}
- 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));
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;
}
}
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;
+ }
}
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() {
.setDefault(1)
.dest("thread-amount");
- // TODO add get-config option instead of edit + commit
- // TODO different edit config content
-
return parser;
}
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() {
--- /dev/null
+{
+ "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
--- /dev/null
+<?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
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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 {
+
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+<?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
--- /dev/null
+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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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();
+ }
+
+}
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);
}
@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;
}
}
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
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
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 {
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
@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,
@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,
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;
@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;
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;
}
@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());
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)) {
}
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);
}
}
- 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);
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;
@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;
} 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();
}
- 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)) {
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 {
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();
--- /dev/null
+/*
+ * 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();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
*/
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");
--- /dev/null
+/*
+ * 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.");
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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");
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}