<netconf.version>1.2.0-SNAPSHOT</netconf.version>
<netconf.connector.version>1.5.0-SNAPSHOT</netconf.connector.version>
<yangtools.version>1.1.0-SNAPSHOT</yangtools.version>
-
- <features.file>features.xml</features.file>
- <config.configfile.directory>etc/opendaylight/karaf</config.configfile.directory>
- <config.netconf.client.configfile>01-netconf.xml</config.netconf.client.configfile>
- <config.netconf.topology.configfile>02-netconf-topology.xml</config.netconf.topology.configfile>
- <config.netconf.connector.configfile>99-netconf-connector.xml</config.netconf.connector.configfile>
</properties>
<dependencyManagement>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-topology-config</artifactId>
- <classifier>config</classifier>
- <type>xml</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-topology-singleton</artifactId>
</dependency>
-
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-connector-config</artifactId>
- <version>${netconf.version}</version>
- <type>xml</type>
- <classifier>config</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-config</artifactId>
</dependency>
</dependencies>
<bundle>mvn:org.opendaylight.controller.model/model-inventory/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.netconf/netconf-topology/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.netconf/sal-netconf-connector/{{VERSION}}</bundle>
- <configfile finalname='${config.configfile.directory}/${config.netconf.client.configfile}'>mvn:org.opendaylight.netconf/netconf-config/{{VERSION}}/xml/config</configfile>
+ <bundle>mvn:org.opendaylight.netconf/netconf-config/{{VERSION}}</bundle>
</feature>
<feature name='odl-netconf-connector-ssh' version='${project.version}' description="OpenDaylight :: Netconf Connector :: Netconf Connector + Netconf SSH Server + loopback connection configuration">
<feature version='${project.version}'>odl-netconf-topology</feature>
- <configfile finalname="${config.configfile.directory}/${config.netconf.connector.configfile}">mvn:org.opendaylight.netconf/netconf-connector-config/{{VERSION}}/xml/config</configfile>
+ <bundle>mvn:org.opendaylight.netconf/netconf-connector-config/{{VERSION}}</bundle>
</feature>
<feature name='odl-netconf-topology' version='${project.version}' description="OpenDaylight :: Netconf Topology :: Netconf Connector + Netconf SSH Server + Netconf configuration via config topology datastore">
<feature version='${project.version}'>odl-netconf-ssh</feature>
<feature version='${project.version}'>odl-netconf-connector</feature>
- <configfile finalname='${config.configfile.directory}/${config.netconf.topology.configfile}'>mvn:org.opendaylight.netconf/netconf-topology-config/{{VERSION}}/xml/config</configfile>
+ <bundle>mvn:org.opendaylight.netconf/netconf-topology-config/{{VERSION}}</bundle>
</feature>
<feature name='odl-netconf-clustered-topology' version='${project.version}' description="OpenDaylight :: Clustered Netconf Topology :: Netconf Connector + Netconf SSH Server">
<protocol-framework.version>0.9.0-SNAPSHOT</protocol-framework.version>
<yangtools.version>1.1.0-SNAPSHOT</yangtools.version>
<sshd-core.version>0.14.0</sshd-core.version>
-
- <config.configfile.directory>etc/opendaylight/karaf</config.configfile.directory>
- <config.netconf.client.configfile>01-netconf.xml</config.netconf.client.configfile>
</properties>
<dependencyManagement>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-config</artifactId>
- <type>xml</type>
- <classifier>config</classifier>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>netconf-connector-config</artifactId>
- <type>xml</type>
- <classifier>config</classifier>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<groupId>org.opendaylight.netconf</groupId>
<artifactId>mdsal-netconf-ssh</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>mdsal-netconf-tcp</artifactId>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mdsal-netconf-monitoring</artifactId>
<feature version='${config.version}'>odl-config-netty</feature>
<feature version='${project.version}'>odl-netconf-netty-util</feature>
<bundle>mvn:org.opendaylight.netconf/netconf-client/{{VERSION}}</bundle>
- <configfile finalname='${config.configfile.directory}/${config.netconf.client.configfile}'>mvn:org.opendaylight.netconf/netconf-config/{{VERSION}}/xml/config</configfile>
+ <bundle>mvn:org.opendaylight.netconf/netconf-config/{{VERSION}}</bundle>
</feature>
<feature name='odl-netconf-monitoring' version='${project.version}' description="OpenDaylight :: Netconf :: Monitoring">
<feature version='${project.version}'>odl-netconf-client</feature>
<feature version='${controller.mdsal.version}'>odl-mdsal-broker</feature>
<bundle>mvn:org.opendaylight.netconf/mdsal-netconf-ssh/{{VERSION}}</bundle>
+ <bundle>mvn:org.opendaylight.netconf/mdsal-netconf-tcp/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.netconf/mdsal-netconf-notification/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.netconf/mdsal-netconf-connector/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.netconf/mdsal-netconf-monitoring/{{VERSION}}</bundle>
<!--This is the MD-SAL netconf server implementation blueprint xml file-->
- <reference id="globalBossGroup" interface="io.netty.channel.EventLoopGroup" odl:type="global-boss-group"/>
- <reference id="globalWorkerGroup" interface="io.netty.channel.EventLoopGroup" odl:type="global-worker-group"/>
- <reference id="global-timer" interface="io.netty.util.Timer" odl:type="global-timer"/>
- <reference id="scheduledThreadPool" interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"/>
+ <reference id="globalBossGroup"
+ interface="io.netty.channel.EventLoopGroup"
+ odl:type="global-boss-group"/>
+ <reference id="globalWorkerGroup"
+ interface="io.netty.channel.EventLoopGroup"
+ odl:type="global-worker-group"/>
+ <reference id="global-timer"
+ interface="io.netty.util.Timer"
+ odl:type="global-timer"/>
+ <reference id="scheduledThreadPool"
+ interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
+ odl:type="global-netconf-ssh-scheduled-executor"/>
<cm:property-placeholder persistent-id="org.opendaylight.netconf.impl" update-strategy="none">
<cm:default-properties>
<!--NetconfServerMonitoring -->
<bean id="netconfMonitoringService"
- class="org.opendaylight.netconf.impl.osgi.NetconfMonitoringServiceImpl">
+ class="org.opendaylight.netconf.impl.osgi.NetconfMonitoringServiceImpl"
+ destroy-method="close">
<argument ref="aggregatedNetconfOperationServiceFactory"/>
<argument ref="scheduledThreadPool"/>
<argument value="${monitoring-update-interval}"/>
import java.util.Collections;
import java.util.Set;
-public class MdSalMonitoringMapperFactory implements NetconfOperationServiceFactory, AutoCloseable {
+public class MdsalMonitoringMapperFactory implements NetconfOperationServiceFactory, AutoCloseable {
private final NetconfOperationService operationService;
private final MonitoringToMdsalWriter monitoringToMdsalWriter;
private static final Set<Capability> CAPABILITIES = Collections.emptySet();
- public MdSalMonitoringMapperFactory(final NetconfOperationServiceFactoryListener netconfOperationServiceFactoryListener,
+ public MdsalMonitoringMapperFactory(final NetconfOperationServiceFactoryListener netconfOperationServiceFactoryListener,
final NetconfMonitoringService netconfMonitoringService,
final MonitoringToMdsalWriter monitoringToMdsalWriter) {
</bean>
<bean id="mdsalMonitoringMapperFactory"
- class="org.opendaylight.controller.config.yang.netconf.mdsal.monitoring.MdSalMonitoringMapperFactory"
+ class="org.opendaylight.controller.config.yang.netconf.mdsal.monitoring.MdsalMonitoringMapperFactory"
destroy-method="close">
<argument ref="netconfOperationServiceFactoryListener"/>
<argument ref="netconfMonitoringService"/>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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>bundle-parent</artifactId>
+ <version>1.8.0-SNAPSHOT</version>
+ <relativePath/>
+ </parent>
+
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>mdsal-netconf-tcp</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-tcp</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Embed-Dependency>netconf-tcp</Embed-Dependency>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.3.0"
+ odl:restart-dependents-on-updates="true">
+
+ <reference id="netconfServerDispatcher"
+ interface="org.opendaylight.netconf.api.NetconfServerDispatcher"
+ odl:type="netconf-server-dispatcher"/>
+
+ <!-- NETCONF TCP server for MD-SAL (listening by default on port 2831)-->
+
+ <cm:property-placeholder persistent-id="org.opendaylight.netconf.tcp" update-strategy="none">
+ <cm:default-properties>
+ <cm:property name="bindingAddress" value="0.0.0.0"/>
+ <cm:property name="portNumber" value="2831"/>
+ </cm:default-properties>
+ </cm:property-placeholder>
+
+ <!-- If you need/want to use a TCP NETCONF server to interact with MD-SAL, uncomment bellow bean -->
+
+ <!--
+ <bean id="netconfMdsalTcpServer"
+ class="org.opendaylight.netconf.tcp.NetconfNorthboundTcpServer"
+ destroy-method="close">
+ <argument ref="netconfServerDispatcher"/>
+ <argument value="${bindingAddress}"/>
+ <argument value="${portNumber}"/>
+ </bean>
+ -->
+
+</blueprint>
\ No newline at end of file
* Listens for updates on global schema context, transforms context to ietf-yang-library:modules-state and
* writes this state to operational data store
*/
+// TODO Implement also yang-library-change notfication
public class SchemaServiceToMdsalWriter implements SchemaContextListener, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(SchemaServiceToMdsalWriter.class);
public class SchemaLocationBuilder {
public static Location getDefaultInstance(final String defaultValue) {
- throw new java.lang.UnsupportedOperationException("Not yet implemented");
+ return LocationBuilder.getDefaultInstance(defaultValue);
}
}
<groupId>${project.groupId}</groupId>
<artifactId>netconf-config</artifactId>
<version>${project.version}</version>
- <type>xml</type>
- <classifier>config</classifier>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-connector-config</artifactId>
<version>${project.version}</version>
- <type>xml</type>
- <classifier>config</classifier>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mdsal-netconf-ssh</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>mdsal-netconf-tcp</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-topology</artifactId>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-topology-config</artifactId>
<version>${project.version}</version>
- <classifier>config</classifier>
- <type>xml</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
+++ /dev/null
-/*
- * Copyright (c) 2014 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.config.netconf.client.dispatcher;
-
-import com.google.common.reflect.AbstractInvocationHandler;
-import com.google.common.reflect.Reflection;
-import java.lang.reflect.Method;
-import org.opendaylight.controller.config.api.osgi.WaitingServiceTracker;
-import org.opendaylight.netconf.client.NetconfClientDispatcher;
-import org.osgi.framework.BundleContext;
-
-/**
- * @deprecated Replaced by blueprint wiring
- */
-@Deprecated
-public final class NetconfClientDispatcherModule extends org.opendaylight.controller.config.yang.config.netconf.client.dispatcher.AbstractNetconfClientDispatcherModule {
-
- private BundleContext bundleContext;
-
- public NetconfClientDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
- super(identifier, dependencyResolver);
- }
-
- public NetconfClientDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
- NetconfClientDispatcherModule oldModule, java.lang.AutoCloseable oldInstance) {
-
- super(identifier, dependencyResolver, oldModule, oldInstance);
- }
-
- @Override
- protected void customValidation(){
- }
-
- @Override
- public java.lang.AutoCloseable createInstance() {
- final WaitingServiceTracker<NetconfClientDispatcher> tracker =
- WaitingServiceTracker.create(NetconfClientDispatcher.class, bundleContext, "(type=netconf-client-dispatcher)");
- final NetconfClientDispatcher service = tracker.waitForService(WaitingServiceTracker.FIVE_MINUTES);
-
- return Reflection.newProxy(AutoCloseableNetconfClientDispatcher.class, new AbstractInvocationHandler() {
- @Override
- protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
- if (method.getName().equals("close")) {
- tracker.close();
- return null;
- } else {
- return method.invoke(service, args);
- }
- }
- });
- }
-
- void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- private static interface AutoCloseableNetconfClientDispatcher extends NetconfClientDispatcher, AutoCloseable {
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.config.netconf.client.dispatcher;
-
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.osgi.framework.BundleContext;
-
-/**
- * @deprecated Replaced by blueprint wiring
- */
-@Deprecated
-public class NetconfClientDispatcherModuleFactory extends AbstractNetconfClientDispatcherModuleFactory {
- @Override
- public NetconfClientDispatcherModule instantiateModule(String instanceName, DependencyResolver dependencyResolver,
- NetconfClientDispatcherModule oldModule, AutoCloseable oldInstance, BundleContext bundleContext) {
- NetconfClientDispatcherModule module = super.instantiateModule(instanceName, dependencyResolver, oldModule,
- oldInstance, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
- @Override
- public NetconfClientDispatcherModule instantiateModule(String instanceName, DependencyResolver dependencyResolver,
- BundleContext bundleContext) {
- NetconfClientDispatcherModule module = super.instantiateModule(instanceName, dependencyResolver, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-}
+++ /dev/null
-// vi: set smarttab et sw=4 tabstop=4:
-module odl-netconfig-client-cfg {
-
- yang-version 1;
- namespace "urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher";
- prefix "cfg-net-client";
-
- import config { prefix config; revision-date 2013-04-05; }
- import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
- import netty {prefix netty; }
-
- description
- "This module contains the base YANG definitions for
- netconf-client-dispatcher implementation.
-
- Copyright (c)2013 Cisco Systems, Inc. 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";
-
- revision "2014-04-08" {
- description
- "Initial revision.";
- }
-
- identity netconf-client-dispatcher {
- base config:module-type;
- config:provided-service cfg-net:netconf-client-dispatcher;
- config:java-name-prefix NetconfClientDispatcher;
- }
-
- augment "/config:modules/config:module/config:configuration" {
- case netconf-client-dispatcher {
- when "/config:modules/config:module/config:type = 'netconf-client-dispatcher'";
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (c) 2013 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/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.opendaylight.mdsal</groupId>
- <artifactId>binding-parent</artifactId>
- <version>0.10.0-SNAPSHOT</version>
- <relativePath/>
- </parent>
-
- <!--TODO Delete this bundle as soon as https://git.opendaylight.org/gerrit/#/c/46621/ is merged.-->
-
- <groupId>org.opendaylight.netconf</groupId>
- <artifactId>netconf-config-dispatcher</artifactId>
- <version>1.2.0-SNAPSHOT</version>
- <packaging>bundle</packaging>
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.netconf</groupId>
- <artifactId>netconf-subsystem</artifactId>
- <version>${project.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-api</artifactId>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>netconf-client</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-manager</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-manager</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-util</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netty-threadgroup-config</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netty-timer-config</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>mockito-configuration</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-</project>
+++ /dev/null
-// vi: set smarttab et sw=4 tabstop=4:
-module odl-netconf-cfg {
-
-/**
-* TODO Delete this YANG file as soon as https://git.opendaylight.org/gerrit/#/c/46621/ is merged.
-*/
-
- yang-version 1;
- namespace "urn:opendaylight:params:xml:ns:yang:controller:config:netconf";
- prefix "cfg-net";
-
- import config { prefix config; revision-date 2013-04-05; }
-
- description
- "This module contains the base YANG definitions for
- netconf related services.
-
- Copyright (c)2013 Cisco Systems, Inc. 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";
-
- revision "2014-04-08" {
- description
- "Initial revision.";
- }
-
- identity netconf-client-dispatcher {
- base "config:service-type";
- config:java-class "org.opendaylight.netconf.client.NetconfClientDispatcher";
- }
-}
\ No newline at end of file
+++ /dev/null
-// vi: set smarttab et sw=4 tabstop=4:
-module odl-netconfig-client-cfg {
-
-/**
-* TODO Delete this YANG file as soon as https://git.opendaylight.org/gerrit/#/c/46621/ is merged.
-*/
-
- yang-version 1;
- namespace "urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher";
- prefix "cfg-net-client";
-
- import config { prefix config; revision-date 2013-04-05; }
- import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
- import netty {prefix netty; }
-
- description
- "This module contains the base YANG definitions for
- netconf-client-dispatcher implementation.
-
- Copyright (c)2013 Cisco Systems, Inc. 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";
-
- revision "2014-04-08" {
- description
- "Initial revision.";
- }
-
- identity netconf-client-dispatcher {
- base config:module-type;
- config:provided-service cfg-net:netconf-client-dispatcher;
- config:java-name-prefix NetconfClientDispatcher;
- }
-
- augment "/config:modules/config:module/config:configuration" {
- case netconf-client-dispatcher {
- when "/config:modules/config:module/config:type = 'netconf-client-dispatcher'";
- }
- }
-
-}
\ No newline at end of file
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.odlparent</groupId>
- <artifactId>odlparent</artifactId>
+ <artifactId>bundle-parent</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>netconf-config</artifactId>
<version>1.2.0-SNAPSHOT</version>
<description>Configuration files for netconf</description>
- <packaging>jar</packaging>
+ <packaging>bundle</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-artifacts</artifactId>
+ <version>0.6.0-SNAPSHOT</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-impl</artifactId>
+ </dependency>
+ </dependencies>
<build>
<plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>attach-artifacts</id>
- <goals>
- <goal>attach-artifact</goal>
- </goals>
- <phase>package</phase>
- <configuration>
- <artifacts>
- <artifact>
- <file>${project.build.directory}/classes/initial/01-netconf.xml</file>
- <type>xml</type>
- <classifier>config</classifier>
- </artifact>
- </artifacts>
- </configuration>
- </execution>
- </executions>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Embed-Dependency>threadpool-config-impl</Embed-Dependency>
+ </instructions>
+ </configuration>
</plugin>
</plugins>
</build>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- vi: set et smarttab sw=4 tabstop=4: -->
-<!--
- Copyright (c) 2013 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
--->
-<snapshot>
- <configuration>
- <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-
- <!-- Netconf dispatcher to be used by all netconf-connectors -->
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">prefix:netconf-client-dispatcher</type>
- <name>global-netconf-dispatcher</name>
- </module>
-
- <!-- Thread factory to be used by all threadpools in netconf-connectors -->
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">prefix:threadfactory-naming</type>
- <name>global-netconf-processing-executor-threadfactory</name>
- <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">remote-connector-processing-executor</name-prefix>
- </module>
- <!-- flexible threadpool for all netconf connectors, Max thread count is set to 4. -->
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">prefix:threadpool-flexible</type>
- <name>global-netconf-processing-executor</name>
- <minThreadCount xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">1</minThreadCount>
- <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">4</max-thread-count>
- <keepAliveMillis xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">600000</keepAliveMillis>
-
- <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
- <name>global-netconf-processing-executor-threadfactory</name>
- </threadFactory>
- </module>
-
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">prefix:threadpool-scheduled</type>
- <name>global-netconf-ssh-scheduled-executor</name>
- <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">8</max-thread-count>
-
- <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
- <name>global-netconf-processing-executor-threadfactory</name>
- </threadFactory>
- </module>
- </modules>
-
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <service>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
- <instance>
- <name>global-netconf-dispatcher</name>
- <provider>/modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
- <instance>
- <name>global-netconf-processing-executor-threadfactory</name>
- <provider>/modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
- <instance>
- <name>global-netconf-processing-executor</name>
- <provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
- </instance>
- <instance>
- <name>global-netconf-ssh-scheduled-executor</name>
- <provider>/modules/module[type='threadpool-scheduled'][name='global-netconf-ssh-scheduled-executor']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>
- <instance>
- <name>global-netconf-ssh-scheduled-executor</name>
- <provider>/modules/module[type='threadpool-scheduled'][name='global-netconf-ssh-scheduled-executor']</provider>
- </instance>
- </service>
- </services>
-
- </data>
- </configuration>
- <required-capabilities>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher?module=odl-netconfig-client-cfg&revision=2014-04-08</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup?module=threadgroup&revision=2013-11-07</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible?module=threadpool-impl-flexible&revision=2013-12-01</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&revision=2013-12-01</capability>
- </required-capabilities>
-</snapshot>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.3.0"
+ odl:restart-dependents-on-updates="true">
+
+ <!-- Default configuration for thread pools -->
+
+ <cm:property-placeholder persistent-id="org.opendaylight.netconf.config"
+ update-strategy="none">
+ <cm:default-properties>
+ <cm:property name="name-prefix" value="remote-connector-processing-executor"/>
+ <cm:property name="min-thread-count-flexible-thread-pool" value="1"/>
+ <cm:property name="max-thread-count-flexible-thread-pool" value="4"/>
+ <cm:property name="keep-alive-millis-flexible-thread-pool" value="600000"/>
+ <cm:property name="max-thread-count-scheduled-thread-pool" value="8"/>
+ </cm:default-properties>
+ </cm:property-placeholder>
+
+ <!-- Naming Thread Factory -->
+
+ <bean id="namingThreadPoolFactory"
+ class="org.opendaylight.controller.config.threadpool.util.NamingThreadPoolFactory">
+ <argument value="${name-prefix}"/>
+ </bean>
+
+ <!-- Flexible Thread Pool -->
+
+ <bean id="timeUnitMilli" class="java.util.concurrent.TimeUnit"
+ factory-method="valueOf">
+ <argument value="MILLISECONDS"/>
+ </bean>
+
+ <bean id="flexibleThreadPool"
+ class="org.opendaylight.controller.config.threadpool.util.FlexibleThreadPoolWrapper">
+ <argument value="${min-thread-count-flexible-thread-pool}"/>
+ <argument value="${max-thread-count-flexible-thread-pool}"/>
+ <argument value="${keep-alive-millis-flexible-thread-pool}"/>
+ <argument ref="timeUnitMilli"/>
+ <argument ref="namingThreadPoolFactory"/>
+ </bean>
+ <service ref="flexibleThreadPool"
+ interface="org.opendaylight.controller.config.threadpool.ThreadPool"
+ odl:type="global-netconf-processing-executor"/>
+
+ <!-- Scheduled Thread Pool -->
+
+ <bean id="scheduleThreadPool"
+ class="org.opendaylight.controller.config.threadpool.util.ScheduledThreadPoolWrapper">
+ <argument value="${max-thread-count-scheduled-thread-pool}"/>
+ <argument ref="namingThreadPoolFactory"/>
+ </bean>
+ <service ref="scheduleThreadPool"
+ interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
+ odl:type="global-netconf-ssh-scheduled-executor"/>
+
+</blueprint>
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">
+<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>odlparent-lite</artifactId>
- <version>1.8.0-SNAPSHOT</version>
- <relativePath/>
- </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.odlparent</groupId>
+ <artifactId>bundle-parent</artifactId>
+ <version>1.8.0-SNAPSHOT</version>
+ <relativePath/>
+ </parent>
- <groupId>org.opendaylight.netconf</groupId>
- <artifactId>netconf-connector-config</artifactId>
- <description>Configuration files for netconf-connector</description>
- <version>1.2.0-SNAPSHOT</version>
- <packaging>jar</packaging>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>netconf-connector-config</artifactId>
+ <description>Configuration files for netconf-connector</description>
+ <version>1.2.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
- <build>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>attach-artifacts</id>
- <goals>
- <goal>attach-artifact</goal>
- </goals>
- <phase>package</phase>
- <configuration>
- <artifacts>
- <artifact>
- <file>${project.build.directory}/classes/initial/99-netconf-connector.xml</file>
- <type>xml</type>
- <classifier>config</classifier>
- </artifact>
- </artifacts>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-topology</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Embed-Dependency>netconf-topology</Embed-Dependency>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- vi: set et smarttab sw=4 tabstop=4: -->
-<!--
- Copyright (c) 2013 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
--->
-<snapshot>
- <configuration>
- <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <!-- Loopback connection to netconf server in controller using netconf-connector -->
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">prefix:sal-netconf-connector</type>
- <name>controller-config</name>
- <address xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">127.0.0.1</address>
- <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">1830</port>
- <username xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</username>
- <password xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</password>
- <tcp-only xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">false</tcp-only>
- <reconnect-on-changed-schema xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">true</reconnect-on-changed-schema>
- <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
- <name>global-event-executor</name>
- </event-executor>
- <binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
- <name>binding-osgi-broker</name>
- </binding-registry>
- <dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
- <name>dom-broker</name>
- </dom-registry>
- <client-dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
- <name>global-netconf-dispatcher</name>
- </client-dispatcher>
- <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
- <name>global-netconf-processing-executor</name>
- </processing-executor>
- <keepalive-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>
- <name>global-netconf-ssh-scheduled-executor</name>
- </keepalive-executor>
- </module>
- </modules>
- </data>
- </configuration>
- <required-capabilities>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf?module=odl-sal-netconf-connector-cfg&revision=2015-08-03</capability>
- </required-capabilities>
-</snapshot>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.3.0"
+ odl:use-default-for-reference-types="true">
+
+ <reference id="dataBroker" interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/>
+ <odl:static-reference id="netconfConnectorFactory" interface="org.opendaylight.netconf.topology.api.NetconfConnectorFactory"/>
+
+ <cm:property-placeholder persistent-id="org.opendaylight.netconf.sal.connect"
+ update-strategy="none">
+ <cm:default-properties>
+ <cm:property name="name" value="controller-config"/>
+ <cm:property name="address" value="127.0.0.1"/>
+ <cm:property name="port" value="1830"/>
+ <cm:property name="username" value="admin"/>
+ <cm:property name="password" value="admin"/>
+ <cm:property name="tcp-only" value="false"/>
+ <cm:property name="reconnect-on-schema-change" value="true"/>
+ </cm:default-properties>
+ </cm:property-placeholder>
+
+ <bean id="controllerConfig" factory-ref="netconfConnectorFactory" factory-method="newInstance">
+ <argument ref="dataBroker"/>
+ <argument type="java.lang.String" value="${name}"/>
+ <argument type="java.lang.String" value="${address}"/>
+ <argument type="java.lang.Integer" value="${port}"/>
+ <argument type="java.lang.String" value="${username}"/>
+ <argument type="java.lang.String" value="${password}"/>
+ <argument type="java.lang.Boolean" value="${tcp-only}"/>
+ <argument type="java.lang.Boolean" value="${reconnect-on-schema-change}"/>
+ </bean>
+
+</blueprint>
\ No newline at end of file
<artifactId>xmlunit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
</dependencies>
<build>
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
-import io.netty.util.internal.ConcurrentSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import org.opendaylight.controller.config.util.capability.BasicCapability;
import org.opendaylight.controller.config.util.capability.Capability;
import org.opendaylight.netconf.api.monitoring.CapabilityListener;
-import org.opendaylight.netconf.api.monitoring.NetconfManagementSession;
import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
}
};
- private final Set<NetconfManagementSession> sessions = new ConcurrentSet<>();
private final NetconfOperationServiceFactory netconfOperationProvider;
- private final Map<Uri, Capability> capabilities = new ConcurrentHashMap<>();
- private final Map<String, Map<String, String>> mappedModulesToRevisionToSchema = new ConcurrentHashMap<>();
+ private final Map<Uri, Capability> capabilities = Maps.newHashMap();
+ private final Map<String, Map<String, String>> mappedModulesToRevisionToSchema = Maps.newHashMap();
- private final Set<NetconfMonitoringService.CapabilitiesListener> listeners = new ConcurrentSet<>();
+ private final Set<NetconfMonitoringService.CapabilitiesListener> listeners = Sets.newHashSet();
private volatile BaseNotificationPublisherRegistration notificationPublisher;
NetconfCapabilityMonitoringService(final NetconfOperationServiceFactory netconfOperationProvider) {
@Override
public synchronized void close() throws Exception {
listeners.clear();
- sessions.clear();
capabilities.clear();
}
import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
import org.opendaylight.netconf.notifications.NetconfNotificationCollector;
import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
SessionIdProvider idProvider = new SessionIdProvider();
timer = new HashedWheelTimer();
- long connectionTimeoutMillis = NetconfConfigUtil.extractTimeoutMillis(context);
+
+ long connectionTimeoutMillis = NetconfConfiguration.DEFAULT_TIMEOUT_MILLIS;
final NetconfMonitoringServiceImpl monitoringService = startMonitoringService(context, factoriesListener);
serverNegotiatorFactory);
NetconfServerDispatcherImpl dispatch = new NetconfServerDispatcherImpl(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
- LocalAddress address = NetconfConfigUtil.getNetconfLocalAddress();
+ LocalAddress address = NetconfConfiguration.NETCONF_LOCAL_ADDRESS;
LOG.trace("Starting local netconf server at {}", address);
dispatch.createLocalServer(address);
final Document reply =
XmlUtil.readXmlToDocument("<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
"<rpc-error>\n" +
- "<error-type>PROTOCOL</error-type>\n" +
+ "<error-type>protocol</error-type>\n" +
"<error-tag>unknown-element</error-tag>\n" +
- "<error-severity>ERROR</error-severity>\n" +
+ "<error-severity>error</error-severity>\n" +
"<error-message>Unknown tag bad-rpc in message:\n" +
"<bad-rpc/>\n" +
"</error-message>\n" +
final InetSocketAddress sshSocketAddress = netconfConfiguration.getSshServerAddress();
LOG.info("Starting netconf SSH server at {}", sshSocketAddress);
- final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+ final LocalAddress localAddress = NetconfConfiguration.NETCONF_LOCAL_ADDRESS;
authProviderTracker = new AuthProviderTracker(bundleContext);
final String path = netconfConfiguration.getPrivateKeyPath();
import io.netty.handler.logging.LoggingHandler;
import java.io.BufferedReader;
import java.io.InputStreamReader;
-import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
});
// Start the server.
- LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+ LocalAddress localAddress = NetconfConfiguration.NETCONF_LOCAL_ADDRESS;
ChannelFuture f = b.bind(localAddress).sync();
// Wait until the server socket is closed.
import io.netty.handler.logging.LoggingHandler;
import java.net.InetSocketAddress;
import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
public class ProxyServer implements Runnable {
private final ProxyHandlerFactory proxyHandlerFactory;
final EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
- final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+ final LocalAddress localAddress = NetconfConfiguration.NETCONF_LOCAL_ADDRESS;
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler;
import org.opendaylight.netconf.ssh.SshProxyServer;
import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
-import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 10831);
final SshProxyServer sshProxyServer = new SshProxyServer(minaTimerEx, nettyGroup, nioExec);
sshProxyServer.bind(
- new SshProxyServerConfigurationBuilder().setBindingAddress(addr).setLocalAddress(NetconfConfigUtil.getNetconfLocalAddress()).setAuthenticator(new AuthProvider() {
+ new SshProxyServerConfigurationBuilder().setBindingAddress(addr).setLocalAddress(NetconfConfiguration.NETCONF_LOCAL_ADDRESS).setAuthenticator(new AuthProvider() {
@Override
public boolean authenticated(final String username, final String password) {
return true;
import org.opendaylight.netconf.ssh.SshProxyServer;
import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
final InetSocketAddress addr = InetSocketAddress.createUnresolved(HOST, PORT);
server = new SshProxyServer(minaTimerEx, clientGroup, nioExec);
server.bind(
- new SshProxyServerConfigurationBuilder().setBindingAddress(addr).setLocalAddress(NetconfConfigUtil.getNetconfLocalAddress()).setAuthenticator(new AuthProvider() {
+ new SshProxyServerConfigurationBuilder().setBindingAddress(addr).setLocalAddress(NetconfConfiguration.NETCONF_LOCAL_ADDRESS).setAuthenticator(new AuthProvider() {
@Override
public boolean authenticated(final String username, final String password) {
return true;
+++ /dev/null
-/*
- * Copyright (c) 2014 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.netconf.northbound.tcp;
-
-import io.netty.channel.ChannelFuture;
-import io.netty.util.concurrent.GenericFutureListener;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import org.opendaylight.netconf.api.NetconfServerDispatcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class NetconfNorthboundTcpModule extends org.opendaylight.controller.config.yang.netconf.northbound.tcp.AbstractNetconfNorthboundTcpModule {
-
- private static final Logger LOG = LoggerFactory.getLogger(NetconfNorthboundTcpModule.class);
-
-
- public NetconfNorthboundTcpModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
- super(identifier, dependencyResolver);
- }
-
- public NetconfNorthboundTcpModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.northbound.tcp.NetconfNorthboundTcpModule oldModule, java.lang.AutoCloseable oldInstance) {
- super(identifier, dependencyResolver, oldModule, oldInstance);
- }
-
- @Override
- public void customValidation() {
- // add custom validation form module attributes here.
- }
-
- @Override
- public java.lang.AutoCloseable createInstance() {
- final NetconfServerDispatcher dispatch = getDispatcherDependency();
- final ChannelFuture tcpServer = dispatch.createServer(getInetAddress());
-
- tcpServer.addListener(new GenericFutureListener<ChannelFuture>() {
- @Override
- public void operationComplete(ChannelFuture future) throws Exception {
- if (future.isDone() && future.isSuccess()) {
- LOG.info("Netconf TCP endpoint started successfully at {}", getInetAddress());
- } else {
- LOG.warn("Unable to start TCP netconf server at {}", getInetAddress(), future.cause());
- throw new RuntimeException("Unable to start TCP netconf server", future.cause());
- }
- }
- });
-
- return new NetconfServerCloseable(tcpServer);
- }
-
- private InetSocketAddress getInetAddress() {
- try {
- final InetAddress inetAd = InetAddress.getByName(getBindingAddress().getIpv4Address() == null ? getBindingAddress().getIpv6Address().getValue() : getBindingAddress().getIpv4Address().getValue());
- return new InetSocketAddress(inetAd, getPort().getValue());
- } catch (final UnknownHostException e) {
- throw new IllegalArgumentException("Unable to bind netconf endpoint to address " + getBindingAddress(), e);
- }
- }
-
- private static final class NetconfServerCloseable implements AutoCloseable {
- private final ChannelFuture localServer;
-
- public NetconfServerCloseable(final ChannelFuture localServer) {
- this.localServer = localServer;
- }
-
- @Override
- public void close() throws Exception {
- if(localServer.isDone()) {
- localServer.channel().close();
- } else {
- localServer.cancel(true);
- }
- }
- }
-
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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: netconf-northbound-tcp yang module local name: netconf-northbound-tcp
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Thu Apr 23 16:34:55 CEST 2015
-*
-* Do not modify this file unless it is present under src/main directory
-*/
-package org.opendaylight.controller.config.yang.netconf.northbound.tcp;
-public class NetconfNorthboundTcpModuleFactory extends org.opendaylight.controller.config.yang.netconf.northbound.tcp.AbstractNetconfNorthboundTcpModuleFactory {
-
-}
--- /dev/null
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.tcp;
+
+import io.netty.channel.ChannelFuture;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import org.opendaylight.netconf.api.NetconfServerDispatcher;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Create an MD-SAL NETCONF server using TCP.
+ */
+public class NetconfNorthboundTcpServer implements AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfNorthboundTcpServer.class);
+
+ private final ChannelFuture tcpServer;
+
+ public NetconfNorthboundTcpServer(final NetconfServerDispatcher netconfServerDispatcher,
+ final String address,
+ final String port) {
+ InetSocketAddress inetAddress = getInetAddress(address, port);
+ tcpServer = netconfServerDispatcher.createServer(inetAddress);
+ tcpServer.addListener(future -> {
+ if (future.isDone() && future.isSuccess()) {
+ LOG.info("Netconf TCP endpoint started successfully at {}", inetAddress);
+ } else {
+ LOG.warn("Unable to start TCP netconf server at {}", inetAddress, future.cause());
+ throw new RuntimeException("Unable to start TCP netconf server", future.cause());
+ }
+ });
+ }
+
+ private InetSocketAddress getInetAddress(final String bindingAddress, final String portNumber) {
+ try {
+ IpAddress ipAddress = IpAddressBuilder.getDefaultInstance(bindingAddress);
+ final InetAddress inetAd = InetAddress.getByName(ipAddress.getIpv4Address() == null ?
+ ipAddress.getIpv6Address().getValue() : ipAddress.getIpv4Address().getValue());
+ return new InetSocketAddress(inetAd, Integer.valueOf(portNumber));
+ } catch (final UnknownHostException e) {
+ throw new IllegalArgumentException("Unable to bind netconf tcp endpoint to address " + bindingAddress, e);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (tcpServer.isDone()) {
+ tcpServer.channel().close();
+ } else {
+ tcpServer.cancel(true);
+ }
+ }
+}
import java.net.InetSocketAddress;
import org.opendaylight.netconf.tcp.netty.ProxyServer;
import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
-import org.opendaylight.netconf.util.osgi.NetconfConfigUtil.InfixProp;
import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
final InetSocketAddress address = netconfConfiguration.getTcpServerAddress();
if (address.getAddress().isAnyLocalAddress()) {
- LOG.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. Consider changing {} to 127.0.0.1",
- NetconfConfigUtil.getNetconfServerAddressKey(InfixProp.tcp));
+ LOG.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. " +
+ "Consider changing tcp-address in netconf.cfg to 127.0.0.1");
}
LOG.info("Starting TCP netconf server at {}", address);
- proxyServer = new ProxyServer(address, NetconfConfigUtil.getNetconfLocalAddress());
+ proxyServer = new ProxyServer(address, NetconfConfiguration.NETCONF_LOCAL_ADDRESS);
}
@Override
+++ /dev/null
-module netconf-northbound-tcp {
- yang-version 1;
- namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:tcp";
- prefix "nni";
-
- import netconf-northbound-mapper { prefix nnm; revision-date 2015-01-14; }
- import netconf-northbound { prefix nn; revision-date 2015-01-14; }
- import config { prefix config; revision-date 2013-04-05; }
- import threadpool {prefix th;}
- import netty {prefix netty;}
- import ietf-inet-types { prefix inet; revision-date 2013-07-15; }
-
- organization "Cisco Systems, Inc.";
-
- description
- "This module contains the base YANG definitions for
- a default implementation of netconf northbound tcp server";
-
- revision "2015-04-23" {
- description
- "Initial revision.";
- }
-
- identity netconf-northbound-tcp {
- base config:module-type;
- config:java-name-prefix NetconfNorthboundTcp;
- }
-
- augment "/config:modules/config:module/config:configuration" {
- case netconf-northbound-tcp {
- when "/config:modules/config:module/config:type = 'netconf-northbound-tcp'";
-
- leaf port {
- type inet:port-number;
- default 2831;
- }
-
- leaf binding-address {
- type inet:ip-address;
- default "0.0.0.0";
- }
-
- container dispatcher {
- uses config:service-ref {
- refine type {
- config:required-identity nn:netconf-server-dispatcher;
- }
- }
- }
- }
- }
-
-}
<parent>
<groupId>org.opendaylight.odlparent</groupId>
- <artifactId>odlparent-lite</artifactId>
+ <artifactId>bundle-parent</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>netconf-topology-config</artifactId>
<version>1.2.0-SNAPSHOT</version>
<description>Configuration files for netconf topology</description>
- <packaging>jar</packaging>
+ <packaging>bundle</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-topology</artifactId>
+ </dependency>
+ </dependencies>
<build>
<plugins>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>attach-artifacts</id>
- <goals>
- <goal>attach-artifact</goal>
- </goals>
- <phase>package</phase>
- <configuration>
- <artifacts>
- <artifact>
- <file>${project.build.directory}/classes/initial/02-netconf-topology.xml</file>
- <type>xml</type>
- <classifier>config</classifier>
- </artifact>
- </artifacts>
- </configuration>
- </execution>
- </executions>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Embed-Dependency>netconf-topology</Embed-Dependency>
+ </instructions>
+ </configuration>
</plugin>
</plugins>
</build>
-
</project>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- vi: set et smarttab sw=4 tabstop=4: -->
-<!--
- 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
--->
-<snapshot>
- <configuration>
- <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">prefix:netconf-topology-impl</type>
- <name>default-netconf-topology</name>
- <topology-id xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">topology-netconf</topology-id>
- <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
- <name>global-event-executor</name>
- </event-executor>
- <binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
- <name>binding-osgi-broker</name>
- </binding-registry>
- <dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
- <name>dom-broker</name>
- </dom-registry>
- <client-dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
- <name>global-netconf-dispatcher</name>
- </client-dispatcher>
- <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
- <name>global-netconf-processing-executor</name>
- </processing-executor>
- <keepalive-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>
- <name>global-netconf-ssh-scheduled-executor</name>
- </keepalive-executor>
- <shared-schema-repository xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology:shared:schema:repository">prefix:shared-schema-repository</type>
- <name>default-shared-schema-repository</name>
- </shared-schema-repository>
- </module>
- </modules>
-
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <service>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">prefix:netconf-topology</type>
- <instance>
- <name>default-netconf-topology</name>
- <provider>/modules/module[type='netconf-topology-impl'][name='default-netconf-topology']</provider>
- </instance>
- </service>
- </services>
- </data>
- </configuration>
- <required-capabilities>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:topology?module=netconf-topology&revision=2015-07-27</capability>
- </required-capabilities>
-</snapshot>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <reference id="clientDispatcherDependency"
+ interface="org.opendaylight.netconf.client.NetconfClientDispatcher"
+ odl:type="netconf-client-dispatcher"/>
+ <reference id="bindingAwareBroker"
+ interface="org.opendaylight.controller.sal.binding.api.BindingAwareBroker"/>
+ <reference id="keepAliveExecutor"
+ interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
+ odl:type="global-netconf-ssh-scheduled-executor"/>
+ <reference id="processingExecutor"
+ interface="org.opendaylight.controller.config.threadpool.ThreadPool"
+ odl:type="global-netconf-processing-executor"/>
+ <reference id="domBroker"
+ interface="org.opendaylight.controller.sal.core.api.Broker"/>
+ <reference id="eventExecutor"
+ interface="io.netty.util.concurrent.EventExecutor"
+ odl:type="global-event-executor"/>
+ <reference id="dataBroker"
+ interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/>
+ <reference id="domMountPointService"
+ interface="org.opendaylight.controller.md.sal.dom.api.DOMMountPointService"/>
+
+ <bean id="schemaRepository" class="org.opendaylight.netconf.topology.impl.SchemaRepositoryProviderImpl">
+ <argument value="shared-schema-repository-impl"/>
+ </bean>
+
+ <bean id="netconfTopology" class="org.opendaylight.netconf.topology.impl.NetconfTopologyImpl"
+ init-method="init"
+ destroy-method="close">
+ <argument value="topology-netconf"/>
+ <argument ref="clientDispatcherDependency"/>
+ <argument ref="bindingAwareBroker"/>
+ <argument ref="domBroker"/>
+ <argument ref="eventExecutor"/>
+ <argument ref="keepAliveExecutor"/>
+ <argument ref="processingExecutor"/>
+ <argument ref="schemaRepository"/>
+ <argument ref="dataBroker"/>
+ <argument ref="domMountPointService"/>
+ </bean>
+
+ <bean id="netconfConnectorFactory" class="org.opendaylight.netconf.topology.impl.NetconfConnectorFactoryImpl"/>
+ <service ref="netconfConnectorFactory" interface="org.opendaylight.netconf.topology.api.NetconfConnectorFactory"
+ odl:type="default"/>
+
+</blueprint>
\ No newline at end of file
<reference id="bindingAwareBroker"
interface="org.opendaylight.controller.sal.binding.api.BindingAwareBroker"/>
<reference id="keepAliveExecutor"
- interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"/>
+ interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
+ odl:type="global-netconf-ssh-scheduled-executor"/>
<reference id="processingExecutor"
- interface="org.opendaylight.controller.config.threadpool.ThreadPool"/>
+ interface="org.opendaylight.controller.config.threadpool.ThreadPool"
+ odl:type="global-netconf-processing-executor"/>
<reference id="domBroker"
interface="org.opendaylight.controller.sal.core.api.Broker"/>
<reference id="actorSystemProvider"
interface="io.netty.util.concurrent.EventExecutor"
odl:type="global-event-executor"/>
<reference id="clientDispatcherDependency"
- interface="org.opendaylight.netconf.client.NetconfClientDispatcher"/>
+ interface="org.opendaylight.netconf.client.NetconfClientDispatcher"
+ odl:type="netconf-client-dispatcher"/>
<bean id="netconfTopologyManager"
class="org.opendaylight.netconf.topology.singleton.impl.NetconfTopologyManager"
<artifactId>yang-model-api</artifactId>
</dependency>
</dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <version>1.8</version>
- <executions>
- <execution>
- <id>add-source</id>
- <phase>generate-sources</phase>
- <goals>
- <goal>add-source</goal>
- </goals>
- <configuration>
- <sources>
- <source>${project.build.directory}/generated-sources/config</source>;
- </sources>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-
</project>
+++ /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.netconf.topology;
-
-import org.opendaylight.netconf.topology.impl.NetconfTopologyImpl;
-
-public class NetconfTopologyModule extends org.opendaylight.controller.config.yang.netconf.topology.AbstractNetconfTopologyModule {
- public NetconfTopologyModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
- super(identifier, dependencyResolver);
- }
-
- public NetconfTopologyModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.topology.NetconfTopologyModule oldModule, AutoCloseable oldInstance) {
- super(identifier, dependencyResolver, oldModule, oldInstance);
- }
-
- @Override
- public void customValidation() {
- // add custom validation form module attributes here.
- this.getClientDispatcherDependency();
- }
-
- @Override
- public AutoCloseable createInstance() {
- return new NetconfTopologyImpl(getTopologyId(), getClientDispatcherDependency(), getBindingRegistryDependency(),
- getDomRegistryDependency(), getEventExecutorDependency(), getKeepaliveExecutorDependency(),
- getProcessingExecutorDependency(), getSharedSchemaRepositoryDependency());
- }
-
-}
+++ /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: netconf-topology yang module local name: netconf-topology-impl
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Tue Jul 28 15:33:44 CEST 2015
-*
-* Do not modify this file unless it is present under src/main directory
-*/
-package org.opendaylight.controller.config.yang.netconf.topology;
-
-public class NetconfTopologyModuleFactory extends org.opendaylight.controller.config.yang.netconf.topology.AbstractNetconfTopologyModuleFactory {
-
-}
+++ /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.netconf.topology.shared.schema.repository;
-
-import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
-import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
-
-public class SchemaRepositoryImplModule extends org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.AbstractSchemaRepositoryImplModule {
- public SchemaRepositoryImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
- super(identifier, dependencyResolver);
- }
-
- public SchemaRepositoryImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.SchemaRepositoryImplModule oldModule, java.lang.AutoCloseable oldInstance) {
- super(identifier, dependencyResolver, oldModule, oldInstance);
- }
-
- @Override
- public void customValidation() {
- // add custom validation form module attributes here.
- }
-
- @Override
- public java.lang.AutoCloseable createInstance() {
- return new SchemaRepositoryProviderAutoCloseAble(this);
- }
-
- private static class SchemaRepositoryProviderAutoCloseAble implements SchemaRepositoryProvider, AutoCloseable {
-
- private final SharedSchemaRepository schemaRepository;
-
- public SchemaRepositoryProviderAutoCloseAble(SchemaRepositoryImplModule module) {
- schemaRepository = new SharedSchemaRepository(module.getIdentifier().getInstanceName());
- }
-
- @Override
- public void close() throws Exception {
- //NOOP
- }
-
- @Override
- public SharedSchemaRepository getSharedSchemaRepository() {
- return schemaRepository;
- }
- }
-}
+++ /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: shared-schema-repository yang module local name: shared-schema-repository-impl
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Tue Sep 08 13:43:39 CEST 2015
-*
-* Do not modify this file unless it is present under src/main directory
-*/
-package org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository;
-
-import com.google.common.collect.Sets;
-import java.util.Set;
-import org.opendaylight.controller.config.api.DependencyResolverFactory;
-import org.opendaylight.controller.config.api.ModuleIdentifier;
-import org.osgi.framework.BundleContext;
-
-public class SchemaRepositoryImplModuleFactory extends org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.AbstractSchemaRepositoryImplModuleFactory {
-
- public static final ModuleIdentifier defaultInstanceId = new ModuleIdentifier(NAME, "default-shared-schema-repository");
-
- @Override
- public Set<SchemaRepositoryImplModule> getDefaultModules(DependencyResolverFactory dependencyResolverFactory, BundleContext bundleContext) {
- SchemaRepositoryImplModule defaultModule = new SchemaRepositoryImplModule(
- defaultInstanceId, dependencyResolverFactory.createDependencyResolver(defaultInstanceId));
- return Sets.newHashSet(defaultModule);
- }
-}
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 java.util.List;
import java.util.Map;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.opendaylight.controller.sal.core.api.Broker;
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
-import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.netconf.api.NetconfMessage;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
import org.opendaylight.netconf.client.NetconfClientSessionListener;
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.api.NetconfTopology;
+import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
import org.opendaylight.protocol.framework.ReconnectStrategy;
import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
import org.opendaylight.protocol.framework.TimedReconnectStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public abstract class AbstractNetconfTopology implements NetconfTopology, BindingAwareProvider, Provider {
+public abstract class AbstractNetconfTopology implements NetconfTopology {
private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
protected final ScheduledThreadPool keepaliveExecutor;
protected final ThreadPool processingExecutor;
protected final SharedSchemaRepository sharedSchemaRepository;
+ protected final DataBroker dataBroker;
+ protected final DOMMountPointService mountPointService;
protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
- protected DOMMountPointService mountPointService = null;
- protected DataBroker dataBroker = null;
protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
final BindingAwareBroker bindingAwareBroker, final Broker domBroker,
final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
- final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider) {
+ final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider,
+ final DataBroker dataBroker, final DOMMountPointService mountPointService) {
this.topologyId = topologyId;
this.clientDispatcher = clientDispatcher;
this.bindingAwareBroker = bindingAwareBroker;
this.keepaliveExecutor = keepaliveExecutor;
this.processingExecutor = processingExecutor;
this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
- }
-
- protected void registerToSal(BindingAwareProvider baProvider, Provider provider) {
- domBroker.registerProvider(provider);
- bindingAwareBroker.registerProvider(baProvider);
+ this.dataBroker = dataBroker;
+ this.mountPointService = mountPointService;
}
public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
this.schemaContextFactory = schemaContextFactory;
}
- @Override
- public abstract void onSessionInitiated(ProviderContext session);
-
@Override
public ListenableFuture<NetconfDeviceCapabilities> connectNode(NodeId nodeId, Node configNode) {
LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker);
- @Override
- public void onSessionInitiated(ProviderSession session) {
- mountPointService = session.getService(DOMMountPointService.class);
- }
-
- @Override
- public Collection<ProviderFunctionality> getProviderFunctionality() {
- return Collections.emptySet();
- }
-
private InetSocketAddress getSocketAddress(final Host host, int port) {
if(host.getDomainName() != null) {
return new InetSocketAddress(host.getDomainName().getValue(), port);
--- /dev/null
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.topology.api;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+
+/**
+ * Created by adetalhouet on 2016-11-03.
+ */
+public interface NetconfConnectorFactory {
+
+ /**
+ * Create a new netconf connector with default values.
+ * <p>
+ * This method will create a {@link Node} and a {@link NetconfNode} that will be added as an augmentation to the
+ * {@link Node}. Afterward, that {@link Node} will be written in the MDSAL datastore under the {@link NetconfTopology}.
+ * Listeners of that subtree located within network-topology bundle will setup the session.
+ *
+ * @param dataBroker Instance of the {@link DataBroker}
+ * @param instanceName The name of the node
+ * @param address The address
+ * @param port The port
+ * @param username The username of the netconf session
+ * @param password The password of the netconf session
+ * @param tcpOnly Whether to create a TCP or SSH session
+ * @param reconnectOnSchemaChange Whether to enable ietf-netconf-monitoring and register the NETCONF stream.
+ * @return The created {@link Node}
+ */
+ Node newInstance(final DataBroker dataBroker,
+ final String instanceName,
+ final String address,
+ final Integer port,
+ final String username,
+ final String password,
+ final Boolean tcpOnly,
+ final Boolean reconnectOnSchemaChange);
+}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.netconf.topology;
+package org.opendaylight.netconf.topology.api;
import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.netconf.topology;
+package org.opendaylight.netconf.topology.api;
import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
--- /dev/null
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.topology.impl;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+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.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.netconf.topology.api.NetconfConnectorFactory;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.HostBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by adetalhouet on 2016-11-03.
+ */
+public class NetconfConnectorFactoryImpl implements NetconfConnectorFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfConnectorFactoryImpl.class);
+
+ private static final InstanceIdentifier<Topology> TOPOLOGY_PATH = InstanceIdentifier.create(NetworkTopology.class)
+ .child(Topology.class, new TopologyKey(new TopologyId("topology-netconf")));
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Node newInstance(final DataBroker dataBroker,
+ final String instanceName,
+ final String address,
+ final Integer port,
+ final String username,
+ final String password,
+ final Boolean tcpOnly,
+ final Boolean reconnectOnSchemaChange) {
+
+ final NodeId nodeId = new NodeId(instanceName);
+ final NodeKey nodeKey = new NodeKey(nodeId);
+ final Credentials credentials = new LoginPasswordBuilder()
+ .setUsername(username)
+ .setPassword(password)
+ .build();
+ final Host host = HostBuilder.getDefaultInstance(address);
+ final PortNumber portNumber = new PortNumber(port);
+ final NetconfNode netconfNode = new NetconfNodeBuilder()
+ .setHost(host)
+ .setPort(portNumber)
+ .setCredentials(credentials)
+ .setTcpOnly(tcpOnly)
+ .setReconnectOnChangedSchema(reconnectOnSchemaChange)
+ .build();
+ final Node node = new NodeBuilder()
+ .setNodeId(nodeId)
+ .setKey(nodeKey)
+ .addAugmentation(NetconfNode.class, netconfNode)
+ .build();
+
+ final InstanceIdentifier<Node> nodePath = TOPOLOGY_PATH.child(Node.class, nodeKey);
+ final WriteTransaction transaction = dataBroker.newWriteOnlyTransaction();
+ transaction.put(LogicalDatastoreType.CONFIGURATION, nodePath, node);
+ final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = transaction.submit();
+ Futures.addCallback(submitFuture, new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(@Nullable final Void result) {
+ LOG.debug("Node {} was successfully added to the topology", instanceName);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.error("Node {} creation failed: {}", instanceName, t);
+ }
+ });
+ return node;
+ }
+}
\ No newline at end of file
import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
import org.opendaylight.netconf.topology.AbstractNetconfTopology;
-import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
+import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyImpl.class);
- private ListenerRegistration<NetconfTopologyImpl> datastoreListenerRegistration = null;
+ private ListenerRegistration<NetconfTopologyImpl> datastoreListenerRegistration = null;
public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
final BindingAwareBroker bindingAwareBroker, final Broker domBroker,
final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
- final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider) {
+ final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider,
+ final DataBroker dataBroker, final DOMMountPointService mountPointService) {
super(topologyId, clientDispatcher,
bindingAwareBroker, domBroker, eventExecutor,
- keepaliveExecutor, processingExecutor, schemaRepositoryProvider);
- registerToSal(this, this);
+ keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService);
}
@Override
return new NetconfDeviceSalFacade(id, domBroker, bindingAwareBroker);
}
- @Override
- public void onSessionInitiated(ProviderContext session) {
- dataBroker = session.getSALService(DataBroker.class);
-
+ /**
+ * Invoke by blueprint
+ */
+ public void init() {
final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
initTopology(wtx, LogicalDatastoreType.CONFIGURATION);
initTopology(wtx, LogicalDatastoreType.OPERATIONAL);
--- /dev/null
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.topology.impl;
+
+import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+
+public class SchemaRepositoryProviderImpl implements SchemaRepositoryProvider {
+
+ private final SharedSchemaRepository schemaRepository;
+
+ public SchemaRepositoryProviderImpl(final String moduleName) {
+ schemaRepository = new SharedSchemaRepository(moduleName);
+ }
+
+ @Override
+ public SharedSchemaRepository getSharedSchemaRepository() {
+ return schemaRepository;
+ }
+}
\ No newline at end of file
+++ /dev/null
-module netconf-topology {
-
- yang-version 1;
- namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:topology";
- prefix "nt";
-
- import config { prefix config; revision-date 2013-04-05; }
- import threadpool {prefix th;}
- import netty {prefix netty;}
- import opendaylight-md-sal-dom {prefix dom;}
- import opendaylight-md-sal-binding {prefix md-sal-binding; revision-date 2013-10-28;}
- import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
- import shared-schema-repository { prefix sh; revision-date 2015-07-27; }
-
- description
- "Module definition for Netconf topolgy. Netconf topology provides a set of common configuration ";
-
- revision "2015-07-27" {
- description
- "Initial revision";
- }
-
- identity netconf-topology {
- base config:service-type;
- config:java-class "org.opendaylight.netconf.topology.NetconfTopology";
- }
-
- identity netconf-topology-impl {
- base config:module-type;
- config:java-name-prefix NetconfTopology;
- config:provided-service netconf-topology;
- }
-
- augment "/config:modules/config:module/config:configuration" {
- case netconf-topology-impl {
- when "/config:modules/config:module/config:type = 'netconf-topology-impl'";
-
- leaf topology-id {
- mandatory true;
- type string;
- }
-
- container dom-registry {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity dom:dom-broker-osgi-registry;
- }
- }
- }
-
- container binding-registry {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity md-sal-binding:binding-broker-osgi-registry;
- }
- }
- }
-
- container event-executor {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity netty:netty-event-executor;
- }
- }
- }
-
- container processing-executor {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity th:threadpool;
- }
- }
-
- description "Makes up for flaws in netty threading design";
- }
-
- container client-dispatcher {
- uses config:service-ref {
- refine type {
- mandatory false;
- config:required-identity cfg-net:netconf-client-dispatcher;
- }
- }
- }
-
- container keepalive-executor {
- uses config:service-ref {
- refine type {
- mandatory false;
- config:required-identity th:scheduled-threadpool;
- }
- }
-
- description "Dedicated solely to keepalive execution";
- }
-
- container shared-schema-repository {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity sh:shared-schema-repository;
- }
- }
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-module shared-schema-repository {
-
- yang-version 1;
- namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:topology:shared:schema:repository";
- prefix "ssr";
-
- import config { prefix config; revision-date 2013-04-05; }
-
- description
- "Module definition for Shared schema repository.";
-
- revision "2015-07-27" {
- description
- "Initial revision";
- }
-
- identity shared-schema-repository {
- base "config:service-type";
- config:java-class "org.opendaylight.netconf.topology.SchemaRepositoryProvider";
- }
-
- identity shared-schema-repository-impl {
- base "config:module-type";
- config:provided-service shared-schema-repository;
- config:java-name-prefix SchemaRepositoryImpl;
- }
-
- augment "/config:modules/config:module/config:configuration" {
- case shared-schema-repository-impl {
- when "/config:modules/config:module/config:type = 'shared-schema-repository-impl'";
-
- container schema-repository-fallbacks {
- list schema-repository-fallback {
- leaf repository-url {
- type string;
- }
- }
- }
- }
- }
-}
\ No newline at end of file
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
-import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
+import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
@Mock
private SchemaRepositoryProvider mockedSchemaRepositoryProvider;
+ @Mock
+ private DataBroker dataBroker;
+
+ @Mock
+ private DOMMountPointService domMountPointService;
private TestingNetconfTopologyImpl topology;
private TestingNetconfTopologyImpl spyTopology;
when(mockedClientDispatcher.createReconnectingClient(any(NetconfReconnectingClientConfiguration.class))).thenReturn(future);
topology = new TestingNetconfTopologyImpl(TOPOLOGY_ID, mockedClientDispatcher, mockedBindingAwareBroker,
- mockedDataBroker, mockedEventExecutor, mockedKeepaliveExecutor, mockedProcessingExecutor, mockedSchemaRepositoryProvider);
+ mockedDataBroker, mockedEventExecutor, mockedKeepaliveExecutor, mockedProcessingExecutor, mockedSchemaRepositoryProvider,
+ dataBroker, domMountPointService);
spyTopology = spy(topology);
}
@Test
- public void testOnSessionInitiated() {
- BindingAwareBroker.ProviderContext session = mock(BindingAwareBroker.ProviderContext.class);
- DataBroker dataBroker = mock(DataBroker.class);
- when(session.getSALService(DataBroker.class)).thenReturn(dataBroker);
+ public void testInit() {
WriteTransaction wtx = mock(WriteTransaction.class);
when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx);
doNothing().when(wtx).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
when(wtx.submit()).thenReturn(Futures.<Void, TransactionCommitFailedException>immediateCheckedFuture(null));
- topology.onSessionInitiated(session);
+ topology.init();
//verify initialization of topology
final InstanceIdentifier<NetworkTopology> networkTopologyId = InstanceIdentifier.builder(NetworkTopology.class).build();
public static class TestingNetconfTopologyImpl extends NetconfTopologyImpl {
- public TestingNetconfTopologyImpl(String topologyId, NetconfClientDispatcher clientDispatcher, BindingAwareBroker bindingAwareBroker, Broker domBroker, EventExecutor eventExecutor, ScheduledThreadPool keepaliveExecutor, ThreadPool processingExecutor, SchemaRepositoryProvider schemaRepositoryProvider) {
- super(topologyId, clientDispatcher, bindingAwareBroker, domBroker, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider);
+ public TestingNetconfTopologyImpl(String topologyId, NetconfClientDispatcher clientDispatcher,
+ BindingAwareBroker bindingAwareBroker, Broker domBroker,
+ EventExecutor eventExecutor, ScheduledThreadPool keepaliveExecutor,
+ ThreadPool processingExecutor, SchemaRepositoryProvider schemaRepositoryProvider,
+ DataBroker dataBroker, DOMMountPointService domMountPointService) {
+ super(topologyId, clientDispatcher, bindingAwareBroker, domBroker, eventExecutor, keepaliveExecutor,
+ processingExecutor, schemaRepositoryProvider, dataBroker, domMountPointService);
}
@Override
package org.opendaylight.netconf.util.osgi;
-import com.google.common.base.Optional;
-import io.netty.channel.local.LocalAddress;
-import java.net.InetSocketAddress;
import java.util.Collection;
-import java.util.concurrent.TimeUnit;
+import java.util.Optional;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
public final class NetconfConfigUtil {
private static final Logger LOG = LoggerFactory.getLogger(NetconfConfigUtil.class);
- private static final String PREFIX_PROP = "netconf.";
-
private NetconfConfigUtil() {
}
- public enum InfixProp {
- tcp, ssh
- }
-
- private static final String PORT_SUFFIX_PROP = ".port";
- private static final String ADDRESS_SUFFIX_PROP = ".address";
- private static final String PRIVATE_KEY_PATH_PROP = ".pk.path";
-
- private static final String CONNECTION_TIMEOUT_MILLIS_PROP = "connectionTimeoutMillis";
- private static final String LOCAL_HOST = "127.0.0.1";
- private static final String INADDR_ANY = "0.0.0.0";
- public static final long DEFAULT_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(30);
- private static final LocalAddress NETCONF_LOCAL_ADDRESS = new LocalAddress("netconf");
- public static final String DEFAULT_PRIVATE_KEY_PATH = "./configuration/RSA.pk";
- public static final InetSocketAddress DEFAULT_TCP_SERVER_ADRESS = new InetSocketAddress(LOCAL_HOST, 8383);
- public static final InetSocketAddress DEFAULT_SSH_SERVER_ADRESS = new InetSocketAddress(INADDR_ANY, 1830);
-
- public static LocalAddress getNetconfLocalAddress() {
- return NETCONF_LOCAL_ADDRESS;
- }
-
- public static long extractTimeoutMillis(final BundleContext bundleContext) {
- final String key = PREFIX_PROP + CONNECTION_TIMEOUT_MILLIS_PROP;
- final String timeoutString = bundleContext.getProperty(key);
- if (timeoutString == null || timeoutString.length() == 0) {
- return DEFAULT_TIMEOUT_MILLIS;
- }
- try {
- return Long.parseLong(timeoutString);
- } catch (final NumberFormatException e) {
- LOG.warn("Cannot parse {} property: {}, using defaults", key, timeoutString, e);
- return DEFAULT_TIMEOUT_MILLIS;
- }
- }
-
- /**
- * @param context from which properties are being read.
- * @return value of private key path if value is present, Optional.absent otherwise
- */
- public static Optional<String> getPrivateKeyPath(final BundleContext context) {
- return getProperty(context, getPrivateKeyKey());
- }
-
- public static String getPrivateKeyKey() {
- return PREFIX_PROP + InfixProp.ssh + PRIVATE_KEY_PATH_PROP;
- }
-
- public static String getNetconfServerAddressKey(final InfixProp infixProp) {
- return PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP;
- }
-
- /**
- * @param context from which properties are being read.
- * @param infixProp either tcp or ssh
- * @return value if address and port are present and valid, Optional.absent otherwise.
- */
- public static Optional<InetSocketAddress> extractNetconfServerAddress(final BundleContext context,
- final InfixProp infixProp) {
-
- final Optional<String> address = getProperty(context, getNetconfServerAddressKey(infixProp));
- final Optional<String> port = getProperty(context, PREFIX_PROP + infixProp + PORT_SUFFIX_PROP);
-
- if (address.isPresent() && port.isPresent()) {
- try {
- return Optional.of(parseAddress(address, port));
- } catch (final IllegalArgumentException | SecurityException e) {
- LOG.warn("Unable to parse {} netconf address from {}:{}, fallback to default",
- infixProp, address, port, e);
- }
- }
- return Optional.absent();
- }
-
- private static InetSocketAddress parseAddress(final Optional<String> address, final Optional<String> port) {
- final int portNumber = Integer.valueOf(port.get());
- return new InetSocketAddress(address.get(), portNumber);
- }
-
- private static Optional<String> getProperty(final BundleContext context, final String propKey) {
- String value = context.getProperty(propKey);
- if (value != null && value.isEmpty()) {
- value = null;
- }
- return Optional.fromNullable(value);
- }
-
- public static java.util.Optional<NetconfConfiguration> getNetconfConfigurationService(BundleContext bundleContext) {
+ public static Optional<NetconfConfiguration> getNetconfConfigurationService(BundleContext bundleContext) {
final Collection<ServiceReference<ManagedService>> serviceReferences;
try {
serviceReferences = bundleContext.getServiceReferences(ManagedService.class, null);
for (final ServiceReference<ManagedService> serviceReference : serviceReferences) {
ManagedService service = bundleContext.getService(serviceReference);
if (service instanceof NetconfConfiguration){
- return java.util.Optional.of((NetconfConfiguration) service);
+ return Optional.of((NetconfConfiguration) service);
}
}
} catch (InvalidSyntaxException e) {
LOG.error("Unable to retrieve references for ManagedService: {}", e);
}
- LOG.error("Unable to retrieve NetconfConfiguration service. Not found. Bundle netconf-util probably failed.");
- return java.util.Optional.empty();
+ LOG.error("Unable to retrieve NetconfConfiguration service. Not found. Bundle netconf-util probably failed to load.");
+ return Optional.empty();
}
}
package org.opendaylight.netconf.util.osgi;
+import io.netty.channel.local.LocalAddress;
import java.net.InetSocketAddress;
import java.util.Dictionary;
+import java.util.concurrent.TimeUnit;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NetconfConfiguration implements ManagedService {
private static final Logger LOG = LoggerFactory.getLogger(NetconfConfiguration.class);
- private static final NetconfConfiguration instance = new NetconfConfiguration();
- private NetconfConfigurationHolder netconfConfiguration;
+ /**
+ * Props to access information within the dictionary.
+ */
+
+ private static final String SSH_ADDRESS_PROP = "ssh-address";
+ private static final String SSH_PORT_PROP = "ssh-port";
+ private static final String TCP_ADDRESS_PROP = "tcp-address";
+ private static final String TCP_PORT_PROP = "tcp-port";
+ private static final String SSH_PK_PATH_PROP = "ssh-pk-path";
+
+ /**
+ * Default values used if no dictionary is provided.
+ */
+
+ public static final LocalAddress NETCONF_LOCAL_ADDRESS = new LocalAddress("netconf");
+ public static final long DEFAULT_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(30);
- public static final String KEY_SSH_ADDRESS = "ssh-address";
- public static final String KEY_SSH_PORT = "ssh-port";
- public static final String KEY_TCP_ADDRESS = "tcp-address";
- public static final String KEY_TCP_PORT = "tcp-port";
- public static final String KEY_SSH_PK_PATH = "ssh-pk-path";
+ private static final String LOCAL_HOST = "127.0.0.1";
+ private static final String INADDR_ANY = "0.0.0.0";
+ private static final String DEFAULT_PRIVATE_KEY_PATH = "./configuration/RSA.pk";
+ private static final InetSocketAddress DEFAULT_TCP_SERVER_ADRESS = new InetSocketAddress(LOCAL_HOST, 8383);
+ private static final InetSocketAddress DEFAULT_SSH_SERVER_ADRESS = new InetSocketAddress(INADDR_ANY, 1830);
+
+ /**
+ * Singleton instance of this class, registered as a service in the OSGi registry.
+ */
+ private static final NetconfConfiguration INSTANCE = new NetconfConfiguration();
+
+ private NetconfConfigurationHolder netconfConfiguration;
public static NetconfConfiguration getInstance() {
- return instance;
+ return INSTANCE;
}
private NetconfConfiguration() {
- netconfConfiguration = new NetconfConfigurationHolder(NetconfConfigUtil.DEFAULT_TCP_SERVER_ADRESS,
- NetconfConfigUtil.DEFAULT_SSH_SERVER_ADRESS, NetconfConfigUtil.DEFAULT_PRIVATE_KEY_PATH);
+ netconfConfiguration = new NetconfConfigurationHolder(DEFAULT_TCP_SERVER_ADRESS,
+ DEFAULT_SSH_SERVER_ADRESS, DEFAULT_PRIVATE_KEY_PATH);
}
@Override
public void updated(final Dictionary<String, ?> dictionaryConfig) {
if (dictionaryConfig == null) {
- LOG.warn("Netconf configuration cannot be updated.");
+ LOG.trace("CSS netconf server configuration cannot be updated as passed dictionary is null");
return;
}
- final InetSocketAddress sshServerAddress = new InetSocketAddress((String) dictionaryConfig.get(KEY_SSH_ADDRESS),
- Integer.parseInt((String) dictionaryConfig.get(KEY_SSH_PORT)));
- final InetSocketAddress tcpServerAddress = new InetSocketAddress((String) dictionaryConfig.get(KEY_TCP_ADDRESS),
- Integer.parseInt((String) dictionaryConfig.get(KEY_TCP_PORT)));
+ final InetSocketAddress sshServerAddress = new InetSocketAddress((String) dictionaryConfig.get(SSH_ADDRESS_PROP),
+ Integer.parseInt((String) dictionaryConfig.get(SSH_PORT_PROP)));
+ final InetSocketAddress tcpServerAddress = new InetSocketAddress((String) dictionaryConfig.get(TCP_ADDRESS_PROP),
+ Integer.parseInt((String) dictionaryConfig.get(TCP_PORT_PROP)));
- netconfConfiguration = new NetconfConfigurationHolder(tcpServerAddress, sshServerAddress,
- (String) dictionaryConfig.get(KEY_SSH_PK_PATH));
+ netconfConfiguration = new NetconfConfigurationHolder(tcpServerAddress,
+ sshServerAddress,
+ (String) dictionaryConfig.get(SSH_PK_PATH_PROP));
- LOG.info("Netconf configuration was updated: {}", dictionaryConfig.toString());
+ LOG.debug("CSS netconf server configuration was updated: {}", dictionaryConfig.toString());
}
public InetSocketAddress getSshServerAddress(){
@Override
public void stop(BundleContext bundleContext) {
if (configService != null) {
- configService.unregister();
- configService = null;
+ configService.unregister();
+ configService = null;
}
}
- private Hashtable<String, String> getNetconfConfigProperties(){
+ private Hashtable<String, String> getNetconfConfigProperties() {
Hashtable<String, String> properties = new Hashtable<>();
properties.put(Constants.SERVICE_PID, CONFIG_PID);
return properties;
private final InetSocketAddress sshServerAddress;
private final String privateKeyPath;
- NetconfConfigurationHolder(InetSocketAddress tcpServerAddress, InetSocketAddress sshServerAddress, String privateKeyPath){
+ NetconfConfigurationHolder(final InetSocketAddress tcpServerAddress,
+ final InetSocketAddress sshServerAddress,
+ final String privateKeyPath) {
this.tcpServerAddress = tcpServerAddress;
this.sshServerAddress = sshServerAddress;
this.privateKeyPath = privateKeyPath;
InetSocketAddress getTcpServerAddress() {
return tcpServerAddress;
}
-
}
/*
- * Copyright (c) 2014 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,
package org.opendaylight.netconf.util.osgi;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
-import com.google.common.base.Optional;
-import io.netty.channel.local.LocalAddress;
-import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ManagedService;
public class NetconfConfigUtilTest {
- private BundleContext bundleContext;
+ @Mock
+ private ServiceReference<ManagedService> serviceRef;
- @Before
- public void setUp() throws Exception {
- bundleContext = mock(BundleContext.class);
- }
-
- @Test
- public void testNetconfConfigUtil() throws Exception {
- assertEquals(NetconfConfigUtil.getNetconfLocalAddress(), new LocalAddress("netconf"));
-
- doReturn("").when(bundleContext).getProperty("netconf.connectionTimeoutMillis");
- assertEquals(NetconfConfigUtil.extractTimeoutMillis(bundleContext), NetconfConfigUtil.DEFAULT_TIMEOUT_MILLIS);
-
- doReturn("a").when(bundleContext).getProperty("netconf.connectionTimeoutMillis");
- assertEquals(NetconfConfigUtil.extractTimeoutMillis(bundleContext), NetconfConfigUtil.DEFAULT_TIMEOUT_MILLIS);
- }
+ @Mock
+ private ServiceReference<ManagedService> netconfConfigurationRef;
- @Test
- public void testgetPrivateKeyKey() throws Exception {
- assertEquals(NetconfConfigUtil.getPrivateKeyKey(), "netconf.ssh.pk.path");
- }
-
- @Test
- public void testgetNetconfServerAddressKey() throws Exception {
- NetconfConfigUtil.InfixProp prop = NetconfConfigUtil.InfixProp.tcp;
- assertEquals(NetconfConfigUtil.getNetconfServerAddressKey(prop), "netconf.tcp.address");
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
}
@Test
- public void testExtractNetconfServerAddress() throws Exception {
- NetconfConfigUtil.InfixProp prop = NetconfConfigUtil.InfixProp.tcp;
- doReturn("").when(bundleContext).getProperty(anyString());
- assertEquals(NetconfConfigUtil.extractNetconfServerAddress(bundleContext, prop), Optional.absent());
- }
+ public void testGetNetconfConfigurationService() throws Exception {
+ final Collection<ServiceReference<ManagedService>> services = new ArrayList<>();
+ services.add(serviceRef);
+ services.add(netconfConfigurationRef);
+ final BundleContext context = mock(BundleContext.class);
+ doReturn(services).when(context).getServiceReferences(ManagedService.class, null);
+ final ManagedService service = mock(ManagedService.class);
+ doReturn(service).when(context).getService(serviceRef);
+ doReturn(NetconfConfiguration.getInstance()).when(context).getService(netconfConfigurationRef);
+ final java.util.Optional<NetconfConfiguration> netconfConfigurationOptional =
+ NetconfConfigUtil.getNetconfConfigurationService(context);
+ Assert.assertTrue(netconfConfigurationOptional.isPresent());
+ Assert.assertEquals(NetconfConfiguration.getInstance(), netconfConfigurationOptional.get());
- @Test
- public void testExtractNetconfServerAddress2() throws Exception {
- NetconfConfigUtil.InfixProp prop = NetconfConfigUtil.InfixProp.tcp;
- doReturn("1.1.1.1").when(bundleContext).getProperty("netconf.tcp.address");
- doReturn("20").when(bundleContext).getProperty("netconf.tcp.port");
- Optional<InetSocketAddress> inetSocketAddressOptional = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, prop);
- assertTrue(inetSocketAddressOptional.isPresent());
- assertEquals(inetSocketAddressOptional.get(), new InetSocketAddress("1.1.1.1", 20));
}
@Test
- public void testGetPrivateKeyPath() throws Exception {
- doReturn("path").when(bundleContext).getProperty("netconf.ssh.pk.path");
- assertEquals(NetconfConfigUtil.getPrivateKeyPath(bundleContext).get(), "path");
+ public void testGetNetconfConfigurationServiceAbsent() throws Exception {
+ final Collection<ServiceReference<ManagedService>> services = new ArrayList<>();
+ services.add(serviceRef);
+ final BundleContext context = mock(BundleContext.class);
+ doReturn(services).when(context).getServiceReferences(ManagedService.class, null);
+ final ManagedService service = mock(ManagedService.class);
+ doReturn(service).when(context).getService(serviceRef);
+ final java.util.Optional<NetconfConfiguration> netconfConfigurationOptional =
+ NetconfConfigUtil.getNetconfConfigurationService(context);
+ Assert.assertFalse(netconfConfigurationOptional.isPresent());
}
@Test
- public void testGetPrivateKeyPathNotPresent() throws Exception {
- doReturn(null).when(bundleContext).getProperty("netconf.ssh.pk.path");
- assertEquals(NetconfConfigUtil.getPrivateKeyPath(bundleContext), Optional.absent());
+ public void testGetNetconfConfigurationServiceInvalidSyntax() throws Exception {
+ final BundleContext context = mock(BundleContext.class);
+ final InvalidSyntaxException exception = new InvalidSyntaxException("Invalid syntax", "filter");
+ doThrow(exception).when(context).getServiceReferences(ManagedService.class, null);
+ final java.util.Optional<NetconfConfiguration> netconfConfigurationOptional =
+ NetconfConfigUtil.getNetconfConfigurationService(context);
+ Assert.assertFalse(netconfConfigurationOptional.isPresent());
}
}
--- /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.util.osgi;
+
+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.verify;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ManagedService;
+
+public class NetconfConfigurationActivatorTest {
+
+ @Mock
+ private BundleContext context;
+ @Mock
+ private ServiceRegistration registration;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn(registration).when(context)
+ .registerService(eq(ManagedService.class), any(NetconfConfiguration.class), any());
+ doNothing().when(registration).unregister();
+ }
+
+ @Test
+ public void testStartStop() throws Exception {
+ final NetconfConfigurationActivator activator = new NetconfConfigurationActivator();
+ activator.start(context);
+ final Dictionary<String, String> props = new Hashtable<>();
+ props.put(Constants.SERVICE_PID, "netconf");
+ verify(context).registerService(eq(ManagedService.class), eq(NetconfConfiguration.getInstance()), eq(props));
+ activator.stop(context);
+ verify(registration).unregister();
+ }
+
+}
\ 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.netconf.util.osgi;
+
+import io.netty.channel.local.LocalAddress;
+import java.net.InetSocketAddress;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NetconfConfigurationTest {
+
+ @Test
+ public void testUpdated() throws Exception {
+ final NetconfConfiguration config = NetconfConfiguration.getInstance();
+ Assert.assertEquals(new InetSocketAddress("0.0.0.0", 1830), config.getSshServerAddress());
+ Assert.assertEquals(new InetSocketAddress("127.0.0.1", 8383), config.getTcpServerAddress());
+ Assert.assertEquals("./configuration/RSA.pk", config.getPrivateKeyPath());
+ final Dictionary<String, String> newValues = new Hashtable<>();
+ final String newSshIp = "192.168.1.1";
+ final String newTcpIp = "192.168.1.2";
+ final int newSshPort = 1234;
+ final int newTcpPort = 4567;
+ final String newSshKeyPath = "./new_folder/configuration/RSA.pk";
+ newValues.put("ssh-address", newSshIp);
+ newValues.put("ssh-port", Integer.toString(newSshPort));
+ newValues.put("tcp-address", newTcpIp);
+ newValues.put("tcp-port", Integer.toString(newTcpPort));
+ newValues.put("ssh-pk-path", newSshKeyPath);
+ config.updated(newValues);
+ Assert.assertEquals(new InetSocketAddress(newSshIp, newSshPort), config.getSshServerAddress());
+ Assert.assertEquals(new InetSocketAddress(newTcpIp, newTcpPort), config.getTcpServerAddress());
+ Assert.assertEquals(newSshKeyPath, config.getPrivateKeyPath());
+ }
+
+ @Test
+ public void testUpdatedNull() throws Exception {
+ final NetconfConfiguration config = NetconfConfiguration.getInstance();
+ Assert.assertEquals(new InetSocketAddress("0.0.0.0", 1830), config.getSshServerAddress());
+ Assert.assertEquals(new InetSocketAddress("127.0.0.1", 8383), config.getTcpServerAddress());
+ Assert.assertEquals("./configuration/RSA.pk", config.getPrivateKeyPath());
+ final Dictionary<String, String> nullDictionary = null;
+ config.updated(nullDictionary);
+ Assert.assertEquals(new InetSocketAddress("0.0.0.0", 1830), config.getSshServerAddress());
+ Assert.assertEquals(new InetSocketAddress("127.0.0.1", 8383), config.getTcpServerAddress());
+ Assert.assertEquals("./configuration/RSA.pk", config.getPrivateKeyPath());
+ }
+}
\ No newline at end of file
<module>netconf-impl</module>
<module>config-netconf-connector</module>
<module>mdsal-netconf-ssh</module>
+ <module>mdsal-netconf-tcp</module>
<module>mdsal-netconf-connector</module>
<module>mdsal-netconf-monitoring</module>
<module>mdsal-netconf-notification</module>
<module>netconf-netty-util</module>
<module>netconf-mapping-api</module>
<module>netconf-client</module>
- <!--TODO Delete netconf-config-dispatcher bundle as soon as https://git.opendaylight.org/gerrit/#/c/46621/ is merged.-->
- <module>netconf-config-dispatcher</module>
<module>netconf-ssh</module>
<module>netconf-tcp</module>
<module>netconf-monitoring</module>
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
import org.opendaylight.netconf.sal.connect.netconf.sal.tx.ReadOnlyTx;
import org.opendaylight.netconf.sal.connect.netconf.sal.tx.ReadWriteTx;
+import org.opendaylight.netconf.sal.connect.netconf.sal.tx.TxChain;
import org.opendaylight.netconf.sal.connect.netconf.sal.tx.WriteCandidateRunningTx;
import org.opendaylight.netconf.sal.connect.netconf.sal.tx.WriteCandidateTx;
import org.opendaylight.netconf.sal.connect.netconf.sal.tx.WriteRunningTx;
private final NetconfBaseOps netconfOps;
private final boolean rollbackSupport;
- private boolean candidateSupported;
- private boolean runningWritable;
+ private final boolean candidateSupported;
+ private final boolean runningWritable;
public NetconfDeviceDataBroker(final RemoteDeviceId id, final SchemaContext schemaContext, final DOMRpcService rpc, final NetconfSessionPreferences netconfSessionPreferences) {
this.id = id;
@Override
public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
- throw new UnsupportedOperationException(id + ": Transaction chains not supported for netconf mount point");
+ return new TxChain(this, listener);
}
@Override
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import javax.annotation.Nullable;
import org.opendaylight.controller.config.util.xml.DocumentedException;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.netconf.api.NetconfDocumentedException;
import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.ModifyAction;
protected final NetconfBaseOps netOps;
protected final boolean rollbackSupport;
protected final List<ListenableFuture<DOMRpcResult>> resultsFutures;
+ private final List<TxListener> listeners = new CopyOnWriteArrayList<>();
// Allow commit to be called only once
protected boolean finished = false;
if(isFinished()) {
return false;
}
-
+ listeners.forEach(listener -> listener.onTransactionCancelled(this));
finished = true;
cleanup();
return true;
@Override
public final ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ listeners.forEach(listener -> listener.onTransactionSubmitted(this));
checkNotFinished();
finished = true;
+ final ListenableFuture<RpcResult<TransactionStatus>> result = performCommit();
+ Futures.addCallback(result, new FutureCallback<RpcResult<TransactionStatus>>() {
+ @Override
+ public void onSuccess(@Nullable final RpcResult<TransactionStatus> result) {
+ if (result != null && result.isSuccessful()) {
+ listeners.forEach(txListener -> txListener.onTransactionSuccessful(AbstractWriteTx.this));
+ } else {
+ final TransactionCommitFailedException cause = new TransactionCommitFailedException("Transaction failed", result.getErrors().toArray(new RpcError[result.getErrors().size()]));
+ listeners.forEach(listener -> listener.onTransactionFailed(AbstractWriteTx.this, cause));
+ }
+ }
- return performCommit();
+ @Override
+ public void onFailure(final Throwable t) {
+ listeners.forEach(listener -> listener.onTransactionFailed(AbstractWriteTx.this, t));
+ }
+ });
+ return result;
}
protected abstract ListenableFuture<RpcResult<TransactionStatus>> performCommit();
public void onFailure(final Throwable throwable) {
final NetconfDocumentedException exception =
new NetconfDocumentedException(
- new DocumentedException(id + ":RPC during tx returned an exception",
- new Exception(throwable),
- DocumentedException.ErrorType.APPLICATION,
- DocumentedException.ErrorTag.OPERATION_FAILED,
- DocumentedException.ErrorSeverity.ERROR));
+ id + ":RPC during tx returned an exception",
+ new Exception(throwable),
+ DocumentedException.ErrorType.APPLICATION,
+ DocumentedException.ErrorTag.OPERATION_FAILED,
+ DocumentedException.ErrorSeverity.ERROR);
transformed.setException(exception);
}
});
return transformed;
}
+
+ AutoCloseable addListener(final TxListener listener) {
+ listeners.add(listener);
+ return () -> listeners.remove(listener);
+ }
}
--- /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.sal.tx;
+
+import com.google.common.base.Preconditions;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainClosedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link DOMTransactionChain} implementation for Netconf connector.
+ */
+public class TxChain implements DOMTransactionChain, TxListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TxChain.class);
+
+ private final DOMDataBroker dataBroker;
+ private final TransactionChainListener listener;
+ /**
+ * Submitted transactions that haven't completed yet.
+ */
+ private final Map<DOMDataWriteTransaction, AutoCloseable> pendingTransactions = new HashMap<>();
+
+ /**
+ * Transaction created by this chain that hasn't been submitted or cancelled yet.
+ */
+ private AbstractWriteTx currentTransaction = null;
+ private boolean closed = false;
+ private boolean successful = true;
+
+ public TxChain(final DOMDataBroker dataBroker, final TransactionChainListener listener) {
+ this.dataBroker = dataBroker;
+ this.listener = listener;
+ }
+
+ @Override
+ public synchronized DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ checkOperationPermitted();
+ return dataBroker.newReadOnlyTransaction();
+ }
+
+ @Override
+ public synchronized AbstractWriteTx newWriteOnlyTransaction() {
+ checkOperationPermitted();
+ final DOMDataWriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+ Preconditions.checkState(writeTransaction instanceof AbstractWriteTx);
+ final AbstractWriteTx pendingWriteTx = (AbstractWriteTx) writeTransaction;
+ pendingTransactions.put(pendingWriteTx, pendingWriteTx.addListener(this));
+ currentTransaction = pendingWriteTx;
+ return pendingWriteTx;
+ }
+
+ @Override
+ public synchronized DOMDataReadWriteTransaction newReadWriteTransaction() {
+ return new ReadWriteTx(dataBroker.newReadOnlyTransaction(), newWriteOnlyTransaction());
+ }
+
+ @Override
+ public synchronized void close() {
+ if (!closed) {
+ closed = true;
+ notifyChainListenerSuccess();
+ }
+ }
+
+ @Override
+ public synchronized void onTransactionSuccessful(final AbstractWriteTx transaction) {
+ removePendingTx(transaction);
+ notifyChainListenerSuccess();
+ }
+
+ @Override
+ public synchronized void onTransactionFailed(final AbstractWriteTx transaction, final Throwable cause) {
+ removePendingTx(transaction);
+ successful = false;
+ if (currentTransaction != null) {
+ currentTransaction.cancel();
+ }
+ listener.onTransactionChainFailed(this, transaction, cause);
+ }
+
+ @Override
+ public synchronized void onTransactionSubmitted(final AbstractWriteTx transaction) {
+ currentTransaction = null;
+ }
+
+ @Override
+ public synchronized void onTransactionCancelled(final AbstractWriteTx transaction) {
+ removePendingTx(transaction);
+ currentTransaction = null;
+ }
+
+ private void removePendingTx(final AbstractWriteTx transaction) {
+ try {
+ pendingTransactions.remove(transaction).close();
+ } catch (final Exception e) {
+ LOG.error("Can't remove transaction listener registration", e);
+ }
+ }
+
+ /**
+ * Checks, if chain isn't closed and if there is no not submitted write transaction waiting.
+ */
+ private void checkOperationPermitted() {
+ if (closed) {
+ throw new TransactionChainClosedException("Transaction chain was closed");
+ }
+ Preconditions.checkState(currentTransaction == null, "Last write transaction has not finished yet");
+ }
+
+ private void notifyChainListenerSuccess() {
+ if (closed && pendingTransactions.isEmpty() && successful) {
+ listener.onTransactionChainSuccessful(this);
+ }
+ }
+
+}
--- /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.sal.tx;
+
+interface TxListener {
+
+ /**
+ * Invoked, when transaction completes successfully.
+ * @param transaction transaction
+ */
+ void onTransactionSuccessful(AbstractWriteTx transaction);
+
+ /**
+ * Invoked, when transaction fails.
+ *
+ * @param transaction transaction
+ * @param cause cause
+ */
+ void onTransactionFailed(AbstractWriteTx transaction, Throwable cause);
+
+ /**
+ * Invoked, when transaction is cancelled.
+ * @param transaction transaction
+ */
+ void onTransactionCancelled(AbstractWriteTx transaction);
+
+ /**
+ * Invoked, when transaction is submitted.
+ * @param transaction transaction
+ */
+ void onTransactionSubmitted(AbstractWriteTx transaction);
+
+
+}
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_FILTER_QNAME;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import java.net.InetSocketAddress;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME), NetconfBaseOps.getUnLockContent(NETCONF_RUNNING_QNAME));
}
+ @Test
+ public void testListenerSuccess() throws Exception {
+ doReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult((NormalizedNode<?, ?>) null)))
+ .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
+ final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, BaseSchema.BASE_NETCONF_CTX.getSchemaContext()), false);
+ final TxListener listener = mock(TxListener.class);
+ tx.addListener(listener);
+ tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
+ tx.submit();
+ verify(listener).onTransactionSubmitted(tx);
+ verify(listener).onTransactionSuccessful(tx);
+ verify(listener, never()).onTransactionFailed(eq(tx), any());
+ verify(listener, never()).onTransactionCancelled(tx);
+ }
+
+ @Test
+ public void testListenerCancellation() throws Exception {
+ doReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult((NormalizedNode<?, ?>) null)))
+ .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
+ final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, BaseSchema.BASE_NETCONF_CTX.getSchemaContext()), false);
+ final TxListener listener = mock(TxListener.class);
+ tx.addListener(listener);
+ tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
+ tx.cancel();
+ verify(listener).onTransactionCancelled(tx);
+ verify(listener, never()).onTransactionSubmitted(tx);
+ verify(listener, never()).onTransactionSuccessful(tx);
+ verify(listener, never()).onTransactionFailed(eq(tx), any());
+ }
+
+ @Test
+ public void testListenerFailure() throws Exception {
+ final IllegalStateException cause = new IllegalStateException("Failed tx");
+ doReturn(Futures.immediateFailedCheckedFuture(cause))
+ .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
+ final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, BaseSchema.BASE_NETCONF_CTX.getSchemaContext()), false);
+ final TxListener listener = mock(TxListener.class);
+ tx.addListener(listener);
+ tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
+ tx.submit();
+ final ArgumentCaptor<Exception> excCaptor = ArgumentCaptor.forClass(Exception.class);
+ verify(listener).onTransactionSubmitted(tx);
+ verify(listener).onTransactionFailed(eq(tx), excCaptor.capture());
+ Assert.assertEquals(cause, excCaptor.getValue().getCause().getCause());
+ verify(listener, never()).onTransactionSuccessful(tx);
+ verify(listener, never()).onTransactionCancelled(tx);
+ }
}
--- /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.sal.tx;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+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.common.api.data.TransactionChainClosedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+
+public class TxChainTest {
+
+ @Mock
+ private DOMDataBroker broker;
+ @Mock
+ private TransactionChainListener listener;
+ @Mock
+ private DOMDataReadOnlyTransaction readOnlyTx;
+ @Mock
+ private AbstractWriteTx writeOnlyTx1;
+ @Mock
+ private AbstractWriteTx writeOnlyTx2;
+ @Mock
+ private AbstractWriteTx writeOnlyTx3;
+ @Mock
+ private AutoCloseable registration1;
+ @Mock
+ private AutoCloseable registration2;
+ @Mock
+ private AutoCloseable registration3;
+ private final ArgumentCaptor<TxListener> captor = ArgumentCaptor.forClass(TxListener.class);
+ private TxChain chain;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(broker.newReadOnlyTransaction()).thenReturn(readOnlyTx);
+ when(broker.newWriteOnlyTransaction()).thenReturn(writeOnlyTx1).thenReturn(writeOnlyTx2).thenReturn(writeOnlyTx3);
+ when(writeOnlyTx1.addListener(any())).thenReturn(registration1);
+ when(writeOnlyTx2.addListener(any())).thenReturn(registration2);
+ when(writeOnlyTx3.addListener(any())).thenReturn(registration3);
+ chain = new TxChain(broker, listener);
+ }
+
+ @Test()
+ public void testNewReadOnlyTransactionPrevSubmitted() throws Exception {
+ chain.newWriteOnlyTransaction();
+ verify(writeOnlyTx1).addListener(captor.capture());
+ captor.getValue().onTransactionSubmitted(writeOnlyTx1);
+ chain.newReadOnlyTransaction();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testNewReadOnlyTransactionPrevNotSubmitted() throws Exception {
+ chain.newWriteOnlyTransaction();
+ chain.newReadOnlyTransaction();
+ }
+
+ @Test
+ public void testNewReadWriteTransactionPrevSubmitted() throws Exception {
+ chain.newReadWriteTransaction();
+ verify(writeOnlyTx1).addListener(captor.capture());
+ captor.getValue().onTransactionSubmitted(writeOnlyTx1);
+ chain.newReadWriteTransaction();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testNewReadWriteTransactionPrevNotSubmitted() throws Exception {
+ chain.newReadWriteTransaction();
+ chain.newReadWriteTransaction();
+ }
+
+ @Test
+ public void testNewWriteOnlyTransactionPrevSubmitted() throws Exception {
+ chain.newWriteOnlyTransaction();
+ verify(writeOnlyTx1).addListener(captor.capture());
+ captor.getValue().onTransactionSubmitted(writeOnlyTx1);
+ chain.newWriteOnlyTransaction();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testNewWriteOnlyTransactionPrevNotSubmitted() throws Exception {
+ chain.newWriteOnlyTransaction();
+ chain.newWriteOnlyTransaction();
+ }
+
+ @Test(expected = TransactionChainClosedException.class)
+ public void testCloseAfterFinished() throws Exception {
+ chain.close();
+ verify(listener).onTransactionChainSuccessful(chain);
+ chain.newReadOnlyTransaction();
+ }
+
+ @Test
+ public void testChainFail() throws Exception {
+ final AbstractWriteTx writeTx = chain.newWriteOnlyTransaction();
+ final ArgumentCaptor<TxListener> captor = ArgumentCaptor.forClass(TxListener.class);
+ verify(writeOnlyTx1).addListener(captor.capture());
+ writeTx.submit();
+ final TransactionCommitFailedException cause = new TransactionCommitFailedException("fail");
+ captor.getValue().onTransactionFailed(writeOnlyTx1, cause);
+ verify(registration1).close();
+ verify(listener).onTransactionChainFailed(chain, writeOnlyTx1, cause);
+ }
+
+ @Test
+ public void testChainSuccess() throws Exception {
+ final AbstractWriteTx writeTx = chain.newWriteOnlyTransaction();
+ chain.close();
+ verify(writeOnlyTx1).addListener(captor.capture());
+ writeTx.submit();
+ captor.getValue().onTransactionSuccessful(writeOnlyTx1);
+ verify(registration1).close();
+ verify(listener).onTransactionChainSuccessful(chain);
+ }
+
+ @Test
+ public void testCancel() throws Exception {
+ final AbstractWriteTx writeTx = chain.newWriteOnlyTransaction();
+ verify(writeOnlyTx1).addListener(captor.capture());
+ writeTx.cancel();
+ captor.getValue().onTransactionCancelled(writeOnlyTx1);
+ chain.newWriteOnlyTransaction();
+ }
+
+ @Test
+ public void testMultiplePendingTransactions() throws Exception {
+ //create 1st tx
+ final AbstractWriteTx writeTx1 = chain.newWriteOnlyTransaction();
+ final ArgumentCaptor<TxListener> captor1 = ArgumentCaptor.forClass(TxListener.class);
+ verify(writeOnlyTx1).addListener(captor1.capture());
+ //submit 1st tx
+ writeTx1.submit();
+ captor1.getValue().onTransactionSubmitted(writeOnlyTx1);
+
+ //create 2nd tx
+ final AbstractWriteTx writeTx2 = chain.newWriteOnlyTransaction();
+ final ArgumentCaptor<TxListener> captor2 = ArgumentCaptor.forClass(TxListener.class);
+ verify(writeTx2).addListener(captor2.capture());
+ //submit 2nd tx
+ writeTx2.submit();
+ captor2.getValue().onTransactionSubmitted(writeOnlyTx2);
+
+ //create 3rd tx
+ final AbstractWriteTx writeTx3 = chain.newWriteOnlyTransaction();
+ final ArgumentCaptor<TxListener> captor3 = ArgumentCaptor.forClass(TxListener.class);
+ verify(writeTx3).addListener(captor3.capture());
+ //cancel 3rd tx
+ writeTx3.cancel();
+ captor3.getValue().onTransactionCancelled(writeOnlyTx3);
+
+ //close chain
+ chain.close();
+
+ //complete first two transactions successfully
+ captor1.getValue().onTransactionSuccessful(writeOnlyTx1);
+ captor2.getValue().onTransactionSuccessful(writeOnlyTx2);
+
+ verify(registration1).close();
+ verify(registration2).close();
+ verify(registration3).close();
+ verify(listener).onTransactionChainSuccessful(chain);
+ }
+
+ @Test
+ public void testMultiplePendingTransactionsFail() throws Exception {
+ //create 1st tx
+ final AbstractWriteTx writeTx1 = chain.newWriteOnlyTransaction();
+ final ArgumentCaptor<TxListener> captor1 = ArgumentCaptor.forClass(TxListener.class);
+ verify(writeOnlyTx1).addListener(captor1.capture());
+ //submit 1st tx
+ writeTx1.submit();
+ captor1.getValue().onTransactionSubmitted(writeOnlyTx1);
+
+ //create 2nd tx
+ final AbstractWriteTx writeTx2 = chain.newWriteOnlyTransaction();
+ final ArgumentCaptor<TxListener> captor2 = ArgumentCaptor.forClass(TxListener.class);
+ verify(writeTx2).addListener(captor2.capture());
+ //submit 2nd tx
+ writeTx2.submit();
+ captor2.getValue().onTransactionSubmitted(writeOnlyTx2);
+
+ //create 3rd tx
+ final AbstractWriteTx writeTx3 = chain.newWriteOnlyTransaction();
+ final ArgumentCaptor<TxListener> captor3 = ArgumentCaptor.forClass(TxListener.class);
+ verify(writeTx3).addListener(captor3.capture());
+
+ chain.close();
+
+ //fail 1st transaction
+ final Exception cause1 = new Exception("fail");
+ captor1.getValue().onTransactionFailed(writeOnlyTx1, cause1);
+ //current unsubmitted transaction should be cancelled
+ verify(writeTx3).cancel();
+ captor3.getValue().onTransactionCancelled(writeTx3);
+ //2nd transaction success
+ captor2.getValue().onTransactionSuccessful(writeOnlyTx2);
+
+ verify(registration1).close();
+ verify(registration2).close();
+ verify(registration3).close();
+ verify(listener).onTransactionChainFailed(chain, writeOnlyTx1, cause1);
+ // 1 transaction failed, onTransactionChainSuccessful must not be called
+ verify(listener, never()).onTransactionChainSuccessful(chain);
+ }
+}
\ No newline at end of file
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
import java.io.Closeable;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.BindException;
import org.opendaylight.netconf.ssh.SshProxyServer;
import org.opendaylight.netconf.ssh.SshProxyServerConfiguration;
import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
+import org.opendaylight.netconf.test.tool.customrpc.SettableOperationProvider;
import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
this.nioExecutor = nioExecutor;
}
- private NetconfServerDispatcherImpl createDispatcher(final Set<Capability> capabilities, final boolean exi, final int generateConfigsTimeout,
- final Optional<File> notificationsFile, final boolean mdSal, final Optional<File> initialConfigXMLFile,
- final SchemaSourceProvider<YangTextSchemaSource> sourceProvider) {
+ private NetconfServerDispatcherImpl createDispatcher(final Set<Capability> capabilities,
+ final SchemaSourceProvider<YangTextSchemaSource> sourceProvider,
+ final TesttoolParameters params) {
final Set<Capability> transformedCapabilities = Sets.newHashSet(Collections2.transform(capabilities, new Function<Capability, Capability>() {
@Override
}
}
}));
-
- final SessionIdProvider idProvider = new SessionIdProvider();
-
- final AggregatedNetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = new AggregatedNetconfOperationServiceFactory();
- final NetconfOperationServiceFactory operationProvider = mdSal ? new MdsalOperationProvider(idProvider, transformedCapabilities, schemaContext, sourceProvider) :
- new SimulatedOperationProvider(idProvider, transformedCapabilities, notificationsFile, initialConfigXMLFile);
-
transformedCapabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
-
final NetconfMonitoringService monitoringService1 = new DummyMonitoringService(transformedCapabilities);
+ final SessionIdProvider idProvider = new SessionIdProvider();
- final NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory monitoringService =
- new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
- new NetconfMonitoringOperationService(monitoringService1));
- aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(operationProvider);
- aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(monitoringService);
+ final NetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = createOperationServiceFactory(sourceProvider, params, transformedCapabilities, monitoringService1, idProvider);
- final Set<String> serverCapabilities = exi
+ final Set<String> serverCapabilities = params.exi
? NetconfServerSessionNegotiatorFactory.DEFAULT_BASE_CAPABILITIES
: Sets.newHashSet(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0, XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1);
final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new TesttoolNegotiationFactory(
- hashedWheelTimer, aggregatedNetconfOperationServiceFactory, idProvider, generateConfigsTimeout, monitoringService1, serverCapabilities);
+ hashedWheelTimer, aggregatedNetconfOperationServiceFactory, idProvider, params.generateConfigsTimeout, monitoringService1, serverCapabilities);
final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
serverNegotiatorFactory);
return new NetconfServerDispatcherImpl(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
}
+ private NetconfOperationServiceFactory createOperationServiceFactory(final SchemaSourceProvider<YangTextSchemaSource> sourceProvider,
+ final TesttoolParameters params,
+ final Set<Capability> transformedCapabilities,
+ final NetconfMonitoringService monitoringService1,
+ final SessionIdProvider idProvider) {
+ final AggregatedNetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = new AggregatedNetconfOperationServiceFactory();
+
+ final NetconfOperationServiceFactory operationProvider;
+ if (params.mdSal) {
+ operationProvider = new MdsalOperationProvider(idProvider, transformedCapabilities, schemaContext, sourceProvider);
+ } else {
+ operationProvider = new SimulatedOperationProvider(idProvider, transformedCapabilities,
+ Optional.fromNullable(params.notificationFile),
+ Optional.fromNullable(params.initialConfigXMLFile));
+ }
+
+
+ final NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory monitoringService =
+ new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+ new NetconfMonitoringOperationService(monitoringService1));
+ aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(operationProvider);
+ aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(monitoringService);
+ if (params.rpcConfig != null) {
+ final SettableOperationProvider settableService = new SettableOperationProvider(params.rpcConfig);
+ aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(settableService);
+ }
+ return aggregatedNetconfOperationServiceFactory;
+ }
+
public List<Integer> start(final TesttoolParameters params) {
LOG.info("Starting {}, {} simulated devices starting on port {}", params.deviceCount, params.ssh ? "SSH" : "TCP", params.startingPort);
final SharedSchemaRepository schemaRepo = new SharedSchemaRepository("netconf-simulator");
final Set<Capability> capabilities = parseSchemasToModuleCapabilities(params, schemaRepo);
- final NetconfServerDispatcherImpl dispatcher = createDispatcher(capabilities, params.exi, params.generateConfigsTimeout,
- Optional.fromNullable(params.notificationFile), params.mdSal, Optional.fromNullable(params.initialConfigXMLFile),
+ final NetconfServerDispatcherImpl dispatcher = createDispatcher(capabilities,
new SchemaSourceProvider<YangTextSchemaSource>() {
@Override
public CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
return schemaRepo.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class);
}
- });
+ }, params);
int currentPort = params.startingPort;
@Arg(dest = "thread-pool-size")
public int threadPoolSize;
+ @Arg(dest = "rpc-config")
+ public File rpcConfig;
static ArgumentParser getParser() {
final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testtool");
.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");
+ parser.addArgument("--rpc-config")
+ .type(File.class)
+ .help("Rpc config file. It can be used to define custom rpc behavior, or override the default one." +
+ "Usable for testing buggy device behavior.")
+ .dest("rpc-config");
return parser;
}
}
}
}
+ if (rpcConfig != null) {
+ checkArgument(rpcConfig.exists(), "Rpc config file has to exist");
+ checkArgument(!rpcConfig.isDirectory(), "Rpc config file can't be a directory");
+ checkArgument(rpcConfig.canRead(), "Rpc config file to be readable");
+ }
}
public ArrayList<ArrayList<Execution.DestToPayload>> getThreadsPayloads(final List<Integer> openDevices) {
--- /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.test.tool.customrpc;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+
+class Rpc {
+
+ @XmlElement(name = "input")
+ private XmlData input;
+
+ @XmlElement(name = "output")
+ private List<XmlData> output;
+
+ XmlData getInput() {
+ return input;
+ }
+
+ List<XmlData> getOutput() {
+ return output;
+ }
+}
--- /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.test.tool.customrpc;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.xml.bind.JAXB;
+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.w3c.dom.Attr;
+import org.w3c.dom.Document;
+
+/**
+ * Mapping between RPCs and responses.
+ */
+class RpcMapping {
+
+ private final Multimap<Request, Document> requestToResponseMap = ArrayListMultimap.create();
+
+ /**
+ * Creates new mapping from file.
+ *
+ * @param config config file
+ */
+ RpcMapping(final File config) {
+ final Rpcs rpcs = JAXB.unmarshal(config, Rpcs.class);
+ for (final Rpc rpc : rpcs.getRpcs()) {
+ final Stream<Document> stream = rpc.getOutput().stream()
+ .map(XmlData::getData);
+ final XmlElement element = XmlElement.fromDomDocument(rpc.getInput().getData());
+ requestToResponseMap.putAll(new Request(element), stream::iterator);
+ }
+ }
+
+ /**
+ * Returns response matching given input. If multiple responses are configured for the same
+ * request, every invocation of this method returns next response as they are defined in the config file.
+ * Last configured response is used, when number of invocations is higher than number of configured responses
+ * for rpc.
+ *
+ * @param input request
+ * @return response document, or absent if mapping is not defined
+ */
+ Optional<Document> getResponse(final XmlElement input) {
+ final Collection<Document> responses = requestToResponseMap.get(new Request(input));
+ final Iterator<Document> iterator = responses.iterator();
+ if (iterator.hasNext()) {
+ final Document response = iterator.next();
+ if (iterator.hasNext()) {
+ iterator.remove();
+ }
+ return Optional.of(response);
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Rpc input wrapper. Needed because of custom {@link Request#equals(Object)}
+ * and {@link Request#hashCode()} implementations.
+ */
+ private static class Request {
+ private final XmlElement xmlElement;
+ private final int hashCode;
+
+ private Request(final XmlElement element) {
+ this.xmlElement = element;
+ hashCode = XmlUtil.toString(element)
+ .replaceAll("message-id=.*(>| )", "") //message id is variable, remove it
+ .replaceAll("\\s+", "") //remove whitespaces
+ .hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final Request request = (Request) o;
+ return documentEquals(this.xmlElement, request.xmlElement);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ private static boolean documentEquals(final XmlElement e1, final XmlElement e2) {
+ if (!e1.getNamespaceOptionally().equals(e2.getNamespaceOptionally())) {
+ return false;
+ }
+ if (!e1.getName().equals(e2.getName())) {
+ return false;
+ }
+ if (attributesNotEquals(e1, e2)) {
+ return false;
+ }
+
+ final List<XmlElement> e1Children = e1.getChildElements();
+ final List<XmlElement> e2Children = e2.getChildElements();
+ if (e1Children.size() != e2Children.size()) {
+ return false;
+ }
+ final Iterator<XmlElement> e1Iterator = e1Children.iterator();
+ final Iterator<XmlElement> e2Iterator = e2Children.iterator();
+ while (e1Iterator.hasNext() && e2Iterator.hasNext()) {
+ if (!documentEquals(e1Iterator.next(), e2Iterator.next())) {
+ return false;
+ }
+ }
+
+ if (e1Children.isEmpty() && e1Children.isEmpty()) {
+ try {
+ return e1.getTextContent().equals(e2.getTextContent());
+ } catch (final DocumentedException e) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean attributesNotEquals(final XmlElement e1, final XmlElement e2) {
+ final Map<String, Attr> e1Attrs = e1.getAttributes();
+ final Map<String, Attr> e2Attrs = e2.getAttributes();
+ final Iterator<Map.Entry<String, Attr>> e1AttrIt = e1Attrs.entrySet().iterator();
+ final Iterator<Map.Entry<String, Attr>> e2AttrIt = e2Attrs.entrySet().iterator();
+ while (e1AttrIt.hasNext() && e2AttrIt.hasNext()) {
+ final Map.Entry<String, Attr> e1Next = e1AttrIt.next();
+ final Map.Entry<String, Attr> e2Next = e2AttrIt.next();
+ if (e1Next.getKey().equals("message-id") && e2Next.getKey().equals("message-id")) {
+ continue;
+ }
+ if (e1Next.getKey().equals("xmlns") && e2Next.getKey().equals("xmlns")) {
+ continue;
+ }
+ if (!e1Next.getKey().equals(e2Next.getKey())) {
+ return true;
+ }
+ if (!e1Next.getValue().getValue().equals(e2Next.getValue().getValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ }
+}
--- /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.test.tool.customrpc;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "rpcs")
+@XmlAccessorType(XmlAccessType.FIELD)
+class Rpcs {
+
+ @XmlElement(name = "rpc")
+ private List<Rpc> rpcs;
+
+ List<Rpc> getRpcs() {
+ return rpcs;
+ }
+
+}
--- /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.test.tool.customrpc;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+import org.opendaylight.controller.config.util.capability.Capability;
+import org.opendaylight.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
+
+public class SettableOperationProvider implements NetconfOperationServiceFactory {
+
+ private final File rpcConfig;
+
+ public SettableOperationProvider(final File rpcConfig) {
+ this.rpcConfig = rpcConfig;
+ }
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ return () -> {
+ //no op
+ };
+ }
+
+ @Override
+ public NetconfOperationService createService(final String netconfSessionIdForReporting) {
+ return new SettableOperationService(rpcConfig);
+ }
+
+ private static class SettableOperationService implements NetconfOperationService {
+
+ private final SettableRpc rpc;
+
+ private SettableOperationService(final File rpcConfig) {
+ this.rpc = new SettableRpc(rpcConfig);
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return Collections.singleton(rpc);
+ }
+
+ @Override
+ public void close() {
+ // no op
+ }
+ }
+}
--- /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.test.tool.customrpc;
+
+import java.io.File;
+import java.util.Optional;
+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.xml.XmlNetconfConstants;
+import org.opendaylight.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.w3c.dom.Document;
+
+/**
+ * {@link NetconfOperation} implementation. It can be configured to intercept rpcs with defined input
+ * and reply with defined output. If input isn't defined, rpc handling is delegated to the subsequent
+ * {@link NetconfOperation} which is able to handle it.
+ */
+class SettableRpc implements NetconfOperation {
+
+ private final RpcMapping mapping;
+
+ SettableRpc(final File rpcConfig) {
+ mapping = new RpcMapping(rpcConfig);
+ }
+
+ @Override
+ public HandlingPriority canHandle(final Document message) throws DocumentedException {
+ return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY.increasePriority(1000);
+ }
+
+ @Override
+ public Document handle(final Document requestMessage, final NetconfOperationChainedExecution subsequentOperation)
+ throws DocumentedException {
+ final XmlElement requestElement = XmlElement.fromDomDocument(requestMessage);
+ final XmlElement rpcElement = requestElement.getOnlyChildElement();
+ final String msgId = requestElement.getAttribute(XmlNetconfConstants.MESSAGE_ID);
+ final Optional<Document> response = mapping.getResponse(rpcElement);
+ if (response.isPresent()) {
+ final Document document = response.get();
+ checkForError(document);
+ document.getDocumentElement().setAttribute(XmlNetconfConstants.MESSAGE_ID, msgId);
+ return document;
+ } else if (subsequentOperation.isExecutionTermination()) {
+ throw new DocumentedException("Mapping not found " + XmlUtil.toString(requestMessage),
+ DocumentedException.ErrorType.APPLICATION, DocumentedException.ErrorTag.OPERATION_NOT_SUPPORTED,
+ DocumentedException.ErrorSeverity.ERROR);
+ } else {
+ return subsequentOperation.execute(requestMessage);
+ }
+ }
+
+ private void checkForError(final Document document) throws DocumentedException {
+ final XmlElement rpcReply = XmlElement.fromDomDocument(document);
+ if (rpcReply.getOnlyChildElementOptionally("rpc-error").isPresent()) {
+ throw DocumentedException.fromXMLDocument(document);
+ }
+ }
+
+}
--- /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.test.tool.customrpc;
+
+import javax.xml.bind.annotation.XmlAnyElement;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+class XmlData {
+ @XmlAnyElement
+ private Element data;
+
+ Document getData() {
+ return data.getOwnerDocument();
+ }
+
+}
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
/**
- * Wrapping providers from restconf draft02 and draft17.
+ * Wrapping providers from restconf draft02 and draft18.
*
*/
public class RestconfWrapperProviders implements AutoCloseable, RestConnector {
// DRAFT02
private final RestconfProviderImpl providerDraft02;
- // DRAFT17
- private final RestConnectorProvider providerDraft17;
+ // DRAFT18
+ private final RestConnectorProvider providerDraft18;
/**
* Init both providers:
* <ul>
* <li>draft02 - {@link RestconfProviderImpl}
- * <li>draft17 - {@link RestConnectorProvider}
+ * <li>draft18 - {@link RestConnectorProvider}
* </ul>
*
* @param port
this.providerDraft02 = new RestconfProviderImpl();
this.providerDraft02.setWebsocketPort(port);
- this.providerDraft17 = new RestConnectorProvider();
+ this.providerDraft18 = new RestConnectorProvider();
}
/**
* Register both providers, which will use the SAL layer:
* <ul>
* <li>draft02 - {@link RestconfProviderImpl}
- * <li>draft17 - {@link RestConnectorProvider}
+ * <li>draft18 - {@link RestConnectorProvider}
* </ul>
*
* @param broker
// Register draft02 provider
broker.registerProvider(this.providerDraft02);
- // Register draft17 provider
- broker.registerProvider(this.providerDraft17);
+ // Register draft18 provider
+ broker.registerProvider(this.providerDraft18);
}
/**
@Override
public void close() throws Exception {
this.providerDraft02.close();
- this.providerDraft17.close();
+ this.providerDraft18.close();
}
}
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
@Provider
-@Produces({ SchemaRetrievalService.YANG_MEDIA_TYPE, Draft17.MediaTypes.YANG })
+@Produces({ SchemaRetrievalService.YANG_MEDIA_TYPE, Draft18.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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
@Provider
-@Produces({ SchemaRetrievalService.YIN_MEDIA_TYPE, Draft17.MediaTypes.YIN + RestconfConstants.XML })
+@Produces({ SchemaRetrievalService.YIN_MEDIA_TYPE, Draft18.MediaTypes.YIN + RestconfConstants.XML })
public class SchemaExportContentYinBodyWriter implements MessageBodyWriter<SchemaExportContext> {
@Override
*/
package org.opendaylight.netconf.sal.rest.api;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.yangtools.yang.common.QName;
/**
* @deprecated Do not use old implementation of restconf draft. It will be
- * replaced by {@link Draft17}.
+ * replaced by {@link Draft18}.
*
*/
@Deprecated
import org.opendaylight.restconf.restful.services.api.RestconfInvokeOperationsService;
/**
- * The URI hierarchy for the RESTCONF resources consists of an entry point container, 4 top-level resources, and 1
- * field.
+ * The URI hierarchy for the RESTCONF resources consists of an entry point
+ * container, 4 top-level resources, and 1 field.
* <ul>
* <li><b>/restconf</b> - {@link #getRoot()}
* <ul>
- * <li><b>/config</b> - {@link #readConfigurationData(String, UriInfo)}
- * {@link #updateConfigurationData(String, NormalizedNodeContext)}
- * {@link #createConfigurationData(NormalizedNodeContext, UriInfo)}
- * {@link #createConfigurationData(String, NormalizedNodeContext, UriInfo)}
+ * <li><b>/config</b> - {@link #readConfigurationData(String, UriInfo)}
+ * {@link #updateConfigurationData(String, NormalizedNodeContext, UriInfo)}
+ * {@link #createConfigurationData(NormalizedNodeContext, UriInfo)}
+ * {@link #createConfigurationData(String, NormalizedNodeContext, UriInfo)}
* {@link #deleteConfigurationData(String)}
* <li><b>/operational</b> - {@link #readOperationalData(String, UriInfo)}
* <li>/modules - {@link #getModules(UriInfo)}
* <ul>
* <li>/module
* </ul>
- * <li><b>/operations</b> - {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)}
- * {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)}
+ * <li><b>/operations</b> -
+ * {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)}
+ * {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)}
* <li>/version (field)
* </ul>
* </ul>
/**
* @deprecated do not use this method. It will be replaced by
- * {@link RestconfDataService#putData(String, NormalizedNodeContext)}
+ * {@link RestconfDataService#putData(String, NormalizedNodeContext, UriInfo)}
*/
@Deprecated
@PUT
@Path("/config/{identifier:.+}")
@Consumes({ Draft02.MediaTypes.DATA + JSON, Draft02.MediaTypes.DATA + XML, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
- public Response updateConfigurationData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload);
+ public Response updateConfigurationData(@Encoded @PathParam("identifier") String identifier,
+ NormalizedNodeContext payload, @Context UriInfo uriInfo);
/**
* @deprecated do not use this method. It will be replaced by
@GET
@Path("/streams/stream/{identifier:.+}")
- public Response subscribeToStream(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo);
+ public NormalizedNodeContext subscribeToStream(@Encoded @PathParam("identifier") String identifier,
+ @Context UriInfo uriInfo);
/**
* @deprecated do not use this method. It will be replaced by
package org.opendaylight.netconf.sal.rest.impl;
import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
-import com.google.common.annotations.Beta;
+
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
* the opposite of a {@link javax.xml.stream.XMLStreamReader} -- unlike instantiating an iterator over
* the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows
* us to write multiple nodes.
+ *
+ * @deprecated This class will be replaced by
+ * {@link org.opendaylight.restconf.jersey.providers.ParameterAwareNormalizedNodeWriter}
*/
-@Beta
+@Deprecated
public class DepthAwareNormalizedNodeWriter implements RestconfNormalizedNodeWriter {
private final NormalizedNodeStreamWriter writer;
protected int currentDepth = 0;
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
@Provider
@Consumes({ Draft02.MediaTypes.DATA + RestconfService.JSON, Draft02.MediaTypes.OPERATION + RestconfService.JSON,
- Draft17.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
+ Draft18.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
public class JsonNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<NormalizedNodeContext> {
private final static Logger LOG = LoggerFactory.getLogger(JsonNormalizedNodeBodyReader.class);
import java.lang.reflect.Type;
import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.util.Map.Entry;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
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.Draft17;
-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;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+/**
+ * @deprecated This class will be replaced by
+ * {@link org.opendaylight.restconf.jersey.providers.NormalizedNodeJsonBodyWriter}
+ */
+@Deprecated
@Provider
@Produces({ Draft02.MediaTypes.API + RestconfService.JSON, Draft02.MediaTypes.DATA + RestconfService.JSON,
- Draft02.MediaTypes.OPERATION + RestconfService.JSON, Draft17.MediaTypes.DATA + RestconfConstants.JSON,
- MediaType.APPLICATION_JSON })
+ Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON })
public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
private static final int DEFAULT_INDENT_SPACES_NUM = 2;
@Override
- public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
+ public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+ final MediaType mediaType) {
return type.equals(NormalizedNodeContext.class);
}
@Override
- public long getSize(final NormalizedNodeContext t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
+ public long getSize(final NormalizedNodeContext t, final Class<?> type, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
return -1;
}
@Override
- 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 {
+ 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 {
+ for (final Entry<String, Object> entry : t.getNewHeaders().entrySet()) {
+ httpHeaders.add(entry.getKey(), entry.getValue());
+ }
final NormalizedNode<?, ?> data = t.getData();
if (data == null) {
return;
}
@SuppressWarnings("unchecked")
- final InstanceIdentifierContext<SchemaNode> context = (InstanceIdentifierContext<SchemaNode>) t.getInstanceIdentifierContext();
+ final InstanceIdentifierContext<SchemaNode> context =
+ (InstanceIdentifierContext<SchemaNode>) t.getInstanceIdentifierContext();
final SchemaPath path = context.getSchemaNode().getPath();
final JsonWriter jsonWriter = createJsonWriter(entityStream, t.getWriterParameters().isPrettyPrint());
jsonWriter.beginObject();
- writeNormalizedNode(jsonWriter,path,context,data, t.getWriterParameters().getDepth());
+ writeNormalizedNode(jsonWriter,path,context,data, Optional.fromNullable(t.getWriterParameters().getDepth()));
jsonWriter.endObject();
jsonWriter.flush();
}
private void writeNormalizedNode(final JsonWriter jsonWriter, SchemaPath path,
- final InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data, final Optional<Integer> depth) throws
- IOException {
+ final InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data,
+ final Optional<Integer> depth) throws IOException {
final RestconfNormalizedNodeWriter nnWriter;
if (SchemaPath.ROOT.equals(path)) {
/*
} else {
initialNs = null;
}
- final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter.createNestedWriter(codecs,path,initialNs,jsonWriter);
+ final NormalizedNodeStreamWriter streamWriter =
+ JSONNormalizedNodeStreamWriter.createNestedWriter(codecs, path, initialNs, jsonWriter);
if (depth.isPresent()) {
return DepthAwareNormalizedNodeWriter.forStreamWriter(streamWriter, depth.get());
} else {
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
+import java.util.Map.Entry;
+import javanet.staxutils.IndentingXMLStreamWriter;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
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.Draft17;
-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;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import javanet.staxutils.IndentingXMLStreamWriter;
+/**
+ * @deprecated This class will be replaced by
+ * {@link org.opendaylight.restconf.jersey.providers.NormalizedNodeXmlBodyWriter}
+ */
+@Deprecated
@Provider
@Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML,
- Draft02.MediaTypes.OPERATION + RestconfService.XML, Draft17.MediaTypes.DATA + RestconfConstants.XML,
- MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+ Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
private static final XMLOutputFactory XML_FACTORY;
final Annotation[] annotations, final MediaType mediaType,
final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException,
WebApplicationException {
+ for (final Entry<String, Object> entry : t.getNewHeaders().entrySet()) {
+ httpHeaders.add(entry.getKey(), entry.getValue());
+ }
final InstanceIdentifierContext<?> pathContext = t.getInstanceIdentifierContext();
if (t.getData() == null) {
return;
final NormalizedNode<?, ?> data = t.getData();
final SchemaPath schemaPath = pathContext.getSchemaNode().getPath();
-
-
- writeNormalizedNode(xmlWriter, schemaPath, pathContext, data, t.getWriterParameters().getDepth());
-
+ writeNormalizedNode(xmlWriter, schemaPath, pathContext, data, Optional.fromNullable(t.getWriterParameters().getDepth()));
}
- private void writeNormalizedNode(final XMLStreamWriter xmlWriter, final SchemaPath schemaPath, final InstanceIdentifierContext<?>
- pathContext, NormalizedNode<?, ?> data, final 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)) {
}
private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final XMLStreamWriter xmlWriter,
- final SchemaContext schemaContext, final SchemaPath schemaPath, final Optional<Integer> depth) {
- final NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, schemaContext, schemaPath);
+ 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 {
throws IOException {
try {
final QName name = data.getNodeType();
- xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getLocalName(), name.getNamespace().toString());
+ xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getLocalName(),
+ name.getNamespace().toString());
xmlWriter.writeDefaultNamespace(name.getNamespace().toString());
for(final NormalizedNode<?,?> child : data.getValue()) {
nnWriter.write(child);
import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusEntity;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
@Provider
@Produces({Draft02.MediaTypes.PATCH_STATUS + RestconfService.JSON,
- Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.JSON})
+ Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.JSON})
public class PATCHJsonBodyWriter implements MessageBodyWriter<PATCHStatusContext> {
@Override
import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusEntity;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
@Provider
@Produces({Draft02.MediaTypes.PATCH_STATUS + RestconfService.XML,
- Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.XML})
+ Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.XML})
public class PATCHXmlBodyWriter implements MessageBodyWriter<PATCHStatusContext> {
private static final XMLOutputFactory XML_FACTORY;
package org.opendaylight.netconf.sal.rest.impl;
import com.google.common.base.Preconditions;
-import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
@Override
public Object getRoot() {
- return restconf.getRoot();
+ return this.restconf.getRoot();
}
@Override
public NormalizedNodeContext getModules(final UriInfo uriInfo) {
- return restconf.getModules(uriInfo);
+ return this.restconf.getModules(uriInfo);
}
@Override
public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
- return restconf.getModules(identifier, uriInfo);
+ return this.restconf.getModules(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
- return restconf.getModule(identifier, uriInfo);
+ return this.restconf.getModule(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
- return restconf.getOperations(uriInfo);
+ return this.restconf.getOperations(uriInfo);
}
@Override
public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
- return restconf.getOperations(identifier, uriInfo);
+ return this.restconf.getOperations(identifier, uriInfo);
}
@Override
- public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
- return restconf.invokeRpc(identifier, payload, uriInfo);
+ public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ return this.restconf.invokeRpc(identifier, payload, uriInfo);
}
@Override
@Deprecated
public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
- return restconf.invokeRpc(identifier, noPayload, uriInfo);
+ return this.restconf.invokeRpc(identifier, noPayload, uriInfo);
}
@Override
public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
- return restconf.readConfigurationData(identifier, uriInfo);
+ return this.restconf.readConfigurationData(identifier, uriInfo);
}
@Override
public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) {
- return restconf.readOperationalData(identifier, uriInfo);
+ return this.restconf.readOperationalData(identifier, uriInfo);
}
@Override
- public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
- return restconf.updateConfigurationData(identifier, payload);
+ public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ return this.restconf.updateConfigurationData(identifier, payload, uriInfo);
}
@Override
- public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
- return restconf.createConfigurationData(identifier, payload, uriInfo);
+ public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ return this.restconf.createConfigurationData(identifier, payload, uriInfo);
}
@Override
public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
- return restconf.createConfigurationData(payload, uriInfo);
+ return this.restconf.createConfigurationData(payload, uriInfo);
}
@Override
public Response deleteConfigurationData(final String identifier) {
- return restconf.deleteConfigurationData(identifier);
+ return this.restconf.deleteConfigurationData(identifier);
}
@Override
- public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
- return restconf.subscribeToStream(identifier, uriInfo);
+ public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ return this.restconf.subscribeToStream(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
- return restconf.getAvailableStreams(uriInfo);
+ return this.restconf.getAvailableStreams(uriInfo);
}
@Override
- public PATCHStatusContext patchConfigurationData(final String identifier, PATCHContext payload, UriInfo uriInfo) {
- return restconf.patchConfigurationData(identifier, payload, uriInfo);
+ public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext payload,
+ final UriInfo uriInfo) {
+ return this.restconf.patchConfigurationData(identifier, payload, uriInfo);
}
@Override
public PATCHStatusContext patchConfigurationData(final PATCHContext context, final UriInfo uriInfo) {
- return restconf.patchConfigurationData(context, uriInfo);
+ return this.restconf.patchConfigurationData(context, uriInfo);
}
@Override
public SchemaExportContext getSchema(final String mountId) {
- return schema.getSchema(mountId);
+ return this.schema.getSchema(mountId);
}
}
import com.google.common.base.Optional;
import javax.annotation.Nonnull;
+import javax.ws.rs.core.UriInfo;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.yangtools.yang.common.OperationFailedException;
/**
* @author Thomas Pantelis
*/
+@Deprecated
public interface JSONRestconfService {
/**
* The data tree root path.
* @param payload the payload data in JSON format.
* @throws OperationFailedException if the request fails.
*/
- void put(String uriPath, @Nonnull String payload) throws OperationFailedException;
+ void put(String uriPath, @Nonnull String payload, UriInfo uriInfo) throws OperationFailedException;
/**
* Issues a restconf POST request to the configuration data store.
* @param payload the payload data in JSON format.
* @throws OperationFailedException if the request fails.
*/
- void post(String uriPath, @Nonnull String payload) throws OperationFailedException;
+ void post(String uriPath, @Nonnull String payload, UriInfo uriInfo) throws OperationFailedException;
/**
* Issues a restconf DELETE request to the configuration data store.
* @return an Optional containing the data in JSON format if present.
* @throws OperationFailedException if the request fails.
*/
- Optional<String> get(String uriPath, LogicalDatastoreType datastoreType) throws OperationFailedException;
+ Optional<String> get(String uriPath, LogicalDatastoreType datastoreType, UriInfo uriInfo)
+ throws OperationFailedException;
/**
* Invokes a yang-defined RPC.
import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
-
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
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.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+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.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+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.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.slf4j.Logger;
}
}
- // READ configuration
+ /**
+ * Read config data by path
+ *
+ * @param path
+ * - path of data
+ * @return read date
+ */
public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
+ return readConfigurationData(path, null);
+ }
+
+ /**
+ * Read config data by path
+ *
+ * @param path
+ * - path of data
+ * @param withDefa
+ * - value of with-defaults parameter
+ * @return read date
+ */
+ public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
checkPreconditions();
- return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
+ return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path, withDefa);
}
- public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
+ /**
+ * Read config data from mount point by path.
+ *
+ * @param mountPoint
+ * - mount point for reading data
+ * @param path
+ * - path of data
+ * @return read data
+ */
+ public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
+ final YangInstanceIdentifier path) {
+ return readConfigurationData(mountPoint, path, null);
+ }
+
+ /**
+ * Read config data from mount point by path.
+ *
+ * @param mountPoint
+ * - mount point for reading data
+ * @param path
+ * - path of data
+ * @param withDefa
+ * - value of with-defaults parameter
+ * @return read data
+ */
+ public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
+ final String withDefa) {
final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
if (domDataBrokerService.isPresent()) {
- return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
+ return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path,
+ withDefa);
}
final String errMsg = "DOM data broker service isn't available for mount point " + path;
LOG.warn(errMsg);
throw new RestconfDocumentedException(errMsg);
}
- // READ operational
+ /**
+ * Read operational data by path.
+ *
+ * @param path
+ * - path of data
+ * @return read data
+ */
public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
checkPreconditions();
return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
}
+ /**
+ * Read operational data from mount point by path.
+ *
+ * @param mountPoint
+ * - mount point for reading data
+ * @param path
+ * - path of data
+ * @return read data
+ */
public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
if (domDataBrokerService.isPresent()) {
* - path of node
* @param payload
* - input data
+ * @param point
+ * @param insert
* @return wrapper of status and future of PUT
*/
public PutResult commitConfigurationDataPut(
- final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final String insert, final String point) {
Preconditions.checkNotNull(globalSchema);
Preconditions.checkNotNull(path);
Preconditions.checkNotNull(payload);
final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
: Status.CREATED;
final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
- newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema);
+ newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
return new PutResult(status, future);
}
* - path of node
* @param payload
* - input data
+ * @param point
+ * @param insert
* @return wrapper of status and future of PUT
*/
public PutResult commitMountPointDataPut(
- final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final String insert, final String point) {
Preconditions.checkNotNull(mountPoint);
Preconditions.checkNotNull(path);
Preconditions.checkNotNull(payload);
final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
? Status.OK : Status.CREATED;
final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
- newReadWriteTransaction, CONFIGURATION, path,
- payload, mountPoint.getSchemaContext());
+ newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
+ point);
return new PutResult(status, future);
}
final String errMsg = "DOM data broker service isn't available for mount point " + path;
// POST configuration
public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
- final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final String insert, final String point) {
checkPreconditions();
- return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
+ return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
+ globalSchema, insert, point);
}
public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
- final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final String insert, final String point) {
final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
if (domDataBrokerService.isPresent()) {
return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
- payload, mountPoint.getSchemaContext());
+ payload, mountPoint.getSchemaContext(), insert, point);
}
final String errMsg = "DOM data broker service isn't available for mount point " + path;
LOG.warn(errMsg);
private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
+ return readDataViaTransaction(transaction, datastore, path, null);
+ }
+
+ private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
final ReadDataResult<NormalizedNode<?, ?>> readData = new ReadDataResult<>();
LOG.warn(msg);
throw new RestconfDocumentedException(msg, e);
}
- return readData.getResult();
+ if (withDefa == null) {
+ return readData.getResult();
+ } else {
+ return prepareDataByParamWithDef(readData.getResult(), path, withDefa);
+ }
+
+ }
+
+ private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
+ final YangInstanceIdentifier path, final String withDefa) {
+ boolean trim;
+ switch (withDefa) {
+ case "trim":
+ trim = true;
+ break;
+ case "explicit":
+ trim = false;
+ break;
+ default:
+ throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
+ }
+
+ final SchemaContext ctx = ControllerContext.getInstance().getGlobalSchema();
+ final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
+ final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+ if (result instanceof ContainerNode) {
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
+ Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
+ buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
+ return builder.build();
+ } else {
+ final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
+ Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
+ buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
+ ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
+ return builder.build();
+ }
+ }
+
+ private void buildMapEntryBuilder(final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
+ final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
+ final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
+ for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+ final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
+ final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+ if (child instanceof ContainerNode) {
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
+ Builders.containerBuilder((ContainerSchemaNode) childSchema);
+ buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
+ builder.withChild(childBuilder.build());
+ } else if (child instanceof MapNode) {
+ final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
+ Builders.mapBuilder((ListSchemaNode) childSchema);
+ buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
+ ((ListSchemaNode) childSchema).getKeyDefinition());
+ builder.withChild(childBuilder.build());
+ } else if (child instanceof LeafNode) {
+ final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
+ final String nodeVal = ((LeafNode<String>) child).getValue();
+ final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+ Builders.leafBuilder((LeafSchemaNode) childSchema);
+ if (keys.contains(child.getNodeType())) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ } else {
+ if (trim) {
+ if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ }
+ } else {
+ if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
+ final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
+ final List<QName> keys) {
+ for (final MapEntryNode mapEntryNode : result.getValue()) {
+ final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
+ final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
+ final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
+ Builders.mapEntryBuilder((ListSchemaNode) childSchema);
+ buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
+ builder.withChild(mapEntryBuilder.build());
+ }
+ }
+
+ private void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
+ final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
+ final YangInstanceIdentifier actualPath, final boolean trim) {
+ for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+ final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
+ final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+ if(child instanceof ContainerNode){
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
+ Builders.containerBuilder((ContainerSchemaNode) childSchema);
+ buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
+ builder.withChild(builderChild.build());
+ } else if (child instanceof MapNode) {
+ final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
+ Builders.mapBuilder((ListSchemaNode) childSchema);
+ buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
+ ((ListSchemaNode) childSchema).getKeyDefinition());
+ builder.withChild(childBuilder.build());
+ } else if (child instanceof LeafNode) {
+ final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
+ final String nodeVal = ((LeafNode<String>) child).getValue();
+ final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+ Builders.leafBuilder((LeafSchemaNode) childSchema);
+ if (trim) {
+ if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ }
+ } else {
+ if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ }
+ }
+ }
+ }
}
/**
* POST data and submit transaction {@link DOMDataReadWriteTransaction}
- * @return
*/
private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
- final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+ final String insert, final String point) {
LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
- postData(rWTransaction, datastore, path, payload, schemaContext);
+ postData(rWTransaction, datastore, path, payload, schemaContext, insert, point);
return rWTransaction.submit();
}
final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
- postData(rWTransaction, datastore, path, payload, schemaContext);
+ postData(rWTransaction, datastore, path, payload, schemaContext, null, null);
}
- // FIXME: This is doing correct post for container and list children, not sure if this will work for choice case
private void postData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
- final SchemaContext schemaContext) {
+ final SchemaContext schemaContext, final String insert, final String point) {
+ if (insert == null) {
+ makeNormalPost(rWTransaction, datastore, path, payload, schemaContext);
+ } else {
+ final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+ checkItemDoesNotExists(rWTransaction, datastore, path);
+ switch (insert) {
+ case "first":
+ if(schemaNode instanceof ListSchemaNode){
+ final OrderedMapNode readList =
+ (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ } else {
+ rWTransaction.delete(datastore, path.getParent().getParent());
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readList,
+ schemaContext);
+ }
+ } else {
+ final OrderedLeafSetNode readLeafList =
+ (OrderedLeafSetNode) readConfigurationData(path.getParent());
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ } else {
+ rWTransaction.delete(datastore, path.getParent());
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readLeafList,
+ schemaContext);
+ }
+ }
+ break;
+ case "last":
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ break;
+ case "before":
+ if(schemaNode instanceof ListSchemaNode){
+ final OrderedMapNode readList =
+ (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ } else {
+ insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+ readList,
+ true);
+ }
+ } else {
+ final OrderedLeafSetNode<?> readLeafList =
+ (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ } else {
+ insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+ readLeafList, true);
+ }
+ }
+ break;
+ case "after":
+ if (schemaNode instanceof ListSchemaNode) {
+ final OrderedMapNode readList =
+ (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ } else {
+ insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+ readList,
+ false);
+ }
+ } else {
+ final OrderedLeafSetNode<?> readLeafList =
+ (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ } else {
+ insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+ readLeafList, false);
+ }
+ }
+ break;
+ default:
+ throw new RestconfDocumentedException(
+ "Used bad value of insert parameter. Possible values are first, last, before or after, "
+ + "but was: " + insert);
+ }
+ }
+ }
+
+ private void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
+ final boolean before) {
+ rWTransaction.delete(datastore, path.getParent().getParent());
+ final InstanceIdentifierContext<?> instanceIdentifier =
+ ControllerContext.getInstance().toInstanceIdentifier(point);
+ int p = 0;
+ for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+ if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+ break;
+ }
+ p++;
+ }
+ if (!before) {
+ p++;
+ }
+ int h = 0;
+ final NormalizedNode<?, ?> emptySubtree =
+ ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+ if (h == p) {
+ checkItemDoesNotExists(rWTransaction, datastore, path);
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ }
+ final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
+ checkItemDoesNotExists(rWTransaction, datastore, childPath);
+ rWTransaction.put(datastore, childPath, nodeChild);
+ h++;
+ }
+ }
+
+ private void insertWithPointListPost(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+ final String point, final MapNode readList, final boolean before) {
+ rWTransaction.delete(datastore, path.getParent().getParent());
+ final InstanceIdentifierContext<?> instanceIdentifier =
+ ControllerContext.getInstance().toInstanceIdentifier(point);
+ int p = 0;
+ for (final MapEntryNode mapEntryNode : readList.getValue()) {
+ if (mapEntryNode.getIdentifier()
+ .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+ break;
+ }
+ p++;
+ }
+ if (!before) {
+ p++;
+ }
+ int h = 0;
+ final NormalizedNode<?, ?> emptySubtree =
+ ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ for (final MapEntryNode mapEntryNode : readList.getValue()) {
+ if (h == p) {
+ checkItemDoesNotExists(rWTransaction, datastore, path);
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+ }
+ final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
+ checkItemDoesNotExists(rWTransaction, datastore, childPath);
+ rWTransaction.put(datastore, childPath, mapEntryNode);
+ h++;
+ }
+ }
+
+ private DataSchemaNode checkListAndOrderedType(final SchemaContext ctx,
+ final YangInstanceIdentifier path) {
+ final YangInstanceIdentifier parent = path.getParent();
+ final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
+ final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
+
+ if (dataSchemaNode instanceof ListSchemaNode) {
+ if(!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
+ throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
+ }
+ return dataSchemaNode;
+ }
+ if (dataSchemaNode instanceof LeafListSchemaNode) {
+ if(!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
+ throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user leaf-list.");
+ }
+ return dataSchemaNode;
+ }
+ throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
+ }
+
+ private void makeNormalPost(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
if (payload instanceof MapNode) {
final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
checkItemDoesNotExists(rWTransaction, datastore, childPath);
rWTransaction.put(datastore, childPath, child);
}
- } else {
- checkItemDoesNotExists(rWTransaction, datastore, path);
+ } else if (payload instanceof LeafSetNode) {
+ final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
- rWTransaction.put(datastore, path, payload);
+ for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
+ final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+ checkItemDoesNotExists(rWTransaction, datastore, childPath);
+ rWTransaction.put(datastore, childPath, child);
+ }
+ } else {
+ simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
}
}
+ private void simplePostPut(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+ checkItemDoesNotExists(rWTransaction, datastore, path);
+ ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+ rWTransaction.put(datastore, path, payload);
+ }
+
/**
* Check if item already exists. Throws error if it does NOT already exist.
* @param rWTransaction Current transaction
/**
* PUT data and submit {@link DOMDataReadWriteTransaction}
+ *
+ * @param point
+ * @param insert
*/
private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
- final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+ final String insert, final String point) {
LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
- putData(readWriteTransaction, datastore, path, payload, schemaContext);
+ putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
return readWriteTransaction.submit();
}
/**
* PUT data and do NOT submit {@link DOMDataReadWriteTransaction}
+ *
+ * @param insert
+ * @param point
*/
private void putDataWithinTransaction(
final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
- putData(writeTransaction, datastore, path, payload, schemaContext);
+ putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
}
// FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
- private void putData(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ private void putData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+ final String insert, final String point) {
+ if (insert == null) {
+ makePut(rWTransaction, datastore, path, payload, schemaContext);
+ } else {
+ final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+ checkItemDoesNotExists(rWTransaction, datastore, path);
+ switch (insert) {
+ case "first":
+ if (schemaNode instanceof ListSchemaNode) {
+ final OrderedMapNode readList =
+ (OrderedMapNode) this.readConfigurationData(path.getParent());
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ } else {
+ rWTransaction.delete(datastore, path.getParent());
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ makePut(rWTransaction, datastore, path.getParent(), readList, schemaContext);
+ }
+ } else {
+ final OrderedLeafSetNode<?> readLeafList =
+ (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ } else {
+ rWTransaction.delete(datastore, path.getParent());
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ makePut(rWTransaction, datastore, path.getParent(), readLeafList,
+ schemaContext);
+ }
+ }
+ break;
+ case "last":
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ break;
+ case "before":
+ if (schemaNode instanceof ListSchemaNode) {
+ final OrderedMapNode readList =
+ (OrderedMapNode) this.readConfigurationData(path.getParent());
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ } else {
+ insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+ readList, true);
+ }
+ } else {
+ final OrderedLeafSetNode<?> readLeafList =
+ (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ } else {
+ insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+ readLeafList, true);
+ }
+ }
+ break;
+ case "after":
+ if (schemaNode instanceof ListSchemaNode) {
+ final OrderedMapNode readList =
+ (OrderedMapNode) this.readConfigurationData(path.getParent());
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ } else {
+ insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+ readList, false);
+ }
+ } else {
+ final OrderedLeafSetNode<?> readLeafList =
+ (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ } else {
+ insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+ readLeafList, false);
+ }
+ }
+ break;
+ default:
+ throw new RestconfDocumentedException(
+ "Used bad value of insert parameter. Possible values are first, last, before or after, "
+ + "but was: " + insert);
+ }
+ }
+ }
+
+ private void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
+ final boolean before) {
+ rWTransaction.delete(datastore, path.getParent());
+ final InstanceIdentifierContext<?> instanceIdentifier =
+ ControllerContext.getInstance().toInstanceIdentifier(point);
+ int p = 0;
+ for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+ if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+ break;
+ }
+ p++;
+ }
+ if (!before) {
+ p++;
+ }
+ int h = 0;
+ final NormalizedNode<?, ?> emptySubtree =
+ ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+ if (h == p) {
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ }
+ final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
+ rWTransaction.put(datastore, childPath, nodeChild);
+ h++;
+ }
+ }
+
+ private void insertWithPointListPut(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final SchemaContext schemaContext, final String point, final OrderedMapNode readList,
+ final boolean before) {
+ rWTransaction.delete(datastore, path.getParent());
+ final InstanceIdentifierContext<?> instanceIdentifier =
+ ControllerContext.getInstance().toInstanceIdentifier(point);
+ int p = 0;
+ for (final MapEntryNode mapEntryNode : readList.getValue()) {
+ if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+ break;
+ }
+ p++;
+ }
+ if (!before) {
+ p++;
+ }
+ int h = 0;
+ final NormalizedNode<?, ?> emptySubtree =
+ ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ for (final MapEntryNode mapEntryNode : readList.getValue()) {
+ if (h == p) {
+ simplePut(datastore, path, rWTransaction, schemaContext, payload);
+ }
+ final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
+ rWTransaction.put(datastore, childPath, mapEntryNode);
+ h++;
+ }
+ }
+
+ private void makePut(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
if (payload instanceof MapNode) {
final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
writeTransaction.put(datastore, childPath, child);
}
} else {
- ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
- writeTransaction.put(datastore, path, payload);
+ simplePut(datastore, path, writeTransaction, schemaContext, payload);
}
}
+ private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+ final DOMDataReadWriteTransaction writeTransaction, final SchemaContext schemaContext,
+ final NormalizedNode<?, ?> payload) {
+ ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
+ writeTransaction.put(datastore, path, payload);
+ }
+
private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path) {
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
*
* @author Thomas Pantelis
*/
+@Deprecated
public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseable {
private final static Logger LOG = LoggerFactory.getLogger(JSONRestconfServiceImpl.class);
private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
@Override
- public void put(String uriPath, String payload) throws OperationFailedException {
+ public void put(final String uriPath, final String payload, final UriInfo uriInfo) throws OperationFailedException {
Preconditions.checkNotNull(payload, "payload can't be null");
LOG.debug("put: uriPath: {}, payload: {}", uriPath, payload);
- InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
- NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, false);
+ final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
+ final NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, false);
LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier());
LOG.debug("Parsed NormalizedNode: {}", context.getData());
try {
- RestconfImpl.getInstance().updateConfigurationData(uriPath, context);
- } catch (Exception e) {
+ RestconfImpl.getInstance().updateConfigurationData(uriPath, context, uriInfo);
+ } catch (final Exception e) {
propagateExceptionAs(uriPath, e, "PUT");
}
}
@Override
- public void post(String uriPath, String payload) throws OperationFailedException {
+ public void post(final String uriPath, final String payload, final UriInfo uriInfo)
+ throws OperationFailedException {
Preconditions.checkNotNull(payload, "payload can't be null");
LOG.debug("post: uriPath: {}, payload: {}", uriPath, payload);
- InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
- NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
+ final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
+ final NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier());
LOG.debug("Parsed NormalizedNode: {}", context.getData());
try {
- RestconfImpl.getInstance().createConfigurationData(uriPath, context, null);
- } catch (Exception e) {
+ RestconfImpl.getInstance().createConfigurationData(uriPath, context, uriInfo);
+ } catch (final Exception e) {
propagateExceptionAs(uriPath, e, "POST");
}
}
@Override
- public void delete(String uriPath) throws OperationFailedException {
+ public void delete(final String uriPath) throws OperationFailedException {
LOG.debug("delete: uriPath: {}", uriPath);
try {
RestconfImpl.getInstance().deleteConfigurationData(uriPath);
- } catch (Exception e) {
+ } catch (final Exception e) {
propagateExceptionAs(uriPath, e, "DELETE");
}
}
@Override
- public Optional<String> get(String uriPath, LogicalDatastoreType datastoreType) throws OperationFailedException {
+ public Optional<String> get(final String uriPath, final LogicalDatastoreType datastoreType, final UriInfo uriInfo)
+ throws OperationFailedException {
LOG.debug("get: uriPath: {}", uriPath);
try {
NormalizedNodeContext readData;
if(datastoreType == LogicalDatastoreType.CONFIGURATION) {
- readData = RestconfImpl.getInstance().readConfigurationData(uriPath, null);
+ readData = RestconfImpl.getInstance().readConfigurationData(uriPath, uriInfo);
} else {
- readData = RestconfImpl.getInstance().readOperationalData(uriPath, null);
+ readData = RestconfImpl.getInstance().readOperationalData(uriPath, uriInfo);
}
- Optional<String> result = Optional.of(toJson(readData));
+ final Optional<String> result = Optional.of(toJson(readData));
LOG.debug("get returning: {}", result.get());
return result;
- } catch (Exception e) {
+ } catch (final Exception e) {
if(!isDataMissing(e)) {
propagateExceptionAs(uriPath, e, "GET");
}
}
@Override
- public Optional<String> invokeRpc(String uriPath, Optional<String> input) throws OperationFailedException {
+ public Optional<String> invokeRpc(final String uriPath, final Optional<String> input) throws OperationFailedException {
Preconditions.checkNotNull(uriPath, "uriPath can't be null");
- String actualInput = input.isPresent() ? input.get() : null;
+ final String actualInput = input.isPresent() ? input.get() : null;
LOG.debug("invokeRpc: uriPath: {}, input: {}", uriPath, actualInput);
try {
NormalizedNodeContext outputContext;
if(actualInput != null) {
- InputStream entityStream = new ByteArrayInputStream(actualInput.getBytes(StandardCharsets.UTF_8));
- NormalizedNodeContext inputContext = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
+ final InputStream entityStream = new ByteArrayInputStream(actualInput.getBytes(StandardCharsets.UTF_8));
+ final NormalizedNodeContext inputContext = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
LOG.debug("Parsed YangInstanceIdentifier: {}", inputContext.getInstanceIdentifierContext()
.getInstanceIdentifier());
if(outputContext.getData() != null) {
output = toJson(outputContext);
}
- } catch (Exception e) {
+ } catch (final Exception e) {
propagateExceptionAs(uriPath, e, "RPC");
}
public void close() {
}
- private String toJson(NormalizedNodeContext readData) throws IOException {
- NormalizedNodeJsonBodyWriter writer = new NormalizedNodeJsonBodyWriter();
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ private String toJson(final NormalizedNodeContext readData) throws IOException {
+ final NormalizedNodeJsonBodyWriter writer = new NormalizedNodeJsonBodyWriter();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
writer.writeTo(readData, NormalizedNodeContext.class, null, EMPTY_ANNOTATIONS,
MediaType.APPLICATION_JSON_TYPE, null, outputStream );
return outputStream.toString(StandardCharsets.UTF_8.name());
}
- private boolean isDataMissing(Exception e) {
+ private boolean isDataMissing(final Exception e) {
boolean dataMissing = false;
if(e instanceof RestconfDocumentedException) {
- RestconfDocumentedException rde = (RestconfDocumentedException)e;
+ final RestconfDocumentedException rde = (RestconfDocumentedException)e;
if(!rde.getErrors().isEmpty()) {
if(rde.getErrors().get(0).getErrorTag() == ErrorTag.DATA_MISSING) {
dataMissing = true;
return dataMissing;
}
- private static void propagateExceptionAs(String uriPath, Exception e, String operation) throws OperationFailedException {
+ private static void propagateExceptionAs(final String uriPath, final Exception e, final String operation) throws OperationFailedException {
LOG.debug("Error for uriPath: {}", uriPath, e);
if(e instanceof RestconfDocumentedException) {
throw new OperationFailedException(String.format("%s failed for URI %s", operation, uriPath), e);
}
- private static RpcError[] toRpcErrors(List<RestconfError> from) {
- RpcError[] to = new RpcError[from.size()];
+ private static RpcError[] toRpcErrors(final List<RestconfError> from) {
+ final RpcError[] to = new RpcError[from.size()];
int i = 0;
- for(RestconfError e: from) {
+ for(final RestconfError e: from) {
to[i++] = RpcResultBuilder.newError(toRpcErrorType(e.getErrorType()), e.getErrorTag().getTagValue(),
e.getErrorMessage());
}
return to;
}
- private static ErrorType toRpcErrorType(RestconfError.ErrorType errorType) {
+ private static ErrorType toRpcErrorType(final RestconfError.ErrorType errorType) {
switch(errorType) {
case TRANSPORT: {
return ErrorType.TRANSPORT;
*/
package org.opendaylight.netconf.sal.restconf.impl;
+import java.util.HashMap;
+import java.util.Map;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
private final InstanceIdentifierContext<? extends SchemaNode> context;
private final NormalizedNode<?,?> data;
private final WriterParameters writerParameters;
+ private Map<String, Object> headers = new HashMap<>();
public NormalizedNodeContext(final InstanceIdentifierContext<? extends SchemaNode> context,
- final NormalizedNode<?, ?> data, WriterParameters writerParameters) {
+ final NormalizedNode<?, ?> data, final WriterParameters writerParameters) {
this.context = context;
this.data = data;
this.writerParameters = writerParameters;
}
+ public NormalizedNodeContext(final InstanceIdentifierContext<? extends SchemaNode> context,
+ final NormalizedNode<?, ?> data, final WriterParameters writerParameters,
+ final Map<String, Object> headers) {
+ this.context = context;
+ this.data = data;
+ this.writerParameters = writerParameters;
+ this.headers = headers;
+ }
+
public NormalizedNodeContext(final InstanceIdentifierContext<? extends SchemaNode> context,
final NormalizedNode<?, ?> data) {
this.context = context;
this.writerParameters = new WriterParameters.WriterParametersBuilder().build();
}
+ public NormalizedNodeContext(final InstanceIdentifierContext<? extends SchemaNode> context,
+ final NormalizedNode<?, ?> data, final Map<String, Object> headers) {
+ this.context = context;
+ this.data = data;
+ // default writer parameters
+ this.writerParameters = new WriterParameters.WriterParametersBuilder().build();
+ this.headers = headers;
+ }
+
+
public InstanceIdentifierContext<? extends SchemaNode> getInstanceIdentifierContext() {
- return context;
+ return this.context;
}
public NormalizedNode<?, ?> getData() {
- return data;
+ return this.data;
}
public WriterParameters getWriterParameters() {
- return writerParameters;
+ return this.writerParameters;
+ }
+
+ /**
+ * @return
+ */
+ public Map<String, Object> getNewHeaders() {
+ return this.headers;
}
}
@Override
public String toString() {
- return uriParameterName;
+ return this.uriParameterName;
}
}
public static WriterParameters parseWriterParameters(final UriInfo info) {
- WriterParameters.WriterParametersBuilder wpBuilder = new WriterParameters.WriterParametersBuilder();
+ return parseParams(info, false);
+ }
+
+ private static WriterParameters parseParams(final UriInfo info, final boolean tagged) {
+ final WriterParameters.WriterParametersBuilder wpBuilder = new WriterParameters.WriterParametersBuilder();
+ wpBuilder.setTagged(tagged);
if(info == null) {
return wpBuilder.build();
}
return wpBuilder.build();
}
+ public static WriterParameters parseWriterParameters(final UriInfo uriInfo, final boolean tagged) {
+ return parseParams(uriInfo, tagged);
+ }
+
}
import com.google.common.util.concurrent.Futures;
import java.net.URI;
import java.net.URISyntaxException;
+import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
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.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
private static final String SCOPE_PARAM_NAME = "scope";
+ private static final String OUTPUT_TYPE_PARAM_NAME = "notification-output-type";
+
private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
static {
try {
final Date eventSubscriptionAugRevision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
- NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
+ NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null),
+ NETCONF_BASE_PAYLOAD_NAME);
SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
eventSubscriptionAugRevision);
- SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"),
- QName.create(SAL_REMOTE_AUGMENT, "datastore")));
+ SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(
+ QName.create(SAL_REMOTE_AUGMENT, "scope"),
+ QName.create(SAL_REMOTE_AUGMENT, "datastore"),
+ QName.create(SAL_REMOTE_AUGMENT, "notification-output-type")));
} catch (final ParseException e) {
final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date";
LOG.debug(errMsg);
throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
- final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+ .toMountPointIdentifier(identifier);
final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
final Set<Module> modules = this.controllerContext.getAllModules(mountPoint);
final MapNode mountPointModulesMap = makeModuleMapNode(modules);
DOMMountPoint mountPoint = null;
final SchemaContext schemaContext;
if (identifier.contains(ControllerContext.MOUNT)) {
- final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+ .toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
schemaContext = mountPoint.getSchemaContext();
final SchemaContext schemaContext = this.controllerContext.getGlobalSchema();
final Set<String> availableStreams = Notificator.getStreamNames();
final Module restconfModule = getRestconfModule();
- final DataSchemaNode streamSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
- Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
+ final DataSchemaNode streamSchemaNode = this.controllerContext
+ .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
Set<Module> modules = null;
DOMMountPoint mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
- final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+ .toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
modules = this.controllerContext.getAllModules(mountPoint);
} else {
- final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
+ final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to "
+ + "end with ";
LOG.debug(errMsg + ControllerContext.MOUNT + " for " + identifier);
- throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL,
+ ErrorTag.INVALID_VALUE);
}
return operationsFromModulesToNormalizedContext(modules, mountPoint);
final Set<Module> fakeModules = new HashSet<>();
fakeModules.add(fakeModule);
final SchemaContext fakeSchemaCtx = EffectiveSchemaContext.resolveSchemaContext(fakeModules);
- final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext = new InstanceIdentifierContext<>(
- null, fakeContSchNode, mountPoint, fakeSchemaCtx);
+ final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext =
+ new InstanceIdentifierContext<>(null, fakeContSchNode, mountPoint, fakeSchemaCtx);
return new NormalizedNodeContext(instanceIdentifierContext, containerBuilder.build());
}
}
@Override
- public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+ public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
final CheckedFuture<DOMRpcResult, DOMRpcException> response;
final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
final SchemaContext schemaContext;
+
if (mountPoint != null) {
final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
if ( ! mountRpcServices.isPresent()) {
throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
ErrorTag.INVALID_VALUE);
} else if (cause instanceof DOMRpcImplementationNotAvailableException) {
- throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
+ throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION,
+ ErrorTag.OPERATION_NOT_SUPPORTED);
}
- throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",cause);
+ throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
+ cause);
} else {
- throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",e);
+ throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
+ e);
}
} catch (final CancellationException e) {
final String errMsg = "The operation was cancelled while executing.";
}
}
- private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
+ private CheckedFuture<DOMRpcResult, DOMRpcException>
+ invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
final ContainerNode value = (ContainerNode) payload.getData();
final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
String streamName = (String) CREATE_DATA_SUBSCR;
+ NotificationOutputType outputType = null;
if (!pathIdentifier.isEmpty()) {
final String fullRestconfIdentifier = DATA_SUBSCR
+ this.controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
- LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME);
+ LogicalDatastoreType datastore =
+ parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME);
datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
scope = scope == null ? DEFAULT_SCOPE : scope;
+ outputType = parseEnumTypeParameter(value, NotificationOutputType.class,
+ OUTPUT_TYPE_PARAM_NAME);
+ outputType = outputType == null ? NotificationOutputType.XML : outputType;
+
streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
+ "/scope=" + scope);
}
final QName outputQname = QName.create(rpcQName, "output");
final QName streamNameQname = QName.create(rpcQName, "stream-name");
- final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
- .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+ final ContainerNode output =
+ ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
+ .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
if (!Notificator.existListenerFor(streamName)) {
- Notificator.createListener(pathIdentifier, streamName);
+ Notificator.createListener(pathIdentifier, streamName, outputType);
}
final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
@Override
public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
+ boolean withDefa_used = false;
+ String withDefa = null;
+
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ switch (entry.getKey()) {
+ case "with-defaults":
+ if (!withDefa_used) {
+ withDefa_used = true;
+ withDefa = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("With-defaults parameter can be used only once.");
+ }
+ break;
+ }
+ }
+ boolean tagged = false;
+ if (withDefa_used) {
+ if (withDefa.equals("report-all-tagged")) {
+ tagged = true;
+ withDefa = null;
+ }
+ if (withDefa.equals("report-all")) {
+ withDefa = null;
+ }
+ }
+
final InstanceIdentifierContext<?> iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
final DOMMountPoint mountPoint = iiWithData.getMountPoint();
NormalizedNode<?, ?> data = null;
final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
if (mountPoint != null) {
- data = this.broker.readConfigurationData(mountPoint, normalizedII);
+ data = this.broker.readConfigurationData(mountPoint, normalizedII, withDefa);
} else {
- data = this.broker.readConfigurationData(normalizedII);
+ data = this.broker.readConfigurationData(normalizedII, withDefa);
}
if(data == null) {
final String errMsg = "Request could not be completed because the relevant data model content does not exist ";
LOG.debug(errMsg + identifier);
throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
}
- return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo));
+ return new NormalizedNodeContext(iiWithData, data,
+ QueryParametersParser.parseWriterParameters(uriInfo, tagged));
}
@Override
}
@Override
- public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
+ public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ boolean insert_used = false;
+ boolean point_used = false;
+ String insert = null;
+ String point = null;
+
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ switch (entry.getKey()) {
+ case "insert":
+ if (!insert_used) {
+ insert_used = true;
+ insert = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Insert parameter can be used only once.");
+ }
+ break;
+ case "point":
+ if (!point_used) {
+ point_used = true;
+ point = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Point parameter can be used only once.");
+ }
+ break;
+ default:
+ throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
+ }
+ }
+
+ if (point_used && !insert_used) {
+ throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
+ }
+ if (point_used && (insert.equals("first") || insert.equals("last"))) {
+ throw new RestconfDocumentedException(
+ "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
+ }
+
Preconditions.checkNotNull(identifier);
+
final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
validateInput(iiWithData.getSchemaNode(), payload);
while(true) {
if (mountPoint != null) {
- result = this.broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData());
+ result = this.broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData(), insert,
+ point);
} else {
result = this.broker.commitConfigurationDataPut(this.controllerContext.getGlobalSchema(), normalizedII,
- payload.getData());
+ payload.getData(), insert, point);
}
final CountDownLatch waiter = new CountDownLatch(1);
Futures.addCallback(result.getFutureOfPutData(), new FutureCallback<Void>() {
if ( ! uriKeyValue.equals(dataKeyValue)) {
final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
- "' specified in the URI doesn't match the value '" + dataKeyValue + "' specified in the message body. ";
+ "' specified in the URI doesn't match the value '" + dataKeyValue
+ + "' specified in the message body. ";
throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
}
}
@Override
- public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+ public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
return createConfigurationData(payload, uriInfo);
}
if (payload == null) {
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
-
- // FIXME: move this to parsing stage (we can have augmentation nodes here which do not have namespace)
-// final URI payloadNS = payload.getData().getNodeType().getNamespace();
-// if (payloadNS == null) {
-// throw new RestconfDocumentedException(
-// "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
-// ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
-// }
-
final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
+ boolean insert_used = false;
+ boolean point_used = false;
+ String insert = null;
+ String point = null;
+
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ switch (entry.getKey()) {
+ case "insert":
+ if (!insert_used) {
+ insert_used = true;
+ insert = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Insert parameter can be used only once.");
+ }
+ break;
+ case "point":
+ if (!point_used) {
+ point_used = true;
+ point = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Point parameter can be used only once.");
+ }
+ break;
+ default:
+ throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
+ }
+ }
+
+ if (point_used && !insert_used) {
+ throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
+ }
+ if (point_used && (insert.equals("first") || insert.equals("last"))) {
+ throw new RestconfDocumentedException(
+ "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
+ }
+
CheckedFuture<Void, TransactionCommitFailedException> future;
if (mountPoint != null) {
- future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
+ future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData(), insert,
+ point);
} else {
future = this.broker.commitConfigurationDataPost(this.controllerContext.getGlobalSchema(), normalizedII,
- payload.getData());
+ payload.getData(), insert, point);
}
final CountDownLatch waiter = new CountDownLatch(1);
}
}
- private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
+ private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint,
+ final YangInstanceIdentifier normalizedII) {
if(uriInfo == null) {
// This is null if invoked internally
return null;
}
final CountDownLatch waiter = new CountDownLatch(1);
+ final ResultOperation result = new ResultOperation();
Futures.addCallback(future, new FutureCallback<Void>() {
@Override
public void onSuccess(final Void result) {
- handlerLoggerDelete(null);
+ LOG.trace("Successfuly delete data.");
waiter.countDown();
}
@Override
public void onFailure(final Throwable t) {
waiter.countDown();
- handlerLoggerDelete(t);
+ result.setFailed(t);
}
});
LOG.warn(msg);
throw new RestconfDocumentedException(msg, e);
}
-
+ if (result.failed() != null) {
+ final Throwable t = result.failed();
+ final String errMsg = "Error while deleting data";
+ LOG.info(errMsg, t);
+ throw new RestconfDocumentedException(errMsg, RestconfError.ErrorType.APPLICATION,
+ RestconfError.ErrorTag.OPERATION_FAILED, t);
+ }
return Response.status(Status.OK).build();
}
- protected void handlerLoggerDelete(final Throwable t) {
- if (t != null) {
- final String errMsg = "Error while deleting data";
- LOG.info(errMsg, t);
- throw new RestconfDocumentedException(errMsg, t);
- } else {
- LOG.trace("Successfuly delete data.");
+ private class ResultOperation {
+ private Throwable t = null;
+
+ public void setFailed(final Throwable t) {
+ this.t = t;
+ }
+
+ public Throwable failed() {
+ return this.t;
}
}
* </ul>
*/
@Override
- public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ boolean startTime_used = false;
+ boolean stopTime_used = false;
+ boolean filter_used = false;
+ Date start = null;
+ Date stop = null;
+ String filter = null;
+
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ switch (entry.getKey()) {
+ case "start-time":
+ if (!startTime_used) {
+ startTime_used = true;
+ start = parseDateFromQueryParam(entry);
+ } else {
+ throw new RestconfDocumentedException("Start-time parameter can be used only once.");
+ }
+ break;
+ case "stop-time":
+ if (!stopTime_used) {
+ stopTime_used = true;
+ stop = parseDateFromQueryParam(entry);
+ } else {
+ throw new RestconfDocumentedException("Stop-time parameter can be used only once.");
+ }
+ break;
+ case "filter":
+ if (!filter_used) {
+ filter_used = true;
+ filter = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Filter parameter can be used only once.");
+ }
+ break;
+ default:
+ throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey());
+ }
+ }
+ if(!startTime_used && stopTime_used){
+ throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter.");
+ }
+ URI response = null;
if (identifier.contains(DATA_SUBSCR)) {
- return dataSubs(identifier, uriInfo);
+ response = dataSubs(identifier, uriInfo, start, stop, filter);
} else if (identifier.contains(NOTIFICATION_STREAM)) {
- return notifStream(identifier, uriInfo);
+ response = notifStream(identifier, uriInfo, start, stop, filter);
+ }
+
+ if(response != null){
+ // prepare node with value of location
+ final InstanceIdentifierContext<?> iid = prepareIIDSubsStreamOutput();
+ final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> builder = ImmutableLeafNodeBuilder
+ .create().withValue(response.toString());
+ builder.withNodeIdentifier(
+ NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location")));
+
+ // prepare new header with location
+ final Map<String, Object> headers = new HashMap<>();
+ headers.put("Location", response);
+
+ return new NormalizedNodeContext(iid, builder.build(), headers);
}
+
final String msg = "Bad type of notification of sal-remote";
LOG.warn(msg);
throw new RestconfDocumentedException(msg);
}
+ private Date parseDateFromQueryParam(final Entry<String, List<String>> entry) {
+ final DateAndTime event = new DateAndTime(entry.getValue().iterator().next());
+ String numOf_ms = "";
+ final String value = event.getValue();
+ if (value.contains(".")) {
+ numOf_ms = numOf_ms + ".";
+ final int lastChar = value.contains("Z") ? value.indexOf("Z") : (value.contains("+") ? value.indexOf("+")
+ : (value.contains("-") ? value.indexOf("-") : value.length()));
+ for (int i = 0; i < (lastChar - value.indexOf(".") - 1); i++) {
+ numOf_ms = numOf_ms + "S";
+ }
+ }
+ String zone = "";
+ if (!value.contains("Z")) {
+ zone = zone + "XXX";
+ }
+ final DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" + numOf_ms + zone);
+
+ try {
+ return dateFormatter.parse(value.contains("Z") ? value.replace('T', ' ').substring(0, value.indexOf("Z"))
+ : value.replace('T', ' '));
+ } catch (final ParseException e) {
+ throw new RestconfDocumentedException("Cannot parse of value in date: " + value + e);
+ }
+ }
+
+ /**
+ * @return {@link InstanceIdentifierContext} of location leaf for
+ * notification
+ */
+ private InstanceIdentifierContext<?> prepareIIDSubsStreamOutput() {
+ final QName qnameBase = QName.create("subscribe:to:notification", "2016-10-28", "notifi");
+ final SchemaContext schemaCtx = ControllerContext.getInstance().getGlobalSchema();
+ final DataSchemaNode location = ((ContainerSchemaNode) schemaCtx
+ .findModuleByNamespaceAndRevision(qnameBase.getNamespace(), qnameBase.getRevision())
+ .getDataChildByName(qnameBase)).getDataChildByName(QName.create(qnameBase, "location"));
+ final List<PathArgument> path = new ArrayList<>();
+ path.add(NodeIdentifier.create(qnameBase));
+ path.add(NodeIdentifier.create(QName.create(qnameBase, "location")));
+
+ return new InstanceIdentifierContext<SchemaNode>(YangInstanceIdentifier.create(path), location, null,
+ schemaCtx);
+ }
+
/**
* Register notification listener by stream name
*
* - stream name
* @param uriInfo
* - uriInfo
- * @return {@link Response}
+ * @param stop
+ * - stop-time of getting notification
+ * @param start
+ * - start-time of getting notification
+ * @param filter
+ * - indicate wh ich subset of allpossible events are of interest
+ * @return {@link URI} of location
*/
- private Response notifStream(final String identifier, final UriInfo uriInfo) {
+ private URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
+ final String filter) {
final String streamName = Notificator.createStreamNameFromUri(identifier);
if (Strings.isNullOrEmpty(streamName)) {
throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
for (final NotificationListenerAdapter listener : listeners) {
this.broker.registerToListenNotification(listener);
+ listener.setQueryParams(start, stop, filter);
}
final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
- return Response.status(Status.OK).location(uriToWebsocketServer).build();
+ return uriToWebsocketServer;
}
/**
* - stream name
* @param uriInfo
* - uri info
- * @return {@link Response}
+ * @param stop
+ * - start-time of getting notification
+ * @param start
+ * - stop-time of getting notification
+ * @param filter
+ * - indicate which subset of all possible events are of interest
+ * @return {@link URI} of location
*/
- private Response dataSubs(final String identifier, final UriInfo uriInfo) {
+ private URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
+ final String filter) {
final String streamName = Notificator.createStreamNameFromUri(identifier);
if (Strings.isNullOrEmpty(streamName)) {
throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
if (listener == null) {
throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
}
+ listener.setQueryParams(start, stop, filter);
final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
- return Response.status(Status.OK).location(uriToWebsocketServer).build();
+ return uriToWebsocketServer;
}
@Override
- public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext context, final UriInfo uriInfo) {
+ public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext context,
+ final UriInfo uriInfo) {
if (context == null) {
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
return null;
}
final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode =
- ((AugmentationNode) augNode.get()).getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
+ ((AugmentationNode) augNode.get())
+ .getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
if (!enumNode.isPresent()) {
return null;
}
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicLong;
-import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
AtomicLong failurePut = new AtomicLong();
AtomicLong failureDelete = new AtomicLong();
- private static final StatisticsRestconfServiceWrapper INSTANCE = new StatisticsRestconfServiceWrapper(RestconfImpl.getInstance());
+ private static final StatisticsRestconfServiceWrapper INSTANCE =
+ new StatisticsRestconfServiceWrapper(RestconfImpl.getInstance());
final RestconfService delegate;
@Override
public Object getRoot() {
- return delegate.getRoot();
+ return this.delegate.getRoot();
}
@Override
public NormalizedNodeContext getModules(final UriInfo uriInfo) {
- return delegate.getModules(uriInfo);
+ return this.delegate.getModules(uriInfo);
}
@Override
public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
- return delegate.getModules(identifier, uriInfo);
+ return this.delegate.getModules(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
- return delegate.getModule(identifier, uriInfo);
+ return this.delegate.getModule(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
- return delegate.getOperations(uriInfo);
+ return this.delegate.getOperations(uriInfo);
}
@Override
public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
- return delegate.getOperations(identifier, uriInfo);
+ return this.delegate.getOperations(identifier, uriInfo);
}
@Override
- public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
- rpc.incrementAndGet();
- return delegate.invokeRpc(identifier, payload, uriInfo);
+ public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ this.rpc.incrementAndGet();
+ return this.delegate.invokeRpc(identifier, payload, uriInfo);
}
@Override
public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
- rpc.incrementAndGet();
- return delegate.invokeRpc(identifier, noPayload, uriInfo);
+ this.rpc.incrementAndGet();
+ return this.delegate.invokeRpc(identifier, noPayload, uriInfo);
}
@Override
public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
- configGet.incrementAndGet();
+ this.configGet.incrementAndGet();
NormalizedNodeContext normalizedNodeContext = null;
try {
- normalizedNodeContext = delegate.readConfigurationData(identifier, uriInfo);
+ normalizedNodeContext = this.delegate.readConfigurationData(identifier, uriInfo);
if (normalizedNodeContext.getData() != null) {
- successGetConfig.incrementAndGet();
+ this.successGetConfig.incrementAndGet();
}
else {
- failureGetConfig.incrementAndGet();
+ this.failureGetConfig.incrementAndGet();
}
- } catch (Exception e) {
- failureGetConfig.incrementAndGet();
+ } catch (final Exception e) {
+ this.failureGetConfig.incrementAndGet();
throw e;
}
return normalizedNodeContext;
@Override
public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) {
- operationalGet.incrementAndGet();
+ this.operationalGet.incrementAndGet();
NormalizedNodeContext normalizedNodeContext = null;
try {
- normalizedNodeContext = delegate.readOperationalData(identifier, uriInfo);
+ normalizedNodeContext = this.delegate.readOperationalData(identifier, uriInfo);
if (normalizedNodeContext.getData() != null) {
- successGetOperational.incrementAndGet();
+ this.successGetOperational.incrementAndGet();
}
else {
- failureGetOperational.incrementAndGet();
+ this.failureGetOperational.incrementAndGet();
}
- } catch (Exception e) {
- failureGetOperational.incrementAndGet();
+ } catch (final Exception e) {
+ this.failureGetOperational.incrementAndGet();
throw e;
}
return normalizedNodeContext;
}
@Override
- public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
- configPut.incrementAndGet();
+ public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ this.configPut.incrementAndGet();
Response response = null;
try {
- response = delegate.updateConfigurationData(identifier, payload);
+ response = this.delegate.updateConfigurationData(identifier, payload, uriInfo);
if (response.getStatus() == Status.OK.getStatusCode()) {
- successPut.incrementAndGet();
+ this.successPut.incrementAndGet();
}
else {
- failurePut.incrementAndGet();
+ this.failurePut.incrementAndGet();
}
- } catch (Exception e) {
- failurePut.incrementAndGet();
+ } catch (final Exception e) {
+ this.failurePut.incrementAndGet();
throw e;
}
return response;
}
@Override
- public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
- configPost.incrementAndGet();
+ public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload,
+ final UriInfo uriInfo) {
+ this.configPost.incrementAndGet();
Response response = null;
try {
- response = delegate.createConfigurationData(identifier, payload, uriInfo);
+ response = this.delegate.createConfigurationData(identifier, payload, uriInfo);
if (response.getStatus() == Status.OK.getStatusCode()) {
- successPost.incrementAndGet();
+ this.successPost.incrementAndGet();
}
else {
- failurePost.incrementAndGet();
+ this.failurePost.incrementAndGet();
}
- } catch (Exception e) {
- failurePost.incrementAndGet();
+ } catch (final Exception e) {
+ this.failurePost.incrementAndGet();
throw e;
}
return response;
@Override
public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
- configPost.incrementAndGet();
+ this.configPost.incrementAndGet();
Response response = null;
try {
- response = delegate.createConfigurationData(payload, uriInfo);
+ response = this.delegate.createConfigurationData(payload, uriInfo);
if (response.getStatus() == Status.OK.getStatusCode()) {
- successPost.incrementAndGet();
+ this.successPost.incrementAndGet();
}
else {
- failurePost.incrementAndGet();
+ this.failurePost.incrementAndGet();
}
- }catch (Exception e) {
- failurePost.incrementAndGet();
+ }catch (final Exception e) {
+ this.failurePost.incrementAndGet();
throw e;
}
return response;
@Override
public Response deleteConfigurationData(final String identifier) {
- configDelete.incrementAndGet();
+ this.configDelete.incrementAndGet();
Response response = null;
try {
- response = delegate.deleteConfigurationData(identifier);
+ response = this.delegate.deleteConfigurationData(identifier);
if (response.getStatus() == Status.OK.getStatusCode()) {
- successDelete.incrementAndGet();
+ this.successDelete.incrementAndGet();
}
else {
- failureDelete.incrementAndGet();
+ this.failureDelete.incrementAndGet();
}
- } catch (Exception e) {
- failureDelete.incrementAndGet();
+ } catch (final Exception e) {
+ this.failureDelete.incrementAndGet();
throw e;
}
return response;
}
@Override
- public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
- return delegate.subscribeToStream(identifier, uriInfo);
+ public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ return this.delegate.subscribeToStream(identifier, uriInfo);
}
@Override
public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
- return delegate.getAvailableStreams(uriInfo);
+ return this.delegate.getAvailableStreams(uriInfo);
}
@Override
- public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext payload, final UriInfo
- uriInfo) {
- return delegate.patchConfigurationData(identifier, payload, uriInfo);
+ public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext payload,
+ final UriInfo uriInfo) {
+ return this.delegate.patchConfigurationData(identifier, payload, uriInfo);
}
@Override
public PATCHStatusContext patchConfigurationData(final PATCHContext payload, final UriInfo uriInfo) {
- return delegate.patchConfigurationData(payload, uriInfo);
+ return this.delegate.patchConfigurationData(payload, uriInfo);
}
public BigInteger getConfigDelete() {
- return BigInteger.valueOf(configDelete.get());
+ return BigInteger.valueOf(this.configDelete.get());
}
public BigInteger getConfigGet() {
- return BigInteger.valueOf(configGet.get());
+ return BigInteger.valueOf(this.configGet.get());
}
public BigInteger getConfigPost() {
- return BigInteger.valueOf(configPost.get());
+ return BigInteger.valueOf(this.configPost.get());
}
public BigInteger getConfigPut() {
- return BigInteger.valueOf(configPut.get());
+ return BigInteger.valueOf(this.configPut.get());
}
public BigInteger getOperationalGet() {
- return BigInteger.valueOf(operationalGet.get());
+ return BigInteger.valueOf(this.operationalGet.get());
}
public BigInteger getRpc() {
- return BigInteger.valueOf(rpc.get());
+ return BigInteger.valueOf(this.rpc.get());
}
public BigInteger getSuccessGetConfig() {
- return BigInteger.valueOf(successGetConfig.get());
+ return BigInteger.valueOf(this.successGetConfig.get());
}
public BigInteger getSuccessGetOperational() {
- return BigInteger.valueOf(successGetOperational.get());
+ return BigInteger.valueOf(this.successGetOperational.get());
}
public BigInteger getSuccessPost() {
- return BigInteger.valueOf(successPost.get());
+ return BigInteger.valueOf(this.successPost.get());
}
public BigInteger getSuccessPut() {
- return BigInteger.valueOf(successPut.get());
+ return BigInteger.valueOf(this.successPut.get());
}
public BigInteger getSuccessDelete() {
- return BigInteger.valueOf(successDelete.get());
+ return BigInteger.valueOf(this.successDelete.get());
}
public BigInteger getFailureGetConfig() {
- return BigInteger.valueOf(failureGetConfig.get());
+ return BigInteger.valueOf(this.failureGetConfig.get());
}
public BigInteger getFailureGetOperational() {
- return BigInteger.valueOf(failureGetOperational.get());
+ return BigInteger.valueOf(this.failureGetOperational.get());
}
public BigInteger getFailurePost() {
- return BigInteger.valueOf(failurePost.get());
+ return BigInteger.valueOf(this.failurePost.get());
}
public BigInteger getFailurePut() {
- return BigInteger.valueOf(failurePut.get());
+ return BigInteger.valueOf(this.failurePut.get());
}
public BigInteger getFailureDelete() {
- return BigInteger.valueOf(failureDelete.get());
+ return BigInteger.valueOf(this.failureDelete.get());
}
}
\ No newline at end of file
package org.opendaylight.netconf.sal.restconf.impl;
-import com.google.common.base.Optional;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
public class WriterParameters {
- private final Optional<Integer> depth;
+ private final String content;
+ private final Integer depth;
+ private final List<Set<QName>> fields;
private final boolean prettyPrint;
+ private final boolean tagged;
private WriterParameters(final WriterParametersBuilder builder) {
- this.prettyPrint = builder.prettyPrint;
+ this.content = builder.content;
this.depth = builder.depth;
+ this.fields = builder.fields;
+ this.prettyPrint = builder.prettyPrint;
+ this.tagged = builder.tagged;
+ }
+
+ public String getContent() {
+ return this.content;
}
- public Optional<Integer> getDepth() {
- return depth;
+ public Integer getDepth() {
+ return this.depth;
+ }
+
+ public List<Set<QName>> getFields() {
+ return this.fields;
}
public boolean isPrettyPrint() {
- return prettyPrint;
+ return this.prettyPrint;
+ }
+
+ public boolean isTagged() {
+ return this.tagged;
}
public static class WriterParametersBuilder {
- private Optional<Integer> depth = Optional.absent();
+ private String content;
+ private Integer depth;
+ private List<Set<QName>> fields;
private boolean prettyPrint;
+ private boolean tagged;
- public WriterParametersBuilder() {
- }
+ public WriterParametersBuilder() {}
- public Optional<Integer> getDepth() {
- return depth;
+ public WriterParametersBuilder setContent(final String content) {
+ this.content = content;
+ return this;
}
public WriterParametersBuilder setDepth(final int depth) {
- this.depth = Optional.of(depth);
+ this.depth = depth;
return this;
}
- public boolean isPrettyPrint() {
- return prettyPrint;
+ public WriterParametersBuilder setFields(final List<Set<QName>> fields) {
+ this.fields = fields;
+ return this;
}
public WriterParametersBuilder setPrettyPrint(final boolean prettyPrint) {
public WriterParameters build() {
return new WriterParameters(this);
}
+
+ public void setTagged(final boolean tagged) {
+ this.tagged = tagged;
+ }
}
}
-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
+import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
-import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+import org.json.JSONObject;
+import org.json.XML;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.slf4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
/**
* {@link ListenerAdapter} is responsible to track events, which occurred by changing data in data source.
private Set<Channel> subscribers = new ConcurrentSet<>();
private final EventBus eventBus;
private final EventBusChangeRecorder eventBusChangeRecorder;
+ private final NotificationOutputType outputType;
+ private Date start = null;
+ private Date stop = null;
+ private String filter = null;
/**
- * Creates new {@link ListenerAdapter} listener specified by path and stream name.
+ * Creates new {@link ListenerAdapter} listener specified by path and stream
+ * name.
*
* @param path
* Path to data in data store.
* @param streamName
* The name of the stream.
+ * @param outputType
+ * - type of output on notification (JSON, XML)
*/
- ListenerAdapter(final YangInstanceIdentifier path, final String streamName) {
+ ListenerAdapter(final YangInstanceIdentifier path, final String streamName,
+ final NotificationOutputType outputType) {
+ this.outputType = outputType;
Preconditions.checkNotNull(path);
Preconditions.checkArgument((streamName != null) && !streamName.isEmpty());
this.path = path;
@Override
public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
- if (!change.getCreatedData().isEmpty() || !change.getUpdatedData().isEmpty()
- || !change.getRemovedPaths().isEmpty()) {
- final String xml = prepareXmlFrom(change);
+ final Date now = new Date();
+ if (this.stop != null) {
+ if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) {
+ checkFilter(change);
+ }
+ if (this.stop.compareTo(now) < 0) {
+ try {
+ this.close();
+ } catch (final Exception e) {
+ throw new RestconfDocumentedException("Problem with unregister listener." + e);
+ }
+ }
+ } else if (this.start != null) {
+ if (this.start.compareTo(now) < 0) {
+ this.start = null;
+ checkFilter(change);
+ }
+ } else {
+ checkFilter(change);
+ }
+ }
+
+ /**
+ * Check if is filter used and then prepare and post data do client
+ *
+ * @param change
+ * - data of notification
+ */
+ private void checkFilter(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+ final String xml = prepareXmlFrom(change);
+ if (this.filter == null) {
+ prepareAndPostData(xml);
+ } else {
+ try {
+ if (parseFilterParam(xml)) {
+ prepareAndPostData(xml);
+ }
+ } catch (final Exception e) {
+ throw new RestconfDocumentedException("Problem while parsing filter.", e);
+ }
+ }
+ }
+
+ /**
+ * Parse and evaluate filter value by xml
+ *
+ * @param xml
+ * - notification data in xml
+ * @return true or false - depends on filter expression and data of
+ * notifiaction
+ * @throws Exception
+ */
+ private boolean parseFilterParam(final String xml) throws Exception {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ final DocumentBuilder builder = factory.newDocumentBuilder();
+ final Document docOfXml = builder.parse(new InputSource(new StringReader(xml)));
+ final XPath xPath = XPathFactory.newInstance().newXPath();
+ return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
+ }
+
+ /**
+ * Prepare data of notification and data to client
+ *
+ * @param xml
+ */
+ private void prepareAndPostData(final String xml) {
final Event event = new Event(EventType.NOTIFY);
- event.setData(xml);
+ if (this.outputType.equals(NotificationOutputType.JSON)) {
+ final JSONObject jsonObject = XML.toJSONObject(xml);
+ event.setData(jsonObject.toString());
+ } else {
+ event.setData(xml);
+ }
this.eventBus.post(event);
- }
}
/**
final Document doc = createDocument();
final Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
"notification");
+
doc.appendChild(notificationElement);
final Element eventTimeElement = doc.createElement("eventTime");
final Element dataChangedNotificationEventElement = doc.createElementNS(
"urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification");
+
addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, change,
schemaContext, dataContextTree);
notificationElement.appendChild(dataChangedNotificationEventElement);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
- transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8)));
+ transformer.transform(new DOMSource(doc),
+ new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8)));
final byte[] charData = out.toByteArray();
return new String(charData, "UTF-8");
} catch (TransformerException | UnsupportedEncodingException e) {
* @param operation
* {@link Operation}
*/
- private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data, final Element element,
- final Operation operation) {
+ private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data,
+ final Element element, final Operation operation) {
if ((data == null) || data.isEmpty()) {
return;
}
* {@link Operation}
* @return {@link Node} node represented by changed event element.
*/
- private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path, final Operation operation) {
+ private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path,
+ final Operation operation) {
final Element dataChangeEventElement = doc.createElement("data-change-event");
final Element pathElement = doc.createElement("path");
addPathAsValueToElement(path, pathElement);
* {@link Element}
*/
private void addPathAsValueToElement(final YangInstanceIdentifier path, final Element element) {
- // Map< key = namespace, value = prefix>
- final Map<String, String> prefixes = new HashMap<>();
final YangInstanceIdentifier normalizedPath = ControllerContext.getInstance().toXpathRepresentation(path);
final StringBuilder textContent = new StringBuilder();
- // FIXME: BUG-1281: this is duplicated code from yangtools (BUG-1275)
for (final PathArgument pathArgument : normalizedPath.getPathArguments()) {
if (pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier) {
continue;
}
textContent.append("/");
- writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes);
+ writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType());
if (pathArgument instanceof NodeIdentifierWithPredicates) {
final Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
for (final QName keyValue : predicates.keySet()) {
final String predicateValue = String.valueOf(predicates.get(keyValue));
textContent.append("[");
- writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes);
+ writeIdentifierWithNamespacePrefix(element, textContent, keyValue);
textContent.append("='");
textContent.append(predicateValue);
textContent.append("'");
* StringBuilder
* @param qName
* QName
- * @param prefixes
- * Map of namespaces and prefixes.
*/
private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent,
- final QName qName, final Map<String, String> prefixes) {
- final String namespace = qName.getNamespace().toString();
- String prefix = prefixes.get(namespace);
- if (prefix == null) {
- prefix = generateNewPrefix(prefixes.values());
- }
-
- element.setAttribute("xmlns:" + prefix, namespace);
- textContent.append(prefix);
- prefixes.put(namespace, prefix);
+ final QName qName) {
+ final Module module = ControllerContext.getInstance().getGlobalSchema()
+ .findModuleByNamespaceAndRevision(qName.getNamespace(), qName.getRevision());
+ textContent.append(module.getName());
textContent.append(":");
textContent.append(qName.getLocalName());
}
}
}
+ /**
+ * Set query parameters for listener
+ *
+ * @param start
+ * - start-time of getting notification
+ * @param stop
+ * - stop-time of getting notification
+ * @param filter
+ * - indicate which subset of all possible events are of interest
+ */
+ public void setQueryParams(final Date start, final Date stop, final String filter) {
+ this.start = start;
+ this.stop = stop;
+ this.filter = filter;
+ }
+
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
+import java.io.Writer;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.Executors;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
import org.json.JSONObject;
-import org.json.XML;
import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
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.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
/**
* {@link NotificationListenerAdapter} is responsible to track events on
private static final TransformerFactory FACTORY = TransformerFactory.newInstance();
private final String streamName;
- private ListenerRegistration<DOMNotificationListener> registration;
- private Set<Channel> subscribers = new ConcurrentSet<>();
private final EventBus eventBus;
private final EventBusChangeRecorder eventBusChangeRecorder;
private final SchemaPath path;
private final String outputType;
+ private Date start = null;
+ private Date stop = null;
+ private String filter;
+
+ private SchemaContext schemaContext;
+ private DOMNotification notification;
+ private ListenerRegistration<DOMNotificationListener> registration;
+ private Set<Channel> subscribers = new ConcurrentSet<>();
/**
* Set path of listener and stream name, register event bus.
@Override
public void onNotification(final DOMNotification notification) {
- final String xml = prepareXmlFrom(notification);
+ this.schemaContext = ControllerContext.getInstance().getGlobalSchema();
+ this.notification = notification;
+ final Date now = new Date();
+ if (this.stop != null) {
+ if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) {
+ checkFilter();
+ }
+ if (this.stop.compareTo(now) < 0) {
+ try {
+ this.close();
+ } catch (final Exception e) {
+ throw new RestconfDocumentedException("Problem with unregister listener." + e);
+ }
+ }
+ } else if (this.start != null) {
+ if (this.start.compareTo(now) < 0) {
+ this.start = null;
+ checkFilter();
+ }
+ } else {
+ checkFilter();
+ }
+ }
+
+ /**
+ * Check if is filter used and then prepare and post data do client
+ *
+ */
+ private void checkFilter() {
+ final String xml = prepareXml();
+ if (this.filter == null) {
+ prepareAndPostData(xml);
+ } else {
+ try {
+ if (parseFilterParam(xml)) {
+ prepareAndPostData(xml);
+ }
+ } catch (final Exception e) {
+ throw new RestconfDocumentedException("Problem while parsing filter.", e);
+ }
+ }
+ }
+
+ /**
+ * Parse and evaluate filter value by xml
+ *
+ * @param xml
+ * - notification data in xml
+ * @return true or false - depends on filter expression and data of
+ * notifiaction
+ * @throws Exception
+ */
+ private boolean parseFilterParam(final String xml) throws Exception {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ final DocumentBuilder builder = factory.newDocumentBuilder();
+ final Document docOfXml = builder.parse(new InputSource(new StringReader(xml)));
+ final XPath xPath = XPathFactory.newInstance().newXPath();
+ return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
+ }
+
+ /**
+ * Prepare data of notification and data to client
+ *
+ * @param xml
+ */
+ private void prepareAndPostData(final String xml) {
final Event event = new Event(EventType.NOTIFY);
if (this.outputType.equals("JSON")) {
- final JSONObject jsonObject = XML.toJSONObject(xml);
- event.setData(jsonObject.toString());
+ event.setData(prepareJson());
} else {
event.setData(xml);
}
this.eventBus.post(event);
}
+ /**
+ * Prepare json from notification data
+ *
+ * @return json as {@link String}
+ */
+ private String prepareJson() {
+ final JSONObject json = new JSONObject();
+ json.put("ietf-restconf:notification",
+ new JSONObject(writeBodyToString()).put("event-time", ListenerAdapter.toRFC3339(new Date())));
+ return json.toString();
+ }
+
+ private String writeBodyToString() {
+ final Writer writer = new StringWriter();
+ final NormalizedNodeStreamWriter jsonStream =
+ JSONNormalizedNodeStreamWriter.createExclusiveWriter(JSONCodecFactory.create(this.schemaContext),
+ this.notification.getType(), null, JsonWriterFactory.createJsonWriter(writer));
+ final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+ try {
+ nodeWriter.write(this.notification.getBody());
+ nodeWriter.close();
+ } catch (final IOException e) {
+ throw new RestconfDocumentedException("Problem while writing body of notification to JSON. ", e);
+ }
+ return writer.toString();
+ }
+
/**
* Checks if exists at least one {@link Channel} subscriber.
*
this.eventBus.post(event);
}
- private String prepareXmlFrom(final DOMNotification notification) {
- final SchemaContext schemaContext = ControllerContext.getInstance().getGlobalSchema();
+ private String prepareXml() {
final Document doc = ListenerAdapter.createDocument();
final Element notificationElement =
doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
final Element eventTimeElement = doc.createElement("eventTime");
eventTimeElement.setTextContent(ListenerAdapter.toRFC3339(new Date()));
notificationElement.appendChild(eventTimeElement);
- final String notificationNamespace = notification.getType().getLastComponent().getNamespace().toString();
+
final Element notificationEventElement = doc.createElementNS(
- notificationNamespace, "event");
- addValuesToNotificationEventElement(doc, notificationEventElement, notification, schemaContext);
+ "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "create-notification-stream");
+ addValuesToNotificationEventElement(doc, notificationEventElement, this.notification, this.schemaContext);
notificationElement.appendChild(notificationEventElement);
try {
final DOMResult domResult = writeNormalizedNode(body,
YangInstanceIdentifier.create(body.getIdentifier()), schemaContext);
final Node result = doc.importNode(domResult.getNode().getFirstChild(), true);
- element.appendChild(result);
+ final Element dataElement = doc.createElement("notification");
+ dataElement.appendChild(result);
+ element.appendChild(dataElement);
} catch (final IOException e) {
LOG.error("Error in writer ", e);
} catch (final XMLStreamException e) {
private enum EventType {
REGISTER, DEREGISTER, NOTIFY
}
+
+ /**
+ * Set query parameters for listener
+ *
+ * @param start
+ * - start-time of getting notification
+ * @param stop
+ * - stop-time of getting notification
+ * @param filter
+ * - indicate which subset of all possible events are of interest
+ */
+ public void setQueryParams(final Date start, final Date stop, final String filter) {
+ this.start = start;
+ this.stop = stop;
+ this.filter = filter;
+ }
}
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*/
public class Notificator {
- private static Map<String, ListenerAdapter> listenersByStreamName = new ConcurrentHashMap<>();
- private static Map<String, List<NotificationListenerAdapter>> notificationListenersByStreamName = new ConcurrentHashMap<>();
+ private static Map<String, ListenerAdapter> dataChangeListener = new ConcurrentHashMap<>();
+ private static Map<String, List<NotificationListenerAdapter>> notificationListenersByStreamName =
+ new ConcurrentHashMap<>();
private static final Logger LOG = LoggerFactory.getLogger(Notificator.class);
private static final Lock lock = new ReentrantLock();
* Returns list of all stream names
*/
public static Set<String> getStreamNames() {
- return listenersByStreamName.keySet();
+ return dataChangeListener.keySet();
}
/**
* @return {@link ListenerAdapter} specified by stream name.
*/
public static ListenerAdapter getListenerFor(final String streamName) {
- return listenersByStreamName.get(streamName);
+ return dataChangeListener.get(streamName);
}
/**
* @return True if the listener exist, false otherwise.
*/
public static boolean existListenerFor(final String streamName) {
- return listenersByStreamName.containsKey(streamName);
+ return dataChangeListener.containsKey(streamName);
}
/**
- * Creates new {@link ListenerAdapter} listener from {@link YangInstanceIdentifier} path and stream name.
+ * Creates new {@link ListenerAdapter} listener from
+ * {@link YangInstanceIdentifier} path and stream name.
*
* @param path
* Path to data in data repository.
* @param streamName
* The name of the stream.
- * @return New {@link ListenerAdapter} listener from {@link YangInstanceIdentifier} path and stream name.
+ * @param outputType
+ * - Spcific type of output for notifications - XML or JSON
+ * @return New {@link ListenerAdapter} listener from
+ * {@link YangInstanceIdentifier} path and stream name.
*/
- public static ListenerAdapter createListener(final YangInstanceIdentifier path, final String streamName) {
- final ListenerAdapter listener = new ListenerAdapter(path, streamName);
+ public static ListenerAdapter createListener(final YangInstanceIdentifier path, final String streamName,
+ final NotificationOutputType outputType) {
+ final ListenerAdapter listener = new ListenerAdapter(path, streamName, outputType);
try {
lock.lock();
- listenersByStreamName.put(streamName, listener);
+ dataChangeListener.put(streamName, listener);
} finally {
lock.unlock();
}
* Removes all listeners.
*/
public static void removeAllListeners() {
- for (final ListenerAdapter listener : listenersByStreamName.values()) {
+ for (final ListenerAdapter listener : dataChangeListener.values()) {
try {
listener.close();
} catch (final Exception e) {
}
try {
lock.lock();
- listenersByStreamName = new ConcurrentHashMap<>();
+ dataChangeListener = new ConcurrentHashMap<>();
} finally {
lock.unlock();
}
}
try {
lock.lock();
- listenersByStreamName.remove(listener.getStreamName());
+ dataChangeListener.remove(listener.getStreamName());
} finally {
lock.unlock();
}
*/
public static boolean existNotificationListenerFor(final String streamName) {
return notificationListenersByStreamName.containsKey(streamName);
-
}
+ /**
+ * Prepare listener for notification ({@link NotificationDefinition})
+ *
+ * @param paths
+ * - paths of notifications
+ * @param streamName
+ * - name of stream (generated by paths)
+ * @param outputType
+ * - type of output for onNotification - XML or JSON
+ * @return List of {@link NotificationListenerAdapter} by paths
+ */
public static List<NotificationListenerAdapter> createNotificationListener(final List<SchemaPath> paths,
final String streamName, final String outputType) {
final List<NotificationListenerAdapter> listListeners = new ArrayList<>();
* </ul>
* </ul>
*
- * We used old revision {@link Draft17.RestconfModule#REVISION} of restconf yang
+ * We used old revision {@link Draft18.RestconfModule#REVISION} of restconf yang
* because the latest restconf draft has to be supported by Yang 1.1 and we are
* not. Then, this is only partial implementation of the latest restconf draft.
*/
-public final class Draft17 {
+public final class Draft18 {
- private Draft17() {
+ private Draft18() {
throw new UnsupportedOperationException("Util class");
}
public static final String ERROR_LIST_SCHEMA_NODE = "error";
- public static final QName IETF_RESTCONF_QNAME = QName.create(Draft17.RestconfModule.NAMESPACE, Draft17.RestconfModule.REVISION,
- Draft17.RestconfModule.NAME);
+ public static final QName IETF_RESTCONF_QNAME = QName.create(Draft18.RestconfModule.NAMESPACE, Draft18.RestconfModule.REVISION,
+ Draft18.RestconfModule.NAME);
public static final QName ERRORS_CONTAINER_QNAME = QName.create(IETF_RESTCONF_QNAME, ERRORS_CONTAINER_SCHEMA_NODE);
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.restconf.common.wrapper.services.ServicesWrapperImpl;
import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
import org.opendaylight.restconf.handlers.RpcServiceHandler;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.restconf.handlers.TransactionChainHandler;
import org.slf4j.LoggerFactory;
/**
- * Provider for restconf draft17.
+ * Provider for restconf draft18.
*
*/
public class RestConnectorProvider implements Provider, RestConnector, AutoCloseable {
final DOMRpcService rpcService = session.getService(DOMRpcService.class);
final RpcServiceHandler rpcServiceHandler = new RpcServiceHandler(rpcService);
+ final DOMNotificationService notificationService = session.getService(DOMNotificationService.class);
+ final NotificationServiceHandler notificationServiceHandler =
+ new NotificationServiceHandler(notificationService);
+
wrapperServices.setHandlers(schemaCtxHandler, RestConnectorProvider.mountPointServiceHandler,
- RestConnectorProvider.transactionChainHandler, brokerHandler, rpcServiceHandler);
+ RestConnectorProvider.transactionChainHandler, brokerHandler, rpcServiceHandler,
+ notificationServiceHandler);
}
/**
import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContentYangBodyWriter;
import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContentYinBodyWriter;
import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
-import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
-import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
import org.opendaylight.netconf.sal.rest.impl.PATCHJsonBodyWriter;
import org.opendaylight.netconf.sal.rest.impl.PATCHXmlBodyWriter;
import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper;
import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
import org.opendaylight.restconf.common.wrapper.services.ServicesWrapperImpl;
import org.opendaylight.restconf.jersey.providers.JsonToPATCHBodyReader;
+import org.opendaylight.restconf.jersey.providers.NormalizedNodeJsonBodyWriter;
+import org.opendaylight.restconf.jersey.providers.NormalizedNodeXmlBodyWriter;
import org.opendaylight.restconf.jersey.providers.XmlToPATCHBodyReader;
public class RestconfApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
- return ImmutableSet.<Class<?>> builder().add(NormalizedNodeJsonBodyWriter.class)
- .add(NormalizedNodeXmlBodyWriter.class).add(JsonNormalizedNodeBodyReader.class)
- .add(XmlNormalizedNodeBodyReader.class).add(SchemaExportContentYinBodyWriter.class)
+ return ImmutableSet.<Class<?>> builder()
+ .add(NormalizedNodeJsonBodyWriter.class).add(NormalizedNodeXmlBodyWriter.class)
+ .add(JsonNormalizedNodeBodyReader.class).add(XmlNormalizedNodeBodyReader.class)
+ .add(SchemaExportContentYinBodyWriter.class)
.add(JsonToPATCHBodyReader.class).add(XmlToPATCHBodyReader.class)
.add(PATCHJsonBodyWriter.class).add(PATCHXmlBodyWriter.class)
.add(SchemaExportContentYangBodyWriter.class).add(RestconfDocumentedExceptionMapper.class)
import java.util.Date;
import java.util.Set;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
/**
* Get {@link Module} by ietf-restconf qname from
- * {@link Draft17.RestconfModule}
+ * {@link Draft18.RestconfModule}
*
* @return {@link Module}
*/
public Module getRestconfModule() {
- return this.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME.getNamespace(),
- Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+ return this.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME.getNamespace(),
+ Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
}
/**
import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
import org.opendaylight.restconf.handlers.RpcServiceHandler;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.restconf.handlers.TransactionChainHandler;
import org.opendaylight.restconf.rest.services.impl.RestconfOperationsServiceImpl;
import org.opendaylight.restconf.rest.services.impl.RestconfSchemaServiceImpl;
import org.opendaylight.restconf.rest.services.impl.RestconfStreamsServiceImpl;
-import org.opendaylight.restconf.restful.services.api.TransactionServicesWrapper;
import org.opendaylight.restconf.restful.services.api.RestconfDataService;
import org.opendaylight.restconf.restful.services.api.RestconfInvokeOperationsService;
import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService;
+import org.opendaylight.restconf.restful.services.api.TransactionServicesWrapper;
import org.opendaylight.restconf.restful.services.impl.RestconfDataServiceImpl;
import org.opendaylight.restconf.restful.services.impl.RestconfInvokeOperationsServiceImpl;
import org.opendaylight.restconf.restful.services.impl.RestconfStreamsSubscriptionServiceImpl;
return this.delegRestSchService.getSchema(mountAndModuleId);
}
+ @Override
+ public Response readData(final UriInfo uriInfo) {
+ return this.delegRestconfDataService.readData(uriInfo);
+ }
+
@Override
public Response readData(final String identifier, final UriInfo uriInfo) {
return this.delegRestconfDataService.readData(identifier, uriInfo);
}
@Override
- public Response putData(final String identifier, final NormalizedNodeContext payload) {
- return this.delegRestconfDataService.putData(identifier, payload);
+ public Response putData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+ return this.delegRestconfDataService.putData(identifier, payload, uriInfo);
}
@Override
}
@Override
- public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
return this.delegRestconfSubscrService.subscribeToStream(identifier, uriInfo);
}
final DOMMountPointServiceHandler domMountPointServiceHandler,
final TransactionChainHandler transactionChainHandler,
final DOMDataBrokerHandler domDataBrokerHandler,
- final RpcServiceHandler rpcServiceHandler) {
+ final RpcServiceHandler rpcServiceHandler, final NotificationServiceHandler notificationServiceHandler) {
this.delegRestModService = new RestconfModulesServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
this.delegRestOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
this.delegRestSchService = new RestconfSchemaServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
domMountPointServiceHandler);
this.delegRestconfInvokeOpsService = new RestconfInvokeOperationsServiceImpl(rpcServiceHandler,
schemaCtxHandler);
- this.delegRestconfSubscrService = new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler);
+ this.delegRestconfSubscrService =
+ new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler, notificationServiceHandler,
+ schemaCtxHandler);
}
}
--- /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.handlers;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+
+public class NotificationServiceHandler implements Handler<DOMNotificationService> {
+
+ private final DOMNotificationService notificationService;
+
+ /**
+ * Set DOMNotificationService
+ *
+ * @param notificationService
+ * - DOMNotificationService
+ */
+ public NotificationServiceHandler(final DOMNotificationService notificationService) {
+ this.notificationService = notificationService;
+ }
+
+ @Override
+ public DOMNotificationService get() {
+ return this.notificationService;
+ }
+
+}
public DOMRpcService get() {
return this.rpcService;
}
-
-
}
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.slf4j.LoggerFactory;
@Provider
-@Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
+@Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
public class JsonNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<NormalizedNodeContext> {
private final static Logger LOG = LoggerFactory.getLogger(JsonNormalizedNodeBodyReader.class);
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.slf4j.LoggerFactory;
@Provider
-@Consumes({Draft17.MediaTypes.PATCH + RestconfConstants.JSON})
+@Consumes({Draft18.MediaTypes.PATCH + RestconfConstants.JSON})
public class JsonToPATCHBodyReader extends AbstractIdentifierAwareJaxRsProvider
implements MessageBodyReader<PATCHContext> {
--- /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.jersey.providers;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.restconf.Draft18;
+import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+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;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+@Provider
+@Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
+public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
+
+ private static final int DEFAULT_INDENT_SPACES_NUM = 2;
+
+ @Override
+ public boolean isWriteable(final Class<?> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType) {
+ return type.equals(NormalizedNodeContext.class);
+ }
+
+ @Override
+ public long getSize(final NormalizedNodeContext t,
+ final Class<?> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ 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 {
+ final NormalizedNode<?, ?> data = t.getData();
+ if (data == null) {
+ return;
+ }
+
+ @SuppressWarnings("unchecked")
+ final InstanceIdentifierContext<SchemaNode> context =
+ (InstanceIdentifierContext<SchemaNode>) t.getInstanceIdentifierContext();
+ final SchemaPath path = context.getSchemaNode().getPath();
+ final JsonWriter jsonWriter = createJsonWriter(entityStream,
+ t.getWriterParameters().isPrettyPrint());
+
+ jsonWriter.beginObject();
+ writeNormalizedNode(jsonWriter, path, context, data,
+ t.getWriterParameters().getDepth(), t.getWriterParameters().getFields());
+ jsonWriter.endObject();
+ jsonWriter.flush();
+ }
+
+ private void writeNormalizedNode(final JsonWriter jsonWriter,
+ final SchemaPath path,
+ final InstanceIdentifierContext<SchemaNode> context,
+ final NormalizedNode<?, ?> data,
+ final Integer depth,
+ final List<Set<QName>> fields) throws IOException {
+ final RestconfNormalizedNodeWriter nnWriter;
+
+ if (context.getSchemaNode() instanceof RpcDefinition) {
+ /*
+ * RpcDefinition is not supported as initial codec in JSONStreamWriter,
+ * so we need to emit initial output declaration..
+ */
+ nnWriter = createNormalizedNodeWriter(
+ context,
+ ((RpcDefinition) context.getSchemaNode()).getOutput().getPath(),
+ jsonWriter,
+ depth,
+ fields);
+ jsonWriter.name("output");
+ jsonWriter.beginObject();
+ writeChildren(nnWriter, (ContainerNode) data);
+ jsonWriter.endObject();
+ } else {
+ if (SchemaPath.ROOT.equals(path)) {
+ nnWriter = createNormalizedNodeWriter(context, path, jsonWriter, depth, fields);
+ } else {
+ nnWriter = createNormalizedNodeWriter(context, path.getParent(), jsonWriter, depth, fields);
+ }
+
+ if (data instanceof MapEntryNode) {
+ // Restconf allows returning one list item. We need to wrap it
+ // in map node in order to serialize it properly
+ nnWriter.write(
+ ImmutableNodes.mapNodeBuilder(data.getNodeType()).withChild(((MapEntryNode) data)).build());
+ } else {
+ nnWriter.write(data);
+ }
+ }
+
+ nnWriter.flush();
+ }
+
+ private void writeChildren(final RestconfNormalizedNodeWriter nnWriter,
+ final ContainerNode data) throws IOException {
+ for (final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
+ nnWriter.write(child);
+ }
+ }
+
+ private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final InstanceIdentifierContext<SchemaNode> context,
+ final SchemaPath path,
+ final JsonWriter jsonWriter,
+ final Integer depth,
+ final List<Set<QName>> fields) {
+
+ final SchemaNode schema = context.getSchemaNode();
+ final JSONCodecFactory codecs = getCodecFactory(context);
+
+ final URI initialNs;
+ if ((schema instanceof DataSchemaNode)
+ && !((DataSchemaNode)schema).isAugmenting()
+ && !(schema instanceof SchemaContext)) {
+ initialNs = schema.getQName().getNamespace();
+ } else if (schema instanceof RpcDefinition) {
+ initialNs = schema.getQName().getNamespace();
+ } else {
+ initialNs = null;
+ }
+ final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter.createNestedWriter(
+ codecs, path, initialNs, jsonWriter);
+ return ParameterAwareNormalizedNodeWriter.forStreamWriter(streamWriter, depth, fields);
+ }
+
+ private JsonWriter createJsonWriter(final OutputStream entityStream, final boolean prettyPrint) {
+ if (prettyPrint) {
+ return JsonWriterFactory.createJsonWriter(
+ new OutputStreamWriter(entityStream, StandardCharsets.UTF_8), DEFAULT_INDENT_SPACES_NUM);
+ } else {
+ return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, StandardCharsets.UTF_8));
+ }
+ }
+
+ private JSONCodecFactory getCodecFactory(final InstanceIdentifierContext<?> context) {
+ // TODO: Performance: Cache JSON Codec factory and schema context
+ return JSONCodecFactory.create(context.getSchemaContext());
+ }
+}
--- /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.jersey.providers;
+
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Set;
+import javanet.staxutils.IndentingXMLStreamWriter;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.xml.XMLConstants;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.restconf.Draft18;
+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;
+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.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+@Provider
+@Produces({ Draft18.MediaTypes.DATA + RestconfConstants.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
+
+ private static final XMLOutputFactory XML_FACTORY;
+
+ static {
+ XML_FACTORY = XMLOutputFactory.newFactory();
+ XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
+ }
+
+ @Override
+ public boolean isWriteable(final Class<?> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType) {
+ return type.equals(NormalizedNodeContext.class);
+ }
+
+ @Override
+ public long getSize(final NormalizedNodeContext t,
+ final Class<?> type,
+ final Type genericType,
+ final Annotation[] annotations,
+ final MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ 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 {
+ final InstanceIdentifierContext<?> pathContext = t.getInstanceIdentifierContext();
+ if (t.getData() == null) {
+ return;
+ }
+
+ XMLStreamWriter xmlWriter;
+ try {
+ xmlWriter = XML_FACTORY.createXMLStreamWriter(entityStream);
+ if (t.getWriterParameters().isPrettyPrint()) {
+ xmlWriter = new IndentingXMLStreamWriter(xmlWriter);
+ }
+ } catch (final XMLStreamException | FactoryConfigurationError e) {
+ throw new IllegalStateException(e);
+ }
+ final NormalizedNode<?, ?> data = t.getData();
+ final SchemaPath schemaPath = pathContext.getSchemaNode().getPath();
+
+ writeNormalizedNode(xmlWriter, schemaPath, pathContext, data, t.getWriterParameters().getDepth(),
+ t.getWriterParameters().getFields());
+ }
+
+ private void writeNormalizedNode(final XMLStreamWriter xmlWriter,
+ final SchemaPath path,
+ final InstanceIdentifierContext<?> pathContext,
+ final NormalizedNode<?, ?> data,
+ final Integer depth,
+ final List<Set<QName>> fields) throws IOException {
+ final RestconfNormalizedNodeWriter nnWriter;
+ final SchemaContext schemaCtx = pathContext.getSchemaContext();
+
+ if (pathContext.getSchemaNode() instanceof RpcDefinition) {
+ /*
+ * RpcDefinition is not supported as initial codec in XMLStreamWriter,
+ * so we need to emit initial output declaration..
+ */
+ nnWriter = createNormalizedNodeWriter(
+ xmlWriter,
+ schemaCtx,
+ ((RpcDefinition) pathContext.getSchemaNode()).getOutput().getPath(),
+ depth,
+ fields);
+ writeElements(xmlWriter, nnWriter, (ContainerNode) data);
+ } else {
+ if (SchemaPath.ROOT.equals(path)) {
+ nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, path, depth, fields);
+ } else {
+ nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, path.getParent(), depth, fields);
+ }
+
+ if (data instanceof MapEntryNode) {
+ // Restconf allows returning one list item. We need to wrap it
+ // in map node in order to serialize it properly
+ nnWriter.write(ImmutableNodes.mapNodeBuilder(data.getNodeType()).addChild((MapEntryNode) data).build());
+ } else {
+ nnWriter.write(data);
+ }
+ }
+
+ nnWriter.flush();
+ }
+
+ private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final XMLStreamWriter xmlWriter,
+ final SchemaContext schemaContext,
+ final SchemaPath schemaPath,
+ final Integer depth,
+ final List<Set<QName>> fields) {
+ final NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter
+ .create(xmlWriter, schemaContext, schemaPath);
+ return ParameterAwareNormalizedNodeWriter.forStreamWriter(xmlStreamWriter, depth, fields);
+ }
+
+ private void writeElements(final XMLStreamWriter xmlWriter,
+ final RestconfNormalizedNodeWriter nnWriter,
+ final ContainerNode data) throws IOException {
+ try {
+ final QName name = data.getNodeType();
+ xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX,
+ name.getLocalName(), name.getNamespace().toString());
+ xmlWriter.writeDefaultNamespace(name.getNamespace().toString());
+ for (final NormalizedNode<?,?> child : data.getValue()) {
+ nnWriter.write(child);
+ }
+ nnWriter.flush();
+ xmlWriter.writeEndElement();
+ xmlWriter.flush();
+ } catch (final XMLStreamException e) {
+ Throwables.propagate(e);
+ }
+ }
+}
--- /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.jersey.providers;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+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.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is an experimental iterator over a {@link NormalizedNode}. This is essentially
+ * the opposite of a {@link javax.xml.stream.XMLStreamReader} -- unlike instantiating an iterator over
+ * the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows
+ * us to write multiple nodes.
+ */
+@Beta
+public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNodeWriter {
+ private static final QName ROOT_DATA_QNAME = QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "data");
+
+ private final NormalizedNodeStreamWriter writer;
+ private final Integer maxDepth;
+ protected final List<Set<QName>> fields;
+ protected int currentDepth = 0;
+
+ private ParameterAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final Integer maxDepth,
+ final List<Set<QName>> fields) {
+ this.writer = Preconditions.checkNotNull(writer);
+ this.maxDepth = maxDepth;
+ this.fields = fields;
+ }
+
+ protected final NormalizedNodeStreamWriter getWriter() {
+ return writer;
+ }
+
+ /**
+ * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
+ *
+ * @param writer Back-end writer
+ * @param maxDepth Maximal depth to write
+ * @param fields Selected child nodes to write
+ * @return A new instance.
+ */
+ public static ParameterAwareNormalizedNodeWriter forStreamWriter(
+ final NormalizedNodeStreamWriter writer, final Integer maxDepth, final List<Set<QName>> fields) {
+ return forStreamWriter(writer, true, maxDepth, fields);
+ }
+
+ /**
+ * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. Unlike the simple
+ * {@link #forStreamWriter(NormalizedNodeStreamWriter, Integer, List)}
+ * method, this allows the caller to switch off RFC6020 XML compliance, providing better
+ * throughput. The reason is that the XML mapping rules in RFC6020 require the encoding
+ * to emit leaf nodes which participate in a list's key first and in the order in which
+ * they are defined in the key. For JSON, this requirement is completely relaxed and leaves
+ * can be ordered in any way we see fit. The former requires a bit of work: first a lookup
+ * for each key and then for each emitted node we need to check whether it was already
+ * emitted.
+ *
+ * @param writer Back-end writer
+ * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant.
+ * @param maxDepth Maximal depth to write
+ * @param fields Selected child nodes to write
+ * @return A new instance.
+ */
+ public static ParameterAwareNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer,
+ final boolean orderKeyLeaves,
+ final Integer maxDepth,
+ final List<Set<QName>> fields) {
+ if (orderKeyLeaves) {
+ return new OrderedParameterAwareNormalizedNodeWriter(writer, maxDepth, fields);
+ } else {
+ return new ParameterAwareNormalizedNodeWriter(writer, maxDepth, fields);
+ }
+ }
+
+ /**
+ * Iterate over the provided {@link NormalizedNode} and emit write
+ * events to the encapsulated {@link NormalizedNodeStreamWriter}.
+ *
+ * @param node Node
+ * @return
+ * @throws IOException when thrown from the backing writer.
+ */
+ public final ParameterAwareNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+ if (wasProcessedAsCompositeNode(node)) {
+ return this;
+ }
+
+ if (wasProcessAsSimpleNode(node)) {
+ return this;
+ }
+
+ throw new IllegalStateException("It wasn't possible to serialize node " + node);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.flush();
+ writer.close();
+ }
+
+ /**
+ * Emit a best guess of a hint for a particular set of children. It evaluates the
+ * iterable to see if the size can be easily gotten to. If it is, we hint at the
+ * real number of child nodes. Otherwise we emit UNKNOWN_SIZE.
+ *
+ * @param children Child nodes
+ * @return Best estimate of the collection size required to hold all the children.
+ */
+ static final int childSizeHint(final Iterable<?> children) {
+ return (children instanceof Collection) ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
+ }
+
+ private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+ if (node instanceof LeafSetEntryNode) {
+ if (selectedByParameters(node, false)) {
+ final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>) node;
+ if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer).leafSetEntryNode(nodeAsLeafList.getNodeType(),
+ nodeAsLeafList.getValue(), nodeAsLeafList.getAttributes());
+ } else {
+ writer.leafSetEntryNode(nodeAsLeafList.getNodeType(), nodeAsLeafList.getValue());
+ }
+ }
+ return true;
+ } else if (node instanceof LeafNode) {
+ final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
+ if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer).leafNode(
+ nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue(), nodeAsLeaf.getAttributes());
+ } else {
+ writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
+ }
+ return true;
+ } else if (node instanceof AnyXmlNode) {
+ final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
+ writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if node should be written according to parameters fields and depth.
+ * See <a href="https://tools.ietf.org/html/draft-ietf-netconf-restconf-18#page-49">Restconf draft</a>.
+ * @param node Node to be written
+ * @param mixinParent {@code true} if parent is mixin, {@code false} otherwise
+ * @return {@code true} if node will be written, {@code false} otherwise
+ */
+ protected boolean selectedByParameters(final NormalizedNode<?, ?> node, final boolean mixinParent) {
+ // nodes to be written are not limited by fields, only by depth
+ if (fields == null) {
+ return (maxDepth == null || currentDepth < maxDepth);
+ }
+
+ // children of mixin nodes are never selected in fields but must be written if they are first in selected target
+ if (mixinParent && currentDepth == 0) {
+ return true;
+ }
+
+ // always write augmentation nodes
+ if (node instanceof AugmentationNode) {
+ return true;
+ }
+
+ // write only selected nodes
+ if (currentDepth > 0 && currentDepth <= fields.size()) {
+ if (fields.get(currentDepth - 1).contains(node.getNodeType())) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // after this depth only depth parameter is used to determine when to write node
+ return (maxDepth == null || currentDepth < maxDepth);
+ }
+
+ /**
+ * Emit events for all children and then emit an endNode() event.
+ *
+ * @param children Child iterable
+ * @param mixinParent {@code true} if parent is mixin, {@code false} otherwise
+ * @return True
+ * @throws IOException when the writer reports it
+ */
+ protected final boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children,
+ final boolean mixinParent) throws IOException {
+ for (final NormalizedNode<?, ?> child : children) {
+ if (selectedByParameters(child, mixinParent)) {
+ write(child);
+ }
+ }
+ writer.endNode();
+ return true;
+ }
+
+ protected boolean writeMapEntryChildren(final MapEntryNode mapEntryNode) throws IOException {
+ if (selectedByParameters(mapEntryNode, false)) {
+ writeChildren(mapEntryNode.getValue(), false);
+ } else if (fields == null && maxDepth != null && currentDepth == maxDepth) {
+ writeOnlyKeys(mapEntryNode.getIdentifier().getKeyValues());
+ }
+ return true;
+ }
+
+ private void writeOnlyKeys(final Map<QName, Object> keyValues) throws IllegalArgumentException, IOException {
+ for (final Map.Entry<QName, Object> entry : keyValues.entrySet()) {
+ writer.leafNode(new NodeIdentifier(entry.getKey()), entry.getValue());
+ }
+ writer.endNode();
+ }
+
+ protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+ if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer)
+ .startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
+ } else {
+ writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+ }
+ currentDepth++;
+ writeMapEntryChildren(node);
+ currentDepth--;
+ return true;
+ }
+
+ private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
+ boolean processedAsCompositeNode = false;
+ if (node instanceof ContainerNode) {
+ final ContainerNode n = (ContainerNode) node;
+ if (!n.getNodeType().equals(ROOT_DATA_QNAME)) {
+ if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer).startContainerNode(
+ n.getIdentifier(), childSizeHint(n.getValue()), n.getAttributes());
+ } else {
+ writer.startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ }
+ currentDepth++;
+ processedAsCompositeNode = writeChildren(n.getValue(), false);
+ currentDepth--;
+ } else {
+ // write child nodes of data root container
+ for (final NormalizedNode<?, ?> child : n.getValue()) {
+ currentDepth++;
+ if (selectedByParameters(child, false)) {
+ write(child);
+ }
+ currentDepth--;
+ processedAsCompositeNode = true;
+ }
+ }
+ }
+ else if (node instanceof MapEntryNode) {
+ processedAsCompositeNode = writeMapEntryNode((MapEntryNode) node);
+ }
+ else if (node instanceof UnkeyedListEntryNode) {
+ final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
+ writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.getValue()));
+ currentDepth++;
+ processedAsCompositeNode = writeChildren(n.getValue(), false);
+ currentDepth--;
+ }
+ else if (node instanceof ChoiceNode) {
+ final ChoiceNode n = (ChoiceNode) node;
+ writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ processedAsCompositeNode = writeChildren(n.getValue(), true);
+ }
+ else if (node instanceof AugmentationNode) {
+ final AugmentationNode n = (AugmentationNode) node;
+ writer.startAugmentationNode(n.getIdentifier());
+ processedAsCompositeNode = writeChildren(n.getValue(), true);
+ }
+ else if (node instanceof UnkeyedListNode) {
+ final UnkeyedListNode n = (UnkeyedListNode) node;
+ writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.getValue()));
+ processedAsCompositeNode = writeChildren(n.getValue(), false);
+ }
+ else if (node instanceof OrderedMapNode) {
+ final OrderedMapNode n = (OrderedMapNode) node;
+ writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ processedAsCompositeNode = writeChildren(n.getValue(), true);
+ }
+ else if (node instanceof MapNode) {
+ final MapNode n = (MapNode) node;
+ writer.startMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+ processedAsCompositeNode = writeChildren(n.getValue(), true);
+ }
+ else if (node instanceof LeafSetNode) {
+ final LeafSetNode<?> n = (LeafSetNode<?>) node;
+ if (node instanceof OrderedLeafSetNode) {
+ writer.startOrderedLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+ } else {
+ writer.startLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+ }
+ currentDepth++;
+ processedAsCompositeNode = writeChildren(n.getValue(), true);
+ currentDepth--;
+ }
+
+ return processedAsCompositeNode;
+ }
+
+ private static final class OrderedParameterAwareNormalizedNodeWriter extends ParameterAwareNormalizedNodeWriter {
+ private static final Logger LOG = LoggerFactory.getLogger(OrderedParameterAwareNormalizedNodeWriter.class);
+
+ OrderedParameterAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final Integer maxDepth,
+ final List<Set<QName>> fields) {
+ super(writer, maxDepth, fields);
+ }
+
+ @Override
+ protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+ final NormalizedNodeStreamWriter writer = getWriter();
+ if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+ ((NormalizedNodeStreamAttributeWriter) writer).startMapEntryNode(
+ node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
+ } else {
+ writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+ }
+
+ final Set<QName> qnames = node.getIdentifier().getKeyValues().keySet();
+ // Write out all the key children
+ currentDepth++;
+ for (final QName qname : qnames) {
+ final Optional<? extends NormalizedNode<?, ?>> child = node.getChild(new NodeIdentifier(qname));
+ if (child.isPresent()) {
+ if (selectedByParameters(child.get(), false)) {
+ write(child.get());
+ }
+ } else {
+ LOG.info("No child for key element {} found", qname);
+ }
+ }
+ currentDepth--;
+
+ currentDepth++;
+ // Write all the rest
+ final boolean result = writeChildren(Iterables.filter(node.getValue(), new Predicate<NormalizedNode<?, ?>>() {
+ @Override
+ public boolean apply(final NormalizedNode<?, ?> input) {
+ if (input instanceof AugmentationNode) {
+ return true;
+ }
+ if (!qnames.contains(input.getNodeType())) {
+ return true;
+ }
+
+ LOG.debug("Skipping key child {}", input);
+ return false;
+ }
+ }), false);
+ currentDepth--;
+ return result;
+ }
+ }
+}
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.w3c.dom.Element;
@Provider
-@Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+@Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<NormalizedNodeContext> {
private final static Logger LOG = LoggerFactory.getLogger(XmlNormalizedNodeBodyReader.class);
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.w3c.dom.NodeList;
@Provider
-@Consumes({Draft17.MediaTypes.PATCH + RestconfConstants.XML})
+@Consumes({Draft18.MediaTypes.PATCH + RestconfConstants.XML})
public class XmlToPATCHBodyReader extends AbstractIdentifierAwareJaxRsProvider implements
MessageBodyReader<PATCHContext> {
import org.opendaylight.yangtools.concepts.Codec;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
if (allCharsConsumed(variables)
|| (currentChar(variables.getOffset(), variables.getData()) == RestconfConstants.SLASH)) {
prepareIdentifier(qname, path, variables);
- path.add(variables.getCurrent().getIdentifier());
+ if (variables.getCurrent() == null) {
+ path.add(NodeIdentifier.create(qname));
+ } else {
+ path.add(variables.getCurrent().getIdentifier());
+ }
} else if (currentChar(variables.getOffset(),
variables.getData()) == ParserBuilderConstants.Deserializer.EQUAL) {
if (nextContextNode(qname, path, variables).getDataSchemaNode() instanceof ListSchemaNode) {
return qName;
}
}
- return QName.create(schemaNode.getQName().getNamespace(), schemaNode.getQName().getRevision(), value);
+ return QName.create(schemaNode.getQName().getNamespace(), schemaNode.getQName().getRevision(), nodeName);
}
private static String toNodeName(final String str) {
private static void prepareIdentifier(final QName qname, final List<PathArgument> path,
final MainVarsWrapper variables) {
final DataSchemaContextNode<?> currentNode = nextContextNode(qname, path, variables);
+ if (currentNode == null) {
+ return;
+ }
checkValid(!currentNode.isKeyedEntry(), "Entry " + qname + " requires key or value predicate to be present",
variables.getData(), variables.getOffset());
}
final MainVarsWrapper variables) {
variables.setCurrent(variables.getCurrent().getChild(qname));
DataSchemaContextNode<?> current = variables.getCurrent();
+ if (current == null) {
+ for (final RpcDefinition rpcDefinition : variables.getSchemaContext()
+ .findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision()).getRpcs()) {
+ if (rpcDefinition.getQName().getLocalName().equals(qname.getLocalName())) {
+ return null;
+ }
+ }
+ }
checkValid(current != null, qname + " is not correct schema node identifier.", variables.getData(),
variables.getOffset());
while (current.isMixin()) {
private final SchemaContext schemaContext;
private final String data;
+
private DataSchemaContextNode<?> current;
private int offset;
public MainVarsWrapper(final String data, final DataSchemaContextNode<?> current, final int offset,
final SchemaContext schemaContext) {
this.data = data;
- this.setCurrent(current);
- this.setOffset(offset);
+ this.current = current;
+ this.offset = offset;
this.schemaContext = schemaContext;
}
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
/**
*/
@GET
@Path("data/ietf-yang-library:modules-state")
- @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public NormalizedNodeContext getModules(@Context UriInfo uriInfo);
*/
@GET
@Path("data/ietf-yang-library:modules-state/{identifier:.+}")
- @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public NormalizedNodeContext getModules(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
*/
@GET
@Path("data/ietf-yang-library:modules-state/module/{identifier:.+}")
- @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public NormalizedNodeContext getModule(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
}
\ No newline at end of file
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
/**
*/
@GET
@Path("/operations")
- @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public NormalizedNodeContext getOperations(@Context UriInfo uriInfo);
*/
@GET
@Path("/operations/{identifier:.+}")
- @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public NormalizedNodeContext getOperations(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
}
\ No newline at end of file
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
/**
* @return {@link SchemaExportContext}
*/
@GET
- @Produces({ Draft17.MediaTypes.YANG, Draft17.MediaTypes.YIN + RestconfConstants.XML })
+ @Produces({ Draft18.MediaTypes.YANG, Draft18.MediaTypes.YIN + RestconfConstants.XML })
@Path("data/ietf-yang-library:modules/module/{identifier:.+}/schema")
SchemaExportContext getSchema(@PathParam("identifier") String identifier);
}
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
/**
*/
@GET
@Path("data/ietf-restconf-monitoring:restconf-state/streams")
- @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public NormalizedNodeContext getAvailableStreams(@Context UriInfo uriInfo);
}
\ No newline at end of file
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.common.references.SchemaContextRef;
import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
final MapNode moduleMap = RestconfMappingNodeUtil
.restconfMappingNode(schemaContextRef.getRestconfModule(), modules);
final DataSchemaNode moduleSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
- schemaContextRef.getRestconfModule(), Draft17.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+ schemaContextRef.getRestconfModule(), Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE);
Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
if (mountPoint == null) {
return new NormalizedNodeContext(
final MapNode mapNodes = RestconfMappingNodeUtil.restconfMappingNode(restconfModule, modules);
final DataSchemaNode schemaNode = RestconfSchemaUtil.getRestconfSchemaNode(restconfModule,
- Draft17.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
+ Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
Preconditions.checkState(schemaNode instanceof ContainerSchemaNode);
final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> modulContainerSchemaNodeBuilder = Builders
.containerBuilder((ContainerSchemaNode) schemaNode);
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.common.references.SchemaContextRef;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.restconf.rest.services.api.RestconfStreamsService;
final Set<String> availableStreams = Notificator.getStreamNames();
final DataSchemaNode streamListSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
- schemaContextRef.getRestconfModule(), Draft17.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
+ schemaContextRef.getRestconfModule(), Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
Preconditions.checkState(streamListSchemaNode instanceof ListSchemaNode);
final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamBuilder = Builders
.mapBuilder((ListSchemaNode) streamListSchemaNode);
}
final DataSchemaNode streamContSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
- schemaContextRef.getRestconfModule(), Draft17.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
+ schemaContextRef.getRestconfModule(), Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
Preconditions.checkState(streamContSchemaNode instanceof ContainerSchemaNode);
final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder = Builders
.containerBuilder((ContainerSchemaNode) streamContSchemaNode);
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
/**
*/
@GET
@Path("/data/{identifier:.+}")
- @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
- Response readData(@Encoded @PathParam("identifier") String identifier,
- @Context UriInfo uriInfo);
+ Response readData(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo);
+
+ /**
+ * Get target data resource from data root.
+ *
+ * @param uriInfo
+ * - URI info
+ * @return {@link NormalizedNodeContext}
+ */
+ @GET
+ @Path("/data")
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+ Response readData(@Context UriInfo uriInfo);
/**
* Create or replace the target data resource.
*/
@PUT
@Path("/data/{identifier:.+}")
- @Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
- Response putData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload);
+ Response putData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload,
+ @Context UriInfo uriInfo);
/**
* Create a data resource in target.
*/
@POST
@Path("/data/{identifier:.+}")
- @Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
Response postData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload,
@Context UriInfo uriInfo);
*/
@POST
@Path("/data")
- @Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
Response postData(NormalizedNodeContext payload, @Context UriInfo uriInfo);
*/
@PATCH
@Path("/data/{identifier:.+}")
- @Consumes({ Draft17.MediaTypes.PATCH + RestconfConstants.JSON, Draft17.MediaTypes.PATCH + RestconfConstants.XML })
- @Produces({ Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
- Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
+ @Consumes({ Draft18.MediaTypes.PATCH + RestconfConstants.JSON, Draft18.MediaTypes.PATCH + RestconfConstants.XML })
+ @Produces({ Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
+ Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
PATCHStatusContext patchData(@Encoded @PathParam("identifier") String identifier, PATCHContext context,
@Context UriInfo uriInfo);
*/
@PATCH
@Path("/data")
- @Consumes({ Draft17.MediaTypes.PATCH + RestconfConstants.JSON, Draft17.MediaTypes.PATCH + RestconfConstants.XML })
- @Produces({ Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
- Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
+ @Consumes({ Draft18.MediaTypes.PATCH + RestconfConstants.JSON, Draft18.MediaTypes.PATCH + RestconfConstants.XML })
+ @Produces({ Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
+ Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
PATCHStatusContext patchData(PATCHContext context, @Context UriInfo uriInfo);
}
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
/**
*/
@POST
@Path("/operations/{identifier:.+}")
- @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
- @Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+ @Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
NormalizedNodeContext invokeRpc(@Encoded @PathParam("identifier") String identifier,
NormalizedNodeContext payload, @Context UriInfo uriInfo);
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
/**
* Subscribing to streams
* - name of stream
* @param uriInfo
* - URI info
- * @return {@link Response}
+ * @return {@link NormalizedNodeContext}
*/
@GET
@Path("data/ietf-restconf-monitoring:restconf-state/streams/stream/{identifier:.+}")
- Response subscribeToStream(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo);
+ NormalizedNodeContext subscribeToStream(@Encoded @PathParam("identifier") String identifier,
+ @Context UriInfo uriInfo);
}
import com.google.common.base.Preconditions;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
+import java.util.Map.Entry;
import java.util.TimeZone;
import javax.annotation.Nonnull;
import javax.ws.rs.core.Response;
import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.netconf.sal.restconf.impl.WriterParameters;
import org.opendaylight.restconf.RestConnectorProvider;
import org.opendaylight.restconf.common.references.SchemaContextRef;
import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
this.mountPointServiceHandler = mountPointServiceHandler;
}
+ @Override
+ public Response readData(final UriInfo uriInfo) {
+ return readData(null, uriInfo);
+ }
+
@Override
public Response readData(final String identifier, final UriInfo uriInfo) {
- Preconditions.checkNotNull(identifier);
final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
-
final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
- final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
- final String value = uriInfo.getQueryParameters().getFirst(RestconfDataServiceConstant.CONTENT);
+ boolean withDefa_used = false;
+ String withDefa = null;
+
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ switch (entry.getKey()) {
+ case "with-defaults":
+ if (!withDefa_used) {
+ withDefa_used = true;
+ withDefa = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("With-defaults parameter can be used only once.");
+ }
+ break;
+ }
+ }
+ boolean tagged = false;
+ if (withDefa_used) {
+ if (withDefa.equals("report-all-tagged")) {
+ tagged = true;
+ withDefa = null;
+ }
+ if (withDefa.equals("report-all")) {
+ withDefa = null;
+ }
+ }
+
+ final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(
+ instanceIdentifier, uriInfo, tagged);
+
+ final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
final DOMTransactionChain transactionChain;
if (mountPoint == null) {
transactionChain = this.transactionChainHandler.get();
transactionChain = transactionChainOfMountPoint(mountPoint);
}
- final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
- transactionChain);
- final NormalizedNode<?, ?> node = ReadDataTransactionUtil.readData(value, transactionNode);
+ final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
+ instanceIdentifier, mountPoint, transactionChain);
+ final NormalizedNode<?, ?> node =
+ ReadDataTransactionUtil.readData(parameters.getContent(), transactionNode, withDefa);
if (node == null) {
throw new RestconfDocumentedException(
"Request could not be completed because the relevant data model content does not exist",
+ node.getNodeType().getLocalName() + '"';
final Response resp;
- if ((value == null) || value.contains(RestconfDataServiceConstant.ReadData.CONFIG)) {
- resp = Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node)).header("ETag", etag)
- .header("Last-Modified", dateFormatGmt.format(new Date())).build();
+ if ((parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL))
+ || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) {
+ resp = Response.status(200)
+ .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
+ .header("ETag", etag)
+ .header("Last-Modified", dateFormatGmt.format(new Date()))
+ .build();
} else {
- resp = Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node)).build();
+ resp = Response.status(200)
+ .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
+ .build();
}
+
return resp;
}
@Override
- public Response putData(final String identifier, final NormalizedNodeContext payload) {
+ public Response putData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
Preconditions.checkNotNull(payload);
+ boolean insert_used = false;
+ boolean point_used = false;
+ String insert = null;
+ String point = null;
+
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ switch (entry.getKey()) {
+ case "insert":
+ if (!insert_used) {
+ insert_used = true;
+ insert = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Insert parameter can be used only once.");
+ }
+ break;
+ case "point":
+ if (!point_used) {
+ point_used = true;
+ point = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Point parameter can be used only once.");
+ }
+ break;
+ default:
+ throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
+ }
+ }
+
+ checkQueryParams(insert_used, point_used, insert);
+
final InstanceIdentifierContext<? extends SchemaNode> iid = payload
.getInstanceIdentifierContext();
final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
- return PutDataTransactionUtil.putData(payload, ref, transactionNode);
+ return PutDataTransactionUtil.putData(payload, ref, transactionNode, insert, point);
+ }
+
+ private void checkQueryParams(final boolean insert_used, final boolean point_used, final String insert) {
+ if (point_used && !insert_used) {
+ throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
+ }
+ if (point_used && (insert.equals("first") || insert.equals("last"))) {
+ throw new RestconfDocumentedException(
+ "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
+ }
}
@Override
public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
Preconditions.checkNotNull(payload);
+ boolean insert_used = false;
+ boolean point_used = false;
+ String insert = null;
+ String point = null;
+
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ switch (entry.getKey()) {
+ case "insert":
+ if (!insert_used) {
+ insert_used = true;
+ insert = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Insert parameter can be used only once.");
+ }
+ break;
+ case "point":
+ if (!point_used) {
+ point_used = true;
+ point = entry.getValue().iterator().next();
+ } else {
+ throw new RestconfDocumentedException("Point parameter can be used only once.");
+ }
+ break;
+ default:
+ throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
+ }
+ }
+
+ checkQueryParams(insert_used, point_used, insert);
+
final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
final DOMTransactionChain transactionChain;
final SchemaContextRef ref;
}
final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
- return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref);
+ return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref, insert, point);
}
@Override
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
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.common.references.SchemaContextRef;
import org.opendaylight.restconf.handlers.RpcServiceHandler;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
if (mountPoint == null) {
if (namespace.toString().equals(RestconfStreamsConstants.SAL_REMOTE_NAMESPACE)) {
- response = CreateStreamUtil.createStream(payload, refSchemaCtx);
+ if (identifier.contains(RestconfStreamsConstants.CREATE_DATA_SUBSCR)) {
+ response = CreateStreamUtil.createDataChangeNotifiStream(payload, refSchemaCtx);
+ } else if (identifier.contains(RestconfStreamsConstants.CREATE_NOTIFICATION_STREAM)) {
+ response = CreateStreamUtil.createYangNotifiStream(payload, refSchemaCtx);
+ } else {
+ throw new RestconfDocumentedException("Not supported operation", ErrorType.RPC,
+ ErrorTag.OPERATION_NOT_SUPPORTED);
+ }
} else {
response = RestconfInvokeOperationsUtil.invokeRpc(payload.getData(), schemaPath,
this.rpcServiceHandler);
package org.opendaylight.restconf.restful.services.impl;
import java.net.URI;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriBuilder;
+import java.util.Map.Entry;
import javax.ws.rs.core.UriInfo;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+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.netconf.sal.streams.listeners.ListenerAdapter;
-import org.opendaylight.netconf.sal.streams.listeners.Notificator;
import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService;
import org.opendaylight.restconf.restful.utils.RestconfStreamsConstants;
import org.opendaylight.restconf.restful.utils.SubscribeToStreamUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final DOMDataBrokerHandler domDataBrokerHandler;
- public RestconfStreamsSubscriptionServiceImpl(final DOMDataBrokerHandler domDataBrokerHandler) {
+ private final NotificationServiceHandler notificationServiceHandler;
+
+ private final SchemaContextHandler schemaHandler;
+
+ public RestconfStreamsSubscriptionServiceImpl(final DOMDataBrokerHandler domDataBrokerHandler,
+ final NotificationServiceHandler notificationServiceHandler, final SchemaContextHandler schemaHandler) {
this.domDataBrokerHandler = domDataBrokerHandler;
+ this.notificationServiceHandler = notificationServiceHandler;
+ this.schemaHandler = schemaHandler;
}
@Override
- public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
- final Map<String, String> mapOfValues = SubscribeToStreamUtil.mapValuesFromUri(identifier);
+ public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ boolean startTime_used = false;
+ boolean stopTime_used = false;
+ boolean filter_used = false;
+ Date start = null;
+ Date stop = null;
+ String filter = null;
- final LogicalDatastoreType ds = SubscribeToStreamUtil.parseURIEnum(LogicalDatastoreType.class,
- mapOfValues.get(RestconfStreamsConstants.DATASTORE_PARAM_NAME));
- if (ds == null) {
- final String msg = "Stream name doesn't contains datastore value (pattern /datastore=)";
- LOG.debug(msg);
- throw new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+ for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+ switch (entry.getKey()) {
+ case "start-time":
+ if (!startTime_used) {
+ startTime_used = true;
+ start = SubscribeToStreamUtil.parseDateFromQueryParam(entry);
+ } else {
+ throw new RestconfDocumentedException("Start-time parameter can be used only once.");
+ }
+ break;
+ case "stop-time":
+ if (!stopTime_used) {
+ stopTime_used = true;
+ stop = SubscribeToStreamUtil.parseDateFromQueryParam(entry);
+ } else {
+ throw new RestconfDocumentedException("Stop-time parameter can be used only once.");
+ }
+ break;
+ case "filter":
+ if (!filter_used) {
+ filter_used = true;
+ filter = entry.getValue().iterator().next();
+ }
+ break;
+ default:
+ throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey());
+ }
}
-
- final DataChangeScope scope = SubscribeToStreamUtil.parseURIEnum(DataChangeScope.class,
- mapOfValues.get(RestconfStreamsConstants.SCOPE_PARAM_NAME));
- if (scope == null) {
- final String msg = "Stream name doesn't contains datastore value (pattern /scope=)";
- LOG.warn(msg);
- throw new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+ if (!startTime_used && stopTime_used) {
+ throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter.");
+ }
+ URI response = null;
+ if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCR)) {
+ response =
+ SubscribeToStreamUtil.dataSubs(identifier, uriInfo, start, stop, this.domDataBrokerHandler, filter);
+ } else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) {
+ response = SubscribeToStreamUtil.notifStream(identifier, uriInfo, start, stop,
+ this.notificationServiceHandler, filter);
}
- final String streamName = Notificator.createStreamNameFromUri(identifier);
-
- final ListenerAdapter listener = Notificator.getListenerFor(streamName);
- SubscribeToStreamUtil.registration(ds, scope, listener, this.domDataBrokerHandler.get());
+ if (response != null) {
+ // prepare node with value of location
+ final InstanceIdentifierContext<?> iid =
+ SubscribeToStreamUtil.prepareIIDSubsStreamOutput(this.schemaHandler);
+ final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> builder =
+ ImmutableLeafNodeBuilder.create().withValue(response.toString());
+ builder.withNodeIdentifier(
+ NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location")));
- final int port = SubscribeToStreamUtil.prepareNotificationPort();
+ // prepare new header with location
+ final Map<String, Object> headers = new HashMap<>();
+ headers.put("Location", response);
- final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
- final UriBuilder uriToWebSocketServer = uriBuilder.port(port).scheme(RestconfStreamsConstants.SCHEMA_SUBSCIBRE_URI);
- final URI uri = uriToWebSocketServer.replacePath(streamName).build();
+ return new NormalizedNodeContext(iid, builder.build(), headers);
+ }
- return Response.status(Status.OK).location(uri).build();
+ final String msg = "Bad type of notification of sal-remote";
+ LOG.warn(msg);
+ throw new RestconfDocumentedException(msg);
}
}
package org.opendaylight.restconf.restful.utils;
import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
import org.opendaylight.restconf.common.references.SchemaContextRef;
import org.opendaylight.restconf.utils.parser.ParserIdentifier;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
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.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class CreateStreamUtil {
private static final Logger LOG = LoggerFactory.getLogger(CreateStreamUtil.class);
+ private static final String OUTPUT_TYPE_PARAM_NAME = "notification-output-type";
private CreateStreamUtil() {
throw new UnsupportedOperationException("Util class");
* </pre>
*
*/
- public static DOMRpcResult createStream(final NormalizedNodeContext payload,
+ public static DOMRpcResult createDataChangeNotifiStream(final NormalizedNodeContext payload,
final SchemaContextRef refSchemaCtx) {
final ContainerNode data = (ContainerNode) payload.getData();
final QName qname = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
final YangInstanceIdentifier path = preparePath(data, qname);
- final String streamName = prepareStream(path, refSchemaCtx.get(), data);
+ final String streamName = prepareDataChangeNotifiStreamName(path, refSchemaCtx.get(), data);
final QName outputQname = QName.create(qname, "output");
final QName streamNameQname = QName.create(qname, "stream-name");
final ContainerNode output = ImmutableContainerNodeBuilder.create()
.withNodeIdentifier(new NodeIdentifier(outputQname))
.withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+ final NotificationOutputType outputType = prepareOutputType(data);
if (!Notificator.existListenerFor(streamName)) {
- Notificator.createListener(path, streamName);
+ Notificator.createListener(path, streamName, outputType);
}
return new DefaultDOMRpcResult(output);
}
- private static String prepareStream(final YangInstanceIdentifier path, final SchemaContext schemaContext,
+ /**
+ * @param data
+ * - data of notification
+ * @return output type fo notification
+ */
+ private static NotificationOutputType prepareOutputType(final ContainerNode data) {
+ NotificationOutputType outputType = parseEnum(data, NotificationOutputType.class, OUTPUT_TYPE_PARAM_NAME);
+ return outputType = outputType == null ? NotificationOutputType.XML : outputType;
+ }
+
+ private static String prepareDataChangeNotifiStreamName(final YangInstanceIdentifier path, final SchemaContext schemaContext,
final ContainerNode data) {
LogicalDatastoreType ds = parseEnum(data, LogicalDatastoreType.class,
RestconfStreamsConstants.DATASTORE_PARAM_NAME);
DataChangeScope scope = parseEnum(data, DataChangeScope.class, RestconfStreamsConstants.SCOPE_PARAM_NAME);
scope = scope == null ? RestconfStreamsConstants.DEFAULT_SCOPE : scope;
- final String streamName = Notificator
+ final String streamName = RestconfStreamsConstants.DATA_SUBSCR + "/"
+ + Notificator
.createStreamNameFromUri(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext)
+ RestconfStreamsConstants.DS_URI + ds + RestconfStreamsConstants.SCOPE_URI + scope);
return streamName;
}
return (YangInstanceIdentifier) pathValue;
}
+
+ /**
+ * Create stream with POST operation via RPC
+ *
+ * @param payload
+ * - input of RPC
+ * @param refSchemaCtx
+ * - schemaContext
+ * @return {@link DOMRpcResult}
+ */
+ public static DOMRpcResult createYangNotifiStream(final NormalizedNodeContext payload,
+ final SchemaContextRef refSchemaCtx) {
+ final ContainerNode data = (ContainerNode) payload.getData();
+ LeafSetNode leafSet = null;
+ String outputType = "XML";
+ for (final DataContainerChild<? extends PathArgument, ?> dataChild : data.getValue()) {
+ if (dataChild instanceof LeafSetNode) {
+ leafSet = (LeafSetNode) dataChild;
+ } else if (dataChild instanceof AugmentationNode) {
+ outputType = (String) (((AugmentationNode) dataChild).getValue()).iterator().next().getValue();
+ }
+ }
+
+ final Collection<LeafSetEntryNode> entryNodes = leafSet.getValue();
+ final List<SchemaPath> paths = new ArrayList<>();
+ String streamName = RestconfStreamsConstants.CREATE_NOTIFICATION_STREAM + "/";
+
+ final Iterator<LeafSetEntryNode> iterator = entryNodes.iterator();
+ while (iterator.hasNext()) {
+ final QName valueQName = QName.create((String) iterator.next().getValue());
+ final Module module = refSchemaCtx.findModuleByNamespaceAndRevision(valueQName.getModule().getNamespace(),
+ valueQName.getModule().getRevision());
+ Preconditions.checkNotNull(module,
+ "Module for namespace " + valueQName.getModule().getNamespace() + " does not exist");
+ NotificationDefinition notifiDef = null;
+ for (final NotificationDefinition notification : module.getNotifications()) {
+ if (notification.getQName().equals(valueQName)) {
+ notifiDef = notification;
+ break;
+ }
+ }
+ final String moduleName = module.getName();
+ Preconditions.checkNotNull(notifiDef,
+ "Notification " + valueQName + "doesn't exist in module " + moduleName);
+ paths.add(notifiDef.getPath());
+ streamName = streamName + moduleName + ":" + valueQName.getLocalName();
+ if (iterator.hasNext()) {
+ streamName = streamName + ",";
+ }
+ }
+
+ final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
+ final QName outputQname = QName.create(rpcQName, "output");
+ final QName streamNameQname = QName.create(rpcQName, "notification-stream-identifier");
+
+ final ContainerNode output =
+ ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
+ .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+
+ if (!Notificator.existNotificationListenerFor(streamName)) {
+ Notificator.createNotificationListener(paths, streamName, outputType);
+ }
+
+ return new DefaultDOMRpcResult(output);
+ }
}
--- /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.restful.utils;
+
+import com.google.common.collect.Sets;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+
+class ParametersUtil {
+
+ private ParametersUtil() {
+ throw new UnsupportedOperationException("Util class.");
+ }
+
+ /**
+ * Check if URI does not contain not allowed parameters for specified operation
+ *
+ * @param operationType
+ * - type of operation (READ, POST, PUT, DELETE...)
+ * @param usedParameters
+ * - parameters used in URI request
+ * @param allowedParameters
+ * - allowed parameters for operation
+ */
+ static void checkParametersTypes(@Nonnull final String operationType,
+ @Nonnull final Set<String> usedParameters,
+ @Nonnull final String... allowedParameters) {
+ final Set<String> notAllowedParameters = Sets.newHashSet(usedParameters);
+ notAllowedParameters.removeAll(Sets.newHashSet(allowedParameters));
+
+ if (!notAllowedParameters.isEmpty()) {
+ throw new RestconfDocumentedException(
+ "Not allowed parameters for " + operationType + " operation: " + notAllowedParameters,
+ RestconfError.ErrorType.PROTOCOL,
+ RestconfError.ErrorTag.INVALID_VALUE);
+ }
+ }
+
+ /**
+ * Check if URI does not contain value for the same parameter more than once
+ *
+ * @param parameterValues
+ * - URI parameter values
+ * @param parameterName
+ * - URI parameter name
+ */
+ static void checkParameterCount(@Nonnull final List<String> parameterValues, @Nonnull final String parameterName) {
+ if (parameterValues.size() > 1) {
+ throw new RestconfDocumentedException(
+ "Parameter " + parameterName + " can appear at most once in request URI",
+ ErrorType.PROTOCOL,
+ ErrorTag.INVALID_VALUE);
+ }
+ }
+}
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+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.restconf.common.references.SchemaContextRef;
import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
import org.opendaylight.restconf.utils.parser.ParserIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* - wrapper for transaction data
* @param schemaContextRef
* - reference to actual {@link SchemaContext}
+ * @param point
+ * @param insert
* @return {@link CheckedFuture}
*/
public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
- final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef) {
+ final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef, final String insert,
+ final String point) {
final CheckedFuture<Void, TransactionCommitFailedException> future = submitData(
payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
- transactionNode, schemaContextRef.get());
+ transactionNode, schemaContextRef.get(), insert, point);
final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef);
final ResponseFactory dataFactory = new ResponseFactory(null, location);
FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
* - wrapper for data to transaction
* @param schemaContext
* - schema context of data
+ * @param point
+ * - query parameter
+ * @param insert
+ * - query parameter
* @return {@link CheckedFuture}
*/
private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
- final SchemaContext schemaContext) {
- final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
- final DOMDataReadWriteTransaction transaction = transactionChain.newReadWriteTransaction();
+ final SchemaContext schemaContext, final String insert, final String point) {
+ final DOMTransactionChain domTransactionChain = transactionNode.getTransactionChain();
+ final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
+ if (insert == null) {
+ makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ } else {
+ final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
+ switch (insert) {
+ case "first":
+ if (schemaNode instanceof ListSchemaNode) {
+ final NormalizedNode<?, ?> readData =
+ PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+ schemaNode);
+ final OrderedMapNode readList = (OrderedMapNode) readData;
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ } else {
+ newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
+ path.getParent().getParent());
+ simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+ schemaContext, domTransactionChain);
+ makePost(path, readData, schemaContext, domTransactionChain,
+ newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ }
+ } else {
+ final NormalizedNode<?, ?> readData =
+ PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain, schemaNode);
+
+ final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ } else {
+ newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
+ path.getParent().getParent());
+ simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+ schemaContext, domTransactionChain);
+ makePost(path, readData, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ }
+ }
+ case "last":
+ makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ case "before":
+ if (schemaNode instanceof ListSchemaNode) {
+ final NormalizedNode<?, ?> readData =
+ PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+ schemaNode);
+ final OrderedMapNode readList = (OrderedMapNode) readData;
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ } else {
+ insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
+ data, schemaContext, point, readList, true, domTransactionChain);
+ return newReadWriteTransaction.submit();
+ }
+ } else {
+ final NormalizedNode<?, ?> readData =
+ PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+ schemaNode);
+
+ final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ } else {
+ insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
+ path, data, schemaContext, point, readLeafList, true, domTransactionChain);
+ return newReadWriteTransaction.submit();
+ }
+ }
+ case "after":
+ if (schemaNode instanceof ListSchemaNode) {
+ final NormalizedNode<?, ?> readData =
+ PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+ schemaNode);
+ final OrderedMapNode readList = (OrderedMapNode) readData;
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ } else {
+ insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
+ data, schemaContext, point, readList, false, domTransactionChain);
+ return newReadWriteTransaction.submit();
+ }
+ } else {
+ final NormalizedNode<?, ?> readData =
+ PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+ schemaNode);
+
+ final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+ return newReadWriteTransaction.submit();
+ } else {
+ insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
+ path, data, schemaContext, point, readLeafList, true, domTransactionChain);
+ return newReadWriteTransaction.submit();
+ }
+ }
+ default:
+ throw new RestconfDocumentedException(
+ "Used bad value of insert parameter. Possible values are first, last, before or after, "
+ + "but was: " + insert);
+ }
+ }
+ }
+
+ private static void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
+ final boolean before, final DOMTransactionChain domTransactionChain) {
+ rWTransaction.delete(datastore, path.getParent().getParent());
+ final InstanceIdentifierContext<?> instanceIdentifier =
+ ControllerContext.getInstance().toInstanceIdentifier(point);
+ int p = 0;
+ for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+ if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+ break;
+ }
+ p++;
+ }
+ if (!before) {
+ p++;
+ }
+ int h = 0;
+ final NormalizedNode<?, ?> emptySubtree =
+ ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+ if (h == p) {
+ TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, path,
+ RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+ rWTransaction.put(datastore, path, payload);
+ }
+ final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
+ TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, childPath,
+ RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+ rWTransaction.put(datastore, childPath, nodeChild);
+ h++;
+ }
+ }
+
+ private static void insertWithPointListPost(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final SchemaContext schemaContext, final String point, final MapNode readList, final boolean before,
+ final DOMTransactionChain domTransactionChain) {
+ rWTransaction.delete(datastore, path.getParent().getParent());
+ final InstanceIdentifierContext<?> instanceIdentifier =
+ ControllerContext.getInstance().toInstanceIdentifier(point);
+ int p = 0;
+ for (final MapEntryNode mapEntryNode : readList.getValue()) {
+ if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+ break;
+ }
+ p++;
+ }
+ if (!before) {
+ p++;
+ }
+ int h = 0;
+ final NormalizedNode<?, ?> emptySubtree =
+ ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ for (final MapEntryNode mapEntryNode : readList.getValue()) {
+ if (h == p) {
+ TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, path,
+ RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+ rWTransaction.put(datastore, path, payload);
+ }
+ final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
+ TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, childPath,
+ RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+ rWTransaction.put(datastore, childPath, mapEntryNode);
+ h++;
+ }
+ }
+ private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
+ final SchemaContext schemaContext, final DOMTransactionChain transactionChain,
+ final DOMDataReadWriteTransaction transaction) {
if (data instanceof MapNode) {
boolean merge = false;
for (final MapEntryNode child : ((MapNode) data).getValue()) {
TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
}
-
- return transaction.submit();
}
/**
return uriBuilder.build();
}
+
+ private static void simplePost(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final SchemaContext schemaContext, final DOMTransactionChain transactionChain) {
+ TransactionUtil.checkItemDoesNotExists(transactionChain, rWTransaction, datastore, path,
+ RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+ TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
+ rWTransaction.put(datastore, path, payload);
+ }
}
import javax.ws.rs.core.Response;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
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.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
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.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
* @param schemaCtxRef
* - reference to {@link SchemaContext}
* @param transactionNode
+ * - wrapper of variables for transaction
+ * @param point
+ * - query parameter
+ * @param insert
+ * - query parameter
* @return {@link CheckedFuture}
*/
public static Response putData(final NormalizedNodeContext payload,
- final SchemaContextRef schemaCtxRef, final TransactionVarsWrapper transactionNode) {
+ final SchemaContextRef schemaCtxRef, final TransactionVarsWrapper transactionNode, final String insert, final String point) {
final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
final ResponseFactory responseFactory = new ResponseFactory(
ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode));
final CheckedFuture<Void, TransactionCommitFailedException> submitData = submitData(path, schemaCtxRef.get(),
- transactionNode.getTransactionChain().newWriteOnlyTransaction(), payload.getData());
+ transactionNode.getTransactionChain(), payload.getData(), insert, point);
FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory);
return responseFactory.build();
}
* - path of data
* @param schemaContext
* - {@link SchemaContext}
- * @param writeTx
+ * @param domTransactionChain
* - write transaction
* @param data
* - data
+ * @param point
+ * - query parameter
+ * @param insert
+ * - query parameter
* @return {@link CheckedFuture}
*/
private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
- final SchemaContext schemaContext,
- final DOMDataWriteTransaction writeTx, final NormalizedNode<?, ?> data) {
+ final SchemaContext schemaContext, final DOMTransactionChain domTransactionChain,
+ final NormalizedNode<?, ?> data, final String insert, final String point) {
+ final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
+ if (insert == null) {
+ return makePut(path, schemaContext, newReadWriteTransaction, data);
+ } else {
+ final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+ switch (insert) {
+ case "first":
+ if (schemaNode instanceof ListSchemaNode) {
+ final NormalizedNode<?, ?> readData =
+ readList(path, schemaContext, domTransactionChain, schemaNode);
+ final OrderedMapNode readList = (OrderedMapNode) readData;
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ return makePut(path, schemaContext, newReadWriteTransaction, data);
+ } else {
+ newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
+ simplePut(LogicalDatastoreType.CONFIGURATION, path, newReadWriteTransaction, schemaContext, data);
+ listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), newReadWriteTransaction, schemaContext,
+ readList);
+ return newReadWriteTransaction.submit();
+ }
+ } else {
+ final NormalizedNode<?, ?> readData =
+ readList(path, schemaContext, domTransactionChain, schemaNode);
+
+ final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ return makePut(path, schemaContext, newReadWriteTransaction, data);
+ } else {
+ newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
+ simplePut(LogicalDatastoreType.CONFIGURATION, path, newReadWriteTransaction, schemaContext, data);
+ listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), newReadWriteTransaction, schemaContext,
+ readLeafList);
+ return newReadWriteTransaction.submit();
+ }
+ }
+ case "last":
+ return makePut(path, schemaContext, newReadWriteTransaction, data);
+ case "before":
+ if (schemaNode instanceof ListSchemaNode) {
+ final NormalizedNode<?, ?> readData =
+ readList(path, schemaContext, domTransactionChain, schemaNode);
+ final OrderedMapNode readList = (OrderedMapNode) readData;
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ return makePut(path, schemaContext, newReadWriteTransaction, data);
+ } else {
+ insertWithPointListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+ schemaContext, point, readList, true);
+ return newReadWriteTransaction.submit();
+ }
+ } else {
+ final NormalizedNode<?, ?> readData =
+ readList(path, schemaContext, domTransactionChain, schemaNode);
+
+ final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ return makePut(path, schemaContext, newReadWriteTransaction, data);
+ } else {
+ insertWithPointLeafListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+ schemaContext, point, readLeafList, true);
+ return newReadWriteTransaction.submit();
+ }
+ }
+ case "after":
+ if (schemaNode instanceof ListSchemaNode) {
+ final NormalizedNode<?, ?> readData =
+ readList(path, schemaContext, domTransactionChain, schemaNode);
+ final OrderedMapNode readList = (OrderedMapNode) readData;
+ if ((readList == null) || readList.getValue().isEmpty()) {
+ return makePut(path, schemaContext, newReadWriteTransaction, data);
+ } else {
+ insertWithPointListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+ schemaContext, point, readList, false);
+ return newReadWriteTransaction.submit();
+ }
+ } else {
+ final NormalizedNode<?, ?> readData =
+ readList(path, schemaContext, domTransactionChain, schemaNode);
+
+ final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+ if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+ return makePut(path, schemaContext, newReadWriteTransaction, data);
+ } else {
+ insertWithPointLeafListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+ schemaContext, point, readLeafList, true);
+ return newReadWriteTransaction.submit();
+ }
+ }
+ default:
+ throw new RestconfDocumentedException(
+ "Used bad value of insert parameter. Possible values are first, last, before or after, "
+ + "but was: " + insert);
+ }
+ }
+ }
+
+ public static NormalizedNode<?, ?> readList(final YangInstanceIdentifier path, final SchemaContext schemaContext,
+ final DOMTransactionChain domTransactionChain, final DataSchemaNode schemaNode) {
+ final InstanceIdentifierContext<?> iid = new InstanceIdentifierContext<SchemaNode>(
+ path.getParent(), schemaNode, null, schemaContext);
+ final TransactionVarsWrapper transactionNode =
+ new TransactionVarsWrapper(iid, null, domTransactionChain);
+ final NormalizedNode<?, ?> readData = ReadDataTransactionUtil
+ .readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode);
+ return readData;
+ }
+
+ private static void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data, final SchemaContext schemaContext, final String point,
+ final OrderedLeafSetNode<?> readLeafList, final boolean before) {
+ rWTransaction.delete(datastore, path.getParent());
+ final InstanceIdentifierContext<?> instanceIdentifier =
+ ControllerContext.getInstance().toInstanceIdentifier(point);
+ int p = 0;
+ for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+ if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+ break;
+ }
+ p++;
+ }
+ if (!before) {
+ p++;
+ }
+ int h = 0;
+ final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+ if (h == p) {
+ simplePut(datastore, path, rWTransaction, schemaContext, data);
+ }
+ final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
+ rWTransaction.put(datastore, childPath, nodeChild);
+ h++;
+ }
+ }
+
+ private static void insertWithPointListPut(final DOMDataReadWriteTransaction writeTx,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data, final SchemaContext schemaContext, final String point,
+ final OrderedMapNode readList, final boolean before) {
+ writeTx.delete(datastore, path.getParent());
+ final InstanceIdentifierContext<?> instanceIdentifier =
+ ControllerContext.getInstance().toInstanceIdentifier(point);
+ int p = 0;
+ for (final MapEntryNode mapEntryNode : readList.getValue()) {
+ if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+ break;
+ }
+ p++;
+ }
+ if (!before) {
+ p++;
+ }
+ int h = 0;
+ final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+ writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ for (final MapEntryNode mapEntryNode : readList.getValue()) {
+ if (h == p) {
+ simplePut(datastore, path, writeTx, schemaContext, data);
+ }
+ final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
+ writeTx.put(datastore, childPath, mapEntryNode);
+ h++;
+ }
+ }
+
+ private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+ final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+ final OrderedLeafSetNode<?> payload) {
+ final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+ writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+ for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
+ final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+ writeTx.put(datastore, childPath, child);
+ }
+ }
+
+ private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+ final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+ final OrderedMapNode payload) {
+ final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+ writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+ for (final MapEntryNode child : ((MapNode) payload).getValue()) {
+ final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+ writeTx.put(datastore, childPath, child);
+ }
+ }
+
+ private static void simplePut(final LogicalDatastoreType configuration, final YangInstanceIdentifier path,
+ final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+ final NormalizedNode<?, ?> data) {
+ TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
+ }
+
+ private static CheckedFuture<Void, TransactionCommitFailedException> makePut(final YangInstanceIdentifier path,
+ final SchemaContext schemaContext, final DOMDataWriteTransaction writeTx, final NormalizedNode<?, ?> data) {
TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
return writeTx.submit();
}
+
+ public static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
+ final YangInstanceIdentifier parent = path.getParent();
+ final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
+ final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
+
+ if (dataSchemaNode instanceof ListSchemaNode) {
+ if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
+ throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
+ }
+ return dataSchemaNode;
+ }
+ if (dataSchemaNode instanceof LeafListSchemaNode) {
+ if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
+ throw new RestconfDocumentedException(
+ "Insert parameter can be used only with ordered-by user leaf-list.");
+ }
+ return dataSchemaNode;
+ }
+ throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
+ }
}
package org.opendaylight.restconf.restful.utils;
import com.google.common.base.Optional;
+import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.CheckedFuture;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import javax.ws.rs.core.UriInfo;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+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.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.netconf.sal.restconf.impl.WriterParameters;
+import org.opendaylight.netconf.sal.restconf.impl.WriterParameters.WriterParametersBuilder;
import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
+import org.opendaylight.restconf.utils.parser.ParserFieldsParameter;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.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.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
/**
* Util class for read data from data store via transaction.
throw new UnsupportedOperationException("Util class.");
}
+ /**
+ * Parse parameters from URI request and check their types and values.
+ *
+ *
+ * @param identifier
+ * - {@link InstanceIdentifierContext}
+ * @param uriInfo
+ * - URI info
+ * @param tagged
+ * - set tagged for {@link WriterParameters}
+ * @return {@link WriterParameters}
+ */
+ public static @Nonnull WriterParameters parseUriParameters(@Nonnull final InstanceIdentifierContext<?> identifier,
+ @Nullable final UriInfo uriInfo, final boolean tagged) {
+ return parseParams(identifier, uriInfo, tagged);
+ }
+
+ /**
+ * Parse parameters from URI request and check their types and values.
+ *
+ *
+ * @param identifier
+ * - {@link InstanceIdentifierContext}
+ * @param uriInfo
+ * - URI info
+ * @return {@link WriterParameters}
+ */
+ public static @Nonnull WriterParameters parseUriParameters(@Nonnull final InstanceIdentifierContext<?> identifier,
+ @Nullable final UriInfo uriInfo) {
+ return parseParams(identifier, uriInfo, false);
+ }
+
+ private static WriterParameters parseParams(final InstanceIdentifierContext<?> identifier, final UriInfo uriInfo,
+ final boolean tagged) {
+ final WriterParametersBuilder builder = new WriterParametersBuilder();
+ builder.setTagged(tagged);
+
+ if (uriInfo == null) {
+ return builder.build();
+ }
+
+ // check only allowed parameters
+ ParametersUtil.checkParametersTypes(
+ RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+ uriInfo.getQueryParameters().keySet(),
+ RestconfDataServiceConstant.ReadData.CONTENT,
+ RestconfDataServiceConstant.ReadData.DEPTH,
+ RestconfDataServiceConstant.ReadData.FIELDS, RestconfDataServiceConstant.ReadData.WITH_DEFAULTS);
+
+ // read parameters from URI or set default values
+ final List<String> content = uriInfo.getQueryParameters().getOrDefault(
+ RestconfDataServiceConstant.ReadData.CONTENT,
+ Collections.singletonList(RestconfDataServiceConstant.ReadData.ALL));
+ final List<String> depth = uriInfo.getQueryParameters().getOrDefault(
+ RestconfDataServiceConstant.ReadData.DEPTH,
+ Collections.singletonList(RestconfDataServiceConstant.ReadData.UNBOUNDED));
+ // fields
+ final List<String> fields = uriInfo.getQueryParameters().getOrDefault(
+ RestconfDataServiceConstant.ReadData.FIELDS,
+ Collections.emptyList());
+
+ // parameter can be in URI at most once
+ ParametersUtil.checkParameterCount(content, RestconfDataServiceConstant.ReadData.CONTENT);
+ ParametersUtil.checkParameterCount(depth, RestconfDataServiceConstant.ReadData.DEPTH);
+ ParametersUtil.checkParameterCount(fields, RestconfDataServiceConstant.ReadData.FIELDS);
+
+ // check and set content
+ final String contentValue = content.get(0);
+ if (!contentValue.equals(RestconfDataServiceConstant.ReadData.ALL)) {
+ if (!contentValue.equals(RestconfDataServiceConstant.ReadData.CONFIG)
+ && !contentValue.equals(RestconfDataServiceConstant.ReadData.NONCONFIG)) {
+ throw new RestconfDocumentedException(
+ new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
+ "Invalid content parameter: " + contentValue, null,
+ "The content parameter value must be either config, nonconfig or all (default)"));
+ }
+ }
+
+ builder.setContent(content.get(0));
+
+ // check and set depth
+ if (!depth.get(0).equals(RestconfDataServiceConstant.ReadData.UNBOUNDED)) {
+ final Integer value = Ints.tryParse(depth.get(0));
+
+ if ((value == null)
+ || (!((value >= RestconfDataServiceConstant.ReadData.MIN_DEPTH)
+ && (value <= RestconfDataServiceConstant.ReadData.MAX_DEPTH)))) {
+ throw new RestconfDocumentedException(
+ new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
+ "Invalid depth parameter: " + depth, null,
+ "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
+ } else {
+ builder.setDepth(value);
+ }
+ }
+
+ // check and set fields
+ if (!fields.isEmpty()) {
+ builder.setFields(ParserFieldsParameter.parseFieldsParameter(identifier, fields.get(0)));
+ }
+
+ return builder.build();
+ }
+
/**
* Read specific type of data from data store via transaction.
*
* - {@link TransactionVarsWrapper} - wrapper for variables
* @return {@link NormalizedNode}
*/
- public static @Nullable NormalizedNode<?, ?> readData(@Nullable final String valueOfContent,
- @Nonnull final TransactionVarsWrapper transactionNode) {
- final NormalizedNode<?, ?> data;
- if (valueOfContent != null) {
- switch (valueOfContent) {
- case RestconfDataServiceConstant.ReadData.CONFIG:
- transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
- data = readDataViaTransaction(transactionNode);
- break;
- case RestconfDataServiceConstant.ReadData.NONCONFIG:
- transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
- data = readDataViaTransaction(transactionNode);
- break;
- case RestconfDataServiceConstant.ReadData.ALL:
- data = readAllData(transactionNode);
- break;
- default:
- throw new RestconfDocumentedException("Bad query parameter for content.", ErrorType.APPLICATION,
- ErrorTag.INVALID_VALUE);
- }
+ public static @Nullable NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
+ @Nonnull final TransactionVarsWrapper transactionNode) {
+ return readData(valueOfContent, transactionNode, null);
+ }
+
+ /**
+ * Read specific type of data from data store via transaction.
+ *
+ * @param valueOfContent
+ * - type of data to read (config, state, all)
+ * @param transactionNode
+ * - {@link TransactionVarsWrapper} - wrapper for variables
+ * @param withDefa
+ * - vaule of with-defaults parameter
+ * @return {@link NormalizedNode}
+ */
+ public static @Nullable NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
+ @Nonnull final TransactionVarsWrapper transactionNode, final String withDefa) {
+ switch (valueOfContent) {
+ case RestconfDataServiceConstant.ReadData.CONFIG:
+ transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
+ if (withDefa == null) {
+ return readDataViaTransaction(transactionNode);
+ } else {
+ return prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
+ transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa);
+ }
+ case RestconfDataServiceConstant.ReadData.NONCONFIG:
+ transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
+ return readDataViaTransaction(transactionNode);
+
+ case RestconfDataServiceConstant.ReadData.ALL:
+ return readAllData(transactionNode, withDefa);
+
+ default:
+ throw new RestconfDocumentedException(
+ new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
+ "Invalid content parameter: " + valueOfContent, null,
+ "The content parameter value must be either config, nonconfig or all (default)"));
+ }
+ }
+
+ private static NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
+ final YangInstanceIdentifier path, final String withDefa) {
+ boolean trim;
+ switch (withDefa) {
+ case "trim":
+ trim = true;
+ break;
+ case "explicit":
+ trim = false;
+ break;
+ default:
+ throw new RestconfDocumentedException("");
+ }
+
+ final SchemaContext ctx = ControllerContext.getInstance().getGlobalSchema();
+ final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
+ final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+ if (result instanceof ContainerNode) {
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
+ Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
+ buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
+ return builder.build();
} else {
- data = readAllData(transactionNode);
+ final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
+ Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
+ buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
+ ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
+ return builder.build();
+ }
+ }
+
+ private static void buildMapEntryBuilder(
+ final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
+ final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
+ final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
+ for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+ final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
+ final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+ if (child instanceof ContainerNode) {
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
+ Builders.containerBuilder((ContainerSchemaNode) childSchema);
+ buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
+ builder.withChild(childBuilder.build());
+ } else if (child instanceof MapNode) {
+ final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
+ Builders.mapBuilder((ListSchemaNode) childSchema);
+ buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
+ ((ListSchemaNode) childSchema).getKeyDefinition());
+ builder.withChild(childBuilder.build());
+ } else if (child instanceof LeafNode) {
+ final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
+ final String nodeVal = ((LeafNode<String>) child).getValue();
+ final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+ Builders.leafBuilder((LeafSchemaNode) childSchema);
+ if (keys.contains(child.getNodeType())) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ } else {
+ if (trim) {
+ if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ }
+ } else {
+ if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
+ final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
+ final List<QName> keys) {
+ for (final MapEntryNode mapEntryNode : result.getValue()) {
+ final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
+ final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
+ final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
+ Builders.mapEntryBuilder((ListSchemaNode) childSchema);
+ buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
+ builder.withChild(mapEntryBuilder.build());
}
+ }
- return data;
+ private static void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
+ final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
+ final YangInstanceIdentifier actualPath, final boolean trim) {
+ for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+ final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
+ final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+ if (child instanceof ContainerNode) {
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
+ Builders.containerBuilder((ContainerSchemaNode) childSchema);
+ buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
+ builder.withChild(builderChild.build());
+ } else if (child instanceof MapNode) {
+ final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
+ Builders.mapBuilder((ListSchemaNode) childSchema);
+ buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
+ ((ListSchemaNode) childSchema).getKeyDefinition());
+ builder.withChild(childBuilder.build());
+ } else if (child instanceof LeafNode) {
+ final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
+ final String nodeVal = ((LeafNode<String>) child).getValue();
+ final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+ Builders.leafBuilder((LeafSchemaNode) childSchema);
+ if (trim) {
+ if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ }
+ } else {
+ if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
+ leafBuilder.withValue(((LeafNode) child).getValue());
+ builder.withChild(leafBuilder.build());
+ }
+ }
+ }
+ }
}
/**
*
* @param transactionNode
* - {@link TransactionVarsWrapper} - wrapper for variables
+ * @param withDefa
* @return {@link NormalizedNode}
*/
- private static @Nullable NormalizedNode<?, ?> readAllData(@Nonnull final TransactionVarsWrapper transactionNode) {
+ private static @Nullable NormalizedNode<?, ?> readAllData(@Nonnull final TransactionVarsWrapper transactionNode,
+ final String withDefa) {
// PREPARE STATE DATA NODE
transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
// PREPARE CONFIG DATA NODE
transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
- final NormalizedNode<?, ?> configDataNode = readDataViaTransaction(transactionNode);
+ final NormalizedNode<?, ?> configDataNode;
+ if (withDefa == null) {
+ configDataNode = readDataViaTransaction(transactionNode);
+ } else {
+ configDataNode = prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
+ transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa);
+ }
// if no data exists
if ((stateDataNode == null) && (configDataNode == null)) {
*/
public final class RestconfDataServiceConstant {
- public static final String CONTENT = "content";
public static final QName NETCONF_BASE_QNAME;
static {
try {
*
*/
public final class ReadData {
+ // URI parameters
+ public static final String CONTENT = "content";
+ public static final String DEPTH = "depth";
+ public static final String FIELDS = "fields";
+ // content values
public static final String CONFIG = "config";
- public static final String NONCONFIG = "nonconfig";
public static final String ALL = "all";
+ public static final String NONCONFIG = "nonconfig";
+
+ // depth values
+ public static final String UNBOUNDED = "unbounded";
+ public static final int MIN_DEPTH = 1;
+ public static final int MAX_DEPTH = 65535;
+
public static final String READ_TYPE_TX = "READ";
+ public static final String WITH_DEFAULTS = "with-defaults";
private ReadData() {
throw new UnsupportedOperationException("Util class.");
public static final String SCHEMA_SUBSCIBRE_URI = "ws";
+ public static final CharSequence DATA_SUBSCR = "data-change-event-subscription";
+ public static final CharSequence CREATE_DATA_SUBSCR = "create-" + DATA_SUBSCR;
+
+ public static final CharSequence NOTIFICATION_STREAM = "notification-stream";
+ public static final CharSequence CREATE_NOTIFICATION_STREAM = "create-" + NOTIFICATION_STREAM;
+
static {
Date eventSubscriptionAugRevision;
try {
}
SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, eventSubscriptionAugRevision);
SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets
- .newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore")));
+ .newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"),
+ QName.create(SAL_REMOTE_AUGMENT, "notification-output-type")));
}
private RestconfStreamsConstants() {
*/
package org.opendaylight.restconf.restful.utils;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import java.net.URI;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+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.netconf.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer;
+import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Subscribe to stream util class
*/
public final class SubscribeToStreamUtil {
+ private static final Logger LOG = LoggerFactory.getLogger(SubscribeToStreamUtil.class);
+
private SubscribeToStreamUtil() {
throw new UnsupportedOperationException("Util class");
}
* @param domDataBroker
* - data broker for register data change listener
*/
- public static void registration(final LogicalDatastoreType ds, final DataChangeScope scope,
+ private static void registration(final LogicalDatastoreType ds, final DataChangeScope scope,
final ListenerAdapter listener, final DOMDataBroker domDataBroker) {
if (listener.isListening()) {
return;
*
* @return port
*/
- public static int prepareNotificationPort() {
+ private static int prepareNotificationPort() {
int port = RestconfStreamsConstants.NOTIFICATION_PORT;
try {
final WebSocketServer webSocketServer = WebSocketServer.getInstance();
return port;
}
+ /**
+ * Register listeners by streamName in identifier to listen to yang notifications
+ *
+ * @param identifier
+ * - identifier as stream name
+ * @param uriInfo
+ * - for getting base URI information
+ * @param start
+ * - start-time query parameter
+ * @param stop
+ * - stop-time query parameter
+ * @param notifiServiceHandler
+ * - DOMNotificationService handler for register listeners
+ * @param filter
+ * - indicate which subset of all possible events are of interest
+ * @return location for listening
+ */
+ public static URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
+ final NotificationServiceHandler notifiServiceHandler, final String filter) {
+ final String streamName = Notificator.createStreamNameFromUri(identifier);
+ if (Strings.isNullOrEmpty(streamName)) {
+ throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+ final List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName);
+ if ((listeners == null) || listeners.isEmpty()) {
+ throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL,
+ ErrorTag.UNKNOWN_ELEMENT);
+ }
+
+ for (final NotificationListenerAdapter listener : listeners) {
+ registerToListenNotification(listener, notifiServiceHandler);
+ listener.setQueryParams(start, stop, filter);
+ }
+
+ final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
+ int notificationPort = RestconfStreamsConstants.NOTIFICATION_PORT;
+ try {
+ final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
+ notificationPort = webSocketServerInstance.getPort();
+ } catch (final NullPointerException e) {
+ WebSocketServer.createInstance(RestconfStreamsConstants.NOTIFICATION_PORT);
+ }
+ final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
+ final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
+
+ return uriToWebsocketServer;
+ }
+
+ private static void registerToListenNotification(final NotificationListenerAdapter listener,
+ final NotificationServiceHandler notificationServiceHandler) {
+ if (listener.isListening()) {
+ return;
+ }
+
+ final SchemaPath path = listener.getSchemaPath();
+ final ListenerRegistration<DOMNotificationListener> registration =
+ notificationServiceHandler.get().registerNotificationListener(listener, path);
+
+ listener.setRegistration(registration);
+ }
+
+ /**
+ * Prepare InstanceIdentifierContext for Location leaf
+ *
+ * @param schemaHandler
+ * - schemaContext handler
+ * @return InstanceIdentifier of Location leaf
+ */
+ public static InstanceIdentifierContext<?> prepareIIDSubsStreamOutput(final SchemaContextHandler schemaHandler) {
+ final QName qnameBase = QName.create("subscribe:to:notification", "2016-10-28", "notifi");
+ final DataSchemaNode location = ((ContainerSchemaNode) schemaHandler.get()
+ .findModuleByNamespaceAndRevision(qnameBase.getNamespace(), qnameBase.getRevision())
+ .getDataChildByName(qnameBase)).getDataChildByName(QName.create(qnameBase, "location"));
+ final List<PathArgument> path = new ArrayList<>();
+ path.add(NodeIdentifier.create(qnameBase));
+ path.add(NodeIdentifier.create(QName.create(qnameBase, "location")));
+
+ return new InstanceIdentifierContext<SchemaNode>(YangInstanceIdentifier.create(path), location, null,
+ schemaHandler.get());
+ }
+
+ /**
+ * Register listener by streamName in identifier to listen to yang notifications
+ *
+ * @param identifier
+ * - identifier as stream name
+ * @param uriInfo
+ * - for getting base URI information
+ * @param start
+ * - start-time query parameter
+ * @param stop
+ * - stop-time query parameter
+ * @param domDataBrokerHandler
+ * - DOMDataBroker handler for register listener
+ * @param filter
+ * - indicate which subset of all possible events are of interest
+ * @return location for listening
+ */
+ public static URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
+ final DOMDataBrokerHandler domDataBrokerHandler, final String filter) {
+ final Map<String, String> mapOfValues = SubscribeToStreamUtil.mapValuesFromUri(identifier);
+
+ final LogicalDatastoreType ds = SubscribeToStreamUtil.parseURIEnum(LogicalDatastoreType.class,
+ mapOfValues.get(RestconfStreamsConstants.DATASTORE_PARAM_NAME));
+ if (ds == null) {
+ final String msg = "Stream name doesn't contains datastore value (pattern /datastore=)";
+ LOG.debug(msg);
+ throw new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+ }
+
+ final DataChangeScope scope = SubscribeToStreamUtil.parseURIEnum(DataChangeScope.class,
+ mapOfValues.get(RestconfStreamsConstants.SCOPE_PARAM_NAME));
+ if (scope == null) {
+ final String msg = "Stream name doesn't contains datastore value (pattern /scope=)";
+ LOG.warn(msg);
+ throw new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+ }
+
+ final String streamName = Notificator.createStreamNameFromUri(identifier);
+
+ final ListenerAdapter listener = Notificator.getListenerFor(streamName);
+ Preconditions.checkNotNull(listener, "Listener doesn't exist : " + streamName);
+
+ listener.setQueryParams(start, stop, filter);
+
+ SubscribeToStreamUtil.registration(ds, scope, listener, domDataBrokerHandler.get());
+
+ final int port = SubscribeToStreamUtil.prepareNotificationPort();
+
+ final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
+ final UriBuilder uriToWebSocketServer =
+ uriBuilder.port(port).scheme(RestconfStreamsConstants.SCHEMA_SUBSCIBRE_URI);
+ return uriToWebSocketServer.replacePath(streamName).build();
+ }
+
+ public static Date parseDateFromQueryParam(final Entry<String, List<String>> entry) {
+ final DateAndTime event = new DateAndTime(entry.getValue().iterator().next());
+ String numOf_ms = "";
+ final String value = event.getValue();
+ if (value.contains(".")) {
+ numOf_ms = numOf_ms + ".";
+ final int lastChar = value.contains("Z") ? value.indexOf("Z") : (value.contains("+") ? value.indexOf("+")
+ : (value.contains("-") ? value.indexOf("-") : value.length()));
+ for (int i = 0; i < (lastChar - value.indexOf(".") - 1); i++) {
+ numOf_ms = numOf_ms + "S";
+ }
+ }
+ String zone = "";
+ if (!value.contains("Z")) {
+ zone = zone + "XXX";
+ }
+ final DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" + numOf_ms + zone);
+
+ try {
+ return dateFormatter.parse(value.contains("Z") ? value.replace('T', ' ').substring(0, value.indexOf("Z"))
+ : value.replace('T', ' '));
+ } catch (final ParseException e) {
+ throw new RestconfDocumentedException("Cannot parse of value in date: " + value + e);
+ }
+ }
}
import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.Set;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.utils.RestconfConstants;
import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
*/
public static MapNode restconfMappingNode(final Module restconfModule, final Set<Module> modules) {
final DataSchemaNode modulListSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(restconfModule,
- Draft17.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+ Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE);
Preconditions.checkState(modulListSchemaNode instanceof ListSchemaNode);
final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
--- /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 java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+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.parser.builder.ParserBuilderConstants.Deserializer;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class ParserFieldsParameter {
+ private static final char COLON = ':';
+ private static final char SEMICOLON = ';';
+ private static final char SLASH = '/';
+ private static final char STARTING_PARENTHESIS = '(';
+ private static final char CLOSING_PARENTHESIS = ')';
+
+ /**
+ * Parse fields parameter and return complete list of child nodes organized into levels.
+ * @param identifier identifier context created from request URI
+ * @param input input value of fields parameter
+ * @return {@link List}
+ */
+ public static @Nonnull List<Set<QName>> parseFieldsParameter(@Nonnull final InstanceIdentifierContext<?> identifier,
+ @Nonnull final String input) {
+ final List<Set<QName>> parsed = new ArrayList<>();
+ final SchemaContext context = identifier.getSchemaContext();
+ final QNameModule startQNameModule = identifier.getSchemaNode().getQName().getModule();
+ final DataSchemaContextNode<?> startNode = DataSchemaContextNode.fromDataSchemaNode(
+ (DataSchemaNode) identifier.getSchemaNode());
+
+ if (startNode == null) {
+ throw new RestconfDocumentedException(
+ "Start node missing in " + input, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+
+ parseInput(input, startQNameModule, startNode, parsed, context);
+ return parsed;
+ }
+
+ /**
+ * Parse input value of fields parameter and create list of sets. Each set represents one level of child nodes.
+ * @param input input value of fields parameter
+ * @param startQNameModule starting qname module
+ * @param startNode starting node
+ * @param parsed list of results
+ * @param context schema context
+ */
+ private static void parseInput(@Nonnull final String input,
+ @Nonnull final QNameModule startQNameModule,
+ @Nonnull final DataSchemaContextNode<?> startNode,
+ @Nonnull final List<Set<QName>> parsed,
+ @Nonnull final SchemaContext context) {
+ int currentPosition = 0;
+ int startPosition = 0;
+ DataSchemaContextNode<?> currentNode = startNode;
+ QNameModule currentQNameModule = startQNameModule;
+
+ Set<QName> currentLevel = new HashSet<>();
+ parsed.add(currentLevel);
+
+ while (currentPosition < input.length()) {
+ final char currentChar = input.charAt(currentPosition);
+
+ if (Deserializer.IDENTIFIER.matches(currentChar) || currentChar == '/') {
+ if (currentChar == SLASH) {
+ // add parsed identifier to results for current level
+ currentNode = addChildToResult(
+ currentNode,
+ input.substring(startPosition, currentPosition), currentQNameModule, currentLevel);
+ // go one level down
+ currentLevel = new HashSet<>();
+ parsed.add(currentLevel);
+
+ currentPosition++;
+ startPosition = currentPosition;
+ } else {
+ currentPosition++;
+ }
+
+ continue;
+ }
+
+ switch (currentChar) {
+ case COLON :
+ // new namespace and revision found
+ currentQNameModule = context.findModuleByName(
+ input.substring(startPosition, currentPosition), null).getQNameModule();
+ currentPosition++;
+ break;
+ case STARTING_PARENTHESIS:
+ // add current child to parsed results for current level
+ final DataSchemaContextNode<?> child = addChildToResult(
+ currentNode,
+ input.substring(startPosition, currentPosition), currentQNameModule, currentLevel);
+ // call with child node as new start node for one level down
+ int closingParenthesis = currentPosition
+ + findClosingParenthesis(input.substring(currentPosition + 1));
+ parseInput(
+ input.substring(currentPosition + 1, closingParenthesis),
+ currentQNameModule,
+ child,
+ parsed,
+ context);
+
+ // closing parenthesis must be at the end of input or separator and one more character is expected
+ currentPosition = closingParenthesis + 1;
+ if (currentPosition != input.length()) {
+ if (currentPosition + 1 < input.length()) {
+ if (input.charAt(currentPosition) == SEMICOLON) {
+ currentPosition++;
+ } else {
+ throw new RestconfDocumentedException(
+ "Missing semicolon character after "
+ + child.getIdentifier().getNodeType().getLocalName()
+ + " child nodes",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+ } else {
+ throw new RestconfDocumentedException(
+ "Unexpected character '"
+ + input.charAt(currentPosition)
+ + "' found in fields parameter value",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+ }
+
+ break;
+ case SEMICOLON:
+ // complete identifier found
+ addChildToResult(
+ currentNode,
+ input.substring(startPosition, currentPosition), currentQNameModule, currentLevel);
+ currentPosition++;
+ break;
+ default:
+ throw new RestconfDocumentedException(
+ "Unexpected character '" + currentChar + "' found in fields parameter value",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+
+ startPosition = currentPosition;
+ }
+
+ // parse input to end
+ if (startPosition < input.length()) {
+ addChildToResult(currentNode, input.substring(startPosition), currentQNameModule, currentLevel);
+ }
+ }
+
+ /**
+ * Add parsed child of current node to result for current level.
+ * @param currentNode current node
+ * @param identifier parsed identifier of child node
+ * @param currentQNameModule current namespace and revision in {@link QNameModule}
+ * @param level current nodes level
+ * @return {@link DataSchemaContextNode}
+ */
+ private static @Nonnull DataSchemaContextNode<?> addChildToResult(
+ @Nonnull final DataSchemaContextNode<?> currentNode,
+ @Nonnull final String identifier,
+ @Nonnull final QNameModule currentQNameModule,
+ @Nonnull final Set<QName> level) {
+ final QName childQName = QName.create(
+ currentQNameModule.getNamespace().toString(),
+ identifier,
+ currentQNameModule.getRevision());
+
+ // resolve parent node
+ final DataSchemaContextNode<?> parentNode = resolveMixinNode(
+ currentNode, level, currentNode.getIdentifier().getNodeType());
+ if (parentNode == null) {
+ throw new RestconfDocumentedException(
+ "Not-mixin node missing in " + currentNode.getIdentifier().getNodeType().getLocalName(),
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+
+ // resolve child node
+ final DataSchemaContextNode<?> childNode = resolveMixinNode(
+ parentNode.getChild(childQName), level, childQName);
+ if (childNode == null) {
+ throw new RestconfDocumentedException(
+ "Child " + identifier + " node missing in "
+ + currentNode.getIdentifier().getNodeType().getLocalName(),
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+
+ // add final childNode node to level nodes
+ level.add(childNode.getIdentifier().getNodeType());
+ return childNode;
+ }
+
+ /**
+ * Resolve mixin node by searching for inner nodes until not mixin node or null is found.
+ * All nodes expect of not mixin node are added to current level nodes.
+ * @param node initial mixin or not-mixin node
+ * @param level current nodes level
+ * @param qName qname of initial node
+ * @return {@link DataSchemaContextNode}
+ */
+ private static @Nullable DataSchemaContextNode<?> resolveMixinNode(@Nullable final DataSchemaContextNode<?> node,
+ @Nonnull final Set<QName> level,
+ @Nonnull final QName qName) {
+ DataSchemaContextNode<?> currentNode = node;
+ while (currentNode != null && currentNode.isMixin()) {
+ level.add(qName);
+ currentNode = currentNode.getChild(qName);
+ }
+
+ return currentNode;
+ }
+
+ /**
+ * Find position of matching parenthesis increased by one, but at most equals to input size.
+ * @param input input where to find for closing parenthesis
+ * @return int position of closing parenthesis increased by one
+ */
+ private static int findClosingParenthesis(@Nonnull final String input) {
+ int position = 0;
+ int count = 1;
+
+ while (position < input.length()) {
+ final char currentChar = input.charAt(position);
+
+ if (currentChar == STARTING_PARENTHESIS) {
+ count++;
+ }
+
+ if (currentChar == CLOSING_PARENTHESIS) {
+ count--;
+ }
+
+ if (count == 0) {
+ break;
+ }
+
+ position++;
+ }
+
+ // closing parenthesis was not found
+ if (position >= input.length()) {
+ throw new RestconfDocumentedException("Missing closing parenthesis in fields parameter",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+
+ return ++position;
+ }
+}
*/
package org.opendaylight.restconf.utils.parser;
-import com.google.common.base.Optional;
-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 org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
/**
* Util class for parsing identifier
final String identifier,
final SchemaContext schemaContext,
final Optional<DOMMountPointService> mountPointService) {
- if (identifier != null && identifier.contains(RestconfConstants.MOUNT)) {
+ if ((identifier != null) && identifier.contains(RestconfConstants.MOUNT)) {
if (!mountPointService.isPresent()) {
throw new RestconfDocumentedException("Mount point service is not available");
}
final DataSchemaContextNode<?> child = DataSchemaContextTree.from(
mountPoint.get().getSchemaContext()).getChild(pathYangInstanceIdentifier);
-
- return new InstanceIdentifierContext<SchemaNode>(
- pathYangInstanceIdentifier, child.getDataSchemaNode(), mountPoint.get(),
+ if (child != null) {
+ return new InstanceIdentifierContext<SchemaNode>(pathYangInstanceIdentifier, child.getDataSchemaNode(),
+ mountPoint.get(), mountPoint.get().getSchemaContext());
+ }
+ final QName rpcQName = mountYangInstanceIdentifier.getLastPathArgument().getNodeType();
+ RpcDefinition def = null;
+ for (final RpcDefinition rpcDefinition : schemaContext
+ .findModuleByNamespaceAndRevision(rpcQName.getNamespace(), rpcQName.getRevision()).getRpcs()) {
+ if (rpcDefinition.getQName().getLocalName().equals(rpcQName.getLocalName())) {
+ def = rpcDefinition;
+ break;
+ }
+ }
+ return new InstanceIdentifierContext<RpcDefinition>(mountYangInstanceIdentifier, def, mountPoint.get(),
mountPoint.get().getSchemaContext());
} else {
final YangInstanceIdentifier deserialize = IdentifierCodec.deserialize(identifier, schemaContext);
final DataSchemaContextNode<?> child = DataSchemaContextTree.from(schemaContext).getChild(deserialize);
- return new InstanceIdentifierContext<SchemaNode>(
- deserialize, child.getDataSchemaNode(), null, schemaContext);
+ if(child != null){
+ return new InstanceIdentifierContext<SchemaNode>(deserialize, child.getDataSchemaNode(), null, schemaContext);
+ }
+ final QName rpcQName = deserialize.getLastPathArgument().getNodeType();
+ RpcDefinition def = null;
+ for (final RpcDefinition rpcDefinition : schemaContext.findModuleByNamespaceAndRevision(rpcQName.getNamespace(), rpcQName.getRevision()).getRpcs()) {
+ if (rpcDefinition.getQName().getLocalName().equals(rpcQName.getLocalName())) {
+ def = rpcDefinition;
+ break;
+ }
+ }
+ return new InstanceIdentifierContext<RpcDefinition>(deserialize, def, null, schemaContext);
}
}
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.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
final Set<GroupingDefinition> groupings = restconfModule.getGroupings();
final GroupingDefinition restGroup = findSchemaNodeInCollection(groupings,
- Draft17.RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE);
+ Draft18.RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE);
final Collection<DataSchemaNode> childNodes = restGroup.getChildNodes();
final DataSchemaNode restCont = childNodes.iterator().next();
private static DataSchemaNode findSchemaNode(final DataSchemaNode restCont, final String schemaNodeName) {
switch (schemaNodeName) {
//MODULES
- case Draft17.RestconfModule.MODULE_LIST_SCHEMA_NODE:
+ case Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE:
final DataSchemaNode moduleListSchNode = findSchemaNodeInCollection(
((DataNodeContainer) findSchemaNode(restCont,
- Draft17.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE)).getChildNodes(),
- Draft17.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+ Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE)).getChildNodes(),
+ Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE);
Preconditions.checkNotNull(moduleListSchNode);
return moduleListSchNode;
- case Draft17.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE:
+ case Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE:
final DataSchemaNode modulesContSchNode = findSchemaNodeInCollection(((DataNodeContainer) restCont).getChildNodes(),
- Draft17.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
+ Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
Preconditions.checkNotNull(modulesContSchNode);
return modulesContSchNode;
//STREAMS
- case Draft17.MonitoringModule.STREAM_LIST_SCHEMA_NODE:
+ case Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE:
final DataSchemaNode streamListSchNode = findSchemaNodeInCollection(
((DataNodeContainer) findSchemaNode(restCont,
- Draft17.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE)).getChildNodes(),
- Draft17.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
+ Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE)).getChildNodes(),
+ Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
Preconditions.checkNotNull(streamListSchNode);
return streamListSchNode;
- case Draft17.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE:
+ case Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE:
final DataSchemaNode streamsContSchNode = findSchemaNodeInCollection(
((DataNodeContainer) restCont).getChildNodes(),
- Draft17.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
+ Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
Preconditions.checkNotNull(streamsContSchNode);
return streamsContSchNode;
default:
<servlet-mapping>
<servlet-name>Restconf</servlet-name>
- <url-pattern>/17/*</url-pattern>
+ <url-pattern>/18/*</url-pattern>
</servlet-mapping>
<filter>
revision "2014-07-08" {
}
+ grouping notification-output-type-grouping{
+ leaf notification-output-type {
+ type enumeration {
+ enum JSON;
+ enum XML;
+ }
+ default "XML";
+ description "Input parameter which type of output will be parsed on notification";
+ }
+ }
+
augment "/salrmt:create-data-change-event-subscription/salrmt:input" {
leaf datastore {
type enumeration {
enum SUBTREE;
}
}
+ uses notification-output-type-grouping;
}
augment "/salrmt:create-notification-stream/salrmt:input" {
- leaf notification-output-type {
- type enumeration {
- enum JSON;
- enum XML;
- }
- default "XML";
- description "Input parameter which type of output will be parsed on notification";
- }
+ uses notification-output-type-grouping;
}
}
--- /dev/null
+module subscribe-to-notification {
+
+ yang-version 1;
+ namespace "subscribe:to:notification";
+ prefix "subs-to-notifi";
+
+ description
+ "Added input parameters to rpc create-data-change-event-subscription and to create-notification-stream";
+
+ revision "2016-10-28" {
+ }
+
+ container "notifi"{
+ leaf "location"{
+ type string;
+ }
+ }
+}
import java.net.URI;
import java.util.List;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
+import org.mockito.Mockito;
import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
restconfImpl.setControllerContext(controllerContext);
final PutResult result = mock(PutResult.class);
when(brokerFacade.commitConfigurationDataPut(any(SchemaContext.class), any(YangInstanceIdentifier.class),
- any(NormalizedNode.class)))
+ any(NormalizedNode.class), Mockito.anyString(), Mockito.anyString()))
.thenReturn(result);
when(result.getFutureOfPutData()).thenReturn(mock(CheckedFuture.class));
when(result.getStatus()).thenReturn(Status.OK);
final NormalizedNodeContext testCompositeContext = new NormalizedNodeContext(new InstanceIdentifierContext<>(
null, testNodeSchemaNode, null, schemaContextTestModule), testNodeContainer.build());
- restconfImpl.updateConfigurationData(toUri(uriKey1, uriKey2), testCompositeContext);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ restconfImpl.updateConfigurationData(toUri(uriKey1, uriKey2), testCompositeContext, uriInfo);
}
public void putListDataWithWrapperTest(final String uriKey1, final String uriKey2, final String payloadKey1,
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.CheckedFuture;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
private final BrokerFacade brokerFacade = BrokerFacade.getInstance();
private final NormalizedNode<?, ?> dummyNode = createDummyNode("test:module", "2014-01-09", "interfaces");
private final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> dummyNodeInFuture =
- wrapDummyNode(dummyNode);
+ wrapDummyNode(this.dummyNode);
private final QName qname = TestUtils.buildQName("interfaces","test:module", "2014-01-09");
- private final SchemaPath type = SchemaPath.create(true, qname);
- private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(qname).build();
+ private final SchemaPath type = SchemaPath.create(true, this.qname);
+ private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(this.qname).build();
@Before
public void setUp() throws Exception {
when(this.rwTransaction.read(LogicalDatastoreType.CONFIGURATION, this.instanceID)).thenReturn(readFuture);
final PutResult result = this.brokerFacade.commitConfigurationDataPut(mock(SchemaContext.class),
- this.instanceID, this.dummyNode);
+ this.instanceID, this.dummyNode, null, null);
final Future<Void> actualFuture = result.getFutureOfPutData();
when(this.rwTransaction.submit()).thenReturn(expFuture);
final CheckedFuture<Void, TransactionCommitFailedException> actualFuture = this.brokerFacade
- .commitConfigurationDataPost(mock(SchemaContext.class), this.instanceID, this.dummyNode);
+ .commitConfigurationDataPost(mock(SchemaContext.class), this.instanceID, this.dummyNode, null, null);
assertSame("commitConfigurationDataPost", expFuture, actualFuture);
.thenReturn(successFuture);
try {
// Schema context is only necessary for ensuring parent structure
- this.brokerFacade.commitConfigurationDataPost((SchemaContext) null, this.instanceID, this.dummyNode);
+ this.brokerFacade.commitConfigurationDataPost((SchemaContext) null, this.instanceID, this.dummyNode, null,
+ null);
} catch (final RestconfDocumentedException e) {
assertEquals("getErrorTag", RestconfError.ErrorTag.DATA_EXISTS, e.getErrors().get(0).getErrorTag());
throw e;
@Test
public void testRegisterToListenDataChanges() {
- final ListenerAdapter listener = Notificator.createListener(this.instanceID, "stream");
+ final ListenerAdapter listener = Notificator.createListener(this.instanceID, "stream",
+ NotificationOutputType.XML);
@SuppressWarnings("unchecked")
final ListenerRegistration<DOMDataChangeListener> mockRegistration = mock(ListenerRegistration.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.controller.sal.restconf.impl.test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public class ExpressionParserTest {
+
+ private Collection<File> xmls;
+
+ @Before
+ public void setup() throws Exception {
+ this.xmls = TestRestconfUtils.loadFiles("/notifications/xml/output/");
+ }
+
+ @Test
+ public void trueDownFilterTest() throws Exception {
+ final boolean parser =
+ parser("notification/data-changed-notification/data-change-event/data/toasterStatus='down'",
+ "data_change_notification_toaster_status_DOWN.xml");
+ Assert.assertTrue(parser);
+ }
+
+ @Test
+ public void falseDownFilterTest() throws Exception {
+ final boolean parser =
+ parser("notification/data-changed-notification/data-change-event/data/toasterStatus='up'",
+ "data_change_notification_toaster_status_DOWN.xml");
+ Assert.assertFalse(parser);
+ }
+
+ @Test
+ public void trueNumberEqualsFilterTest() throws Exception {
+ final boolean parser = parser(
+ "notification/data-changed-notification/data-change-event/data/toasterStatus=1",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertTrue(parser);
+ }
+
+ @Test
+ public void falseNumberEqualsFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus=0",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertFalse(parser);
+ }
+
+ @Test
+ public void trueNumberLessFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<2",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertTrue(parser);
+ }
+
+ @Test
+ public void falseNumberLessFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<0",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertFalse(parser);
+ }
+
+ @Test
+ public void trueNumberLessEqualsFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<=2",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertTrue(parser);
+ }
+
+ @Test
+ public void falseNumberLessEqualsFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<=-1",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertFalse(parser);
+ }
+
+ @Test
+ public void trueNumberGreaterFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>0",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertTrue(parser);
+ }
+
+ @Test
+ public void falseNumberGreaterFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>5",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertFalse(parser);
+ }
+
+ @Test
+ public void trueNumberGreaterEqualsFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>=0",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertTrue(parser);
+ }
+
+ @Test
+ public void falseNumberGreaterEqualsFilterTest() throws Exception {
+ final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>=5",
+ "data_change_notification_toaster_status_NUMBER.xml");
+ Assert.assertFalse(parser);
+ }
+
+ private boolean parser(final String filter, final String fileName) throws Exception {
+ File xml = null;
+ for (final File file : this.xmls) {
+ if (file.getName().equals(fileName)) {
+ xml = file;
+ }
+ }
+ final YangInstanceIdentifier path = Mockito.mock(YangInstanceIdentifier.class);
+ final ListenerAdapter listener = Notificator.createListener(path, "streamName", NotificationOutputType.JSON);
+ listener.setQueryParams(null, null, filter);
+ final Method m = listener.getClass().getDeclaredMethod("parseFilterParam", String.class);
+ m.setAccessible(true);
+
+ return (boolean) m.invoke(listener, readFile(xml));
+ }
+
+ private String readFile(final File xml) throws Exception {
+ final BufferedReader br = new BufferedReader(new FileReader(xml));
+ try {
+ final StringBuilder sb = new StringBuilder();
+ String line = br.readLine();
+
+ while (line != null) {
+ sb.append(line);
+ sb.append("\n");
+ line = br.readLine();
+ }
+ return sb.toString();
+ } finally {
+ br.close();
+ }
+ }
+
+}
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
*
* @author Thomas Pantelis
*/
+@Deprecated
public class JSONRestconfServiceImplTest {
static final String IETF_INTERFACES_NS = "urn:ietf:params:xml:ns:yang:ietf-interfaces";
static final String IETF_INTERFACES_VERSION = "2013-07-04";
public void testPut() throws Exception {
final PutResult result = mock(PutResult.class);
when(brokerFacade.commitConfigurationDataPut(notNull(SchemaContext.class),
- notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class))).thenReturn(result);
+ notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class), Mockito.anyString(),
+ Mockito.anyString())).thenReturn(result);
when(result.getFutureOfPutData())
.thenReturn(Futures.immediateCheckedFuture(null));
when(result.getStatus()).thenReturn(Status.OK);
final String uriPath = "ietf-interfaces:interfaces/interface/eth0";
final String payload = loadData("/parts/ietf-interfaces_interfaces.json");
-
- this.service.put(uriPath, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ this.service.put(uriPath, payload, uriInfo);
final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
verify(brokerFacade).commitConfigurationDataPut(notNull(SchemaContext.class), capturedPath.capture(),
- capturedNode.capture());
+ capturedNode.capture(), Mockito.anyString(), Mockito.anyString());
verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME,
new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"});
final DOMMountPoint mockMountPoint = setupTestMountPoint();
final PutResult result = mock(PutResult.class);
when(brokerFacade.commitMountPointDataPut(notNull(DOMMountPoint.class),
- notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class))).thenReturn(result);
+ notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class), Mockito.anyString(),
+ Mockito.anyString())).thenReturn(result);
when(result.getFutureOfPutData()).thenReturn(Futures.immediateCheckedFuture(null));
when(result.getStatus()).thenReturn(Status.OK);
final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1";
final String payload = loadData("/full-versions/testCont1Data.json");
- this.service.put(uriPath, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ this.service.put(uriPath, payload, uriInfo);
final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
verify(brokerFacade).commitMountPointDataPut(same(mockMountPoint), capturedPath.capture(),
- capturedNode.capture());
+ capturedNode.capture(), Mockito.anyString(), Mockito.anyString());
verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME);
@Test(expected = OperationFailedException.class)
public void testPutFailure() throws Throwable {
final PutResult result = mock(PutResult.class);
+
when(result.getFutureOfPutData())
.thenReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")));
when(result.getStatus()).thenReturn(Status.OK);
when(brokerFacade.commitConfigurationDataPut(notNull(SchemaContext.class),
- notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class))).thenReturn(result);
+ notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class), Mockito.anyString(),
+ Mockito.anyString())).thenReturn(result);
final String uriPath = "ietf-interfaces:interfaces/interface/eth0";
final String payload = loadData("/parts/ietf-interfaces_interfaces.json");
- this.service.put(uriPath, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ this.service.put(uriPath, payload, uriInfo);
}
@SuppressWarnings("rawtypes")
@Test
public void testPost() throws Exception {
doReturn(Futures.immediateCheckedFuture(null)).when(brokerFacade).commitConfigurationDataPost(
- any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+ any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class),
+ Mockito.anyString(), Mockito.anyString());
final String uriPath = null;
final String payload = loadData("/parts/ietf-interfaces_interfaces_absolute_path.json");
- this.service.post(uriPath, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ final UriBuilder uriBuilder = UriBuilder.fromPath("");
+ Mockito.when(uriInfo.getBaseUriBuilder()).thenReturn(uriBuilder);
+ this.service.post(uriPath, payload, uriInfo);
final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
verify(brokerFacade).commitConfigurationDataPost(notNull(SchemaContext.class), capturedPath.capture(),
- capturedNode.capture());
+ capturedNode.capture(), Mockito.anyString(), Mockito.anyString());
verifyPath(capturedPath.getValue(), INTERFACES_QNAME);
public void testPostBehindMountPoint() throws Exception {
final DOMMountPoint mockMountPoint = setupTestMountPoint();
doReturn(Futures.immediateCheckedFuture(null)).when(brokerFacade).commitConfigurationDataPost(
- notNull(DOMMountPoint.class), notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class));
+ notNull(DOMMountPoint.class), notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class),
+ Mockito.anyString(), Mockito.anyString());
final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont";
final String payload = loadData("/full-versions/testCont1Data.json");
- this.service.post(uriPath, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ final UriBuilder uriBuilder = UriBuilder.fromPath("");
+ Mockito.when(uriInfo.getBaseUriBuilder()).thenReturn(uriBuilder);
+ this.service.post(uriPath, payload, uriInfo);
final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
verify(brokerFacade).commitConfigurationDataPost(same(mockMountPoint), capturedPath.capture(),
- capturedNode.capture());
+ capturedNode.capture(), Mockito.anyString(), Mockito.anyString());
verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME);
public void testPostFailure() throws Throwable {
doReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("mock"))).when(brokerFacade)
.commitConfigurationDataPost(any(SchemaContext.class), any(YangInstanceIdentifier.class),
- any(NormalizedNode.class));
+ any(NormalizedNode.class), Mockito.anyString(), Mockito.anyString());
final String uriPath = null;
final String payload = loadData("/parts/ietf-interfaces_interfaces_absolute_path.json");
- this.service.post(uriPath, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ final UriBuilder uriBuilder = UriBuilder.fromPath("");
+ Mockito.when(uriInfo.getBaseUriBuilder()).thenReturn(uriBuilder);
+ this.service.post(uriPath, payload, uriInfo);
}
@Test
@Test
public void testGetWithNoData() throws OperationFailedException {
- doReturn(null).when(brokerFacade).readConfigurationData(notNull(YangInstanceIdentifier.class));
+ doReturn(null).when(brokerFacade).readConfigurationData(notNull(YangInstanceIdentifier.class),
+ Mockito.anyString());
final String uriPath = "ietf-interfaces:interfaces";
- this.service.get(uriPath, LogicalDatastoreType.CONFIGURATION);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ this.service.get(uriPath, LogicalDatastoreType.CONFIGURATION, uriInfo);
}
@Test(expected=OperationFailedException.class)
public void testGetFailure() throws Exception {
final String invalidUriPath = "/ietf-interfaces:interfaces/invalid";
-
- this.service.get(invalidUriPath, LogicalDatastoreType.CONFIGURATION);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ this.service.get(invalidUriPath, LogicalDatastoreType.CONFIGURATION, uriInfo);
}
@SuppressWarnings("rawtypes")
.build();
if(datastoreType == LogicalDatastoreType.CONFIGURATION) {
- doReturn(entryNode).when(brokerFacade).readConfigurationData(notNull(YangInstanceIdentifier.class));
+ doReturn(entryNode).when(brokerFacade).readConfigurationData(notNull(YangInstanceIdentifier.class),
+ Mockito.anyString());
} else {
doReturn(entryNode).when(brokerFacade).readOperationalData(notNull(YangInstanceIdentifier.class));
}
final String uriPath = "/ietf-interfaces:interfaces/interface/eth0";
-
- final Optional<String> optionalResp = this.service.get(uriPath, datastoreType);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ Mockito.when(uriInfo.getQueryParameters(false)).thenReturn(value);
+ Mockito.when(value.getFirst("depth")).thenReturn("");
+
+ final Optional<String> optionalResp = this.service.get(uriPath, datastoreType, uriInfo);
assertEquals("Response present", true, optionalResp.isPresent());
final String jsonResp = optionalResp.get();
final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
if (datastoreType == LogicalDatastoreType.CONFIGURATION) {
- verify(brokerFacade).readConfigurationData(capturedPath.capture());
+ verify(brokerFacade).readConfigurationData(capturedPath.capture(), Mockito.anyString());
} else {
verify(brokerFacade).readOperationalData(capturedPath.capture());
}
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
+import org.mockito.Mockito;
import org.opendaylight.netconf.sal.rest.api.Draft02;
import org.opendaylight.netconf.sal.rest.api.RestconfService;
import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
final String uriPrefix = "/config/";
final String uriPath = "ietf-interfaces:interfaces";
final String uri = uriPrefix + uriPath;
- when(restconfService.updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class))).thenReturn(null);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ when(restconfService.updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), uriInfo))
+ .thenReturn(null);
put(uri, null, Draft02.MediaTypes.DATA + JSON, jsonData);
- verify(restconfService, times(1)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+ verify(restconfService, times(1)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+ uriInfo);
put(uri, null, Draft02.MediaTypes.DATA + XML, xmlData);
- verify(restconfService, times(2)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+ verify(restconfService, times(2)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+ uriInfo);
put(uri, null, MediaType.APPLICATION_JSON, jsonData);
- verify(restconfService, times(3)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+ verify(restconfService, times(3)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+ uriInfo);
put(uri, null, MediaType.APPLICATION_XML, xmlData);
- verify(restconfService, times(4)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+ verify(restconfService, times(4)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+ uriInfo);
put(uri, null, MediaType.TEXT_XML, xmlData);
- verify(restconfService, times(5)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+ verify(restconfService, times(5)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+ uriInfo);
put(uri, "fooMediaType", MediaType.TEXT_XML, xmlData);
- verify(restconfService, times(6)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+ verify(restconfService, times(6)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+ uriInfo);
}
@Test
private int post(final String uri, final String acceptMediaType, final String contentTypeMediaType, final String data) {
if (acceptMediaType == null) {
- if (contentTypeMediaType == null || data == null) {
+ if ((contentTypeMediaType == null) || (data == null)) {
return target(uri).request().post(null).getStatus();
}
return target(uri).request().post(Entity.entity(data, contentTypeMediaType)).getStatus();
}
- if (contentTypeMediaType == null || data == null) {
+ if ((contentTypeMediaType == null) || (data == null)) {
return target(uri).request(acceptMediaType).post(null).getStatus();
}
return target(uri).request(acceptMediaType).post(Entity.entity(data, contentTypeMediaType)).getStatus();
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
response = target(uri).request(MediaType.APPLICATION_XML).delete();
assertEquals(500, response.getStatus());
}
+
+ @Test
+ public void deleteFailTest() throws Exception {
+ final String uri = "/config/test-interface:interfaces";
+ final Exception exception = new TransactionCommitFailedException("failed test");
+ final CheckedFuture future = Futures.immediateFailedCheckedFuture(exception);
+ when(brokerFacade.commitConfigurationDataDelete(any(YangInstanceIdentifier.class))).thenReturn(future);
+ final Response response = target(uri).request(MediaType.APPLICATION_XML).delete();
+ assertEquals(500, response.getStatus());
+ }
}
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
+import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
@SuppressWarnings("unchecked")
@Test
public void getDataWithUrlMountPoint() throws UnsupportedEncodingException, URISyntaxException, ParseException {
- when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class))).thenReturn(
+ when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class),
+ Mockito.anyString())).thenReturn(
prepareCnDataForMountPointTest(false));
final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
@Test
public void getDataWithSlashesBehindMountPoint() throws Exception {
final YangInstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList();
- when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), eq(awaitedInstanceIdentifier))).thenReturn(
- prepareCnDataForSlashesBehindMountPointTest());
+ when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), eq(awaitedInstanceIdentifier),
+ Mockito.anyString())).thenReturn(prepareCnDataForSlashesBehindMountPointTest());
final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
@Test
public void getDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException,
ParseException {
- when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class))).thenReturn(
+ when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class),
+ Mockito.anyString())).thenReturn(
prepareCnDataForMountPointTest(true));
final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
.withChild(ImmutableNodes.leafNode(newTestModuleQName("type"), newTestModuleQName("test-identity")))
.withChild(ImmutableNodes.leafNode(newTestModuleQName("name"), "foo"))
.withChild(ImmutableNodes.leafNode(newTestModuleQName("data"), "bar")).build()).build();
- when(brokerFacade.readConfigurationData(iid)).thenReturn(data);
+ when(brokerFacade.readConfigurationData(iid, null)).thenReturn(data);
final String uri = "/config/test-module:modules/module/test-module:test-identity/foo";
assertEquals(200, get(uri, MediaType.APPLICATION_XML));
@SuppressWarnings("unchecked")
private void mockReadConfigurationDataMethod() {
- when(brokerFacade.readConfigurationData(any(YangInstanceIdentifier.class))).thenReturn(answerFromGet);
+ when(brokerFacade.readConfigurationData(any(YangInstanceIdentifier.class), Mockito.anyString()))
+ .thenReturn(answerFromGet);
}
@SuppressWarnings("rawtypes")
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
setSchemaControllerContext(schemaContextYangsIetf);
when(
brokerFacade.commitConfigurationDataPost(any(DOMMountPoint.class), any(YangInstanceIdentifier.class),
- any(NormalizedNode.class))).thenReturn(mock(CheckedFuture.class));
+ any(NormalizedNode.class), null, null)).thenReturn(mock(CheckedFuture.class));
final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
final RpcResult<TransactionStatus> rpcResult = new DummyRpcResult.Builder<TransactionStatus>().result(
TransactionStatus.COMMITED).build();
- when(brokerFacade.commitConfigurationDataPost((SchemaContext)null, any(YangInstanceIdentifier.class), any(NormalizedNode.class)))
+ when(brokerFacade.commitConfigurationDataPost((SchemaContext) null, any(YangInstanceIdentifier.class),
+ any(NormalizedNode.class), null, null))
.thenReturn(mock(CheckedFuture.class));
final ArgumentCaptor<YangInstanceIdentifier> instanceIdCaptor = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
// FIXME : NEVER test a nr. of call some service in complex test suite
// verify(brokerFacade, times(2))
verify(brokerFacade, times(1))
- .commitConfigurationDataPost((SchemaContext)null, instanceIdCaptor.capture(), compNodeCaptor.capture());
+ .commitConfigurationDataPost((SchemaContext) null, instanceIdCaptor.capture(), compNodeCaptor.capture(),
+ null, null);
// identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces, (urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)block]";
assertEquals(identifier, ImmutableList.copyOf(instanceIdCaptor.getValue().getPathArguments()).toString());
}
public void createConfigurationDataNullTest() throws UnsupportedEncodingException {
initMocking();
- when(brokerFacade.commitConfigurationDataPost(any(SchemaContext.class), any(YangInstanceIdentifier.class),any(NormalizedNode.class)))
+ when(brokerFacade.commitConfigurationDataPost(any(SchemaContext.class), any(YangInstanceIdentifier.class),
+ any(NormalizedNode.class), Mockito.anyString(), Mockito.anyString()))
.thenReturn(Futures.<Void, TransactionCommitFailedException>immediateCheckedFuture(null));
//FIXME : find who is set schemaContext
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
+import java.util.HashSet;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
mockingBrokerPut(iiCx.getInstanceIdentifier(), data);
- this.restconfService.updateConfigurationData(identifier, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ this.restconfService.updateConfigurationData(identifier, payload, uriInfo);
}
@Test
mockingBrokerPut(iiCx.getInstanceIdentifier(), data);
- this.restconfService.updateConfigurationData(identifier, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ this.restconfService.updateConfigurationData(identifier, payload, uriInfo);
}
@Test(expected=RestconfDocumentedException.class)
mockingBrokerPut(iiCx.getInstanceIdentifier(), data);
- this.restconfService.updateConfigurationData(identifier, payload);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+ this.restconfService.updateConfigurationData(identifier, payload, uriInfo);
}
private void mockingBrokerPut(final YangInstanceIdentifier yii, final NormalizedNode<?, ?> data) {
final PutResult result = Mockito.mock(PutResult.class);
final CheckedFuture<Void, TransactionCommitFailedException> checkedFuture = Futures.immediateCheckedFuture(null);
- Mockito.when(this.brokerFacade.commitConfigurationDataPut(this.schemaCx, yii, data))
+ Mockito.when(this.brokerFacade.commitConfigurationDataPut(this.schemaCx, yii, data, null, null))
.thenReturn(result);
Mockito.when(result.getFutureOfPutData()).thenReturn(checkedFuture);
Mockito.when(result.getStatus()).thenReturn(Status.OK);
final PutResult result = mock(PutResult.class);
when(
brokerFacade.commitMountPointDataPut(any(DOMMountPoint.class), any(YangInstanceIdentifier.class),
- any(NormalizedNode.class))).thenReturn(result);
+ any(NormalizedNode.class), null, null)).thenReturn(result);
when(result.getFutureOfPutData()).thenReturn(dummyFuture);
when(result.getStatus()).thenReturn(Status.OK);
final CheckedFuture<Void, TransactionCommitFailedException> dummyFuture = Futures.immediateCheckedFuture(null);
final PutResult result = mock(PutResult.class);
doReturn(result).when(brokerFacade).commitMountPointDataPut(any(DOMMountPoint.class),
- any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+ any(YangInstanceIdentifier.class), any(NormalizedNode.class), null, null);
when(result.getFutureOfPutData()).thenReturn(dummyFuture);
when(result.getStatus()).thenReturn(Status.OK);
doThrow(OptimisticLockFailedException.class).
when(brokerFacade).commitConfigurationDataPut(
- any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+ any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null,
+ null);
assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
doThrow(OptimisticLockFailedException.class).doReturn(mock(PutResult.class)).when(brokerFacade)
.commitConfigurationDataPut(any(SchemaContext.class), any(YangInstanceIdentifier.class),
- any(NormalizedNode.class));
+ any(NormalizedNode.class), null, null);
assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
}
doThrow(TransactionCommitFailedException.class).
when(brokerFacade).commitConfigurationDataPut(
- any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+ any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null,
+ null);
assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
}
final PutResult putResMock = mock(PutResult.class);
if (noErrors) {
doReturn(putResMock).when(brokerFacade).commitConfigurationDataPut(
- any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+ any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null, null);
} else {
doThrow(RestconfDocumentedException.class).when(brokerFacade).commitConfigurationDataPut(
- any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+ any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null, null);
}
}
--- /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.controller.sal.restconf.impl.test;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl;
+import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class RestconfImplNotificationSubscribingTest {
+
+ private final String identifier = "data-change-event-subscription/datastore=OPERATIONAL/scope=ONE";
+
+ @Mock
+ private BrokerFacade broker;
+
+ @Mock
+ private DOMDataBroker domDataBroker;
+
+ @Mock
+ private UriInfo uriInfo;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ this.broker.setDomDataBroker(this.domDataBroker);
+ RestconfImpl.getInstance().setBroker(this.broker);
+ ControllerContext.getInstance().setGlobalSchema(TestRestconfUtils.loadSchemaContext("/notifications"));
+
+ final YangInstanceIdentifier path = Mockito.mock(YangInstanceIdentifier.class);
+ Notificator.createListener(path, this.identifier, NotificationOutputType.XML);
+ }
+
+ @Test
+ public void startTimeTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T10:02:00Z");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test
+ public void milisecsTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T10:02:00.12345Z");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test
+ public void zonesPlusTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T10:02:00+01:00");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test
+ public void zonesMinusTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T10:02:00-01:00");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test
+ public void startAndStopTimeTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T10:02:00Z");
+ Mockito.when(entry.getValue()).thenReturn(time);
+
+ final Entry<String, List<String>> entry2 = Mockito.mock(Entry.class);
+ Mockito.when(entry2.getKey()).thenReturn("stop-time");
+ final List<String> time2 = new ArrayList<>();
+ time2.add("2014-10-25T12:31:00Z");
+ Mockito.when(entry2.getValue()).thenReturn(time2);
+
+ list.add(entry);
+ list.add(entry2);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test(expected = RestconfDocumentedException.class)
+ public void stopTimeTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("stop-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T12:31:00Z");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test(expected = RestconfDocumentedException.class)
+ public void badParamTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T12:31:00Z");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void badValueTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("badvalue");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void badZonesTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T10:02:00Z+1:00");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void badMilisecsTest() {
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T10:02:00:0026Z");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+ Notificator.removeAllListeners();
+ }
+
+ @Test
+ public void onNotifiTest() throws Exception {
+ final YangInstanceIdentifier path = Mockito.mock(YangInstanceIdentifier.class);
+ final ListenerAdapter listener = Notificator.createListener(path, this.identifier, NotificationOutputType.XML);
+
+ final List<Entry<String, List<String>>> list = new ArrayList<>();
+ final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+ Mockito.when(entry.getKey()).thenReturn("start-time");
+ final List<String> time = new ArrayList<>();
+ time.add("2014-10-25T10:02:00Z");
+ Mockito.when(entry.getValue()).thenReturn(time);
+ list.add(entry);
+
+ subscribe(list);
+
+ final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change =
+ Mockito.mock(AsyncDataChangeEvent.class);
+ Field start = listener.getClass().getDeclaredField("start");
+ start.setAccessible(true);
+ Date startOrig = (Date) start.get(listener);
+ Assert.assertNotNull(startOrig);
+ listener.onDataChanged(change);
+
+ start = listener.getClass().getDeclaredField("start");
+ start.setAccessible(true);
+ startOrig = (Date) start.get(listener);
+ Assert.assertNull(startOrig);
+ }
+
+ private void subscribe(final List<Entry<String, List<String>>> entries) {
+ final MultivaluedMap<String, String> map = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(this.uriInfo.getQueryParameters()).thenReturn(map);
+ final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8181/" + this.identifier);
+ Mockito.when(this.uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder);
+ final Set<Entry<String, List<String>>> set = new HashSet<>();
+ for(final Entry<String, List<String>> entry : entries){
+ set.add(entry);
+ }
+ Mockito.when(map.entrySet()).thenReturn(set);
+ RestconfImpl.getInstance().subscribeToStream(this.identifier, this.uriInfo);
+ }
+
+}
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.FileNotFoundException;
import java.net.URI;
import java.text.ParseException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
import java.util.Set;
import javax.ws.rs.core.MultivaluedHashMap;
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.junit.Before;
doReturn(mount).when(iiCtx).getMountPoint();
final DOMRpcService rpcService = mock(DOMRpcService.class);
doReturn(Optional.of(rpcService)).when(mount).getService(DOMRpcService.class);
- doReturn(Futures.immediateCheckedFuture(mock(DOMRpcResult.class))).when(rpcService).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
+ doReturn(Futures.immediateCheckedFuture(mock(DOMRpcResult.class))).when(rpcService)
+ .invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
this.restconfImpl.invokeRpc("randomId", ctx, uriInfo);
this.restconfImpl.invokeRpc("ietf-netconf", ctx, uriInfo);
verify(rpcService, times(2)).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
when(uriBuilder.build()).thenReturn(new URI(""));
when(uriBuilder.scheme("ws")).thenReturn(uriBuilder);
when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder);
+ final MultivaluedMap<String, String> map = mock(MultivaluedMap.class);
+ final Set<Entry<String, List<String>>> set = new HashSet<>();
+ when(map.entrySet()).thenReturn(set);
+ when(uriInfo.getQueryParameters()).thenReturn(map);
final BrokerFacade brokerFacade = mock(BrokerFacade.class);
this.restconfImpl.setBroker(brokerFacade);
// subscribe to stream and verify response
- final Response response = this.restconfImpl.subscribeToStream(identifier, uriInfo);
- assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ final NormalizedNodeContext response = this.restconfImpl.subscribeToStream(identifier, uriInfo);
// remove test notification stream
Notificator.removeAllListeners();
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil.getRevisionFormat;
+import com.google.common.base.Preconditions;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.util.Date;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
+import org.mockito.Mockito;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
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.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
}
@Test
- @Ignore // URI parsing test - not able to catch a motivation + bad mocking response now - it needs to change Controller RPC table holder approach
public void resolveURIParametersConcreteValues() {
resolveURIParameters("OPERATIONAL", "SUBTREE", LogicalDatastoreType.OPERATIONAL, DataChangeScope.SUBTREE);
}
@Test
- @Ignore // URI parsing test - not able to catch a motivation + bad mocking response now - it needs to change Controller RPC table holder approach
public void resolveURIParametersDefaultValues() {
resolveURIParameters(null, null, LogicalDatastoreType.CONFIGURATION, DataChangeScope.BASE);
}
final String datastoreValue = datastore == null ? "CONFIGURATION" : datastore;
final String scopeValue = scope == null ? "BASE" : scope + "";
Notificator.createListener(iiBuilder.build(), "dummyStreamName/datastore=" + datastoreValue + "/scope="
- + scopeValue);
+ + scopeValue, NotificationOutputType.XML);
final UriInfo mockedUriInfo = mock(UriInfo.class);
@SuppressWarnings("unchecked")
final UriBuilder uriBuilder = UriBuilder.fromUri("www.whatever.com");
when(mockedUriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder);
-// when(mockedBrokerFacade.invokeRpc(any(SchemaPath.class), any(NormalizedNode.class)))
-// .thenReturn(Futures.<DOMRpcResult, DOMRpcException> immediateCheckedFuture(new DefaultDOMRpcResult(Builders.containerBuilder().build())));
-
this.restconf.invokeRpc("sal-remote:create-data-change-event-subscription", prepareDomRpcNode(datastore, scope),
mockedUriInfo);
- final ListenerAdapter listener = Notificator.getListenerFor("opendaylight-inventory:nodes/datastore="
+ final ListenerAdapter listener =
+ Notificator.getListenerFor("data-change-event-subscription/opendaylight-inventory:nodes/datastore="
+ datastoreValue + "/scope=" + scopeValue);
assertNotNull(listener);
-
}
private NormalizedNodeContext prepareDomRpcNode(final String datastore, final String scope) {
final Module rpcSalRemoteModule = schema.findModuleByName("sal-remote", revDate);
final Set<RpcDefinition> setRpcs = rpcSalRemoteModule.getRpcs();
final QName rpcQName = QName.create(rpcSalRemoteModule.getQNameModule(), "create-data-change-event-subscription");
- final QName rpcInputQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote","2014-01-14","input");
+ final QName rpcInputQName =
+ QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "input");
+ final RpcDefinition rpcDef = Mockito.mock(RpcDefinition.class);
ContainerSchemaNode rpcInputSchemaNode = null;
for (final RpcDefinition rpc : setRpcs) {
if (rpcQName.isEqualWithoutRevision(rpc.getQName())) {
}
assertNotNull("RPC ContainerSchemaNode was not found!", rpcInputSchemaNode);
- final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> container = Builders.containerBuilder(rpcInputSchemaNode);
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> container =
+ Builders.containerBuilder(rpcInputSchemaNode);
- final QName pathQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "path");
+ final QName pathQName =
+ QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "path");
final DataSchemaNode pathSchemaNode = rpcInputSchemaNode.getDataChildByName(pathQName);
assertTrue(pathSchemaNode instanceof LeafSchemaNode);
final LeafNode<Object> pathNode = (Builders.leafBuilder((LeafSchemaNode) pathSchemaNode)
- .withValue(YangInstanceIdentifier.builder().node(QName.create("urn:opendaylight:inventory", "2013-08-19", "nodes")).build())).build();
+ .withValue(YangInstanceIdentifier.builder()
+ .node(QName.create("urn:opendaylight:inventory", "2013-08-19", "nodes")).build())).build();
container.withChild(pathNode);
+ final AugmentationSchema augmentationSchema = rpcInputSchemaNode.getAvailableAugmentations().iterator().next();
+ Preconditions.checkNotNull(augmentationSchema);
+ final DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentationBuilder =
+ Builders.augmentationBuilder(augmentationSchema);
+
final QName dataStoreQName = QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "datastore");
- final DataSchemaNode dsSchemaNode = rpcInputSchemaNode.getDataChildByName(dataStoreQName);
+ final DataSchemaNode dsSchemaNode = augmentationSchema.getDataChildByName(dataStoreQName);
assertTrue(dsSchemaNode instanceof LeafSchemaNode);
final LeafNode<Object> dsNode = (Builders.leafBuilder((LeafSchemaNode) dsSchemaNode)
.withValue(datastore)).build();
- container.withChild(dsNode);
+ augmentationBuilder.withChild(dsNode);
final QName scopeQName = QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "scope");
- final DataSchemaNode scopeSchemaNode = rpcInputSchemaNode.getDataChildByName(scopeQName);
+ final DataSchemaNode scopeSchemaNode = augmentationSchema.getDataChildByName(scopeQName);
assertTrue(scopeSchemaNode instanceof LeafSchemaNode);
final LeafNode<Object> scopeNode = (Builders.leafBuilder((LeafSchemaNode) scopeSchemaNode)
.withValue(scope)).build();
- container.withChild(scopeNode);
+ augmentationBuilder.withChild(scopeNode);
+
+ final QName outputQName =
+ QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "notification-output-type");
+ final DataSchemaNode outputSchemaNode = augmentationSchema.getDataChildByName(outputQName);
+ assertTrue(outputSchemaNode instanceof LeafSchemaNode);
+ final LeafNode<Object> outputNode =
+ (Builders.leafBuilder((LeafSchemaNode) outputSchemaNode).withValue("XML")).build();
+ augmentationBuilder.withChild(outputNode);
+
+ container.withChild(augmentationBuilder.build());
+
+ when(rpcDef.getInput()).thenReturn(rpcInputSchemaNode);
+ when(rpcDef.getPath()).thenReturn(SchemaPath.create(true, rpcQName));
+ when(rpcDef.getQName()).thenReturn(rpcQName);
- return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema), container.build());
+ return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null, rpcDef, null, schema),
+ container.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.controller.sal.streams.listeners;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.yangtools.util.SingletonSet;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class NotificationListenerTest {
+
+ private SchemaContext schmeaCtx;
+
+ @Before
+ public void init() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ ControllerContext.getInstance().setGlobalSchema(TestUtils.loadSchemaContext("/notifications"));
+ this.schmeaCtx = ControllerContext.getInstance().getGlobalSchema();
+ }
+
+ @Test
+ public void notifi_leafTest() throws Exception {
+ final QNameModule moduleQName =
+ QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+ final SchemaPath schemaPathNotifi = SchemaPath.create(false, QName.create(moduleQName, "notifi-leaf"));
+
+ final DOMNotification notificationData = mock(DOMNotification.class);
+
+ final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf"));
+ final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), leaf);
+
+ when(notificationData.getType()).thenReturn(schemaPathNotifi);
+ when(notificationData.getBody()).thenReturn(notifiBody);
+
+ final String result = prepareJson(notificationData, schemaPathNotifi);
+
+ assertTrue(result.contains("ietf-restconf:notification"));
+ assertTrue(result.contains("event-time"));
+ assertTrue(result.contains("notifi-module:notifi-leaf"));
+ assertTrue(result.contains("lf" + '"' + ":" + '"' + "value"));
+ }
+
+ @Test
+ public void notifi_cont_leafTest() throws Exception {
+ final QNameModule moduleQName =
+ QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+
+ final SchemaPath schemaPathNotifi =
+ SchemaPath.create(false, QName.create(moduleQName, "notifi-cont"));
+
+ final DOMNotification notificationData = mock(DOMNotification.class);
+
+ final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf"));
+ final ContainerNode cont = mockCont(QName.create(moduleQName, "cont"), leaf);
+ final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), cont);
+
+ when(notificationData.getType()).thenReturn(schemaPathNotifi);
+ when(notificationData.getBody()).thenReturn(notifiBody);
+
+ final String result = prepareJson(notificationData, schemaPathNotifi);
+
+ assertTrue(result.contains("ietf-restconf:notification"));
+ assertTrue(result.contains("event-time"));
+ assertTrue(result.contains("notifi-module:notifi-cont"));
+ assertTrue(result.contains("cont"));
+ assertTrue(result.contains("lf" + '"' + ":" + '"' + "value"));
+ }
+
+ @Test
+ public void notifi_list_Test() throws Exception {
+ final QNameModule moduleQName =
+ QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+
+ final SchemaPath schemaPathNotifi = SchemaPath.create(false, QName.create(moduleQName, "notifi-list"));
+
+ final DOMNotification notificationData = mock(DOMNotification.class);
+
+ final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf"));
+ final MapEntryNode entry = mockMapEntry(QName.create(moduleQName, "lst"), leaf);
+ final MapNode list = mockList(QName.create(moduleQName, "lst"), entry);
+ final ContainerNode cont = mockCont(QName.create(moduleQName, "cont"), list);
+ final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), cont);
+
+ when(notificationData.getType()).thenReturn(schemaPathNotifi);
+ when(notificationData.getBody()).thenReturn(notifiBody);
+
+ final String result = prepareJson(notificationData, schemaPathNotifi);
+
+ assertTrue(result.contains("ietf-restconf:notification"));
+ assertTrue(result.contains("event-time"));
+ assertTrue(result.contains("notifi-module:notifi-list"));
+ assertTrue(result.contains("lst"));
+ assertTrue(result.contains("lf" + '"' + ":" + '"' + "value"));
+ }
+
+ @Test
+ public void notifi_grpTest() throws Exception {
+ final QNameModule moduleQName =
+ QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+
+ final SchemaPath schemaPathNotifi = SchemaPath.create(false, QName.create(moduleQName, "notifi-grp"));
+
+ final DOMNotification notificationData = mock(DOMNotification.class);
+
+ final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf"));
+ final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), leaf);
+
+ when(notificationData.getType()).thenReturn(schemaPathNotifi);
+ when(notificationData.getBody()).thenReturn(notifiBody);
+
+ final String result = prepareJson(notificationData, schemaPathNotifi);
+
+ assertTrue(result.contains("ietf-restconf:notification"));
+ assertTrue(result.contains("event-time"));
+ assertTrue(result.contains("lf" + '"' + ":" + '"' + "value"));
+ }
+
+ @Test
+ public void notifi_augmTest() throws Exception {
+ final QNameModule moduleQName =
+ QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+
+ final SchemaPath schemaPathNotifi = SchemaPath.create(false, QName.create(moduleQName, "notifi-augm"));
+
+ final DOMNotification notificationData = mock(DOMNotification.class);
+
+ final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf-augm"));
+ final AugmentationNode augm = mockAugm(leaf);
+ final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), augm);
+
+ when(notificationData.getType()).thenReturn(schemaPathNotifi);
+ when(notificationData.getBody()).thenReturn(notifiBody);
+
+ final String result = prepareJson(notificationData, schemaPathNotifi);
+
+ assertTrue(result.contains("ietf-restconf:notification"));
+ assertTrue(result.contains("event-time"));
+ assertTrue(result.contains("lf-augm" + '"' + ":" + '"' + "value"));
+ }
+
+ private AugmentationNode mockAugm(final LeafNode leaf) {
+ final AugmentationNode augm = mock(AugmentationNode.class);
+ final AugmentationIdentifier augmId = new AugmentationIdentifier(SingletonSet.of(leaf.getNodeType()));
+ when(augm.getIdentifier()).thenReturn(augmId);
+
+ final Collection<DataContainerChild<? extends PathArgument, ?>> childs = new ArrayList<>();
+ childs.add(leaf);
+
+ when(augm.getValue()).thenReturn(childs);
+ return augm;
+ }
+
+ private MapEntryNode mockMapEntry(final QName entryQName, final LeafNode leaf) {
+ final MapEntryNode entry = mock(MapEntryNode.class);
+ final Map<QName, Object> keyValues = new HashMap<>();
+ keyValues.put(leaf.getNodeType(), "value");
+ final NodeIdentifierWithPredicates nodeId = new NodeIdentifierWithPredicates(leaf.getNodeType(), keyValues);
+ when(entry.getIdentifier()).thenReturn(nodeId);
+ when(entry.getChild(any())).thenReturn(Optional.of(leaf));
+
+ final Collection<DataContainerChild<? extends PathArgument, ?>> childs = new ArrayList<>();
+ childs.add(leaf);
+
+ when(entry.getValue()).thenReturn(childs);
+ return entry;
+ }
+
+ private MapNode mockList(final QName listQName, final MapEntryNode... entries) {
+ final MapNode list = mock(MapNode.class);
+ when(list.getIdentifier()).thenReturn(NodeIdentifier.create(listQName));
+ when(list.getNodeType()).thenReturn(listQName);
+ when(list.getValue()).thenReturn(Lists.newArrayList(entries));
+ return list;
+ }
+
+ private ContainerNode mockCont(final QName contQName, final DataContainerChild<? extends PathArgument, ?> child) {
+ final ContainerNode cont = mock(ContainerNode.class);
+ when(cont.getIdentifier()).thenReturn(NodeIdentifier.create(contQName));
+ when(cont.getNodeType()).thenReturn(contQName);
+
+ final Collection<DataContainerChild<? extends PathArgument, ?>> childs = new ArrayList<>();
+ childs.add(child);
+ when(cont.getValue()).thenReturn(childs);
+ return cont;
+ }
+
+ private LeafNode mockLeaf(final QName leafQName) {
+ final LeafNode child = mock(LeafNode.class);
+ when(child.getNodeType()).thenReturn(leafQName);
+ when(child.getIdentifier()).thenReturn(NodeIdentifier.create(leafQName));
+ when(child.getValue()).thenReturn("value");
+ return child;
+ }
+
+ private String prepareJson(final DOMNotification notificationData, final SchemaPath schemaPathNotifi)
+ throws Exception {
+ final List<SchemaPath> paths = new ArrayList<>();
+ paths.add(schemaPathNotifi);
+ final List<NotificationListenerAdapter> listNotifi =
+ Notificator.createNotificationListener(paths, "stream-name", NotificationOutputType.JSON.toString());
+ final NotificationListenerAdapter notifi = listNotifi.get(0);
+
+ final Class<?> vars[] = {};
+ final Method prepareJsonM = notifi.getClass().getDeclaredMethod("prepareJson", vars);
+ prepareJsonM.setAccessible(true);
+
+ final Field notification = notifi.getClass().getDeclaredField("notification");
+ notification.setAccessible(true);
+ notification.set(notifi, notificationData);
+
+ final Field schema = notifi.getClass().getDeclaredField("schemaContext");
+ schema.setAccessible(true);
+ schema.set(notifi, this.schmeaCtx);
+
+ final String result = (String) prepareJsonM.invoke(notifi, null);
+ Preconditions.checkNotNull(result);
+ return result;
+ }
+}
--- /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.rest.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+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.stream.NormalizedNodeStreamWriter;
+
+public class DepthAwareNormalizedNodeWriterTest {
+
+ @Mock
+ private NormalizedNodeStreamWriter writer;
+ @Mock
+ private ContainerNode containerNodeData;
+ @Mock
+ private MapNode mapNodeData;
+ @Mock
+ private MapEntryNode mapEntryNodeData;
+ @Mock
+ private LeafSetNode<String> leafSetNodeData;
+ @Mock
+ private LeafSetEntryNode<String> leafSetEntryNodeData;
+ @Mock
+ private LeafNode<String> keyLeafNodeData;
+ @Mock
+ private LeafNode<String> anotherLeafNodeData;
+
+ private NodeIdentifier containerNodeIdentifier;
+ private NodeIdentifier mapNodeIdentifier;
+ private NodeIdentifierWithPredicates mapEntryNodeIdentifier;
+ private NodeIdentifier leafSetNodeIdentifier;
+ private NodeWithValue<?> leafSetEntryNodeIdentifier;
+ private NodeIdentifier keyLeafNodeIdentifier;
+ private NodeIdentifier anotherLeafNodeIdentifier;
+
+ private Collection<DataContainerChild<?, ?>> containerNodeValue;
+ private Collection<MapEntryNode> mapNodeValue;
+ private Collection<DataContainerChild<?, ?>> mapEntryNodeValue;
+ private Collection<LeafSetEntryNode<String>> leafSetNodeValue;
+ private String leafSetEntryNodeValue;
+ private String keyLeafNodeValue;
+ private String anotherLeafNodeValue;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // identifiers
+ containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container"));
+ Mockito.when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier);
+
+ mapNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "list"));
+ Mockito.when(mapNodeData.getIdentifier()).thenReturn(mapNodeIdentifier);
+
+ final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry");
+ leafSetEntryNodeValue = "leaf-set-value";
+ leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue);
+ Mockito.when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier);
+ Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeQName);
+
+ leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set"));
+ Mockito.when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier);
+
+ final QName mapEntryNodeKey = QName.create("namespace", "key-field");
+ keyLeafNodeIdentifier = NodeIdentifier.create(mapEntryNodeKey);
+ keyLeafNodeValue = "key-value";
+
+ mapEntryNodeIdentifier = new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+ QName.create("namespace", "list-entry"), Collections.singletonMap(mapEntryNodeKey, keyLeafNodeValue));
+ Mockito.when(mapEntryNodeData.getIdentifier()).thenReturn(mapEntryNodeIdentifier);
+ Mockito.when(mapEntryNodeData.getChild(keyLeafNodeIdentifier)).thenReturn(Optional.of(keyLeafNodeData));
+
+ Mockito.when(keyLeafNodeData.getValue()).thenReturn(keyLeafNodeValue);
+ Mockito.when(keyLeafNodeData.getIdentifier()).thenReturn(keyLeafNodeIdentifier);
+
+ anotherLeafNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "another-field"));
+ anotherLeafNodeValue = "another-value";
+
+ Mockito.when(anotherLeafNodeData.getValue()).thenReturn(anotherLeafNodeValue);
+ Mockito.when(anotherLeafNodeData.getIdentifier()).thenReturn(anotherLeafNodeIdentifier);
+
+ // values
+ Mockito.when(leafSetEntryNodeData.getValue()).thenReturn(leafSetEntryNodeValue);
+
+ leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData);
+ Mockito.when(leafSetNodeData.getValue()).thenReturn(leafSetNodeValue);
+
+ containerNodeValue = Collections.singleton(leafSetNodeData);
+ Mockito.when(containerNodeData.getValue()).thenReturn(containerNodeValue);
+
+ mapEntryNodeValue = Sets.newHashSet(keyLeafNodeData, anotherLeafNodeData);
+ Mockito.when(mapEntryNodeData.getValue()).thenReturn(mapEntryNodeValue);
+
+ mapNodeValue = Collections.singleton(mapEntryNodeData);
+ Mockito.when(mapNodeData.getValue()).thenReturn(mapNodeValue);
+ }
+
+ /**
+ * Test write {@link ContainerNode} with children but write data only to depth 1 (children will not be written).
+ */
+ @Test
+ public void writeContainerWithoutChildrenTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(writer, 1);
+
+ depthWriter.write(containerNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write {@link ContainerNode} with children and write also all its children.
+ */
+ @Test
+ public void writeContainerWithChildrenTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, Integer.MAX_VALUE);
+
+ depthWriter.write(containerNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapNode} with children but write data only to depth 1 (children will not be written).
+ */
+ @Test
+ public void writeMapNodeWithoutChildrenTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(writer, 1);
+
+ depthWriter.write(mapNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write {@link MapNode} with children and write also all its children.
+ *
+ * FIXME
+ * Although ordered writer is used leaves are not written in expected order.
+ *
+ */
+ @Ignore
+ @Test
+ public void writeMapNodeWithChildrenTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, Integer.MAX_VALUE);
+
+ depthWriter.write(mapNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(2)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ // FIXME this assertion is not working because leaves are not written in expected order
+ inOrder.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link LeafSetNode} with depth 1 (children will not be written).
+ */
+ @Test
+ public void writeLeafSetNodeWithoutChildrenTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, 1);
+
+ depthWriter.write(leafSetNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link LeafSetNode} when all its children will be written.
+ */
+ @Test
+ public void writeLeafSetNodeWithChildrenTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, Integer.MAX_VALUE);
+
+ depthWriter.write(leafSetNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link LeafSetEntryNode}.
+ */
+ @Test
+ public void writeLeafSetEntryNodeTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, Integer.MAX_VALUE);
+
+ depthWriter.write(leafSetEntryNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapEntryNode} unordered to depth 1 to write only keys.
+ */
+ @Test
+ public void writeMapEntryNodeUnorderedOnlyKeysTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, false, 1);
+
+ depthWriter.write(mapEntryNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ // write only the key
+ inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapEntryNode} unordered with full depth.
+ */
+ @Test
+ public void writeMapEntryNodeUnorderedTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, false, Integer.MAX_VALUE);
+
+ depthWriter.write(mapEntryNodeData);
+
+ // unordered
+ Mockito.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ Mockito.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ Mockito.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+ Mockito.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapEntryNode} ordered with depth 1 (children will not be written).
+ */
+ @Test
+ public void writeMapEntryNodeOrderedWithoutChildrenTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, true, 1);
+
+ depthWriter.write(mapEntryNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapEntryNode} ordered and write also all its children.
+ *
+ * FIXME
+ * Although ordered writer is used leaves are not written in expected order.
+ *
+ */
+ @Ignore
+ @Test
+ public void writeMapEntryNodeOrderedTest() throws Exception {
+ final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+ writer, true, Integer.MAX_VALUE);
+
+ depthWriter.write(mapEntryNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(2)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ // FIXME this assertion is not working because leaves are not written in expected order
+ inOrder.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+}
\ 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.restconf.jersey.providers;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+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.stream.NormalizedNodeStreamWriter;
+
+/**
+ * Unit test for {@link ParameterAwareNormalizedNodeWriter} used with depth parameter.
+ */
+public class ParameterAwareNormalizedNodeWriterDepthTest {
+
+ @Mock
+ private NormalizedNodeStreamWriter writer;
+ @Mock
+ private ContainerNode containerNodeData;
+ @Mock
+ private MapNode mapNodeData;
+ @Mock
+ private MapEntryNode mapEntryNodeData;
+ @Mock
+ private LeafSetNode<String> leafSetNodeData;
+ @Mock
+ private LeafSetEntryNode<String> leafSetEntryNodeData;
+ @Mock
+ private LeafNode<String> keyLeafNodeData;
+ @Mock
+ private LeafNode<String> anotherLeafNodeData;
+
+ private NodeIdentifier containerNodeIdentifier;
+ private NodeIdentifier mapNodeIdentifier;
+ private NodeIdentifierWithPredicates mapEntryNodeIdentifier;
+ private NodeIdentifier leafSetNodeIdentifier;
+ private NodeWithValue<?> leafSetEntryNodeIdentifier;
+ private NodeIdentifier keyLeafNodeIdentifier;
+ private NodeIdentifier anotherLeafNodeIdentifier;
+
+ private Collection<DataContainerChild<?, ?>> containerNodeValue;
+ private Collection<MapEntryNode> mapNodeValue;
+ private Collection<DataContainerChild<?, ?>> mapEntryNodeValue;
+ private Collection<LeafSetEntryNode<String>> leafSetNodeValue;
+ private String leafSetEntryNodeValue;
+ private String keyLeafNodeValue;
+ private String anotherLeafNodeValue;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // identifiers
+ containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container"));
+ Mockito.when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier);
+ Mockito.when(containerNodeData.getNodeType()).thenReturn(containerNodeIdentifier.getNodeType());
+
+ mapNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "list"));
+ Mockito.when(mapNodeData.getIdentifier()).thenReturn(mapNodeIdentifier);
+
+ final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry");
+ leafSetEntryNodeValue = "leaf-set-value";
+ leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue);
+ Mockito.when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier);
+ Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeQName);
+
+ leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set"));
+ Mockito.when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier);
+
+ final QName mapEntryNodeKey = QName.create("namespace", "key-field");
+ keyLeafNodeIdentifier = NodeIdentifier.create(mapEntryNodeKey);
+ keyLeafNodeValue = "key-value";
+
+ mapEntryNodeIdentifier = new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+ QName.create("namespace", "list-entry"), Collections.singletonMap(mapEntryNodeKey, keyLeafNodeValue));
+ Mockito.when(mapEntryNodeData.getIdentifier()).thenReturn(mapEntryNodeIdentifier);
+ Mockito.when(mapEntryNodeData.getChild(keyLeafNodeIdentifier)).thenReturn(Optional.of(keyLeafNodeData));
+
+ Mockito.when(keyLeafNodeData.getValue()).thenReturn(keyLeafNodeValue);
+ Mockito.when(keyLeafNodeData.getIdentifier()).thenReturn(keyLeafNodeIdentifier);
+
+ anotherLeafNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "another-field"));
+ anotherLeafNodeValue = "another-value";
+
+ Mockito.when(anotherLeafNodeData.getValue()).thenReturn(anotherLeafNodeValue);
+ Mockito.when(anotherLeafNodeData.getIdentifier()).thenReturn(anotherLeafNodeIdentifier);
+
+ // values
+ Mockito.when(leafSetEntryNodeData.getValue()).thenReturn(leafSetEntryNodeValue);
+
+ leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData);
+ Mockito.when(leafSetNodeData.getValue()).thenReturn(leafSetNodeValue);
+
+ containerNodeValue = Collections.singleton(leafSetNodeData);
+ Mockito.when(containerNodeData.getValue()).thenReturn(containerNodeValue);
+
+ mapEntryNodeValue = Sets.newHashSet(keyLeafNodeData, anotherLeafNodeData);
+ Mockito.when(mapEntryNodeData.getValue()).thenReturn(mapEntryNodeValue);
+
+ mapNodeValue = Collections.singleton(mapEntryNodeData);
+ Mockito.when(mapNodeData.getValue()).thenReturn(mapNodeValue);
+ }
+
+ /**
+ * Test write {@link ContainerNode} with children but write data only to depth 1 (children will not be written).
+ * Depth parameter is limited to 1.
+ */
+ @Test
+ public void writeContainerWithoutChildrenDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter
+ .forStreamWriter(writer, 1, null);
+
+ parameterWriter.write(containerNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write {@link ContainerNode} with children and write also all its children.
+ * Depth parameter has higher value than maximal children depth.
+ */
+ @Test
+ public void writeContainerWithChildrenDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, Integer.MAX_VALUE, null);
+
+ parameterWriter.write(containerNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapNode} with children but write data only to depth 1 (children will not be written).
+ * Depth parameter limits depth to 1.
+ */
+ @Test
+ public void writeMapNodeWithoutChildrenDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter
+ .forStreamWriter(writer, 1, null);
+
+ parameterWriter.write(mapNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write {@link MapNode} with children and write also all its children.
+ * Depth parameter has higher value than maximal children depth.
+ *
+ *
+ * FIXME
+ * Although ordered writer is used leaves are not written in expected order.
+ *
+ */
+ @Ignore
+ @Test
+ public void writeMapNodeWithChildrenDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, Integer.MAX_VALUE, null);
+
+ parameterWriter.write(mapNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(2)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ // FIXME this assertion is not working because leaves are not written in expected order
+ inOrder.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link LeafSetNode} with depth 1 (children will not be written).
+ * Depth parameter limits depth to 1.
+ */
+ @Test
+ public void writeLeafSetNodeWithoutChildrenDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, 1, null);
+
+ parameterWriter.write(leafSetNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link LeafSetNode} when all its children will be written.
+ * Depth parameter has higher value than maximal children depth.
+ */
+ @Test
+ public void writeLeafSetNodeWithChildrenDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, Integer.MAX_VALUE, null);
+
+ parameterWriter.write(leafSetNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link LeafSetEntryNode}.
+ * Depth parameter has higher value than maximal children depth.
+ */
+ @Test
+ public void writeLeafSetEntryNodeDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, Integer.MAX_VALUE, null);
+
+ parameterWriter.write(leafSetEntryNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapEntryNode} unordered to depth 1 to write only keys.
+ * Depth parameter limits depth to 1.
+ */
+ @Test
+ public void writeMapEntryNodeUnorderedOnlyKeysDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, false, 1, null);
+
+ parameterWriter.write(mapEntryNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ // write only the key
+ inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapEntryNode} unordered with full depth.
+ * Depth parameter has higher value than maximal children depth.
+ */
+ @Test
+ public void writeMapEntryNodeUnorderedDepthTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, false, Integer.MAX_VALUE, null);
+
+ parameterWriter.write(mapEntryNodeData);
+
+ // unordered
+ Mockito.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ Mockito.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ Mockito.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+ Mockito.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapEntryNode} ordered with depth 1 (children will not be written).
+ * Depth parameter limits depth to 1.
+ */
+ @Test
+ public void writeMapEntryNodeOrderedWithoutChildrenTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, true, 1, null);
+
+ parameterWriter.write(mapEntryNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write with {@link MapEntryNode} ordered and write also all its children.
+ * Depth parameter has higher value than maximal children depth.
+ *
+ * FIXME
+ * Although ordered writer is used leaves are not written in expected order.
+ *
+ */
+ @Ignore
+ @Test
+ public void writeMapEntryNodeOrderedTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, true, Integer.MAX_VALUE, null);
+
+ parameterWriter.write(mapEntryNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(2)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ // FIXME this assertion is not working because leaves are not written in expected order
+ inOrder.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+}
\ 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.restconf.jersey.providers;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+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.stream.NormalizedNodeStreamWriter;
+
+/**
+ * Unit test for {@link ParameterAwareNormalizedNodeWriter} used with fields parameter.
+ */
+public class ParameterAwareNormalizedNodeWriterFieldsTest {
+
+ @Mock
+ private NormalizedNodeStreamWriter writer;
+ @Mock
+ private ContainerNode containerNodeData;
+ @Mock
+ private MapNode mapNodeData;
+ @Mock
+ private MapEntryNode mapEntryNodeData;
+ @Mock
+ private LeafSetNode<String> leafSetNodeData;
+ @Mock
+ private LeafSetEntryNode<String> leafSetEntryNodeData;
+ @Mock
+ private LeafNode<String> keyLeafNodeData;
+
+ private NodeIdentifier containerNodeIdentifier;
+ private NodeIdentifier mapNodeIdentifier;
+ private NodeIdentifierWithPredicates mapEntryNodeIdentifier;
+ private NodeIdentifier leafSetNodeIdentifier;
+ private NodeWithValue<?> leafSetEntryNodeIdentifier;
+ private NodeIdentifier keyLeafNodeIdentifier;
+
+ private Collection<DataContainerChild<?, ?>> containerNodeValue;
+ private Collection<MapEntryNode> mapNodeValue;
+ private Collection<DataContainerChild<?, ?>> mapEntryNodeValue;
+ private Collection<LeafSetEntryNode<String>> leafSetNodeValue;
+ private String leafSetEntryNodeValue;
+ private String keyLeafNodeValue;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // identifiers
+ containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container"));
+ Mockito.when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier);
+ Mockito.when(containerNodeData.getNodeType()).thenReturn(containerNodeIdentifier.getNodeType());
+
+ mapNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "list"));
+ Mockito.when(mapNodeData.getIdentifier()).thenReturn(mapNodeIdentifier);
+ Mockito.when(mapNodeData.getNodeType()).thenReturn(mapNodeIdentifier.getNodeType());
+
+ final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry");
+ leafSetEntryNodeValue = "leaf-set-value";
+ leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue);
+ Mockito.when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier);
+ Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeIdentifier.getNodeType());
+ Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeQName);
+
+ leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set"));
+ Mockito.when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier);
+ Mockito.when(leafSetNodeData.getNodeType()).thenReturn(leafSetNodeIdentifier.getNodeType());
+
+ final QName mapEntryNodeKey = QName.create("namespace", "key-field");
+ keyLeafNodeIdentifier = NodeIdentifier.create(mapEntryNodeKey);
+ keyLeafNodeValue = "key-value";
+
+ mapEntryNodeIdentifier = new NodeIdentifierWithPredicates(
+ QName.create("namespace", "list-entry"), Collections.singletonMap(mapEntryNodeKey, keyLeafNodeValue));
+ Mockito.when(mapEntryNodeData.getIdentifier()).thenReturn(mapEntryNodeIdentifier);
+ Mockito.when(mapEntryNodeData.getNodeType()).thenReturn(mapEntryNodeIdentifier.getNodeType());
+ Mockito.when(mapEntryNodeData.getChild(keyLeafNodeIdentifier)).thenReturn(Optional.of(keyLeafNodeData));
+
+ Mockito.when(keyLeafNodeData.getValue()).thenReturn(keyLeafNodeValue);
+ Mockito.when(keyLeafNodeData.getIdentifier()).thenReturn(keyLeafNodeIdentifier);
+ Mockito.when(keyLeafNodeData.getNodeType()).thenReturn(keyLeafNodeIdentifier.getNodeType());
+
+ // values
+ Mockito.when(leafSetEntryNodeData.getValue()).thenReturn(leafSetEntryNodeValue);
+
+ leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData);
+ Mockito.when(leafSetNodeData.getValue()).thenReturn(leafSetNodeValue);
+
+ containerNodeValue = Collections.singleton(leafSetNodeData);
+ Mockito.when(containerNodeData.getValue()).thenReturn(containerNodeValue);
+
+ mapEntryNodeValue = Sets.newHashSet(keyLeafNodeData);
+ Mockito.when(mapEntryNodeData.getValue()).thenReturn(mapEntryNodeValue);
+
+ mapNodeValue = Collections.singleton(mapEntryNodeData);
+ Mockito.when(mapNodeData.getValue()).thenReturn(mapNodeValue);
+ }
+
+ /**
+ * Test write {@link ContainerNode} when children which will be written are limited.
+ * Fields parameter selects 0/1 of container children to be written.
+ */
+ @Test
+ public void writeContainerWithLimitedFieldsTest() throws Exception {
+ final List<Set<QName>> limitedFields = new ArrayList<>();
+ limitedFields.add(Sets.newHashSet());
+
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, null, limitedFields);
+
+ parameterWriter.write(containerNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write {@link ContainerNode} when all its children are selected to be written.
+ * Fields parameter selects 1/1 of container children to be written.
+ */
+ @Test
+ public void writeContainerAllFieldsTest() throws Exception {
+ final List<Set<QName>> limitedFields = new ArrayList<>();
+ limitedFields.add(Sets.newHashSet(leafSetNodeIdentifier.getNodeType()));
+
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, null, limitedFields);
+
+ parameterWriter.write(containerNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write {@link MapEntryNode} as child of {@link MapNode} when children which will be written are limited.
+ * Fields parameter selects 0/1 of map entry node children to be written.
+ */
+ @Test
+ public void writeMapEntryNodeWithLimitedFieldsTest() throws Exception {
+ final List<Set<QName>> limitedFields = new ArrayList<>();
+ limitedFields.add(Sets.newHashSet());
+
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, null, limitedFields);
+
+ parameterWriter.write(mapNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write {@link MapEntryNode} as child of {@link MapNode} when all its children will be written.
+ * Fields parameter selects 1/1 of map entry node children to be written.
+ */
+ @Test
+ public void writeMapNodeAllFieldsTest() throws Exception {
+ final List<Set<QName>> limitedFields = new ArrayList<>();
+ limitedFields.add(Sets.newHashSet(keyLeafNodeData.getNodeType()));
+
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, null, limitedFields);
+
+ parameterWriter.write(mapNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+}
\ 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.restconf.jersey.providers;
+
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+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.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+/**
+ * Unit test for {@link ParameterAwareNormalizedNodeWriter} used with all parameters.
+ */
+public class ParameterAwareNormalizedNodeWriterParametersTest {
+
+ @Mock
+ private NormalizedNodeStreamWriter writer;
+ @Mock
+ private ContainerNode containerNodeData;
+ @Mock
+ private LeafSetNode<String> leafSetNodeData;
+ @Mock
+ private LeafSetEntryNode<String> leafSetEntryNodeData;
+ @Mock
+ private ContainerNode rootDataContainerData;
+
+ private NodeIdentifier containerNodeIdentifier;
+ private NodeIdentifier leafSetNodeIdentifier;
+ private NodeWithValue<?> leafSetEntryNodeIdentifier;
+ private NodeIdentifier rootDataContainerIdentifier;
+
+ private Collection<DataContainerChild<?, ?>> containerNodeValue;
+ private Collection<LeafSetEntryNode<String>> leafSetNodeValue;
+ private String leafSetEntryNodeValue;
+ private Collection<DataContainerChild<?, ?>> rootDataContainerValue;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // identifiers
+ containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container"));
+ Mockito.when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier);
+ Mockito.when(containerNodeData.getNodeType()).thenReturn(containerNodeIdentifier.getNodeType());
+
+ final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry");
+ leafSetEntryNodeValue = "leaf-set-value";
+ leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue);
+ Mockito.when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier);
+ Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeIdentifier.getNodeType());
+ Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeQName);
+
+ leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set"));
+ Mockito.when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier);
+ Mockito.when(leafSetNodeData.getNodeType()).thenReturn(leafSetNodeIdentifier.getNodeType());
+
+ rootDataContainerIdentifier = NodeIdentifier.create(
+ QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "data"));
+ Mockito.when(rootDataContainerData.getIdentifier()).thenReturn(rootDataContainerIdentifier);
+ Mockito.when(rootDataContainerData.getNodeType()).thenReturn(rootDataContainerIdentifier.getNodeType());
+
+ // values
+ Mockito.when(leafSetEntryNodeData.getValue()).thenReturn(leafSetEntryNodeValue);
+
+ leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData);
+ Mockito.when(leafSetNodeData.getValue()).thenReturn(leafSetNodeValue);
+
+ containerNodeValue = Collections.singleton(leafSetNodeData);
+ Mockito.when(containerNodeData.getValue()).thenReturn(containerNodeValue);
+
+ rootDataContainerValue = Collections.singleton(leafSetNodeData);
+ Mockito.when(rootDataContainerData.getValue()).thenReturn(rootDataContainerValue);
+ }
+
+ /**
+ * Test write {@link ContainerNode} when all its children are selected to be written by fields parameter.
+ * Depth parameter is also used and limits output to depth 1.
+ * Fields parameter has effect limiting depth parameter in the way that selected nodes and its ancestors are
+ * written regardless of their depth (some of container children have depth > 1).
+ * Fields parameter selects all container children to be written and also all children of those children.
+ */
+ @Test
+ public void writeContainerParameterPrioritiesTest() throws Exception {
+ final List<Set<QName>> limitedFields = new ArrayList<>();
+ limitedFields.add(Sets.newHashSet(leafSetNodeIdentifier.getNodeType()));
+ limitedFields.add(Sets.newHashSet(leafSetEntryNodeIdentifier.getNodeType()));
+
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, 1, limitedFields);
+
+ parameterWriter.write(containerNodeData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ inOrder.verify(writer, Mockito.times(2)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+
+ /**
+ * Test write {@link ContainerNode} which represents data at restconf/data root.
+ * No parameters are used.
+ */
+ @Test
+ public void writeRootDataTest() throws Exception {
+ final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+ writer, null, null);
+
+ parameterWriter.write(rootDataContainerData);
+
+ final InOrder inOrder = Mockito.inOrder(writer);
+ inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+ inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+ leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+ inOrder.verify(writer, Mockito.times(1)).endNode();
+ Mockito.verifyNoMoreInteractions(writer);
+ }
+}
\ No newline at end of file
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.Draft17.RestconfModule;
+import org.opendaylight.restconf.Draft18.RestconfModule;
import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.restconf.rest.services.api.RestconfModulesService;
import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.restconf.utils.RestconfConstants;
when(schemaContext.findModuleByNamespaceAndRevision(any(URI.class), any(Date.class))).thenAnswer(invocation -> {
final Object[] args = invocation.getArguments();
- if ((args[0] == Draft17.RestconfModule.IETF_RESTCONF_QNAME.getNamespace())
- && (args[1] == Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision())) {
+ if ((args[0] == Draft18.RestconfModule.IETF_RESTCONF_QNAME.getNamespace())
+ && (args[1] == Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision())) {
return parseCustomRestconfSource(restconfModuleName).findModuleByName(
restconfModuleName, (Date) args[1]);
} else {
import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.restconf.rest.services.api.RestconfStreamsService;
import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants;
import org.opendaylight.restconf.utils.mapping.RestconfMappingStreamConstants;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
Notificator.removeAllListeners();
// put test streams
- Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0));
- Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1));
- Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2));
+ Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0),
+ NotificationOutputType.XML);
+ Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1),
+ NotificationOutputType.XML);
+ Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2),
+ NotificationOutputType.XML);
}
@AfterClass
public void getAvailableStreamsTest() throws Exception {
// prepare conditions - get correct Restconf module
when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
- when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
- .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+ when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+ .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
.thenReturn(getTestingRestconfModule("ietf-restconf"));
// make test
public void getAvailableStreamsMissingRestconfModuleNegativeTest() {
// prepare conditions - get null Restconf module
when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
- when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
- .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision())).thenReturn(null);
+ when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+ .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision())).thenReturn(null);
// make test
this.thrown.expect(NullPointerException.class);
public void getAvailableStreamsMissingListStreamNegativeTest() {
// prepare conditions - get Restconf module with missing list stream
when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
- when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
- .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+ when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+ .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
.thenReturn(getTestingRestconfModule("restconf-module-with-missing-list-stream"));
// make test and verify
public void getAvailableStreamsMissingContainerStreamsNegativeTest() {
// prepare conditions - get Restconf module with missing container streams
when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
- when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
- .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+ when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+ .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
.thenReturn(getTestingRestconfModule("restconf-module-with-missing-container-streams"));
// make test and verify
public void getAvailableStreamsIllegalListStreamNegativeTest() {
// prepare conditions - get Restconf module with illegal list stream
when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
- when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
- .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+ when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+ .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
.thenReturn(getTestingRestconfModule("restconf-module-with-illegal-list-stream"));
// make test
public void getAvailableStreamsIllegalContainerStreamsNegativeTest() {
// prepare conditions - get Restconf module with illegal container streams
when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
- when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
- .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+ when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+ .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
.thenReturn(getTestingRestconfModule("restconf-module-with-illegal-container-streams"));
// make test
public void getAvailableStreamsIllegalLeafDescriptionNegativeTest() {
// prepare conditions - get Restconf module with illegal leaf description in list stream
when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
- when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
- .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+ when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+ .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
.thenReturn(getTestingRestconfModule("restconf-module-with-illegal-leaf-description"));
// make test
* @return Restconf module
*/
private Module getTestingRestconfModule(final String s) {
- return this.schemaContext.findModuleByName(s, Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+ return this.schemaContext.findModuleByName(s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
}
/**
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-
import com.google.common.base.Optional;
import com.google.common.util.concurrent.Futures;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
- containerPlayerQname = QName.create(baseQName, "player");
- leafQname = QName.create(baseQName, "gap");
+ final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+ Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+ Mockito.when(this.uriInfo.getQueryParameters()).thenReturn(value);
+
+ this.baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
+ this.containerPlayerQname = QName.create(this.baseQName, "player");
+ this.leafQname = QName.create(this.baseQName, "gap");
- final QName containerLibraryQName = QName.create(baseQName, "library");
- final QName listPlaylistQName = QName.create(baseQName, "playlist");
+ final QName containerLibraryQName = QName.create(this.baseQName, "library");
+ final QName listPlaylistQName = QName.create(this.baseQName, "playlist");
final LeafNode buildLeaf = Builders.leafBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.leafQname))
.withValue(0.2)
.build();
- buildPlayerCont = Builders.containerBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQname))
+ this.buildPlayerCont = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.containerPlayerQname))
.withChild(buildLeaf)
.build();
- buildLibraryCont = Builders.containerBuilder()
+ this.buildLibraryCont = Builders.containerBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerLibraryQName))
.build();
- buildPlaylistList = Builders.mapBuilder()
+ this.buildPlaylistList = Builders.mapBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listPlaylistQName))
.build();
- buildBaseCont = Builders.containerBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
- .withChild(buildPlayerCont)
+ this.buildBaseCont = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.baseQName))
+ .withChild(this.buildPlayerCont)
.build();
// config contains one child the same as in operational and one additional
- buildBaseContConfig = Builders.containerBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
- .withChild(buildPlayerCont)
- .withChild(buildLibraryCont)
+ this.buildBaseContConfig = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.baseQName))
+ .withChild(this.buildPlayerCont)
+ .withChild(this.buildLibraryCont)
.build();
// operational contains one child the same as in config and one additional
- buildBaseContOperational = Builders.containerBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
- .withChild(buildPlayerCont)
- .withChild(buildPlaylistList)
+ this.buildBaseContOperational = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.baseQName))
+ .withChild(this.buildPlayerCont)
+ .withChild(this.buildPlaylistList)
.build();
- iidBase = YangInstanceIdentifier.builder()
- .node(baseQName)
+ this.iidBase = YangInstanceIdentifier.builder()
+ .node(this.baseQName)
.build();
- contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
- schemaNode = DataSchemaContextTree.from(contextRef.get()).getChild(iidBase).getDataSchemaNode();
+ this.contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+ this.schemaNode = DataSchemaContextTree.from(this.contextRef.get()).getChild(this.iidBase).getDataSchemaNode();
final SchemaContextHandler schemaContextHandler = new SchemaContextHandler();
- schemaContextHandler.onGlobalContextUpdated(contextRef.get());
- dataService = new RestconfDataServiceImpl(schemaContextHandler, transactionChainHandler, mountPointServiceHandler);
- doReturn(domTransactionChain).when(transactionChainHandler).get();
- doReturn(read).when(domTransactionChain).newReadOnlyTransaction();
- doReturn(readWrite).when(domTransactionChain).newReadWriteTransaction();
- doReturn(write).when(domTransactionChain).newWriteOnlyTransaction();
- doReturn(mountPointService).when(mountPointServiceHandler).get();
- doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(any(YangInstanceIdentifier.class));
- doReturn(contextRef.get()).when(mountPoint).getSchemaContext();
- doReturn(Optional.of(mountDataBroker)).when(mountPoint).getService(DOMDataBroker.class);
- doReturn(transactionChain).when(mountDataBroker).createTransactionChain(any(TransactionChainListener.class));
- doReturn(read).when(transactionChain).newReadOnlyTransaction();
- doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
+ schemaContextHandler.onGlobalContextUpdated(this.contextRef.get());
+ this.dataService = new RestconfDataServiceImpl(schemaContextHandler, this.transactionChainHandler, this.mountPointServiceHandler);
+ doReturn(this.domTransactionChain).when(this.transactionChainHandler).get();
+ doReturn(this.read).when(this.domTransactionChain).newReadOnlyTransaction();
+ doReturn(this.readWrite).when(this.domTransactionChain).newReadWriteTransaction();
+ doReturn(this.write).when(this.domTransactionChain).newWriteOnlyTransaction();
+ doReturn(this.mountPointService).when(this.mountPointServiceHandler).get();
+ doReturn(Optional.of(this.mountPoint)).when(this.mountPointService).getMountPoint(any(YangInstanceIdentifier.class));
+ doReturn(this.contextRef.get()).when(this.mountPoint).getSchemaContext();
+ doReturn(Optional.of(this.mountDataBroker)).when(this.mountPoint).getService(DOMDataBroker.class);
+ doReturn(this.transactionChain).when(this.mountDataBroker).createTransactionChain(any(TransactionChainListener.class));
+ doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
+ doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
}
@Test
public void testReadData() {
- doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
- doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, iidBase);
- doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL, iidBase);
- final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
+ doReturn(new MultivaluedHashMap<String, String>()).when(this.uriInfo).getQueryParameters();
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.OPERATIONAL, this.iidBase);
+ final Response response = this.dataService.readData("example-jukebox:jukebox", this.uriInfo);
assertNotNull(response);
assertEquals(200, response.getStatus());
- assertEquals(buildBaseCont, ((NormalizedNodeContext) response.getEntity()).getData());
+ assertEquals(this.buildBaseCont, ((NormalizedNodeContext) response.getEntity()).getData());
}
/**
*/
@Test
public void testReadDataMountPoint() {
- doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
- doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseContConfig))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, iidBase);
- doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseContOperational))).when(read)
- .read(LogicalDatastoreType.OPERATIONAL, iidBase);
+ doReturn(new MultivaluedHashMap<String, String>()).when(this.uriInfo).getQueryParameters();
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContConfig))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContOperational))).when(this.read)
+ .read(LogicalDatastoreType.OPERATIONAL, this.iidBase);
- final Response response = dataService.readData(
- "example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", uriInfo);
+ final Response response = this.dataService.readData(
+ "example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", this.uriInfo);
assertNotNull(response);
assertEquals(200, response.getStatus());
final NormalizedNode<?, ?> data = ((NormalizedNodeContext) response.getEntity()).getData();
assertTrue(data instanceof ContainerNode);
assertEquals(3, ((ContainerNode) data).getValue().size());
- assertTrue(((ContainerNode) data).getChild(buildPlayerCont.getIdentifier()).isPresent());
- assertTrue(((ContainerNode) data).getChild(buildLibraryCont.getIdentifier()).isPresent());
- assertTrue(((ContainerNode) data).getChild(buildPlaylistList.getIdentifier()).isPresent());
+ assertTrue(((ContainerNode) data).getChild(this.buildPlayerCont.getIdentifier()).isPresent());
+ assertTrue(((ContainerNode) data).getChild(this.buildLibraryCont.getIdentifier()).isPresent());
+ assertTrue(((ContainerNode) data).getChild(this.buildPlaylistList.getIdentifier()).isPresent());
}
@Test(expected = RestconfDocumentedException.class)
public void testReadDataNoData() {
- doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
- doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION,
- iidBase);
- doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL,
- iidBase);
- dataService.readData("example-jukebox:jukebox", uriInfo);
+ doReturn(new MultivaluedHashMap<String, String>()).when(this.uriInfo).getQueryParameters();
+ doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION,
+ this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.OPERATIONAL,
+ this.iidBase);
+ this.dataService.readData("example-jukebox:jukebox", this.uriInfo);
+ }
+
+ /**
+ * Read data from config datastore according to content parameter
+ */
+ @Test
+ public void testReadDataConfigTest() {
+ final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+ parameters.put("content", Collections.singletonList("config"));
+
+ doReturn(parameters).when(this.uriInfo).getQueryParameters();
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContConfig))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContOperational))).when(this.read)
+ .read(LogicalDatastoreType.OPERATIONAL, this.iidBase);
+
+ final Response response = this.dataService.readData("example-jukebox:jukebox", this.uriInfo);
+
+ assertNotNull(response);
+ assertEquals(200, response.getStatus());
+
+ // response must contain only config data
+ final NormalizedNode<?, ?> data = ((NormalizedNodeContext) response.getEntity()).getData();
+
+ // config data present
+ assertTrue(((ContainerNode) data).getChild(this.buildPlayerCont.getIdentifier()).isPresent());
+ assertTrue(((ContainerNode) data).getChild(this.buildLibraryCont.getIdentifier()).isPresent());
+
+ // state data absent
+ assertFalse(((ContainerNode) data).getChild(this.buildPlaylistList.getIdentifier()).isPresent());
+ }
+
+ /**
+ * Read data from operational datastore according to content parameter
+ */
+ @Test
+ public void testReadDataOperationalTest() {
+ final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+ parameters.put("content", Collections.singletonList("nonconfig"));
+
+ doReturn(parameters).when(this.uriInfo).getQueryParameters();
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContConfig))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContOperational))).when(this.read)
+ .read(LogicalDatastoreType.OPERATIONAL, this.iidBase);
+
+ final Response response = this.dataService.readData("example-jukebox:jukebox", this.uriInfo);
+
+ assertNotNull(response);
+ assertEquals(200, response.getStatus());
+
+ // response must contain only operational data
+ final NormalizedNode<?, ?> data = ((NormalizedNodeContext) response.getEntity()).getData();
+
+ // state data present
+ assertTrue(((ContainerNode) data).getChild(this.buildPlayerCont.getIdentifier()).isPresent());
+ assertTrue(((ContainerNode) data).getChild(this.buildPlaylistList.getIdentifier()).isPresent());
+
+ // config data absent
+ assertFalse(((ContainerNode) data).getChild(this.buildLibraryCont.getIdentifier()).isPresent());
}
@Test
public void testPutData() {
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
-
- doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, iidBase);
- doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
- doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
- final Response response = dataService.putData(null, payload);
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef.get());
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
+
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, payload.getData());
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ final Response response = this.dataService.putData(null, payload, this.uriInfo);
assertNotNull(response);
assertEquals(200, response.getStatus());
}
final DOMDataBroker dataBroker = Mockito.mock(DOMDataBroker.class);
final DOMMountPoint mountPoint = Mockito.mock(DOMMountPoint.class);
doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
- doReturn(transactionChainHandler.get()).when(dataBroker).createTransactionChain(RestConnectorProvider.transactionListener);
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, mountPoint, contextRef.get());
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
-
- doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, iidBase);
- doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
- doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
- final Response response = dataService.putData(null, payload);
+ doReturn(this.transactionChainHandler.get()).when(dataBroker).createTransactionChain(RestConnectorProvider.transactionListener);
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, mountPoint, this.contextRef.get());
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
+
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, payload.getData());
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ final Response response = this.dataService.putData(null, payload, this.uriInfo);
assertNotNull(response);
assertEquals(200, response.getStatus());
}
@Test
public void testPostData() {
- final QName listQname = QName.create(baseQName, "playlist");
- final QName listKeyQname = QName.create(baseQName, "name");
+ final QName listQname = QName.create(this.baseQName, "playlist");
+ final QName listKeyQname = QName.create(this.baseQName, "name");
final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQname, "name of band");
final LeafNode<Object> content = Builders.leafBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(this.baseQName, "name")))
.withValue("name of band")
.build();
final LeafNode<Object> content2 = Builders.leafBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(this.baseQName, "description")))
.withValue("band description")
.build();
final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
.withChild(mapEntryNode)
.build();
- doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
- final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, null, null, contextRef.get());
+ doReturn(new MultivaluedHashMap<String, String>()).when(this.uriInfo).getQueryParameters();
+ final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, null, null, this.contextRef.get());
final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildList);
- doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iidBase);
+ doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
final MapNode data = (MapNode) payload.getData();
final YangInstanceIdentifier.NodeIdentifierWithPredicates identifier = data.getValue().iterator().next().getIdentifier();
final YangInstanceIdentifier node = payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier);
- doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
- doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
- doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
- doReturn(UriBuilder.fromUri("http://localhost:8181/restconf/15/")).when(uriInfo).getBaseUriBuilder();
+ doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
+ doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ doReturn(UriBuilder.fromUri("http://localhost:8181/restconf/15/")).when(this.uriInfo).getBaseUriBuilder();
- final Response response = dataService.postData(null, payload, uriInfo);
+ final Response response = this.dataService.postData(null, payload, this.uriInfo);
assertEquals(201, response.getStatus());
}
@Test
public void testDeleteData() {
- doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase);
- doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
- doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
- final Response response = dataService.deleteData("example-jukebox:jukebox");
+ doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ doReturn(Futures.immediateCheckedFuture(true)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ final Response response = this.dataService.deleteData("example-jukebox:jukebox");
assertNotNull(response);
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
}
*/
@Test
public void testDeleteDataMountPoint() {
- doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase);
- doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
- doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
- final Response response = dataService.deleteData("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox");
+ doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ doReturn(Futures.immediateCheckedFuture(true)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ final Response response = this.dataService.deleteData("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox");
assertNotNull(response);
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
}
@Test
public void testPatchData() throws Exception {
- final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
+ final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef.get());
final List<PATCHEntity> entity = new ArrayList<>();
- final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
- .node(containerPlayerQname)
- .node(leafQname)
+ final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(this.iidBase)
+ .node(this.containerPlayerQname)
+ .node(this.leafQname)
.build();
- entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
- entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseCont));
+ entity.add(new PATCHEntity("create data", "CREATE", this.iidBase, this.buildBaseCont));
+ entity.add(new PATCHEntity("replace data", "REPLACE", this.iidBase, this.buildBaseCont));
entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
- doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, iidBase);
- doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
- doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
- doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
- doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
- doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
- doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
- final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, this.buildBaseCont);
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.write).submit();
+ doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(true)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
+ final PATCHStatusContext status = this.dataService.patchData(patch, this.uriInfo);
assertTrue(status.isOk());
assertEquals(3, status.getEditCollection().size());
assertEquals("replace data", status.getEditCollection().get(1).getEditId());
@Test
public void testPatchDataMountPoint() throws Exception {
final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(
- iidBase, schemaNode, mountPoint, contextRef.get());
+ this.iidBase, this.schemaNode, this.mountPoint, this.contextRef.get());
final List<PATCHEntity> entity = new ArrayList<>();
- final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
- .node(containerPlayerQname)
- .node(leafQname)
+ final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(this.iidBase)
+ .node(this.containerPlayerQname)
+ .node(this.leafQname)
.build();
- entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
- entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseCont));
+ entity.add(new PATCHEntity("create data", "CREATE", this.iidBase, this.buildBaseCont));
+ entity.add(new PATCHEntity("replace data", "REPLACE", this.iidBase, this.buildBaseCont));
entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
- doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, iidBase);
- doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
- doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
- doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
- doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
- doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
- doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, this.buildBaseCont);
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.write).submit();
+ doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(true)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
- final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
+ final PATCHStatusContext status = this.dataService.patchData(patch, this.uriInfo);
assertTrue(status.isOk());
assertEquals(3, status.getEditCollection().size());
assertNull(status.getGlobalErrors());
broker.setAccessible(true);
broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
- final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
+ final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef.get());
final List<PATCHEntity> entity = new ArrayList<>();
- final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
- .node(containerPlayerQname)
- .node(leafQname)
+ final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(this.iidBase)
+ .node(this.containerPlayerQname)
+ .node(this.leafQname)
.build();
- entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
+ entity.add(new PATCHEntity("create data", "CREATE", this.iidBase, this.buildBaseCont));
entity.add(new PATCHEntity("remove data", "REMOVE", iidleaf));
entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
- doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, iidBase);
- doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
- doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
- doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
- doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
- doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
- doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
- doReturn(true).when(readWrite).cancel();
- final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+ .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, this.buildBaseCont);
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.write).submit();
+ doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+ doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
+ doReturn(true).when(this.readWrite).cancel();
+ final PATCHStatusContext status = this.dataService.patchData(patch, this.uriInfo);
handler.set(RestConnectorProvider.class, null);
handler.setAccessible(false);
package org.opendaylight.restconf.restful.services.impl;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import java.util.Iterator;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import javax.ws.rs.core.UriInfo;
+import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.handlers.RpcServiceHandler;
import org.opendaylight.restconf.handlers.SchemaContextHandler;
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.LeafNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
public class RestconfInvokeOperationsServiceImplTest {
- private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/streams";
+ private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/invoke-rpc";
- @Test
- public void testInvokeRpc() throws Exception {
+ private RestconfInvokeOperationsServiceImpl invokeOperationsService;
+
+ @Mock
+ private RpcServiceHandler rpcServiceHandler;
+
+ @Mock
+ private DOMRpcService rpcService;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
final SchemaContextRef contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
final SchemaContextHandler schemaContextHandler = new SchemaContextHandler();
schemaContextHandler.onGlobalContextUpdated(contextRef.get());
- final RestconfInvokeOperationsServiceImpl invokeOperationsService = new RestconfInvokeOperationsServiceImpl(null, schemaContextHandler);
-
- final QName qname = QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", "toasterStatus");
- final QName qname1 = QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", "toaster");
- final QName rpcQnameInput = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "input");
- final QName inputQname = QName.create(rpcQnameInput, "path");
- final YangInstanceIdentifier iid = YangInstanceIdentifier.builder()
- .node(rpcQnameInput)
- .build();
- final YangInstanceIdentifier iidAsLeafValue = YangInstanceIdentifier.builder()
- .node(qname1)
- .node(qname)
- .build();
-
- final LeafNode contentLeaf = Builders.leafBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(inputQname))
- .withValue(iidAsLeafValue)
- .build();
- final ContainerNode input = Builders.containerBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(rpcQnameInput))
- .withChild(contentLeaf)
- .build();
- final Iterator<RpcDefinition> iterator = contextRef.get().getOperations().iterator();
- RpcDefinition rpcDef = null;
- while (iterator.hasNext()) {
- rpcDef = iterator.next();
- if ("create-data-change-event-subscription".equals(rpcDef.getQName().getLocalName())) {
- break;
- }
- }
+ this.invokeOperationsService =
+ new RestconfInvokeOperationsServiceImpl(this.rpcServiceHandler, schemaContextHandler);
+ Mockito.when(this.rpcServiceHandler.get()).thenReturn(this.rpcService);
+ }
- final InstanceIdentifierContext<RpcDefinition> iidContext = new InstanceIdentifierContext<>(iid, rpcDef, null, contextRef.get());
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, input);
- final NormalizedNodeContext context = invokeOperationsService.invokeRpc(null, payload, null);
+ @Test
+ public void testInvokeRpc() throws Exception {
+ final String identifier = "invoke-rpc-module:rpcTest";
+ final NormalizedNode result = Mockito.mock(NormalizedNode.class);
+ final NormalizedNodeContext payload = prepNNC(result);
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
- final QName rpcQnameOutput = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "output");
- final QName outputQname = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "stream-name");
- final LeafNode contentLeaf2 = Builders.leafBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(outputQname))
- .withValue("toaster:toaster/toasterStatus/datastore=CONFIGURATION/scope=BASE")
- .build();
- final ContainerNode output = Builders.containerBuilder()
- .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(rpcQnameOutput))
- .withChild(contentLeaf2)
- .build();
- final InstanceIdentifierContext<RpcDefinition> iidContextResult = new InstanceIdentifierContext<>(null, rpcDef, null, contextRef.get());
- final NormalizedNodeContext payloadResult = new NormalizedNodeContext(iidContextResult, output);
+ final NormalizedNodeContext rpc = this.invokeOperationsService.invokeRpc(identifier, payload, uriInfo);
+ Assert.assertEquals(result, rpc.getData());
+ }
- assertNotNull(context);
- assertEquals(payloadResult.getData(), context.getData());
- assertEquals(payloadResult.getInstanceIdentifierContext().getSchemaNode(),
- context.getInstanceIdentifierContext().getSchemaNode());
- assertEquals(payloadResult.getInstanceIdentifierContext().getSchemaContext(),
- context.getInstanceIdentifierContext().getSchemaContext());
- assertNull(context.getInstanceIdentifierContext().getMountPoint());
- assertNull(context.getInstanceIdentifierContext().getInstanceIdentifier());
+ private NormalizedNodeContext prepNNC(final NormalizedNode result) {
+ final InstanceIdentifierContext context = Mockito.mock(InstanceIdentifierContext.class);
+ final RpcDefinition schemaNode = Mockito.mock(RpcDefinition.class);
+ final QName qname = QName.create("invoke:rpc:module", "2013-12-3", "rpcTest");
+ final SchemaPath schemaPath = SchemaPath.create(true, qname);
+ Mockito.when(schemaNode.getPath()).thenReturn(schemaPath);
+ Mockito.when(schemaNode.getQName()).thenReturn(qname);
+ Mockito.when(context.getSchemaNode()).thenReturn(schemaNode);
+ final NormalizedNode<?, ?> data = Mockito.mock(NormalizedNode.class);
+ final DOMRpcResult domRpcResult = Mockito.mock(DOMRpcResult.class);
+ final CheckedFuture<DOMRpcResult, DOMRpcException> checkdFuture = Futures.immediateCheckedFuture(domRpcResult);
+ Mockito.when(this.rpcService.invokeRpc(schemaPath, data)).thenReturn(checkdFuture);
+ Mockito.when(domRpcResult.getResult()).thenReturn(result);
+ return new NormalizedNodeContext(context, data);
}
}
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-
import java.lang.reflect.Field;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
public class RestconfStreamsSubscriptionServiceImplTest {
- private static final String uri = "/restconf/15/data/ietf-restconf-monitoring:restconf-state/streams/stream/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE";
+ private static final String uri = "/restconf/18/data/ietf-restconf-monitoring:restconf-state/streams/stream/"
+ + "toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE";
private static Field listenersByStreamName;
@Mock
private DOMDataBrokerHandler dataBrokerHandler;
@Mock
private UriInfo uriInfo;
+ @Mock
+ private NotificationServiceHandler notificationServiceHandler;
+
+ private final SchemaContextHandler schemaHandler = new SchemaContextHandler();
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
final DOMDataBroker dataBroker = mock(DOMDataBroker.class);
final ListenerRegistration<DOMDataChangeListener> listener = mock(ListenerRegistration.class);
- doReturn(dataBroker).when(dataBrokerHandler).get();
+ doReturn(dataBroker).when(this.dataBrokerHandler).get();
doReturn(listener).when(dataBroker).registerDataChangeListener(any(), any(), any(), any());
+ final MultivaluedMap<String, String> map = Mockito.mock(MultivaluedMap.class);
+ final Set<Entry<String, List<String>>> set = new HashSet<>();
+ Mockito.when(map.entrySet()).thenReturn(set);
+ Mockito.when(this.uriInfo.getQueryParameters()).thenReturn(map);
+ this.schemaHandler.onGlobalContextUpdated(TestRestconfUtils.loadSchemaContext("/notifications"));
}
@BeforeClass
final Map<String, ListenerAdapter> listenersByStreamNameSetter = new HashMap<>();
final ListenerAdapter adapter = mock(ListenerAdapter.class);
doReturn(false).when(adapter).isListening();
- listenersByStreamNameSetter.put("toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", adapter);
- listenersByStreamName = Notificator.class.getDeclaredField("listenersByStreamName");
+ listenersByStreamNameSetter.put(
+ "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
+ adapter);
+ listenersByStreamName = Notificator.class.getDeclaredField("dataChangeListener");
listenersByStreamName.setAccessible(true);
listenersByStreamName.set(Notificator.class, listenersByStreamNameSetter);
@Test
public void testSubscribeToStream() {
final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
- doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
- final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
- final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", uriInfo);
- assertEquals(200, response.getStatus());
- assertEquals("ws://:8181/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", response.getHeaderString("Location"));
+ doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+ final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+ new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
+ this.schemaHandler);
+ final NormalizedNodeContext response = streamsSubscriptionService
+ .subscribeToStream(
+ "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
+ this.uriInfo);
+ assertEquals(
+ "ws://:8181/data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
+ response.getNewHeaders().get("Location").toString());
}
@Test(expected = RestconfDocumentedException.class)
public void testSubscribeToStreamMissingDatastoreInPath() {
final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
- doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
- final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
- final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", uriInfo);
+ doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+ final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+ new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
+ this.schemaHandler);
+ streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", this.uriInfo);
}
@Test(expected = RestconfDocumentedException.class)
public void testSubscribeToStreamMissingScopeInPath() {
final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
- doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
- final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
- final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL", uriInfo);
+ doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+ final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+ new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
+ this.schemaHandler);
+ streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL",
+ this.uriInfo);
}
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-
import java.util.Collections;
import java.util.Set;
import org.junit.Before;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+ this.refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
}
@Test
public void createStreamTest() {
- payload = prepareDomPayload("create-data-change-event-subscription", "input", "toaster", "path");
- final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+ this.payload = prepareDomPayload("create-data-change-event-subscription", "input", "toaster", "path");
+ final DOMRpcResult result = CreateStreamUtil.createDataChangeNotifiStream(this.payload, this.refSchemaCtx);
assertEquals(result.getErrors(), Collections.emptyList());
final NormalizedNode<?, ?> testedNn = result.getResult();
assertNotNull(testedNn);
- final NormalizedNodeContext contextRef = prepareDomPayload("create-data-change-event-subscription", "output", "toaster:toaster/datastore=CONFIGURATION/scope=BASE", "stream-name");
+ final NormalizedNodeContext contextRef = prepareDomPayload("create-data-change-event-subscription", "output",
+ "data-change-event-subscription/toaster:toaster/datastore=CONFIGURATION/scope=BASE", "stream-name");
assertEquals(contextRef.getData(), testedNn);
}
@Test(expected = RestconfDocumentedException.class)
public void createStreamWrongValueTest() {
- payload = prepareDomPayload("create-data-change-event-subscription", "input", "String value", "path");
- final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+ this.payload = prepareDomPayload("create-data-change-event-subscription", "input", "String value", "path");
+ final DOMRpcResult result = CreateStreamUtil.createDataChangeNotifiStream(this.payload, this.refSchemaCtx);
assertEquals(result.getErrors(), Collections.emptyList());
}
@Test(expected = RestconfDocumentedException.class)
public void createStreamWrongInputRpcTest() {
- payload = prepareDomPayload("create-data-change-event-subscription2", "input", "toaster", "path2");
- final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+ this.payload = prepareDomPayload("create-data-change-event-subscription2", "input", "toaster", "path2");
+ final DOMRpcResult result = CreateStreamUtil.createDataChangeNotifiStream(this.payload, this.refSchemaCtx);
assertEquals(result.getErrors(), Collections.emptyList());
}
- private NormalizedNodeContext prepareDomPayload(final String rpcName, final String inputOutput, final String toasterValue, final String inputOutputName) {
- final SchemaContext schema = refSchemaCtx.get();
+ private NormalizedNodeContext prepareDomPayload(final String rpcName, final String inputOutput,
+ final String toasterValue, final String inputOutputName) {
+ final SchemaContext schema = this.refSchemaCtx.get();
final Module rpcModule = schema.findModuleByName("sal-remote", null);
assertNotNull(rpcModule);
final QName rpcQName = QName.create(rpcModule.getQNameModule(), rpcName);
}
assertNotNull(rpcInputSchemaNode);
- final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> container = Builders.containerBuilder(rpcInputSchemaNode);
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> container =
+ Builders.containerBuilder(rpcInputSchemaNode);
final QName lfQName = QName.create(rpcModule.getQNameModule(), inputOutputName);
final DataSchemaNode lfSchemaNode = rpcInputSchemaNode.getDataChildByName(lfQName);
.withValue(o)).build();
container.withChild(lfNode);
- return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema), container.build());
+ return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema),
+ container.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.restful.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.Assert;
+import org.junit.Test;
+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;
+
+/**
+ * Unit test for {@link ParametersUtil}
+ */
+public class ParametersUtilTest {
+
+ /**
+ * Test when all parameters are allowed
+ */
+ @Test
+ public void checkParametersTypesTest() {
+ ParametersUtil.checkParametersTypes(
+ RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+ Sets.newHashSet("content"),
+ RestconfDataServiceConstant.ReadData.CONTENT, RestconfDataServiceConstant.ReadData.DEPTH);
+ }
+
+ /**
+ * Test when not allowed parameter type is used
+ */
+ @Test
+ public void checkParametersTypesNegativeTest() {
+ try {
+ ParametersUtil.checkParametersTypes(
+ RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+ Sets.newHashSet("not-allowed-parameter"),
+ RestconfDataServiceConstant.ReadData.CONTENT, RestconfDataServiceConstant.ReadData.DEPTH);
+
+ Assert.fail("Test expected to fail due to not allowed parameter used with operation");
+ } catch (final RestconfDocumentedException e) {
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+
+ /**
+ * Test when parameter is present at most once
+ */
+ @Test
+ public void checkParameterCountTest() {
+ ParametersUtil.checkParameterCount(Lists.newArrayList("all"), RestconfDataServiceConstant.ReadData.CONTENT);
+ }
+
+ /**
+ * Test when parameter is present more than once
+ */
+ @Test
+ public void checkParameterCountNegativeTest() {
+ try {
+ ParametersUtil.checkParameterCount(Lists.newArrayList("config", "nonconfig", "all"),
+ RestconfDataServiceConstant.ReadData.CONTENT);
+
+ Assert.fail("Test expected to fail due to multiple values of the same parameter");
+ } catch (final RestconfDocumentedException e) {
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+}
\ No newline at end of file
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-
import com.google.common.util.concurrent.Futures;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain);
- final Response response = PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx);
+ final Response response =
+ PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx, null, null);
assertEquals(201, response.getStatus());
verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain);
- final Response response = PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx);
+ final Response response =
+ PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx, null, null);
assertEquals(201, response.getStatus());
verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, data.getValue().iterator().next());
payload.getData());
doReturn(Futures.immediateFailedCheckedFuture(new DOMException((short) 414, "Post request failed"))).when(this.readWrite).submit();
final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain);
- final Response response = PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx);
+ final Response response =
+ PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx, null, null);
assertEquals(Response.Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
- verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, iid2);
+ verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
}
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-
import com.google.common.base.Optional;
import com.google.common.util.concurrent.Futures;
import org.junit.Before;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
- schema = refSchemaCtx.get();
+ this.refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+ this.schema = this.refSchemaCtx.get();
final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
final QName containerQname = QName.create(baseQName, "player");
final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey2 =
new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQname, "name of band 2");
- iid = YangInstanceIdentifier.builder()
+ this.iid = YangInstanceIdentifier.builder()
.node(baseQName)
.node(containerQname)
.node(leafQname)
.build();
- schemaNode = DataSchemaContextTree.from(schema).getChild(iid).getDataSchemaNode();
+ this.schemaNode = DataSchemaContextTree.from(this.schema).getChild(this.iid).getDataSchemaNode();
- iid2 = YangInstanceIdentifier.builder()
+ this.iid2 = YangInstanceIdentifier.builder()
.node(baseQName)
.build();
- schemaNode2 = DataSchemaContextTree.from(schema).getChild(iid2).getDataSchemaNode();
+ this.schemaNode2 = DataSchemaContextTree.from(this.schema).getChild(this.iid2).getDataSchemaNode();
- iid3 = YangInstanceIdentifier.builder()
+ this.iid3 = YangInstanceIdentifier.builder()
.node(baseQName)
.node(listQname)
.node(nodeWithKey)
.build();
- schemaNode3 = DataSchemaContextTree.from(schema).getChild(iid3).getDataSchemaNode();
+ this.schemaNode3 = DataSchemaContextTree.from(this.schema).getChild(this.iid3).getDataSchemaNode();
- buildLeaf = Builders.leafBuilder()
+ this.buildLeaf = Builders.leafBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
.withValue(0.2)
.build();
final ContainerNode buildPlayerCont = Builders.containerBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
- .withChild(buildLeaf)
+ .withChild(this.buildLeaf)
.build();
- buildBaseCont = Builders.containerBuilder()
+ this.buildBaseCont = Builders.containerBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
.withChild(buildPlayerCont)
.build();
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
.withValue("band description")
.build();
- buildListEntry = Builders.mapEntryBuilder()
+ this.buildListEntry = Builders.mapEntryBuilder()
.withNodeIdentifier(nodeWithKey)
.withChild(content)
.withChild(content2)
.build();
final MapNode buildList = Builders.mapBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listQname))
- .withChild(buildListEntry)
+ .withChild(this.buildListEntry)
.withChild(buildListEntry2)
.build();
- buildBaseContWithList = Builders.containerBuilder()
+ this.buildBaseContWithList = Builders.containerBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
.withChild(buildList)
.build();
@Test
public void testValidInputData() throws Exception {
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
PutDataTransactionUtil.validInputData(iidContext.getSchemaNode(), payload);
}
@Test
public void testValidTopLevelNodeName() throws Exception {
- InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
- NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+ InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+ NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
PutDataTransactionUtil.validTopLevelNodeName(iidContext.getInstanceIdentifier(), payload);
- iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
- payload = new NormalizedNodeContext(iidContext, buildBaseCont);
+ iidContext = new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+ payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
PutDataTransactionUtil.validTopLevelNodeName(iidContext.getInstanceIdentifier(), payload);
}
@Test(expected = RestconfDocumentedException.class)
public void testValidTopLevelNodeNamePathEmpty() throws Exception {
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
PutDataTransactionUtil.validTopLevelNodeName(YangInstanceIdentifier.EMPTY, payload);
}
@Test(expected = RestconfDocumentedException.class)
public void testValidTopLevelNodeNameWrongTopIdentifier() throws Exception {
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
- PutDataTransactionUtil.validTopLevelNodeName(iid.getAncestor(1), payload);
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
+ PutDataTransactionUtil.validTopLevelNodeName(this.iid.getAncestor(1), payload);
}
@Test
public void testValidateListKeysEqualityInPayloadAndUri() throws Exception {
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid3, schemaNode3, null, schema);
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildListEntry);
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid3, this.schemaNode3, null, this.schema);
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildListEntry);
PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
}
@Test
public void testPutContainerData() throws Exception {
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
-
- doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
- doReturn(read).when(transactionChain).newReadOnlyTransaction();
- doReturn(write).when(transactionChain).newWriteOnlyTransaction();
- doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
- doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
- payload.getData());
- doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-
- PutDataTransactionUtil.putData(payload, refSchemaCtx,
- new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
- verify(read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
- verify(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
+
+ doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+ doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
+ doReturn(this.write).when(this.transactionChain).newWriteOnlyTransaction();
+ doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iid2);
+ doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
payload.getData());
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+
+ PutDataTransactionUtil.putData(payload, this.refSchemaCtx,
+ new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain), null,
+ null);
+ verify(this.read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
+ verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
+ payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
}
@Test
public void testPutleafData() throws Exception {
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
-
- doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
- doReturn(read).when(transactionChain).newReadOnlyTransaction();
- doReturn(write).when(transactionChain).newWriteOnlyTransaction();
- doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid);
- doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
- payload.getData());
- doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-
- PutDataTransactionUtil.putData(payload, refSchemaCtx,
- new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
- verify(read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
- verify(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
+
+ doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+ doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
+ doReturn(this.write).when(this.transactionChain).newWriteOnlyTransaction();
+ doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iid);
+ doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
payload.getData());
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+
+ PutDataTransactionUtil.putData(payload, this.refSchemaCtx,
+ new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain), null,
+ null);
+ verify(this.read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
+ verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
+ payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
}
@Test
public void testPutListData() throws Exception {
- final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
- final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseContWithList);
-
- doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
- doReturn(read).when(transactionChain).newReadOnlyTransaction();
- doReturn(write).when(transactionChain).newWriteOnlyTransaction();
- doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
- doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+ final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+ final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseContWithList);
+
+ doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+ doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
+ doReturn(this.write).when(this.transactionChain).newWriteOnlyTransaction();
+ doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iid2);
+ doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
payload.getData());
- doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
- PutDataTransactionUtil.putData(payload, refSchemaCtx,
- new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
- verify(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
- verify(write).put(LogicalDatastoreType.CONFIGURATION, iid2, payload.getData());
+ doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+ PutDataTransactionUtil.putData(payload, this.refSchemaCtx,
+ new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain), null,
+ null);
+ verify(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iid2);
+ verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, this.iid2, payload.getData());
}
}
package org.opendaylight.restconf.restful.utils;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.Futures;
+import java.util.Collections;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.UriInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
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.netconf.sal.restconf.impl.WriterParameters;
import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public class ReadDataTransactionUtilTest {
- private static final TestData data = new TestData();
- private static final YangInstanceIdentifier.NodeIdentifier nodeIdentifier = new YangInstanceIdentifier
+ private static final TestData DATA = new TestData();
+ private static final YangInstanceIdentifier.NodeIdentifier NODE_IDENTIFIER = new YangInstanceIdentifier
.NodeIdentifier(QName.create("ns", "2016-02-28", "container"));
private TransactionVarsWrapper wrapper;
@Mock
private DOMTransactionChain transactionChain;
@Mock
- private InstanceIdentifierContext<?> context;
+ private InstanceIdentifierContext<ContainerSchemaNode> context;
@Mock
private DOMDataReadOnlyTransaction read;
+ @Mock
+ private SchemaContext schemaContext;
+ @Mock
+ private ContainerSchemaNode containerSchemaNode;
+ @Mock
+ private LeafSchemaNode containerChildNode;
+ private QName containerChildQName;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- doReturn(read).when(transactionChain).newReadOnlyTransaction();
+ containerChildQName = QName.create("ns", "2016-02-28", "container-child");
+
+ when(transactionChain.newReadOnlyTransaction()).thenReturn(read);
+ when(context.getSchemaContext()).thenReturn(schemaContext);
+ when(context.getSchemaNode()).thenReturn(containerSchemaNode);
+ when(containerSchemaNode.getQName()).thenReturn(NODE_IDENTIFIER.getNodeType());
+ when(containerChildNode.getQName()).thenReturn(containerChildQName);
+ when(containerSchemaNode.getDataChildByName(containerChildQName)).thenReturn(containerChildNode);
+
wrapper = new TransactionVarsWrapper(this.context, null, this.transactionChain);
}
@Test
public void readDataConfigTest() {
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.data3))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, data.path);
- doReturn(data.path).when(context).getInstanceIdentifier();
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data3))).when(read)
+ .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
+ doReturn(DATA.path).when(context).getInstanceIdentifier();
final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
- assertEquals(data.data3, normalizedNode);
+ assertEquals(DATA.data3, normalizedNode);
}
@Test
public void readAllHavingOnlyConfigTest() {
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.data3))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, data.path);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data3))).when(read)
+ .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read)
- .read(LogicalDatastoreType.OPERATIONAL, data.path);
- doReturn(data.path).when(context).getInstanceIdentifier();
+ .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+ doReturn(DATA.path).when(context).getInstanceIdentifier();
final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
- assertEquals(data.data3, normalizedNode);
+ assertEquals(DATA.data3, normalizedNode);
}
@Test
public void readAllHavingOnlyNonConfigTest() {
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.data2))).when(read)
- .read(LogicalDatastoreType.OPERATIONAL, data.path2);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data2))).when(read)
+ .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, data.path2);
- doReturn(data.path2).when(context).getInstanceIdentifier();
+ .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
+ doReturn(DATA.path2).when(context).getInstanceIdentifier();
final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
- assertEquals(data.data2, normalizedNode);
+ assertEquals(DATA.data2, normalizedNode);
}
@Test
public void readDataNonConfigTest() {
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.data2))).when(read)
- .read(LogicalDatastoreType.OPERATIONAL, data.path2);
- doReturn(data.path2).when(context).getInstanceIdentifier();
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data2))).when(read)
+ .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
+ doReturn(DATA.path2).when(context).getInstanceIdentifier();
final String valueOfContent = RestconfDataServiceConstant.ReadData.NONCONFIG;
final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
- assertEquals(data.data2, normalizedNode);
+ assertEquals(DATA.data2, normalizedNode);
}
@Test
public void readContainerDataAllTest() {
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.data3))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, data.path);
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.data4))).when(read)
- .read(LogicalDatastoreType.OPERATIONAL, data.path);
- doReturn(data.path).when(context).getInstanceIdentifier();
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data3))).when(read)
+ .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data4))).when(read)
+ .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+ doReturn(DATA.path).when(context).getInstanceIdentifier();
final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
final ContainerNode checkingData = Builders
.containerBuilder()
- .withNodeIdentifier(nodeIdentifier)
- .withChild(data.contentLeaf)
- .withChild(data.contentLeaf2)
+ .withNodeIdentifier(NODE_IDENTIFIER)
+ .withChild(DATA.contentLeaf)
+ .withChild(DATA.contentLeaf2)
.build();
assertEquals(checkingData, normalizedNode);
}
@Test
public void readContainerDataConfigNoValueOfContentTest() {
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.data3))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, data.path);
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.data4))).when(read)
- .read(LogicalDatastoreType.OPERATIONAL, data.path);
- doReturn(data.path).when(context).getInstanceIdentifier();
- final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(null, wrapper);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data3))).when(read)
+ .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data4))).when(read)
+ .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+ doReturn(DATA.path).when(context).getInstanceIdentifier();
+ final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
+ RestconfDataServiceConstant.ReadData.ALL, wrapper);
final ContainerNode checkingData = Builders
.containerBuilder()
- .withNodeIdentifier(nodeIdentifier)
- .withChild(data.contentLeaf)
- .withChild(data.contentLeaf2)
+ .withNodeIdentifier(NODE_IDENTIFIER)
+ .withChild(DATA.contentLeaf)
+ .withChild(DATA.contentLeaf2)
.build();
assertEquals(checkingData, normalizedNode);
}
@Test
public void readListDataAllTest() {
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.listData))).when(read)
- .read(LogicalDatastoreType.OPERATIONAL, data.path3);
- doReturn(Futures.immediateCheckedFuture(Optional.of(data.listData2))).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, data.path3);
- doReturn(data.path3).when(context).getInstanceIdentifier();
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.listData))).when(read)
+ .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
+ doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.listData2))).when(read)
+ .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
+ doReturn(DATA.path3).when(context).getInstanceIdentifier();
final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
final MapNode checkingData = Builders
.mapBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("ns", "2016-02-28", "list")))
- .withChild(data.checkData)
+ .withChild(DATA.checkData)
.build();
assertEquals(checkingData, normalizedNode);
}
@Test
public void readDataWrongPathOrNoContentTest() {
doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read)
- .read(LogicalDatastoreType.CONFIGURATION, data.path2);
- doReturn(data.path2).when(context).getInstanceIdentifier();
+ .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
+ doReturn(DATA.path2).when(context).getInstanceIdentifier();
final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
assertNull(normalizedNode);
@Test(expected = RestconfDocumentedException.class)
public void readDataFailTest() {
final String valueOfContent = RestconfDataServiceConstant.ReadData.READ_TYPE_TX;
- final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, null);
+ final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
+ valueOfContent, wrapper);
assertNull(normalizedNode);
}
+
+ /**
+ * Test of parsing default parameters from URI request
+ */
+ @Test
+ public void parseUriParametersDefaultTest() {
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+ // no parameters, default values should be used
+ when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+ final WriterParameters parsedParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+
+ assertEquals("Not correctly parsed URI parameter",
+ RestconfDataServiceConstant.ReadData.ALL, parsedParameters.getContent());
+ assertNull("Not correctly parsed URI parameter",
+ parsedParameters.getDepth());
+ assertNull("Not correctly parsed URI parameter",
+ parsedParameters.getFields());
+ }
+
+ /**
+ * Test of parsing user defined parameters from URI request
+ */
+ @Test
+ public void parseUriParametersUserDefinedTest() {
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+ final String content = "config";
+ final String depth = "10";
+ final String fields = containerChildQName.getLocalName();
+
+ parameters.put("content", Collections.singletonList(content));
+ parameters.put("depth", Collections.singletonList(depth));
+ parameters.put("fields", Collections.singletonList(fields));
+
+ when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+ final WriterParameters parsedParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+
+ // content
+ assertEquals("Not correctly parsed URI parameter",
+ content, parsedParameters.getContent());
+
+ // depth
+ assertNotNull("Not correctly parsed URI parameter",
+ parsedParameters.getDepth());
+ assertEquals("Not correctly parsed URI parameter",
+ depth, parsedParameters.getDepth().toString());
+
+ // fields
+ assertNotNull("Not correctly parsed URI parameter",
+ parsedParameters.getFields());
+ assertEquals("Not correctly parsed URI parameter",
+ 1, parsedParameters.getFields().size());
+ assertEquals("Not correctly parsed URI parameter",
+ 1, parsedParameters.getFields().get(0).size());
+ assertEquals("Not correctly parsed URI parameter",
+ containerChildQName, parsedParameters.getFields().get(0).iterator().next());
+ }
+
+ /**
+ * Negative test of parsing request URI parameters when content parameter has not allowed value.
+ */
+ @Test
+ public void parseUriParametersContentParameterNegativeTest() {
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+ parameters.put("content", Collections.singletonList("not-allowed-parameter-value"));
+ when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+ try {
+ ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+ fail("Test expected to fail due to not allowed parameter value");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+
+ /**
+ * Negative test of parsing request URI parameters when depth parameter has not allowed value.
+ */
+ @Test
+ public void parseUriParametersDepthParameterNegativeTest() {
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+ // inserted value is not allowed
+ parameters.put("depth", Collections.singletonList("bounded"));
+ when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+ try {
+ ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+ fail("Test expected to fail due to not allowed parameter value");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+
+ /**
+ * Negative test of parsing request URI parameters when depth parameter has not allowed value (less than minimum).
+ */
+ @Test
+ public void parseUriParametersDepthMinimalParameterNegativeTest() {
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+ // inserted value is too low
+ parameters.put(
+ "depth", Collections.singletonList(String.valueOf(RestconfDataServiceConstant.ReadData.MIN_DEPTH - 1)));
+ when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+ try {
+ ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+ fail("Test expected to fail due to not allowed parameter value");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+
+ /**
+ * Negative test of parsing request URI parameters when depth parameter has not allowed value (more than maximum).
+ */
+ @Test
+ public void parseUriParametersDepthMaximalParameterNegativeTest() {
+ final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+ // inserted value is too high
+ parameters.put(
+ "depth", Collections.singletonList(String.valueOf(RestconfDataServiceConstant.ReadData.MAX_DEPTH + 1)));
+ when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+ try {
+ ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+ fail("Test expected to fail due to not allowed parameter value");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
}
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.Draft17;
-import org.opendaylight.restconf.Draft17.MonitoringModule;
-import org.opendaylight.restconf.Draft17.RestconfModule;
+import org.opendaylight.restconf.Draft18;
+import org.opendaylight.restconf.Draft18.MonitoringModule;
+import org.opendaylight.restconf.Draft18.RestconfModule;
import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
*/
private Module getTestingRestconfModule(final String s) {
return RestconfMappingNodeUtilTest.schemaContext.findModuleByName(
- s, Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+ s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
}
/**
--- /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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+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.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Unit test for {@link ParserFieldsParameter}
+ */
+public class ParserFieldsParameterTest {
+
+ @Mock
+ private InstanceIdentifierContext<ContainerSchemaNode> identifierContext;
+
+ // container jukebox
+ @Mock
+ private ContainerSchemaNode containerJukebox;
+ private QName jukeboxQName;
+
+ // container player
+ @Mock
+ private ContainerSchemaNode containerPlayer;
+ private QName playerQName;
+
+ // container library
+ @Mock
+ private ContainerSchemaNode containerLibrary;
+ private QName libraryQName;
+
+ // container augmented library
+ @Mock
+ private ContainerSchemaNode augmentedContainerLibrary;
+ private QName augmentedLibraryQName;
+
+ // list album
+ @Mock
+ private ListSchemaNode listAlbum;
+ private QName albumQName;
+
+ // leaf name
+ @Mock
+ private LeafSchemaNode leafName;
+ private QName nameQName;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final SchemaContext schemaContext = TestRestconfUtils.loadSchemaContext("/jukebox");
+
+ final QNameModule qNameModule = QNameModule.create(URI.create("http://example.com/ns/example-jukebox"),
+ new SimpleDateFormat("yyyy-MM-dd").parse("2015-04-04"));
+
+ jukeboxQName = QName.create(qNameModule, "jukebox");
+ playerQName = QName.create(qNameModule, "player");
+ libraryQName = QName.create(qNameModule, "library");
+ augmentedLibraryQName = QName.create(
+ QNameModule.create(
+ URI.create("http://example.com/ns/augmented-jukebox"),
+ new SimpleDateFormat("yyyy-MM-dd").parse("2016-05-05")),
+ "augmented-library");
+ albumQName = QName.create(qNameModule, "album");
+ nameQName = QName.create(qNameModule, "name");
+
+ Mockito.when(identifierContext.getSchemaContext()).thenReturn(schemaContext);
+ Mockito.when(containerJukebox.getQName()).thenReturn(jukeboxQName);
+ Mockito.when(identifierContext.getSchemaNode()).thenReturn(containerJukebox);
+
+ Mockito.when(containerLibrary.getQName()).thenReturn(libraryQName);
+ Mockito.when(containerJukebox.getDataChildByName(libraryQName)).thenReturn(containerLibrary);
+
+ Mockito.when(augmentedContainerLibrary.getQName()).thenReturn(augmentedLibraryQName);
+ Mockito.when(containerJukebox.getDataChildByName(augmentedLibraryQName)).thenReturn(augmentedContainerLibrary);
+
+ Mockito.when(containerPlayer.getQName()).thenReturn(playerQName);
+ Mockito.when(containerJukebox.getDataChildByName(playerQName)).thenReturn(containerPlayer);
+
+ Mockito.when(listAlbum.getQName()).thenReturn(albumQName);
+ Mockito.when(containerLibrary.getDataChildByName(albumQName)).thenReturn(listAlbum);
+
+ Mockito.when(leafName.getQName()).thenReturn(nameQName);
+ Mockito.when(listAlbum.getDataChildByName(nameQName)).thenReturn(leafName);
+ }
+
+ /**
+ * Test parse fields parameter containing only one child selected
+ */
+ @Test
+ public void parseFieldsParameterSimplePathTest() {
+ final String input = "library";
+ final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+ assertNotNull(parsedFields);
+ assertEquals(1, parsedFields.size());
+ assertEquals(1, parsedFields.get(0).size());
+ assertTrue(parsedFields.get(0).contains(libraryQName));
+ }
+
+ /**
+ * Test parse fields parameter containing two child nodes selected
+ */
+ @Test
+ public void parseFieldsParameterDoublePathTest() {
+ final String input = "library;player";
+ final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+ assertNotNull(parsedFields);
+ assertEquals(1, parsedFields.size());
+ assertEquals(2, parsedFields.get(0).size());
+ assertTrue(parsedFields.get(0).contains(libraryQName));
+ assertTrue(parsedFields.get(0).contains(playerQName));
+ }
+
+ /**
+ * Test parse fields parameter containing sub-children selected delimited by slash
+ */
+ @Test
+ public void parseFieldsParameterSubPathTest() {
+ final String input = "library/album/name";
+ final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+ assertNotNull(parsedFields);
+ assertEquals(3, parsedFields.size());
+
+ assertEquals(1, parsedFields.get(0).size());
+ assertTrue(parsedFields.get(0).contains(libraryQName));
+
+ assertEquals(1, parsedFields.get(1).size());
+ assertTrue(parsedFields.get(1).contains(albumQName));
+
+ assertEquals(1, parsedFields.get(2).size());
+ assertTrue(parsedFields.get(2).contains(nameQName));
+ }
+
+ /**
+ * Test parse fields parameter containing sub-children selected delimited by parenthesis
+ */
+ @Test
+ public void parseFieldsParameterChildrenPathTest() {
+ final String input = "library(album(name))";
+ final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+ assertNotNull(parsedFields);
+ assertEquals(3, parsedFields.size());
+
+ assertEquals(1, parsedFields.get(0).size());
+ assertTrue(parsedFields.get(0).contains(libraryQName));
+
+ assertEquals(1, parsedFields.get(1).size());
+ assertTrue(parsedFields.get(1).contains(albumQName));
+
+ assertEquals(1, parsedFields.get(2).size());
+ assertTrue(parsedFields.get(2).contains(nameQName));
+ }
+
+ /**
+ * Test parse fields parameter when augmentation with different namespace is used
+ */
+ @Test
+ public void parseFieldsParameterNamespaceTest() {
+ final String input = "augmented-jukebox:augmented-library";
+ final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+ assertNotNull(parsedFields);
+ assertEquals(1, parsedFields.size());
+
+ assertEquals(1, parsedFields.get(0).size());
+ assertTrue(parsedFields.get(0).contains(augmentedLibraryQName));
+ }
+
+ /**
+ * Test parse fields parameter containing not expected character
+ */
+ @Test
+ public void parseFieldsParameterNotExpectedCharacterNegativeTest() {
+ final String input = "*";
+
+ try {
+ ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+ fail("Test should fail due to not expected character used in parameter input value");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+
+ /**
+ * Test parse fields parameter with missing closing parenthesis
+ */
+ @Test
+ public void parseFieldsParameterMissingParenthesisNegativeTest() {
+ final String input = "library(";
+
+ try {
+ ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+ fail("Test should fail due to missing closing parenthesis");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+
+ /**
+ * Test parse fields parameter when not existing child node selected
+ */
+ @Test
+ public void parseFieldsParameterMissingChildNodeNegativeTest() {
+ final String input = "library(not-existing)";
+
+ try {
+ ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+ fail("Test should fail due to missing child node in parent node");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+
+ /**
+ * Test parse fields parameter with unexpected character after parenthesis
+ */
+ @Test
+ public void parseFieldsParameterAfterParenthesisNegativeTest() {
+ final String input = "library(album);";
+
+ try {
+ ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+ fail("Test should fail due to unexpected character after parenthesis");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+
+ /**
+ * Test parse fields parameter with missing semicolon after parenthesis
+ */
+ @Test
+ public void parseFieldsParameterMissingSemicolonNegativeTest() {
+ final String input = "library(album)player";
+
+ try {
+ ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+ fail("Test should fail due to missing semicolon after parenthesis");
+ } catch (final RestconfDocumentedException e) {
+ // Bad request
+ assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+ assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+ assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+ }
+ }
+}
\ No newline at end of file
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
-import static org.opendaylight.restconf.Draft17.MonitoringModule;
-import static org.opendaylight.restconf.Draft17.RestconfModule;
+import static org.opendaylight.restconf.Draft18.MonitoringModule;
+import static org.opendaylight.restconf.Draft18.RestconfModule;
import com.google.common.collect.Sets;
import java.util.NoSuchElementException;
import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
* @return Restconf module
*/
private Module getTestingRestconfModule(final String s) {
- return schemaContext.findModuleByName(s, Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+ return schemaContext.findModuleByName(s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
}
}
enum SUBTREE;
}
}
+ leaf notification-output-type {
+ type enumeration {
+ enum JSON;
+ enum XML;
+ }
+ default "XML";
+ description "Input parameter which type of output will be parsed on notification";
+ }
}
}
\ No newline at end of file
--- /dev/null
+module subscribe-to-notification {
+
+ yang-version 1;
+ namespace "subscribe:to:notification";
+ prefix "subs-to-notifi";
+
+ description
+ "Added input parameters to rpc create-data-change-event-subscription and to create-notification-stream";
+
+ revision "2016-10-28" {
+ }
+
+ container "notifi"{
+ leaf "location"{
+ type string;
+ }
+ }
+}
--- /dev/null
+module augmented-jukebox {
+
+ namespace "http://example.com/ns/augmented-jukebox";
+ prefix "augmented-jbox";
+
+ revision "2016-05-05" {
+ description "Initial version.";
+ }
+
+ import example-jukebox {prefix jbox; revision-date "2015-04-04";}
+
+ augment "/jbox:jukebox" {
+ container augmented-library {
+ }
+ }
+ }
\ No newline at end of file
--- /dev/null
+module notifi-module {
+ namespace "notifi:mod";
+ prefix notm;
+
+ revision "2016-11-23" {
+ }
+
+ notification notifi-leaf {
+ leaf lf {
+ type string;
+ }
+ }
+
+ notification notifi-cont {
+ container cont {
+ leaf lf {
+ type string;
+ }
+ }
+ }
+
+ notification notifi-list {
+ list lst {
+ key lf;
+ leaf lf {
+ type string;
+ }
+ }
+ }
+
+ notification notifi-grp {
+ uses grp;
+ }
+
+ grouping grp {
+ leaf lf {
+ type string;
+ }
+ }
+
+ notification notifi-augm {
+ }
+
+ augment notifi-augm {
+ leaf lf-augm {
+ type string;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module subscribe-to-notification {
+
+ yang-version 1;
+ namespace "subscribe:to:notification";
+ prefix "subs-to-notifi";
+
+ description
+ "Added input parameters to rpc create-data-change-event-subscription and to create-notification-stream";
+
+ revision "2016-10-28" {
+ }
+
+ container "notifi"{
+ leaf "location"{
+ type string;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+ <eventTime>2016-11-10T04:45:31+01:00</eventTime>
+ <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+ <data-change-event>
+ <path>/toaster:toaster/toaster:toasterStatus</path>
+ <operation>updated</operation>
+ <data>
+ <toasterStatus xmlns="http://netconfcentral.org/ns/toaster">down</toasterStatus>
+ </data>
+ </data-change-event>
+ </data-changed-notification>
+</notification>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+ <eventTime>2016-11-10T04:45:31+01:00</eventTime>
+ <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+ <data-change-event>
+ <path>/toaster:toaster/toaster:toasterStatus</path>
+ <operation>updated</operation>
+ <data>
+ <toasterStatus xmlns="http://netconfcentral.org/ns/toaster">1</toasterStatus>
+ </data>
+ </data-change-event>
+ </data-changed-notification>
+</notification>
\ No newline at end of file
--- /dev/null
+module ordered-example {
+ namespace "ordered:example";
+ prefix "oex";
+
+ revision 2016-11-13 {
+ description
+ "Initial revision.";
+ }
+
+ container cont {
+ list playlist {
+ key name;
+
+ leaf name {
+ type string;
+ }
+ list song {
+ key index;
+ ordered-by user;
+
+ leaf index {
+ type uint32;
+ }
+ leaf id {
+ type instance-identifier;
+ mandatory true;
+ description
+ "Song identifier. Must identify an instance of
+ /songs-cont/songs/song-name.";
+ }
+ }
+ }
+ }
+
+ container songs-cont{
+ list songs{
+ key song-name;
+
+ leaf song-name{
+ type string;
+ }
+ }
+ }
+}
\ No newline at end of file