-->
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
- <version>${feature.test.version}</version>
<scope>test</scope>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<karaf.distro.version>${karaf.empty.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
</dependency>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
</dependency>
</dependencies>
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
</dependency>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
</dependency>
</dependencies>
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
</dependency>
<!-- test the features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
</dependency>
</dependencies>
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
<branding.version>1.1.0-SNAPSHOT</branding.version>
<karaf.resources.version>1.5.0-SNAPSHOT</karaf.resources.version>
<karaf.version>3.0.1</karaf.version>
- <feature.test.version>0.7.0-SNAPSHOT</feature.test.version>
<karaf.empty.version>1.5.0-SNAPSHOT</karaf.empty.version>
<surefire.version>2.16</surefire.version>
</properties>
<version>${jolokia.version}</version>
</dependency>
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
- <version>${feature.test.version}</version>
<scope>test</scope>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<karaf.distro.version>${karaf.empty.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
</dependency>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
</dependency>
</dependencies>
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
<artifactId>sal-inmemory-datastore</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>mdsal-netconf-connector</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>mdsal-netconf-monitoring</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-netconf-connector</artifactId>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
- <version>0.7.0-SNAPSHOT</version>
</dependency>
</dependencies>
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
</feature>
<!-- TODO move to netconf features, however there are some weird dependencies on features-config-persister all over that cause cyclic dependencies-->
- <feature name='odl-netconf-mdsal' version='${project.version}' description="OpenDaylight :: Netconf :: All">
+ <!-- TODO when installing this in pure karaf distro, many optimistic lock exceptions are thrown from config manager -->
+ <feature name='odl-netconf-mdsal' version='${project.version}' description="OpenDaylight :: Netconf :: Mdsal">
<feature version='${config.version}'>odl-config-all</feature>
<feature version='${netconf.version}'>odl-netconf-all</feature>
<bundle>mvn:org.opendaylight.controller/netconf-ssh/${netconf.version}</bundle>
<feature version='${mdsal.version}'>odl-mdsal-broker</feature>
<bundle>mvn:org.opendaylight.controller/mdsal-netconf-connector/${netconf.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/mdsal-netconf-monitoring/${netconf.version}</bundle>
<!-- TODO 01-netconf.xml file requires netconf-config-dispatcher to be present and its part of netconf-connector features. Clean Up-->
<bundle>mvn:org.opendaylight.controller/netconf-config-dispatcher/${config.version}</bundle>
<configfile finalname='${config.configfile.directory}/${config.netconf.client.configfile}'>mvn:org.opendaylight.controller/netconf-config/${netconf.version}/xml/config</configfile>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-config-dispatcher</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>mdsal-netconf-connector</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-tcp</artifactId>
<!-- test to validate features.xml -->
<!--FIXME BUG-2195 When running single feature tests for netconf connector, features including ssh proxy server always fail (this behavior does not appear when running karaf distro directly)-->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
- <version>${yangtools.version}</version>
<scope>test</scope>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
</feature>
<feature name='odl-netconf-ssh' version='${netconf.version}' description="OpenDaylight :: Netconf Connector :: SSH">
<feature version='${netconf.version}'>odl-netconf-tcp</feature>
+ <feature version='${config.version}'>odl-config-netty</feature>
<!-- FIXME: This introduces cycle between projects, which makes version updates
harder. Should be moved to different.
-->
</dependency>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
- <version>${yangtools.version}</version>
<scope>test</scope>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
<bundle>mvn:org.opendaylight.controller/ietf-netconf-monitoring-extension/${project.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf-inet-types.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools.model/ietf-yang-types/${ietf-yang-types.version}</bundle>
+ <bundle>mvn:org.opendaylight.yangtools.model/ietf-yang-types-20130715/2013.07.15.7-SNAPSHOT</bundle>
</feature>
<feature name='odl-netconf-mapping-api' version='${project.version}' description="OpenDaylight :: Netconf :: Mapping API">
<feature version='${project.version}'>odl-netconf-api</feature>
</dependency>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
</dependency>
</dependencies>
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
-->
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
- <version>${yangtools.version}</version>
<scope>test</scope>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
<!-- karaf distro -->
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-karaf</directory>
+ <directory>karaf</directory>
<includes>
<include>pom.xml</include>
</includes>
<!-- features -->
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-features</directory>
+ <directory>features</directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-features/src/main/features</directory>
+ <directory>features/src/main/features</directory>
<includes>
<include>**/*.xml</include>
</includes>
<!-- impl -->
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-impl</directory>
+ <directory>impl</directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-impl/src/main/java</directory>
+ <directory>impl/src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-impl/src/main/config</directory>
+ <directory>impl/src/test/java</directory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </fileSet>
+ <fileSet filtered="true" encoding="UTF-8">
+ <directory>impl/src/main/config</directory>
<includes>
<include>**/*.xml</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-impl/src/main/yang</directory>
+ <directory>impl/src/main/yang</directory>
<includes>
<include>**/*.yang</include>
</includes>
<!-- api -->
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-api</directory>
+ <directory>api</directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-api/src/main/yang</directory>
+ <directory>api/src/main/yang</directory>
<includes>
<include>**/*.yang</include>
</includes>
<!-- artifacts -->
<fileSet filtered="true" encoding="UTF-8">
- <directory>__artifactId__-artifacts</directory>
+ <directory>artifacts</directory>
<includes>
<include>pom.xml</include>
</includes>
<properties>
<mdsal.version>1.2.0-SNAPSHOT</mdsal.version>
<yangtools.version>0.7.0-SNAPSHOT</yangtools.version>
+ <configfile.directory>etc/opendaylight/karaf</configfile.directory>
</properties>
<dependencyManagement>
<dependencies>
<repository>mvn:org.opendaylight.yangtools/features-yangtools/${symbol_dollar}{yangtools.version}/xml/features</repository>
<repository>mvn:org.opendaylight.controller/features-mdsal/${symbol_dollar}{mdsal.version}/xml/features</repository>
<repository>mvn:org.opendaylight.controller/features-restconf/${symbol_dollar}{mdsal.version}/xml/features</repository>
- <feature name='odl-${artifactId}-api' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${artifactId} :: api '>
+ <feature name='odl-${artifactId}-api' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${artifactId} :: api'>
<feature version='${symbol_dollar}{yangtools.version}'>odl-yangtools-models</feature>
<bundle>mvn:${groupId}/${artifactId}-api/${symbol_dollar}{project.version}</bundle>
</feature>
- <feature name='odl-${artifactId}-impl' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${artifactId} :: impl '>
+ <feature name='odl-${artifactId}' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${artifactId}'>
<feature version='${symbol_dollar}{mdsal.version}'>odl-mdsal-broker</feature>
<feature version='${symbol_dollar}{project.version}'>odl-${artifactId}-api</feature>
<bundle>mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version}</bundle>
- <configfile finalname="${artifactId}-impl-default-config.xml">mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version}/xml/config</configfile>
+ <configfile finalname="${configfile.directory}/${artifactId}.xml">mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version}/xml/config</configfile>
</feature>
- <feature name='odl-${artifactId}-impl-rest' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${artifactId} :: impl :: REST '>
- <feature version="${symbol_dollar}{project.version}">odl-${artifactId}-impl</feature>
+ <feature name='odl-${artifactId}-rest' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${artifactId} :: REST'>
+ <feature version="${symbol_dollar}{project.version}">odl-${artifactId}</feature>
<feature version="${symbol_dollar}{mdsal.version}">odl-restconf</feature>
</feature>
- <feature name='odl-${artifactId}-impl-ui' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${artifactId} :: impl :: UI'>
- <feature version="${symbol_dollar}{project.version}">odl-${artifactId}-impl-rest</feature>
+ <feature name='odl-${artifactId}-ui' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${artifactId} :: UI'>
+ <feature version="${symbol_dollar}{project.version}">odl-${artifactId}-rest</feature>
<feature version="${symbol_dollar}{mdsal.version}">odl-mdsal-apidocs</feature>
<feature version="${symbol_dollar}{mdsal.version}">odl-mdsal-xsql</feature>
</feature>
<artifactId>${artifactId}-api</artifactId>
<version>${symbol_dollar}{project.version}</version>
</dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
<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:${artifactId}:impl">prefix:${artifactId}-impl</type>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:${artifactId}:impl">prefix:${artifactId}</type>
<name>${artifactId}-default</name>
<broker>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
* 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 ${package};
+package ${package}.impl;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
*/
package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210;
-import ${package}.${classPrefix}Provider;
+import ${package}.impl.${classPrefix}Provider;
-public class ${classPrefix}ImplModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210.Abstract${classPrefix}ImplModule {
- public ${classPrefix}ImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+public class ${classPrefix}Module extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210.Abstract${classPrefix}Module {
+ public ${classPrefix}Module(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
}
- public ${classPrefix}ImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210.${classPrefix}ImplModule oldModule, java.lang.AutoCloseable oldInstance) {
+ public ${classPrefix}Module(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210.${classPrefix}Module oldModule, java.lang.AutoCloseable oldInstance) {
super(identifier, dependencyResolver, oldModule, oldInstance);
}
* Do not modify this file unless it is present under src/main directory
*/
package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210;
-public class ${classPrefix}ImplModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210.Abstract${classPrefix}ImplModuleFactory {
+public class ${classPrefix}ModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210.Abstract${classPrefix}ModuleFactory {
}
"Initial revision";
}
- identity ${artifactId}-impl {
+ identity ${artifactId} {
base config:module-type;
- config:java-name-prefix ${classPrefix}Impl;
+ config:java-name-prefix ${classPrefix};
}
augment "/config:modules/config:module/config:configuration" {
- case ${artifactId}-impl {
- when "/config:modules/config:module/config:type = '${artifactId}-impl'";
+ case ${artifactId} {
+ when "/config:modules/config:module/config:type = '${artifactId}'";
container broker {
uses config:service-ref {
refine type {
--- /dev/null
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+#set( $provider = "${classPrefix}Provider" )
+/*
+ * ${copyright} 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 ${package}.impl;
+
+import org.junit.Test;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+
+import static org.mockito.Mockito.mock;
+
+public class ${classPrefix}ProviderTest {
+ @Test
+ public void testOnSessionInitiated() {
+ ${provider} provider = new ${provider}();
+
+ // ensure no exceptions
+ // currently this method is empty
+ provider.onSessionInitiated(mock(BindingAwareBroker.ProviderContext.class));
+ }
+
+ @Test
+ public void testClose() throws Exception {
+ ${provider} provider = new ${provider}();
+
+ // ensure no exceptions
+ // currently this method is empty
+ provider.close();
+ }
+}
--- /dev/null
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+#set( $factory = "${classPrefix}ModuleFactory" )
+/*
+ * ${copyright} 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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210;
+
+import org.junit.Test;
+
+public class ${classPrefix}ModuleFactoryTest {
+ @Test
+ public void testFactoryConstructor() {
+ // ensure no exceptions on construction
+ new ${factory}();
+ }
+}
--- /dev/null
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+#set( $module = "${classPrefix}Module" )
+/*
+ * ${copyright} 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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.JmxAttribute;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import ${package}.impl.${classPrefix}Provider;
+
+import javax.management.ObjectName;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ${classPrefix}ModuleTest {
+ @Test
+ public void testCustomValidation() {
+ ${module} module = new ${module}(mock(ModuleIdentifier.class), mock(DependencyResolver.class));
+
+ // ensure no exceptions on validation
+ // currently this method is empty
+ module.customValidation();
+ }
+
+ @Test
+ public void testCreateInstance() throws Exception {
+ // configure mocks
+ DependencyResolver dependencyResolver = mock(DependencyResolver.class);
+ BindingAwareBroker broker = mock(BindingAwareBroker.class);
+ when(dependencyResolver.resolveInstance(eq(BindingAwareBroker.class), any(ObjectName.class), any(JmxAttribute.class))).thenReturn(broker);
+
+ // create instance of module with injected mocks
+ ${module} module = new ${module}(mock(ModuleIdentifier.class), dependencyResolver);
+
+ // getInstance calls resolveInstance to get the broker dependency and then calls createInstance
+ AutoCloseable closeable = module.getInstance();
+
+ // verify that the module registered the returned provider with the broker
+ verify(broker).registerProvider((${classPrefix}Provider)closeable);
+
+ // ensure no exceptions on close
+ closeable.close();
+ }
+}
<maven>3.1.1</maven>
</prerequisites>
<properties>
- <karaf.localFeature>odl-${artifactId}-impl-ui</karaf.localFeature>
+ <karaf.localFeature>odl-${artifactId}-ui</karaf.localFeature>
</properties>
<dependencyManagement>
<dependencies>
<scope>runtime</scope>
</dependency>
</dependencies>
+ <!-- DO NOT install or deploy the karaf artifact -->
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}-aggregator</artifactId>
<version>${version}</version>
- <name>${project.artifactId}</name>
+ <name>${artifactId}</name>
<packaging>pom</packaging>
<modelVersion>4.0.0</modelVersion>
<prerequisites>
<maven>3.1.1</maven>
</prerequisites>
<modules>
- <module>${artifactId}-api</module>
- <module>${artifactId}-impl</module>
- <module>${artifactId}-karaf</module>
- <module>${artifactId}-features</module>
- <module>${artifactId}-artifacts</module>
+ <module>api</module>
+ <module>impl</module>
+ <module>karaf</module>
+ <module>features</module>
+ <module>artifacts</module>
</modules>
<!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->
<build>
</plugin>
</plugins>
</build>
+
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/${artifactId}.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/${artifactId}.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/${artifactId}:Main</url>
+ </scm>
</project>
<properties>
- <akka.version>2.3.4</akka.version>
+ <akka.version>2.3.9</akka.version>
<appauth.version>0.5.0-SNAPSHOT</appauth.version>
<archetype-app-northbound>0.1.0-SNAPSHOT</archetype-app-northbound>
<arphandler.version>0.6.0-SNAPSHOT</arphandler.version>
<!-- OpenEXI third party lib for netconf-->
<exi.nagasena.version>0000.0002.0038.0</exi.nagasena.version>
<felix.util.version>1.6.0</felix.util.version>
+ <features.test.version>1.5.0-SNAPSHOT</features.test.version>
<filtervalve.version>1.5.0-SNAPSHOT</filtervalve.version>
<findbugs.maven.plugin.version>2.4.0</findbugs.maven.plugin.version>
<flowprogrammer.northbound.version>0.5.0-SNAPSHOT</flowprogrammer.northbound.version>
<jmxGeneratorPath>src/main/yang-gen-config</jmxGeneratorPath>
<jolokia-bridge.version>0.1.0-SNAPSHOT</jolokia-bridge.version>
<jolokia.version>1.1.4</jolokia.version>
- <jsr305.api.version>2.0.1</jsr305.api.version>
<jsr311.api.version>1.1.1</jsr311.api.version>
<jsr311.v2.api.version>2.0</jsr311.v2.api.version>
<karaf.branding.version>1.1.0-SNAPSHOT</karaf.branding.version>
<artifactId>java-concurrent-hash-trie-map</artifactId>
<version>${ctrie.version}</version>
</dependency>
- <dependency>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>jsr305</artifactId>
- <version>${jsr305.api.version}</version>
- </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<!-- 3rd party dependencies needed by config-->
<dependency>
- <groupId>com.jcabi</groupId>
- <artifactId>jcabi-maven-slf4j</artifactId>
- <version>0.8</version>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-core</artifactId>
+ <version>3.1.1</version>
+ <scope>provided</scope>
</dependency>
<dependency>
<version>${yangtools.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.odlparent</groupId>
+ <artifactId>features-test</artifactId>
+ <version>${features.test.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>features-yangtools</artifactId>
Set<String> getAvailableModuleNames();
-
- /**
- * Find all runtime beans
- *
- * @return objectNames
- */
- Set<ObjectName> lookupRuntimeBeans();
-
- /**
- * Find all runtime of specified module
- *
- * @param moduleName
- * of bean
- * @param instanceName
- * of bean
- * @return objectNames
- */
- Set<ObjectName> lookupRuntimeBeans(String moduleName, String instanceName);
-
}
*/
Set<String> getAvailableModuleFactoryQNames();
+ /**
+ * Find all runtime beans
+ *
+ * @return objectNames
+ */
+ Set<ObjectName> lookupRuntimeBeans();
+
+ /**
+ * Find all runtime of specified module
+ *
+ * @param moduleName
+ * of bean
+ * @param instanceName
+ * of bean
+ * @return objectNames
+ */
+ Set<ObjectName> lookupRuntimeBeans(String moduleName, String instanceName);
}
public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException {
txLookupRegistry.checkConfigBeanExists(objectName);
}
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans() {
+ return txLookupRegistry.lookupRuntimeBeans();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans(String moduleName,
+ String instanceName) {
+ return txLookupRegistry.lookupRuntimeBeans(moduleName, instanceName);
+ }
+
// --
/**
return ModuleQNameUtil.getQNames(allCurrentFactories);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans() {
+ return lookupRuntimeBeans("*", "*");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans(String moduleName,
+ String instanceName) {
+ String finalModuleName = moduleName == null ? "*" : moduleName;
+ String finalInstanceName = instanceName == null ? "*" : instanceName;
+ ObjectName namePattern = ObjectNameUtil.createRuntimeBeanPattern(
+ finalModuleName, finalInstanceName);
+ return transactionJMXRegistrator.queryNames(namePattern, null);
+ }
@Override
public String toString() {
throw new UnsupportedOperationException();
}
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans(final String moduleName, final String instanceName) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public String toString() {
return "initial";
--- /dev/null
+package org.opendaylight.controller.config.util;
+
+import javax.management.ObjectName;
+
+/**
+ * Created by mmarsale on 20.2.2015.
+ */
+public interface BeanReader {
+ Object getAttributeCurrentValue(ObjectName on, String attributeName);
+}
import javax.management.ObjectName;
import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
-public interface ConfigRegistryClient extends ConfigRegistryMXBean {
+public interface ConfigRegistryClient extends ConfigRegistryMXBean, BeanReader {
ConfigTransactionClient createTransaction();
Object invokeMethod(ObjectName on, String name, Object[] params,
String[] signature);
- Object getAttributeCurrentValue(ObjectName on, String attributeName);
-
}
} catch (AttributeNotFoundException | InstanceNotFoundException
| MBeanException | ReflectionException e) {
throw new RuntimeException("Unable to get attribute "
- + attributeName + " for " + on, e);
+ + attributeName + " for " + on + ". Available beans: " + lookupConfigBeans(), e);
}
}
import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
public interface ConfigTransactionClient extends
- ConfigTransactionControllerMXBean {
+ ConfigTransactionControllerMXBean, BeanReader {
CommitStatus commit() throws ConflictingVersionException,
ValidationException;
* @param on - ObjectName of the Object from which the attribute should be read
* @param jmxName - name of the attribute to be read
*
- * @return Attribute of Object on with attribute name jmxName
+ * @return Object of Object on with attribute name jmxName
*/
Attribute getAttribute(ObjectName on, String jmxName);
}
configTransactionControllerMXBeanProxy.checkServiceReferenceExists(objectName);
}
+ @Override
+ public Attribute getAttribute(ObjectName on, String attrName) {
+ if (ObjectNameUtil.getTransactionName(on) == null) {
+ throw new IllegalArgumentException("Not in transaction instance "
+ + on + ", no transaction name present");
+ }
+
+ try {
+ return new Attribute(attrName, configMBeanServer.getAttribute(on,attrName));
+ } catch (JMException e) {
+ throw new IllegalStateException("Unable to get attribute "
+ + attrName + " for " + on, e);
+ }
+ }
+
+ @Override
+ public Object getAttributeCurrentValue(ObjectName on, String attrName) {
+ return getAttribute(on, attrName).getValue();
+ }
+
@Override
public void validateBean(ObjectName configBeanON)
throws ValidationException {
}
@Override
- public Attribute getAttribute(ObjectName on, String attrName) {
- if (ObjectNameUtil.getTransactionName(on) == null) {
- throw new IllegalArgumentException("Not in transaction instance "
- + on + ", no transaction name present");
- }
+ public Set<String> getAvailableModuleFactoryQNames() {
+ return configTransactionControllerMXBeanProxy.getAvailableModuleFactoryQNames();
+ }
- try {
- return new Attribute(attrName, configMBeanServer.getAttribute(on,attrName));
- } catch (JMException e) {
- throw new IllegalStateException("Unable to get attribute "
- + attrName + " for " + on, e);
- }
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans() {
+ return configTransactionControllerMXBeanProxy.lookupRuntimeBeans();
}
@Override
- public Set<String> getAvailableModuleFactoryQNames() {
- return configTransactionControllerMXBeanProxy.getAvailableModuleFactoryQNames();
+ public Set<ObjectName> lookupRuntimeBeans(final String moduleName, final String instanceName) {
+ return configTransactionControllerMXBeanProxy.lookupRuntimeBeans(moduleName, instanceName);
}
}
package org.opendaylight.controller.config.util;
import com.google.common.collect.Sets;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
return Sets.newHashSet("availableModuleFactoryQNames");
}
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<ObjectName> lookupRuntimeBeans(final String moduleName, final String instanceName) {
+ return Collections.emptySet();
+ }
+
@Override
public ObjectName getServiceReference(String serviceInterfaceQName, String refName) throws InstanceNotFoundException {
return conf3;
<artifactId>guava</artifactId>
</dependency>
- <dependency>
- <groupId>com.jcabi</groupId>
- <artifactId>jcabi-maven-slf4j</artifactId>
- </dependency>
-
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<artifactId>commons-lang3</artifactId>
</dependency>
- <dependency>
- <groupId>org.codehaus.gmaven.runtime</groupId>
- <artifactId>gmaven-runtime-2.0</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.sonatype.gossip</groupId>
- <artifactId>gossip</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>yang-jmx-generator</artifactId>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin-spi</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-core</artifactId>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
-import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang2sources.spi.CodeGenerator;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
+import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.slf4j.impl.StaticLoggerBinder;
/**
* This class interfaces with yang-maven-plugin. Gets parsed yang modules in
* {@link SchemaContext}, and parameters form the plugin configuration, and
* writes service interfaces and/or modules.
*/
-public class JMXGenerator implements CodeGenerator {
+public class JMXGenerator implements BasicCodeGenerator, MavenProjectAware {
+ private static final class NamespaceMapping {
+ private final String namespace, packageName;
+ public NamespaceMapping(final String namespace, final String packagename) {
+ this.namespace = namespace;
+ this.packageName = packagename;
+ }
+ }
+
+ @VisibleForTesting
static final String NAMESPACE_TO_PACKAGE_DIVIDER = "==";
+ @VisibleForTesting
static final String NAMESPACE_TO_PACKAGE_PREFIX = "namespaceToPackage";
+ @VisibleForTesting
static final String MODULE_FACTORY_FILE_BOOLEAN = "moduleFactoryFile";
+ private static final Logger LOG = LoggerFactory.getLogger(JMXGenerator.class);
+ private static final Pattern NAMESPACE_MAPPING_PATTERN = Pattern.compile("(.+)" + NAMESPACE_TO_PACKAGE_DIVIDER + "(.+)");
+
private PackageTranslator packageTranslator;
private final CodeWriter codeWriter;
- private static final Logger LOG = LoggerFactory
- .getLogger(JMXGenerator.class);
private Map<String, String> namespaceToPackageMapping;
private File resourceBaseDir;
private File projectBaseDir;
private boolean generateModuleFactoryFile = true;
public JMXGenerator() {
- this.codeWriter = new CodeWriter();
+ this(new CodeWriter());
}
- public JMXGenerator(CodeWriter codeWriter) {
+ public JMXGenerator(final CodeWriter codeWriter) {
this.codeWriter = codeWriter;
}
@Override
- public Collection<File> generateSources(SchemaContext context,
- File outputBaseDir, Set<Module> yangModulesInCurrentMavenModule) {
+ public Collection<File> generateSources(final SchemaContext context,
+ final File outputBaseDir, final Set<Module> yangModulesInCurrentMavenModule) {
Preconditions.checkArgument(context != null, "Null context received");
Preconditions.checkArgument(outputBaseDir != null,
return generatedFiles.getFiles();
}
- static File concatFolders(File projectBaseDir, String... folderNames) {
+ @VisibleForTesting
+ static File concatFolders(final File projectBaseDir, final String... folderNames) {
StringBuilder b = new StringBuilder();
for (String folder : folderNames) {
b.append(folder);
}
@Override
- public void setAdditionalConfig(Map<String, String> additionalCfg) {
- if (LOG != null) {
- LOG.debug(getClass().getCanonicalName(),
- ": Additional configuration received: ",
- additionalCfg.toString());
- }
+ public void setAdditionalConfig(final Map<String, String> additionalCfg) {
+ LOG.debug("{}: Additional configuration received: {}", getClass().getCanonicalName(), additionalCfg);
this.namespaceToPackageMapping = extractNamespaceMapping(additionalCfg);
this.generateModuleFactoryFile = extractModuleFactoryBoolean(additionalCfg);
}
private boolean extractModuleFactoryBoolean(
- Map<String, String> additionalCfg) {
+ final Map<String, String> additionalCfg) {
String bool = additionalCfg.get(MODULE_FACTORY_FILE_BOOLEAN);
if (bool == null) {
return true;
return true;
}
- @Override
- public void setLog(Log log) {
- StaticLoggerBinder.getSingleton().setMavenLog(log);
- }
-
private static Map<String, String> extractNamespaceMapping(
- Map<String, String> additionalCfg) {
+ final Map<String, String> additionalCfg) {
Map<String, String> namespaceToPackage = Maps.newHashMap();
for (String key : additionalCfg.keySet()) {
if (key.startsWith(NAMESPACE_TO_PACKAGE_PREFIX)) {
return namespaceToPackage;
}
- static Pattern namespaceMappingPattern = Pattern.compile("(.+)"
- + NAMESPACE_TO_PACKAGE_DIVIDER + "(.+)");
-
- private static NamespaceMapping extractNamespaceMapping(String mapping) {
- Matcher matcher = namespaceMappingPattern.matcher(mapping);
- Preconditions
- .checkArgument(matcher.matches(), String.format("Namespace to package mapping:%s is in invalid " +
- "format, requested format is: %s", mapping, namespaceMappingPattern));
+ private static NamespaceMapping extractNamespaceMapping(final String mapping) {
+ Matcher matcher = NAMESPACE_MAPPING_PATTERN.matcher(mapping);
+ Preconditions.checkArgument(matcher.matches(),
+ "Namespace to package mapping:%s is in invalid format, requested format is: %s",
+ mapping, NAMESPACE_MAPPING_PATTERN);
return new NamespaceMapping(matcher.group(1), matcher.group(2));
}
- private static class NamespaceMapping {
- public NamespaceMapping(String namespace, String packagename) {
- this.namespace = namespace;
- this.packageName = packagename;
- }
-
- private final String namespace, packageName;
- }
-
@Override
- public void setResourceBaseDir(File resourceDir) {
+ public void setResourceBaseDir(final File resourceDir) {
this.resourceBaseDir = resourceDir;
}
@Override
- public void setMavenProject(MavenProject project) {
+ public void setMavenProject(final MavenProject project) {
this.projectBaseDir = project.getBasedir();
-
- if (LOG != null) {
- LOG.debug(getClass().getCanonicalName(), " project base dir: ",
- projectBaseDir);
- }
+ LOG.debug("{}: project base dir: {}", getClass().getCanonicalName(), projectBaseDir);
}
@VisibleForTesting
static class GeneratedFilesTracker {
private final Set<File> files = Sets.newHashSet();
- void addFile(File file) {
+ void addFile(final File file) {
if (files.contains(file)) {
List<File> undeletedFiles = Lists.newArrayList();
for (File presentFile : files) {
files.add(file);
}
- void addFile(Collection<File> files) {
+ void addFile(final Collection<File> files) {
for (File file : files) {
addFile(file);
}
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
-import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType;
import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
public class TemplateFactory {
* bean as value that should be persisted from this instance.
*/
public static Map<String, FtlTemplate> getTOAndMXInterfaceFtlFiles(
- RuntimeBeanEntry entry) {
+ final RuntimeBeanEntry entry) {
Map<String, FtlTemplate> result = new HashMap<>();
{ // create GeneralInterfaceFtlFile for runtime MXBean. Attributes will
// be transformed to getter methods
}
// FIXME: put into Type.toString
- static String serializeType(Type type, boolean addWildcards) {
+ static String serializeType(final Type type, final boolean addWildcards) {
if (type instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType) type;
StringBuilder sb = new StringBuilder();
}
}
- static String serializeType(Type type) {
+ static String serializeType(final Type type) {
return serializeType(type, false);
}
- private static String getReturnType(AttributeIfc attributeIfc) {
+ private static String getReturnType(final AttributeIfc attributeIfc) {
String returnType;
if (attributeIfc instanceof TypedAttribute) {
Type type = ((TypedAttribute) attributeIfc).getType();
}
public static GeneralInterfaceTemplate serviceInterfaceFromSie(
- ServiceInterfaceEntry sie) {
+ final ServiceInterfaceEntry sie) {
List<String> extendedInterfaces = Lists
.newArrayList(AbstractServiceInterface.class.getCanonicalName());
}
public static AbstractFactoryTemplate abstractFactoryTemplateFromMbe(
- ModuleMXBeanEntry mbe) {
+ final ModuleMXBeanEntry mbe) {
AbstractFactoryAttributesProcessor attrProcessor = new AbstractFactoryAttributesProcessor();
attrProcessor.processAttributes(mbe.getAttributes(),
mbe.getPackageName());
}
public static AbstractModuleTemplate abstractModuleTemplateFromMbe(
- ModuleMXBeanEntry mbe) {
+ final ModuleMXBeanEntry mbe) {
AbstractModuleAttributesProcessor attrProcessor = new AbstractModuleAttributesProcessor(mbe.getAttributes());
List<ModuleField> moduleFields = attrProcessor.getModuleFields();
}
public static StubFactoryTemplate stubFactoryTemplateFromMbe(
- ModuleMXBeanEntry mbe) {
+ final ModuleMXBeanEntry mbe) {
return new StubFactoryTemplate(getHeaderFromEntry(mbe),
mbe.getPackageName(), mbe.getStubFactoryName(),
mbe.getFullyQualifiedName(mbe.getAbstractFactoryName())
}
public static GeneralInterfaceTemplate mXBeanInterfaceTemplateFromMbe(
- ModuleMXBeanEntry mbe) {
+ final ModuleMXBeanEntry mbe) {
MXBeanInterfaceAttributesProcessor attrProcessor = new MXBeanInterfaceAttributesProcessor();
attrProcessor.processAttributes(mbe.getAttributes());
GeneralInterfaceTemplate ifcTemplate = new GeneralInterfaceTemplate(
}
public static Map<String, GeneralClassTemplate> tOsFromMbe(
- ModuleMXBeanEntry mbe) {
+ final ModuleMXBeanEntry mbe) {
Map<String, GeneralClassTemplate> retVal = Maps.newHashMap();
TOAttributesProcessor processor = new TOAttributesProcessor();
processor.processAttributes(mbe.getAttributes());
}
public static Map<String, GeneralClassTemplate> tOsFromRbe(
- RuntimeBeanEntry rbe) {
+ final RuntimeBeanEntry rbe) {
Map<String, GeneralClassTemplate> retVal = Maps.newHashMap();
TOAttributesProcessor processor = new TOAttributesProcessor();
Map<String, AttributeIfc> yangPropertiesToTypesMap = Maps.newHashMap(rbe.getYangPropertiesToTypesMap());
return retVal;
}
- private static Header getHeaderFromEntry(AbstractEntry mbe) {
+ private static Header getHeaderFromEntry(final AbstractEntry mbe) {
return new Header(mbe.getYangModuleName(), mbe.getYangModuleLocalname());
}
private final List<TOInternal> tos = Lists.newArrayList();
- void processAttributes(Map<String, AttributeIfc> attributes) {
+ void processAttributes(final Map<String, AttributeIfc> attributes) {
for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
AttributeIfc attributeIfc = attrEntry.getValue();
if (attributeIfc instanceof TOAttribute) {
}
}
- private void createTOInternal(TOAttribute toAttribute) {
+ private void createTOInternal(final TOAttribute toAttribute) {
Map<String, AttributeIfc> attrs = toAttribute.getCapitalizedPropertiesToTypesMap();
// recursive processing of TO's attributes
private List<Field> fields;
private List<MethodDefinition> methods;
- public TOInternal(Type type, Map<String, AttributeIfc> attrs) {
+ public TOInternal(final Type type, final Map<String, AttributeIfc> attrs) {
this(type.getFullyQualifiedName(), type.getName(), attrs, type.getPackageName());
}
- public TOInternal(String fullyQualifiedName, String name,
- Map<String, AttributeIfc> attrs, String packageName) {
+ public TOInternal(final String fullyQualifiedName, final String name,
+ final Map<String, AttributeIfc> attrs, final String packageName) {
this.fullyQualifiedName = fullyQualifiedName;
this.name = name;
processAttrs(attrs, packageName);
private final static String dependencyResolverVarName = "dependencyResolver";
private final static String dependencyResolverInjectMethodName = "injectDependencyResolver";
- private void processAttrs(Map<String, AttributeIfc> attrs, String packageName) {
+ private void processAttrs(final Map<String, AttributeIfc> attrs, final String packageName) {
fields = Lists.newArrayList();
methods = Lists.newArrayList();
for (Entry<String, AttributeIfc> attrEntry : attrs.entrySet()) {
String innerName = attrEntry.getKey();
- String varName = BindingGeneratorUtil
- .parseToValidParamName(attrEntry.getKey());
+ String varName = BindingMapping.getPropertyName(attrEntry.getKey());
String fullyQualifiedName, nullableDefault = null;
if (attrEntry.getValue() instanceof TypedAttribute) {
private static class MXBeanInterfaceAttributesProcessor {
private final List<MethodDeclaration> methods = Lists.newArrayList();
- void processAttributes(Map<String, AttributeIfc> attributes) {
+ void processAttributes(final Map<String, AttributeIfc> attributes) {
for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
String returnType;
AttributeIfc attributeIfc = attrEntry.getValue();
MethodDeclaration getter = new MethodDeclaration(returnType,
getterName, Collections.<Field> emptyList());
- String varName = BindingGeneratorUtil
- .parseToValidParamName(attrEntry.getKey());
+ String varName = BindingMapping.getPropertyName(attrEntry.getKey());
String setterName = "set"
+ attributeIfc.getUpperCaseCammelCase();
MethodDeclaration setter = new MethodDeclaration("void",
private final List<Field> fields = Lists.newArrayList();
- void processAttributes(Map<String, AttributeIfc> attributes,
- String packageName) {
+ void processAttributes(final Map<String, AttributeIfc> attributes,
+ final String packageName) {
for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
String type;
String nullableDefaultWrapped = null;
private final List<ModuleField> moduleFields;
private final List<MethodDefinition> methods;
- private Holder(List<ModuleField> moduleFields, List<MethodDefinition> methods) {
+ private Holder(final List<ModuleField> moduleFields, final List<MethodDefinition> methods) {
this.moduleFields = Collections.unmodifiableList(moduleFields);
this.methods = Collections.unmodifiableList(methods);
}
private final Holder holder;
- private AbstractModuleAttributesProcessor(Map<String, AttributeIfc> attributes) {
+ private AbstractModuleAttributesProcessor(final Map<String, AttributeIfc> attributes) {
this.holder = processAttributes(attributes);
}
- private static Holder processAttributes(Map<String, AttributeIfc> attributes) {
+ private static Holder processAttributes(final Map<String, AttributeIfc> attributes) {
List<ModuleField> moduleFields = new ArrayList<>();
List<MethodDefinition> methods = new ArrayList<>();
for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
}
}
- String varName = BindingGeneratorUtil
- .parseToValidParamName(attrEntry.getKey());
+ String varName = BindingMapping.getPropertyName(attrEntry.getKey());
ModuleField field;
if (isIdentity) {
}
- private static boolean needsDepResolver(AttributeIfc value) {
+ private static boolean needsDepResolver(final AttributeIfc value) {
if(value instanceof TOAttribute) {
return true;
}
return false;
}
- private static String getInnerTypeFromIdentity(Type type) {
+ private static String getInnerTypeFromIdentity(final Type type) {
Preconditions.checkArgument(type instanceof ParameterizedType);
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
Preconditions.checkArgument(args.length ==1);
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.FileUtils;
-import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.IProblem;
File targetDir = new File(generatorOutputPath, "target");
generatedResourcesDir = new File(targetDir, "generated-resources");
jmxGenerator.setResourceBaseDir(generatedResourcesDir);
- Log mockedLog = mock(Log.class);
- doReturn(false).when(mockedLog).isDebugEnabled();
- doNothing().when(mockedLog).debug(any(CharSequence.class));
- doNothing().when(mockedLog).info(any(CharSequence.class));
- doNothing().when(mockedLog).error(any(CharSequence.class),
- any(Throwable.class));
- jmxGenerator.setLog(mockedLog);
MavenProject project = mock(MavenProject.class);
doReturn(generatorOutputPath).when(project).getBasedir();
jmxGenerator.setMavenProject(project);
verifyModuleFactoryFile(false);
}
- private void verifyModuleFactoryFile(boolean shouldBePresent) {
+ private void verifyModuleFactoryFile(final boolean shouldBePresent) {
File factoryFile = new File(generatedResourcesDir, "META-INF"
+ File.separator + "services" + File.separator
+ ModuleFactory.class.getName());
- if (!shouldBePresent)
+ if (!shouldBePresent) {
assertFalse("Factory file should not be generated",
factoryFile.exists());
- else
+ } else {
assertTrue("Factory file should be generated", factoryFile.exists());
+ }
}
- public static List<String> toFileNames(Collection<File> files) {
+ public static List<String> toFileNames(final Collection<File> files) {
List<String> result = new ArrayList<>();
for (File f : files) {
result.add(f.getName());
new Predicate<File>() {
@Override
- public boolean apply(File input) {
+ public boolean apply(final File input) {
return input.getName().endsWith("xml");
}
});
new Predicate<File>() {
@Override
- public boolean apply(File input) {
+ public boolean apply(final File input) {
return input.getName().endsWith("java");
}
});
String name = file.getName();
MbeASTVisitor visitor = new MbeASTVisitor();
verifiers.put(name, visitor);
- if (name.equals("AbstractDynamicThreadPoolModule.java"))
+ if (name.equals("AbstractDynamicThreadPoolModule.java")) {
abstractDynamicThreadPoolModuleVisitor = visitor;
- if (name.equals("AsyncEventBusModuleMXBean.java"))
+ }
+ if (name.equals("AsyncEventBusModuleMXBean.java")) {
asyncEventBusModuleMXBeanVisitor = visitor;
- if (name.equals("AbstractNamingThreadFactoryModuleFactory.java"))
+ }
+ if (name.equals("AbstractNamingThreadFactoryModuleFactory.java")) {
abstractNamingThreadFactoryModuleFactoryVisitor = visitor;
- if (name.equals("AsyncEventBusModule.java"))
+ }
+ if (name.equals("AsyncEventBusModule.java")) {
asyncEventBusModuleVisitor = visitor;
- if (name.equals("EventBusModuleFactory.java"))
+ }
+ if (name.equals("EventBusModuleFactory.java")) {
eventBusModuleFactoryVisitor = visitor;
+ }
}
processGeneratedCode(javaFiles, verifiers);
}
- private void verifyXmlFiles(Collection<File> xmlFiles) throws Exception {
+ private void verifyXmlFiles(final Collection<File> xmlFiles) throws Exception {
ErrorHandler errorHandler = new ErrorHandler() {
@Override
- public void warning(SAXParseException exception)
+ public void warning(final SAXParseException exception)
throws SAXException {
fail("Generated blueprint xml is not well formed "
+ exception.getMessage());
}
@Override
- public void fatalError(SAXParseException exception)
+ public void fatalError(final SAXParseException exception)
throws SAXException {
fail("Generated blueprint xml is not well formed "
+ exception.getMessage());
}
@Override
- public void error(SAXParseException exception) throws SAXException {
+ public void error(final SAXParseException exception) throws SAXException {
fail("Generated blueprint xml is not well formed "
+ exception.getMessage());
}
}
- private void assertEventBusModuleFactory(MbeASTVisitor visitor) {
+ private void assertEventBusModuleFactory(final MbeASTVisitor visitor) {
assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+ ".threads.java", visitor.packageName);
assertEquals("EventBusModuleFactory", visitor.type);
visitor.methodJavadoc.size());
}
- private void assertAsyncEventBusModule(MbeASTVisitor visitor) {
+ private void assertAsyncEventBusModule(final MbeASTVisitor visitor) {
assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+ ".threads.java", visitor.packageName);
assertEquals("AsyncEventBusModule", visitor.type);
}
private void assertAbstractNamingThreadFactoryModuleFactory(
- MbeASTVisitor visitor) {
+ final MbeASTVisitor visitor) {
assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+ ".threads.java", visitor.packageName);
assertEquals("AbstractNamingThreadFactoryModuleFactory", visitor.type);
}
- private void assertFactoryMethods(Set<String> methods, int expectedSize) {
+ private void assertFactoryMethods(final Set<String> methods, final int expectedSize) {
List<ArgumentAssertion> args = Lists.newArrayList();
ArgumentAssertion oldInstanceArg = new ArgumentAssertion(DynamicMBeanWithInstance.class.getCanonicalName(), "old");
}
- private void assertMethodPresent(Set<String> methods, MethodAssertion methodAssertion) {
+ private void assertMethodPresent(final Set<String> methods, final MethodAssertion methodAssertion) {
assertTrue(String.format("Generated methods did not contain %s, generated methods: %s",
methodAssertion.toString(), methods), methods.contains(methodAssertion.toString()));
}
- private void assertAsyncEventBusModuleMXBean(MbeASTVisitor visitor) {
+ private void assertAsyncEventBusModuleMXBean(final MbeASTVisitor visitor) {
assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+ ".threads.java", visitor.packageName);
assertEquals("AsyncEventBusModuleMXBean", visitor.type);
}
- private void assertAbstractDynamicThreadPoolModule(MbeASTVisitor visitor) {
+ private void assertAbstractDynamicThreadPoolModule(final MbeASTVisitor visitor) {
assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+ ".threads.java", visitor.packageName);
assertNotNull(visitor.javadoc);
visitor.methodJavadoc.get("void setMaximumSize(java.lang.Long maximumSize)"));
}
- private void assertDeclaredField(Set<String> fieldDeclarations,
- String declaration) {
+ private void assertDeclaredField(final Set<String> fieldDeclarations,
+ final String declaration) {
assertTrue("Missing field " + declaration + ", got: "
+ fieldDeclarations,
fieldDeclarations.contains(declaration + ";\n"));
protected Map<String, String> methodDescriptions = Maps.newHashMap();
@Override
- public boolean visit(PackageDeclaration node) {
+ public boolean visit(final PackageDeclaration node) {
packageName = node.getName().toString();
return super.visit(node);
}
@Override
- public boolean visit(NormalAnnotation node) {
+ public boolean visit(final NormalAnnotation node) {
if (node.getTypeName().toString()
.equals(Description.class.getCanonicalName())) {
if (node.getParent() instanceof TypeDeclaration) {
}
@Override
- public boolean visit(TypeDeclaration node) {
+ public boolean visit(final TypeDeclaration node) {
javadoc = node.getJavadoc() == null ? null : node.getJavadoc()
.toString();
type = node.getName().toString();
private final Map<String, String> methodJavadoc = Maps.newHashMap();
@Override
- public boolean visit(NormalAnnotation node) {
+ public boolean visit(final NormalAnnotation node) {
boolean result = super.visit(node);
if (node.getTypeName().toString()
.equals(RequireInterface.class.getCanonicalName())
}
@Override
- public boolean visit(FieldDeclaration node) {
+ public boolean visit(final FieldDeclaration node) {
fieldDeclarations.add(node.toString());
return super.visit(node);
}
@Override
- public boolean visit(MethodDeclaration node) {
- if (node.isConstructor())
+ public boolean visit(final MethodDeclaration node) {
+ if (node.isConstructor()) {
constructors.add(node.toString());
- else {
+ } else {
String methodSignature = node.getReturnType2() + " " + node.getName() + "(";
boolean first = true;
for (Object o : node.parameters()) {
}
@Override
- public boolean visit(TypeDeclaration node) {
+ public boolean visit(final TypeDeclaration node) {
boolean visit = super.visit(node);
List<?> superIfcs = node.superInterfaceTypes();
implmts = superIfcs != null && !superIfcs.isEmpty() ? superIfcs
}
- private void assertContains(String source, String... contained) {
+ private void assertContains(final String source, final String... contained) {
for (String string : contained) {
assertThat(source, containsString(string));
}
}
- private void processGeneratedCode(Collection<File> files,
- Map<String, ASTVisitor> verifiers) throws IOException {
+ private void processGeneratedCode(final Collection<File> files,
+ final Map<String, ASTVisitor> verifiers) throws IOException {
ASTParser parser = ASTParser.newParser(AST.JLS3);
Map<?, ?> options = JavaCore.getOptions();
JavaCore.setComplianceOptions(JavaCore.VERSION_1_7, options);
for (IProblem c : cu.getProblems()) {
// 1610613332 = Syntax error, annotations are only available if
// source level is 5.0
- if (c.getID() == 1610613332)
+ if (c.getID() == 1610613332) {
continue;
+ }
// 1610613332 = Syntax error, parameterized types are only
// available if source level is 5.0
- if (c.getID() == 1610613329)
+ if (c.getID() == 1610613329) {
continue;
- if (c.getID() == 1610613328) // 'for each' statements are only available if source level is 5.0
+ }
+ if (c.getID() == 1610613328) {
continue;
+ }
fail("Error in generated source code " + file + ":"
+ c.getSourceLineNumber() + " id: " + c.getID() + " message:" + c.toString());
}
ASTVisitor visitor = verifiers.get(file.getName());
- if (visitor == null)
+ if (visitor == null) {
fail("Unknown generated file " + file.getName());
+ }
cu.accept(visitor);
}
}
- public static char[] readFileAsChars(File file) throws IOException {
+ public static char[] readFileAsChars(final File file) throws IOException {
List<String> readLines = Files
.readLines(file, Charset.forName("utf-8"));
char[] retVal = new char[0];
private static class MethodAssertion extends ArgumentAssertion{
- private List<ArgumentAssertion> arguments;
+ private final List<ArgumentAssertion> arguments;
- MethodAssertion(String type, String name, List<ArgumentAssertion> arguments) {
+ MethodAssertion(final String type, final String name, final List<ArgumentAssertion> arguments) {
super(type, name);
this.arguments = arguments;
}
- MethodAssertion(String type, String name) {
+ MethodAssertion(final String type, final String name) {
this(type, name, Collections.<ArgumentAssertion>emptyList());
}
for (ArgumentAssertion argument : arguments) {
sb.append(argument.type).append(' ');
sb.append(argument.name);
- if(++i != arguments.size())
+ if(++i != arguments.size()) {
sb.append(',');
+ }
}
sb.append(')');
return sb.toString();
protected final String type, name;
- private ArgumentAssertion(String type, String name) {
+ private ArgumentAssertion(final String type, final String name) {
this.type = type;
this.name = name;
}
<dependency>\r
<groupId>org.opendaylight.controller</groupId>\r
<artifactId>ietf-netconf-notifications</artifactId>\r
- <version>0.3.0-SNAPSHOT</version>\r
</dependency>\r
<dependency>\r
<groupId>org.opendaylight.controller</groupId>\r
<plugin>\r
<groupId>org.codehaus.mojo</groupId>\r
<artifactId>build-helper-maven-plugin</artifactId>\r
- <version>1.8</version>\r
<executions>\r
<execution>\r
<id>add-source</id>\r
public void notifyNode(final NodeId nodeId) {
JoinTopicInput jti = getJoinTopicInputArgument(nodeId);
EventSourceService ess = mdSal.getRpcService(EventSourceService.class);
-
- if (ess == null) {
- throw new IllegalStateException("EventSourceService is not registered.");
- }
+ Preconditions.checkState(ess != null, "EventSourceService is not registered");
ess.joinTopic(jti);
}
*/
package org.opendaylight.controller.cluster.raft;
+import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Override
public void snapshotPreCommit(long snapshotCapturedIndex, long snapshotCapturedTerm) {
+ Preconditions.checkArgument(snapshotCapturedIndex >= snapshotIndex,
+ "snapshotCapturedIndex must be greater than or equal to snapshotIndex");
+
snapshottedJournal = new ArrayList<>(journal.size());
- snapshottedJournal.addAll(journal.subList(0, (int)(snapshotCapturedIndex - snapshotIndex)));
+ List<ReplicatedLogEntry> snapshotJournalEntries = journal.subList(0, (int) (snapshotCapturedIndex - snapshotIndex));
+
+ snapshottedJournal.addAll(snapshotJournalEntries);
clear(0, (int) (snapshotCapturedIndex - snapshotIndex));
previousSnapshotIndex = snapshotIndex;
* The interval in which the leader needs to check itself if its isolated
* @return FiniteDuration
*/
- FiniteDuration getIsolatedCheckInterval();
+ long getIsolatedCheckIntervalInMillis();
/**
private FiniteDuration heartBeatInterval = HEART_BEAT_INTERVAL;
private long snapshotBatchCount = SNAPSHOT_BATCH_COUNT;
private int journalRecoveryLogBatchSize = JOURNAL_RECOVERY_LOG_BATCH_SIZE;
- private FiniteDuration isolatedLeaderCheckInterval =
- new FiniteDuration(HEART_BEAT_INTERVAL.length() * 1000, HEART_BEAT_INTERVAL.unit());
+ private long isolatedLeaderCheckInterval = HEART_BEAT_INTERVAL.$times(1000).toMillis();
// 12 is just an arbitrary percentage. This is the amount of the total memory that a raft actor's
// in-memory journal can use before it needs to snapshot
}
public void setIsolatedLeaderCheckInterval(FiniteDuration isolatedLeaderCheckInterval) {
- this.isolatedLeaderCheckInterval = isolatedLeaderCheckInterval;
+ this.isolatedLeaderCheckInterval = isolatedLeaderCheckInterval.toMillis();
}
public void setElectionTimeoutFactor(long electionTimeoutFactor){
}
@Override
- public FiniteDuration getIsolatedCheckInterval() {
+ public long getIsolatedCheckIntervalInMillis() {
return isolatedLeaderCheckInterval;
}
public long timeSinceLastActivity() {
return stopwatch.elapsed(TimeUnit.MILLISECONDS);
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("FollowerLogInformationImpl [id=").append(id).append(", nextIndex=").append(nextIndex)
+ .append(", matchIndex=").append(matchIndex).append(", stopwatch=")
+ .append(stopwatch.elapsed(TimeUnit.MILLISECONDS))
+ .append(", followerTimeoutMillis=").append(followerTimeoutMillis).append("]");
+ return builder.toString();
+ }
+
+
}
import com.google.protobuf.ByteString;
import java.io.Serializable;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.cluster.DataPersistenceProvider;
import org.opendaylight.controller.cluster.common.actor.AbstractUntypedPersistentActor;
import org.opendaylight.controller.cluster.notifications.RoleChanged;
* </ul>
*/
public abstract class RaftActor extends AbstractUntypedPersistentActor {
+
+ private static final long APPLY_STATE_DELAY_THRESHOLD_IN_NANOS = TimeUnit.MILLISECONDS.toNanos(50L); // 50 millis
+
protected final Logger LOG = LoggerFactory.getLogger(getClass());
/**
if (message instanceof ApplyState){
ApplyState applyState = (ApplyState) message;
+ long elapsedTime = (System.nanoTime() - applyState.getStartTime());
+ if(elapsedTime >= APPLY_STATE_DELAY_THRESHOLD_IN_NANOS){
+ LOG.warn("ApplyState took more time than expected. Elapsed Time = {} ms ApplyState = {}",
+ TimeUnit.NANOSECONDS.toMillis(elapsedTime), applyState);
+ }
+
if(LOG.isDebugEnabled()) {
LOG.debug("{}: Applying state for log index {} data {}",
persistenceId(), applyState.getReplicatedLogEntry().getIndex(),
context.getReplicatedLog().snapshotPreCommit(captureSnapshot.getLastAppliedIndex(),
captureSnapshot.getLastAppliedTerm());
- } else {
+ getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex());
+ } else if(captureSnapshot.getReplicatedToAllIndex() != -1){
// clear the log based on replicatedToAllIndex
context.getReplicatedLog().snapshotPreCommit(captureSnapshot.getReplicatedToAllIndex(),
captureSnapshot.getReplicatedToAllTerm());
+
+ getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex());
+ } else {
+ // The replicatedToAllIndex was not found in the log
+ // This means that replicatedToAllIndex never moved beyond -1 or that it is already in the snapshot.
+ // In this scenario we may need to save the snapshot to the akka persistence
+ // snapshot for recovery but we do not need to do the replicated log trimming.
+ context.getReplicatedLog().snapshotPreCommit(replicatedLog.getSnapshotIndex(),
+ replicatedLog.getSnapshotTerm());
}
- getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex());
+
LOG.info("{}: Removed in-memory snapshotted entries, adjusted snaphsotIndex:{} " +
"and term:{}", persistenceId(), captureSnapshot.getLastAppliedIndex(),
dataSizeSinceLastSnapshot = 0;
- LOG.info("{}: Initiating Snapshot Capture..", persistenceId());
+ LOG.info("{}: Initiating Snapshot Capture, journalSize = {}, dataSizeForCheck = {}," +
+ " dataThreshold = {}", persistenceId(), journalSize, dataSizeForCheck, dataThreshold);
+
long lastAppliedIndex = -1;
long lastAppliedTerm = -1;
package org.opendaylight.controller.cluster.raft.base.messages;
import akka.actor.ActorRef;
-import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
-
import java.io.Serializable;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
public class ApplyState implements Serializable {
private static final long serialVersionUID = 1L;
private final ActorRef clientActor;
private final String identifier;
private final ReplicatedLogEntry replicatedLogEntry;
+ private final long startTime;
public ApplyState(ActorRef clientActor, String identifier,
ReplicatedLogEntry replicatedLogEntry) {
this.clientActor = clientActor;
this.identifier = identifier;
this.replicatedLogEntry = replicatedLogEntry;
+ this.startTime = System.nanoTime();
}
public ActorRef getClientActor() {
public ReplicatedLogEntry getReplicatedLogEntry() {
return replicatedLogEntry;
}
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplyState{" +
+ "identifier='" + identifier + '\'' +
+ ", replicatedLogEntry.index =" + replicatedLogEntry.getIndex() +
+ ", startTime=" + startTime +
+ '}';
+ }
}
// (heartbeat) to each server; repeat during idle periods to
// prevent election timeouts (§5.2)
sendAppendEntries(0, false);
+
+ // It is important to schedule this heartbeat here
+ scheduleHeartBeat(context.getConfigParams().getHeartBeatInterval());
}
/**
return this;
}
+ if(followerLogInformation.timeSinceLastActivity() >
+ context.getConfigParams().getElectionTimeOutInterval().toMillis()) {
+ LOG.error("{} : handleAppendEntriesReply delayed beyond election timeout, " +
+ "appendEntriesReply : {}, timeSinceLastActivity : {}, lastApplied : {}, commitIndex : {}",
+ logName(), appendEntriesReply, followerLogInformation.timeSinceLastActivity(),
+ context.getLastApplied(), context.getCommitIndex());
+ }
+
followerLogInformation.markFollowerActive();
if (appendEntriesReply.isSuccess()) {
return this;
}
+ protected void beforeSendHeartbeat(){}
+
@Override
public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
Preconditions.checkNotNull(sender, "sender should not be null");
}
}
- try {
- if (message instanceof SendHeartBeat) {
- sendHeartBeat();
- return this;
+ if (message instanceof SendHeartBeat) {
+ beforeSendHeartbeat();
+ sendHeartBeat();
+ scheduleHeartBeat(context.getConfigParams().getHeartBeatInterval());
+ return this;
- } else if(message instanceof SendInstallSnapshot) {
- // received from RaftActor
- setSnapshot(Optional.of(((SendInstallSnapshot) message).getSnapshot()));
- sendInstallSnapshot();
+ } else if(message instanceof SendInstallSnapshot) {
+ // received from RaftActor
+ setSnapshot(Optional.of(((SendInstallSnapshot) message).getSnapshot()));
+ sendInstallSnapshot();
- } else if (message instanceof Replicate) {
- replicate((Replicate) message);
+ } else if (message instanceof Replicate) {
+ replicate((Replicate) message);
- } else if (message instanceof InstallSnapshotReply){
- handleInstallSnapshotReply((InstallSnapshotReply) message);
+ } else if (message instanceof InstallSnapshotReply){
+ handleInstallSnapshotReply((InstallSnapshotReply) message);
- }
- } finally {
- scheduleHeartBeat(context.getConfigParams().getHeartBeatInterval());
}
+
return super.handleMessage(sender, message);
}
import akka.actor.ActorRef;
import com.google.common.annotations.VisibleForTesting;
-import com.google.protobuf.ByteString;
import java.util.ArrayList;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftState;
}
}
- @Override public void close() throws Exception {
+ @Override
+ public void close() throws Exception {
stopElection();
}
@VisibleForTesting
- ByteString getSnapshotChunksCollected(){
- return snapshotTracker != null ? snapshotTracker.getCollectedChunks() : ByteString.EMPTY;
+ SnapshotTracker getSnapshotTracker(){
+ return snapshotTracker;
}
}
package org.opendaylight.controller.cluster.raft.behaviors;
import akka.actor.ActorRef;
-import akka.actor.Cancellable;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.base.messages.IsolatedLeaderCheck;
-import scala.concurrent.duration.FiniteDuration;
/**
* The behavior of a RaftActor when it is in the Leader state
* set commitIndex = N (§5.3, §5.4).
*/
public class Leader extends AbstractLeader {
- private Cancellable installSnapshotSchedule = null;
- private Cancellable isolatedLeaderCheckSchedule = null;
+ private static final IsolatedLeaderCheck ISOLATED_LEADER_CHECK = new IsolatedLeaderCheck();
+ private final Stopwatch isolatedLeaderCheck;
public Leader(RaftActorContext context) {
super(context);
-
- scheduleIsolatedLeaderCheck(
- new FiniteDuration(context.getConfigParams().getHeartBeatInterval().length() * 10,
- context.getConfigParams().getHeartBeatInterval().unit()));
+ isolatedLeaderCheck = Stopwatch.createStarted();
}
@Override public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
if (originalMessage instanceof IsolatedLeaderCheck) {
if (isLeaderIsolated()) {
- LOG.info("{}: At least {} followers need to be active, Switching {} from Leader to IsolatedLeader",
+ LOG.warn("{}: At least {} followers need to be active, Switching {} from Leader to IsolatedLeader",
context.getId(), minIsolatedLeaderPeerCount, leaderId);
+
return switchBehavior(new IsolatedLeader(context));
}
}
return super.handleMessage(sender, originalMessage);
}
- protected void stopIsolatedLeaderCheckSchedule() {
- if (isolatedLeaderCheckSchedule != null && !isolatedLeaderCheckSchedule.isCancelled()) {
- isolatedLeaderCheckSchedule.cancel();
+ @Override
+ protected void beforeSendHeartbeat(){
+ if(isolatedLeaderCheck.elapsed(TimeUnit.MILLISECONDS) > context.getConfigParams().getIsolatedCheckIntervalInMillis()){
+ context.getActor().tell(ISOLATED_LEADER_CHECK, context.getActor());
+ isolatedLeaderCheck.reset().start();
}
- }
- protected void scheduleIsolatedLeaderCheck(FiniteDuration isolatedCheckInterval) {
- isolatedLeaderCheckSchedule = context.getActorSystem().scheduler().schedule(isolatedCheckInterval, isolatedCheckInterval,
- context.getActor(), new IsolatedLeaderCheck(),
- context.getActorSystem().dispatcher(), context.getActor());
}
@Override
public void close() throws Exception {
- stopIsolatedLeaderCheckSchedule();
super.close();
}
public <T extends Object> Object toSerializable(){
InstallSnapshotMessages.InstallSnapshot.Builder builder = InstallSnapshotMessages.InstallSnapshot.newBuilder()
+ .setTerm(this.getTerm())
.setLeaderId(this.getLeaderId())
.setChunkIndex(this.getChunkIndex())
.setData(this.getData())
* Identifier of the actor whose election term information this is
*/
private final String id = id1;
- private long currentTerm = 0;
+ private long currentTerm = 1;
private String votedFor = "";
@Override
public void initReplicatedLog(){
this.replicatedLog = new SimpleReplicatedLog();
- this.replicatedLog.append(new MockReplicatedLogEntry(1, 0, new MockPayload("1")));
- this.replicatedLog.append(new MockReplicatedLogEntry(1, 1, new MockPayload("2")));
+ long term = getTermInformation().getCurrentTerm();
+ this.replicatedLog.append(new MockReplicatedLogEntry(term, 0, new MockPayload("1")));
+ this.replicatedLog.append(new MockReplicatedLogEntry(term, 1, new MockPayload("2")));
}
@Override public ActorRef actorOf(Props props) {
}
@Override
+ // FIXME : A lot of tests try to manipulate the replicated log by setting it using this method
+ // This is OK to do if the underlyingActor is not RafActor or a derived class. If not then you should not
+ // used this way to manipulate the log because the RaftActor actually has a field replicatedLog
+ // which it creates internally and sets on the RaftActorContext
+ // The only right way to manipulate the replicated log therefore is to get it from either the RaftActor
+ // or the RaftActorContext and modify the entries in there instead of trying to replace it by using this setter
+ // Simple assertion that will fail if you do so
+ // ReplicatedLog log = new ReplicatedLogImpl();
+ // raftActor.underlyingActor().getRaftActorContext().setReplicatedLog(log);
+ // assertEquals(log, raftActor.underlyingActor().getReplicatedLog())
public void setReplicatedLog(ReplicatedLog replicatedLog) {
this.replicatedLog = replicatedLog;
}
public String toString() {
return value;
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MockPayload other = (MockPayload) obj;
+ if (value == null) {
+ if (other.value != null) {
+ return false;
+ }
+ } else if (!value.equals(other.value)) {
+ return false;
+ }
+ return true;
+ }
}
public static class MockReplicatedLogEntry implements ReplicatedLogEntry, Serializable {
public int size() {
return getData().size();
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((data == null) ? 0 : data.hashCode());
+ result = prime * result + (int) (index ^ (index >>> 32));
+ result = prime * result + (int) (term ^ (term >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MockReplicatedLogEntry other = (MockReplicatedLogEntry) obj;
+ if (data == null) {
+ if (other.data != null) {
+ return false;
+ }
+ } else if (!data.equals(other.data)) {
+ return false;
+ }
+ if (index != other.index) {
+ return false;
+ }
+ if (term != other.term) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("MockReplicatedLogEntry [term=").append(term).append(", index=").append(index)
+ .append(", data=").append(data).append("]");
+ return builder.toString();
+ }
}
public static class MockReplicatedLogBuilder {
assertNotEquals("voted for", "foobar", mockRaftActor.getRaftActorContext().getTermInformation().getVotedFor());
mockRaftActor.onReceiveRecover(mock(RecoveryCompleted.class));
-
}};
}
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
mockRaftActor.getRaftActorContext().getTermInformation().updateAndPersist(10, "foobar");
assertEquals("Persist called", true, persistLatch.await(5, TimeUnit.SECONDS));
-
}
-
};
}
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
MockRaftActorContext.MockReplicatedLogEntry logEntry = new MockRaftActorContext.MockReplicatedLogEntry(10, 10, mock(Payload.class));
mockRaftActor.getRaftActorContext().getReplicatedLog().appendAndPersist(logEntry);
verify(dataPersistenceProvider).persist(eq(logEntry), any(Procedure.class));
-
}
-
};
}
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
mockRaftActor.getReplicatedLog().appendAndPersist(new MockRaftActorContext.MockReplicatedLogEntry(1, 0, mock(Payload.class)));
mockRaftActor.getRaftActorContext().getReplicatedLog().removeFromAndPersist(0);
verify(dataPersistenceProvider, times(2)).persist(anyObject(), any(Procedure.class));
-
}
-
};
}
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
mockRaftActor.onReceiveCommand(new ApplyLogEntries(10));
verify(dataPersistenceProvider, times(1)).persist(anyObject(), any(Procedure.class));
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
ByteString snapshotBytes = fromObject(Arrays.asList(
new MockRaftActorContext.MockPayload("A"),
new MockRaftActorContext.MockPayload("B"),
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
mockRaftActor.getReplicatedLog().append(new MockRaftActorContext.MockReplicatedLogEntry(1, 0, mock(Payload.class)));
mockRaftActor.getReplicatedLog().append(new MockRaftActorContext.MockReplicatedLogEntry(1, 1, mock(Payload.class)));
mockRaftActor.getReplicatedLog().append(new MockRaftActorContext.MockReplicatedLogEntry(1, 2, mock(Payload.class)));
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
ReplicatedLogEntry entry = new MockRaftActorContext.MockReplicatedLogEntry(1, 5,
new MockRaftActorContext.MockPayload("F"));
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
ReplicatedLog oldReplicatedLog = mockRaftActor.getReplicatedLog();
oldReplicatedLog.append(new MockRaftActorContext.MockReplicatedLogEntry(1, 0, mock(Payload.class)));
MockRaftActor mockRaftActor = mockActorRef.underlyingActor();
+ mockRaftActor.waitForInitializeBehaviorComplete();
+
ByteString snapshotBytes = fromObject(Arrays.asList(
new MockRaftActorContext.MockPayload("A"),
new MockRaftActorContext.MockPayload("B"),
public void testRaftRoleChangeNotifier() throws Exception {
new JavaTestKit(getSystem()) {{
ActorRef notifierActor = factory.createActor(Props.create(MessageCollectorActor.class));
+ MessageCollectorActor.waitUntilReady(notifierActor);
+
DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
+ long heartBeatInterval = 100;
+ config.setHeartBeatInterval(FiniteDuration.create(heartBeatInterval, TimeUnit.MILLISECONDS));
+ config.setElectionTimeoutFactor(1);
+
String persistenceId = factory.generateActorId("notifier-");
factory.createTestActor(MockRaftActor.props(persistenceId,
Collections.<String, String>emptyMap(), Optional.<ConfigParams>of(config), notifierActor), persistenceId);
- // sleeping for a minimum of 2 seconds, if it spans more its fine.
- Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
+ List<RoleChanged> matches = null;
+ for(int i = 0; i < 5000 / heartBeatInterval; i++) {
+ matches = MessageCollectorActor.getAllMatching(notifierActor, RoleChanged.class);
+ assertNotNull(matches);
+ if(matches.size() == 3) {
+ break;
+ }
+ Uninterruptibles.sleepUninterruptibly(heartBeatInterval, TimeUnit.MILLISECONDS);
+ }
- List<Object> matches = MessageCollectorActor.getAllMatching(notifierActor, RoleChanged.class);
- assertNotNull(matches);
assertEquals(3, matches.size());
// check if the notifier got a role change from null to Follower
- RoleChanged raftRoleChanged = (RoleChanged) matches.get(0);
+ RoleChanged raftRoleChanged = matches.get(0);
assertEquals(persistenceId, raftRoleChanged.getMemberId());
assertNull(raftRoleChanged.getOldRole());
assertEquals(RaftState.Follower.name(), raftRoleChanged.getNewRole());
// check if the notifier got a role change from Follower to Candidate
- raftRoleChanged = (RoleChanged) matches.get(1);
+ raftRoleChanged = matches.get(1);
assertEquals(persistenceId, raftRoleChanged.getMemberId());
assertEquals(RaftState.Follower.name(), raftRoleChanged.getOldRole());
assertEquals(RaftState.Candidate.name(), raftRoleChanged.getNewRole());
// check if the notifier got a role change from Candidate to Leader
- raftRoleChanged = (RoleChanged) matches.get(2);
+ raftRoleChanged = matches.get(2);
assertEquals(persistenceId, raftRoleChanged.getMemberId());
assertEquals(RaftState.Candidate.name(), raftRoleChanged.getOldRole());
assertEquals(RaftState.Leader.name(), raftRoleChanged.getNewRole());
Map<String, String> peerAddresses = new HashMap<>();
peerAddresses.put(follower1Id, followerActor1.path().toString());
- TestActorRef<MockRaftActor> mockActorRef = TestActorRef.create(getSystem(),
+ TestActorRef<MockRaftActor> mockActorRef = factory.createTestActor(
MockRaftActor.props(persistenceId, peerAddresses,
Optional.<ConfigParams>of(config), dataPersistenceProvider), persistenceId);
MockRaftActor leaderActor = mockActorRef.underlyingActor();
+
leaderActor.getRaftActorContext().setCommitIndex(4);
leaderActor.getRaftActorContext().setLastApplied(4);
leaderActor.getRaftActorContext().getTermInformation().update(1, persistenceId);
Map<String, String> peerAddresses = new HashMap<>();
peerAddresses.put(leaderId, leaderActor1.path().toString());
- TestActorRef<MockRaftActor> mockActorRef = TestActorRef.create(getSystem(),
+ TestActorRef<MockRaftActor> mockActorRef = factory.createTestActor(
MockRaftActor.props(persistenceId, peerAddresses,
Optional.<ConfigParams>of(config), dataPersistenceProvider), persistenceId);
};
}
+
+ private static class NonPersistentProvider implements DataPersistenceProvider {
+ @Override
+ public boolean isRecoveryApplicable() {
+ return false;
+ }
+
+ @Override
+ public <T> void persist(T o, Procedure<T> procedure) {
+ try {
+ procedure.apply(o);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void saveSnapshot(Object o) {
+
+ }
+
+ @Override
+ public void deleteSnapshots(SnapshotSelectionCriteria criteria) {
+
+ }
+
+ @Override
+ public void deleteMessages(long sequenceNumber) {
+
+ }
+ }
+
+ @Test
+ public void testRealSnapshotWhenReplicatedToAllIndexMinusOne() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ String persistenceId = factory.generateActorId("leader-");
+ DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
+ config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS));
+ config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS));
+ config.setSnapshotBatchCount(5);
+
+ DataPersistenceProvider dataPersistenceProvider = new NonPersistentProvider();
+
+ Map<String, String> peerAddresses = new HashMap<>();
+
+ TestActorRef<MockRaftActor> mockActorRef = factory.createTestActor(
+ MockRaftActor.props(persistenceId, peerAddresses,
+ Optional.<ConfigParams>of(config), dataPersistenceProvider), persistenceId);
+
+ MockRaftActor leaderActor = mockActorRef.underlyingActor();
+ leaderActor.getRaftActorContext().setCommitIndex(3);
+ leaderActor.getRaftActorContext().setLastApplied(3);
+ leaderActor.getRaftActorContext().getTermInformation().update(1, persistenceId);
+
+ leaderActor.waitForInitializeBehaviorComplete();
+ for(int i=0;i< 4;i++) {
+ leaderActor.getReplicatedLog()
+ .append(new MockRaftActorContext.MockReplicatedLogEntry(1, i,
+ new MockRaftActorContext.MockPayload("A")));
+ }
+
+ Leader leader = new Leader(leaderActor.getRaftActorContext());
+ leaderActor.setCurrentBehavior(leader);
+ assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
+
+ // Persist another entry (this will cause a CaptureSnapshot to be triggered
+ leaderActor.persistData(mockActorRef, "x", new MockRaftActorContext.MockPayload("duh"));
+
+ // Now send a CaptureSnapshotReply
+ mockActorRef.tell(new CaptureSnapshotReply(fromObject("foo").toByteArray()), mockActorRef);
+
+ // Trimming log in this scenario is a no-op
+ assertEquals(-1, leaderActor.getReplicatedLog().getSnapshotIndex());
+ assertFalse(leaderActor.getRaftActorContext().isSnapshotCaptureInitiated());
+ assertEquals(-1, leader.getReplicatedToAllIndex());
+
+ }};
+ }
+
+ @Test
+ public void testRealSnapshotWhenReplicatedToAllIndexNotInReplicatedLog() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ String persistenceId = factory.generateActorId("leader-");
+ DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
+ config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS));
+ config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS));
+ config.setSnapshotBatchCount(5);
+
+ DataPersistenceProvider dataPersistenceProvider = new NonPersistentProvider();
+
+ Map<String, String> peerAddresses = new HashMap<>();
+
+ TestActorRef<MockRaftActor> mockActorRef = factory.createTestActor(
+ MockRaftActor.props(persistenceId, peerAddresses,
+ Optional.<ConfigParams>of(config), dataPersistenceProvider), persistenceId);
+
+ MockRaftActor leaderActor = mockActorRef.underlyingActor();
+ leaderActor.getRaftActorContext().setCommitIndex(3);
+ leaderActor.getRaftActorContext().setLastApplied(3);
+ leaderActor.getRaftActorContext().getTermInformation().update(1, persistenceId);
+ leaderActor.getReplicatedLog().setSnapshotIndex(3);
+
+ leaderActor.waitForInitializeBehaviorComplete();
+ Leader leader = new Leader(leaderActor.getRaftActorContext());
+ leaderActor.setCurrentBehavior(leader);
+ leader.setReplicatedToAllIndex(3);
+ assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
+
+ // Persist another entry (this will cause a CaptureSnapshot to be triggered
+ leaderActor.persistData(mockActorRef, "x", new MockRaftActorContext.MockPayload("duh"));
+
+ // Now send a CaptureSnapshotReply
+ mockActorRef.tell(new CaptureSnapshotReply(fromObject("foo").toByteArray()), mockActorRef);
+
+ // Trimming log in this scenario is a no-op
+ assertEquals(3, leaderActor.getReplicatedLog().getSnapshotIndex());
+ assertFalse(leaderActor.getRaftActorContext().isSnapshotCaptureInitiated());
+ assertEquals(3, leader.getReplicatedToAllIndex());
+
+ }};
+ }
+
private ByteString fromObject(Object snapshot) throws Exception {
ByteArrayOutputStream b = null;
ObjectOutputStream o = null;
import akka.actor.ActorSystem;
import akka.actor.PoisonPill;
import akka.actor.Props;
+import akka.testkit.JavaTestKit;
import akka.testkit.TestActorRef;
import java.util.LinkedList;
import java.util.List;
}
@Override
- public void close() throws Exception {
- for(ActorRef actor : createdActors){
- LOG.info("Killing actor {}", actor);
- actor.tell(PoisonPill.getInstance(), null);
- }
+ public void close() {
+ new JavaTestKit(system) {{
+ for(ActorRef actor : createdActors) {
+ watch(actor);
+ LOG.info("Killing actor {}", actor);
+ actor.tell(PoisonPill.getInstance(), ActorRef.noSender());
+ expectTerminated(duration("5 seconds"), actor);
+ }
+ }};
}
}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.raft.behaviors;
+
+import static org.junit.Assert.assertTrue;
+import akka.actor.ActorRef;
+import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
+import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
+import org.opendaylight.controller.cluster.raft.base.messages.SendHeartBeat;
+import org.opendaylight.controller.cluster.raft.utils.ForwardMessageToBehaviorActor;
+import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
+import scala.concurrent.duration.FiniteDuration;
+
+public abstract class AbstractLeaderTest extends AbstractRaftActorBehaviorTest{
+
+ /**
+ * When we removed scheduling of heartbeat in the AbstractLeader constructor we ended up with a situation where
+ * if no follower responded to an initial AppendEntries heartbeats would not be sent to it. This test verifies
+ * that regardless of whether followers respond or not we schedule heartbeats.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testLeaderSchedulesHeartbeatsEvenWhenNoFollowersRespondToInitialAppendEntries() throws Exception {
+ logStart("testLeaderSchedulesHeartbeatsEvenWhenNoFollowersRespondToInitialAppendEntries");
+ new JavaTestKit(getSystem()) {{
+ String leaderActorId = actorFactory.generateActorId("leader");
+ String follower1ActorId = actorFactory.generateActorId("follower");
+ String follower2ActorId = actorFactory.generateActorId("follower");
+
+ TestActorRef<ForwardMessageToBehaviorActor> leaderActor =
+ actorFactory.createTestActor(ForwardMessageToBehaviorActor.props(), leaderActorId);
+ ActorRef follower1Actor = actorFactory.createActor(MessageCollectorActor.props(), follower1ActorId);
+ ActorRef follower2Actor = actorFactory.createActor(MessageCollectorActor.props(), follower2ActorId);
+
+ MockRaftActorContext leaderActorContext =
+ new MockRaftActorContext(leaderActorId, getSystem(), leaderActor);
+
+ DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
+ configParams.setHeartBeatInterval(new FiniteDuration(200, TimeUnit.MILLISECONDS));
+ configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
+
+ leaderActorContext.setConfigParams(configParams);
+
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(1,5,1).build());
+
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put(follower1ActorId,
+ follower1Actor.path().toString());
+ peerAddresses.put(follower2ActorId,
+ follower2Actor.path().toString());
+
+
+ leaderActorContext.setPeerAddresses(peerAddresses);
+
+ RaftActorBehavior leader = createBehavior(leaderActorContext);
+
+ leaderActor.underlyingActor().setBehavior(leader);
+
+ Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
+
+ List<SendHeartBeat> allMessages = MessageCollectorActor.getAllMatching(leaderActor, SendHeartBeat.class);
+
+ // Need more than 1 heartbeat to be delivered because we waited for 1 second with heartbeat interval 200ms
+ assertTrue(String.format("%s messages is less than expected", allMessages.size()),
+ allMessages.size() > 1);
+
+ }};
+ }
+
+}
package org.opendaylight.controller.cluster.raft.behaviors;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import akka.actor.ActorRef;
import akka.actor.Props;
-import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
+import com.google.protobuf.ByteString;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import org.junit.After;
+import org.junit.Assert;
import org.junit.Test;
import org.opendaylight.controller.cluster.raft.AbstractActorTest;
import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
+import org.opendaylight.controller.cluster.raft.RaftState;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
import org.opendaylight.controller.cluster.raft.SerializationUtils;
+import org.opendaylight.controller.cluster.raft.TestActorFactory;
import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
import org.opendaylight.controller.cluster.raft.messages.RequestVote;
import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
+import org.slf4j.LoggerFactory;
public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
- private final ActorRef behaviorActor = getSystem().actorOf(Props.create(
- DoNothingActor.class));
+ protected final TestActorFactory actorFactory = new TestActorFactory(getSystem());
+
+ private final TestActorRef<MessageCollectorActor> behaviorActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("behavior"));
+
+ RaftActorBehavior behavior;
+
+ @After
+ public void tearDown() throws Exception {
+ if(behavior != null) {
+ behavior.close();
+ }
+
+ actorFactory.close();
+ }
/**
* This test checks that when a new Raft RPC message is received with a newer
*/
@Test
public void testHandleRaftRPCWithNewerTerm() throws Exception {
- new JavaTestKit(getSystem()) {{
+ RaftActorContext actorContext = createActorContext();
- assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(),
+ assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor,
createAppendEntriesWithNewerTerm());
- assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(),
+ assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor,
createAppendEntriesReplyWithNewerTerm());
- assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(),
+ assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor,
createRequestVoteWithNewerTerm());
- assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(),
+ assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor,
createRequestVoteReplyWithNewerTerm());
-
-
- }};
}
* @throws Exception
*/
@Test
- public void testHandleAppendEntriesSenderTermLessThanReceiverTerm()
- throws Exception {
- new JavaTestKit(getSystem()) {{
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
+ public void testHandleAppendEntriesSenderTermLessThanReceiverTerm() throws Exception {
+ MockRaftActorContext context = createActorContext();
// First set the receivers term to a high number (1000)
context.getTermInformation().update(1000, "test");
- AppendEntries appendEntries =
- new AppendEntries(100, "leader-1", 0, 0, null, 101, -1);
+ AppendEntries appendEntries = new AppendEntries(100, "leader-1", 0, 0, null, 101, -1);
- RaftActorBehavior behavior = createBehavior(context);
+ behavior = createBehavior(context);
// Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ RaftActorBehavior expected = behavior.handleMessage(behaviorActor, "unknown");
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ RaftActorBehavior raftBehavior = behavior.handleMessage(behaviorActor, appendEntries);
- assertEquals(expected, raftBehavior);
+ assertEquals("Raft state", expected.state(), raftBehavior.state());
// Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
-
-
- }};
- }
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(
+ behaviorActor, AppendEntriesReply.class);
- @Test
- public void testHandleAppendEntriesAddSameEntryToLog(){
- new JavaTestKit(getSystem()) {
- {
+ assertEquals("isSuccess", false, reply.isSuccess());
+ }
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
- // First set the receivers term to lower number
- context.getTermInformation().update(2, "test");
+ @Test
+ public void testHandleAppendEntriesAddSameEntryToLog() throws Exception {
+ MockRaftActorContext context = createActorContext();
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
+ context.getTermInformation().update(2, "test");
- context.setReplicatedLog(log);
+ // Prepare the receivers log
+ MockRaftActorContext.MockPayload payload = new MockRaftActorContext.MockPayload("zero");
+ setLastLogEntry(context, 2, 0, payload);
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(new MockRaftActorContext.MockReplicatedLogEntry(2, 0, payload));
- AppendEntries appendEntries =
- new AppendEntries(2, "leader-1", -1, 1, entries, 0, -1);
+ AppendEntries appendEntries = new AppendEntries(2, "leader-1", -1, -1, entries, 2, -1);
- RaftActorBehavior behavior = createBehavior(context);
+ behavior = createBehavior(context);
- if (AbstractRaftActorBehaviorTest.this instanceof CandidateTest) {
- // Resetting the Candidates term to make sure it will match
- // the term sent by AppendEntries. If this was not done then
- // the test will fail because the Candidate will assume that
- // the message was sent to it from a lower term peer and will
- // thus respond with a failure
- context.getTermInformation().update(2, "test");
- }
+ if (behavior instanceof Candidate) {
+ // Resetting the Candidates term to make sure it will match
+ // the term sent by AppendEntries. If this was not done then
+ // the test will fail because the Candidate will assume that
+ // the message was sent to it from a lower term peer and will
+ // thus respond with a failure
+ context.getTermInformation().update(2, "test");
+ }
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ // Send an unknown message so that the state of the RaftActor remains unchanged
+ RaftActorBehavior expected = behavior.handleMessage(behaviorActor, "unknown");
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ RaftActorBehavior raftBehavior = behavior.handleMessage(behaviorActor, appendEntries);
- assertEquals(expected, raftBehavior);
+ assertEquals("Raft state", expected.state(), raftBehavior.state());
- assertEquals(1, log.size());
+ assertEquals("ReplicatedLog size", 1, context.getReplicatedLog().size());
+ handleAppendEntriesAddSameEntryToLogReply(behaviorActor);
+ }
- }};
+ protected void handleAppendEntriesAddSameEntryToLogReply(TestActorRef<MessageCollectorActor> replyActor)
+ throws Exception {
+ AppendEntriesReply reply = MessageCollectorActor.getFirstMatching(replyActor, AppendEntriesReply.class);
+ Assert.assertNull("Expected no AppendEntriesReply", reply);
}
/**
* This test verifies that when a RequestVote is received by the RaftActor
- * with a term which is greater than the RaftActors' currentTerm and the
- * senders' log is more upto date than the receiver that the receiver grants
- * the vote to the sender
+ * with the senders' log is more up to date than the receiver that the receiver grants
+ * the vote to the sender.
*/
@Test
- public void testHandleRequestVoteWhenSenderTermGreaterThanCurrentTermAndSenderLogMoreUpToDate() {
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorBehavior behavior = createBehavior(
- createActorContext(behaviorActor));
-
- RaftActorBehavior raftBehavior = behavior.handleMessage(getTestActor(),
- new RequestVote(1000, "test", 10000, 999));
-
- if(!(behavior instanceof Follower)){
- assertTrue(raftBehavior instanceof Follower);
- } else {
-
- final Boolean out =
- new ExpectMsg<Boolean>(duration("1 seconds"),
- "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply =
- (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
- }
- }
- };
- }};
+ public void testHandleRequestVoteWhenSenderLogMoreUpToDate() {
+ MockRaftActorContext context = createActorContext();
+
+ behavior = createBehavior(context);
+
+ context.getTermInformation().update(1, "test");
+
+ behavior.handleMessage(behaviorActor, new RequestVote(context.getTermInformation().getCurrentTerm(),
+ "test", 10000, 999));
+
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor,
+ RequestVoteReply.class);
+ assertEquals("isVoteGranted", true, reply.isVoteGranted());
}
/**
* log then the receiving RaftActor will not grant the vote to the sender
*/
@Test
- public void testHandleRequestVoteWhenSenderTermGreaterThanCurrentTermButSenderLogLessUptoDate() {
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext actorContext =
- createActorContext(behaviorActor);
-
- MockRaftActorContext.SimpleReplicatedLog
- log = new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(20000,
- 1000000, new MockRaftActorContext.MockPayload("")));
-
- ((MockRaftActorContext) actorContext).setReplicatedLog(log);
-
- RaftActorBehavior behavior = createBehavior(actorContext);
-
- RaftActorBehavior raftBehavior = behavior.handleMessage(getTestActor(),
- new RequestVote(1000, "test", 10000, 999));
-
- if(!(behavior instanceof Follower)){
- assertTrue(raftBehavior instanceof Follower);
- } else {
- final Boolean out =
- new ExpectMsg<Boolean>(duration("1 seconds"),
- "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply =
- (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
- }
- }
- };
- }};
+ public void testHandleRequestVoteWhenSenderLogLessUptoDate() {
+ MockRaftActorContext context = createActorContext();
+
+ behavior = createBehavior(context);
+
+ context.getTermInformation().update(1, "test");
+
+ int index = 2000;
+ setLastLogEntry(context, context.getTermInformation().getCurrentTerm(), index,
+ new MockRaftActorContext.MockPayload(""));
+
+ behavior.handleMessage(behaviorActor, new RequestVote(
+ context.getTermInformation().getCurrentTerm(), "test",
+ index - 1, context.getTermInformation().getCurrentTerm()));
+
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor,
+ RequestVoteReply.class);
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
}
*/
@Test
public void testHandleRequestVoteWhenSenderTermLessThanCurrentTerm() {
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext context =
- createActorContext(behaviorActor);
-
- context.getTermInformation().update(1000, null);
-
- RaftActorBehavior follower = createBehavior(context);
-
- follower.handleMessage(getTestActor(),
- new RequestVote(999, "test", 10000, 999));
-
- final Boolean out =
- new ExpectMsg<Boolean>(duration("1 seconds"),
- "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply =
- (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
- }
- };
- }};
+ RaftActorContext context = createActorContext();
+
+ context.getTermInformation().update(1000, null);
+
+ behavior = createBehavior(context);
+
+ behavior.handleMessage(behaviorActor, new RequestVote(999, "test", 10000, 999));
+
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor,
+ RequestVoteReply.class);
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
}
@Test
}
- protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(
- ActorRef actorRef, RaftRPC rpc) {
+ protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
+ ActorRef actorRef, RaftRPC rpc) throws Exception {
- RaftActorContext actorContext = createActorContext();
Payload p = new MockRaftActorContext.MockPayload("");
- setLastLogEntry(
- (MockRaftActorContext) actorContext, 0, 0, p);
+ setLastLogEntry((MockRaftActorContext) actorContext, 1, 0, p);
+ actorContext.getTermInformation().update(1, "test");
+
+ RaftActorBehavior origBehavior = createBehavior(actorContext);
+ RaftActorBehavior raftBehavior = origBehavior.handleMessage(actorRef, rpc);
- RaftActorBehavior raftBehavior = createBehavior(actorContext)
- .handleMessage(actorRef, rpc);
+ assertEquals("New raft state", RaftState.Follower, raftBehavior.state());
+ assertEquals("New election term", rpc.getTerm(), actorContext.getTermInformation().getCurrentTerm());
- assertTrue(raftBehavior instanceof Follower);
+ origBehavior.close();
+ raftBehavior.close();
}
protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry(
new MockRaftActorContext.MockReplicatedLogEntry(term, index, data));
}
- protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry(
- MockRaftActorContext actorContext, ReplicatedLogEntry logEntry) {
- MockRaftActorContext.SimpleReplicatedLog
- log = new MockRaftActorContext.SimpleReplicatedLog();
+ protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry(MockRaftActorContext actorContext,
+ ReplicatedLogEntry logEntry) {
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
log.append(logEntry);
actorContext.setReplicatedLog(log);
return createBehavior(createActorContext());
}
- protected RaftActorContext createActorContext() {
+ protected MockRaftActorContext createActorContext() {
return new MockRaftActorContext();
}
- protected RaftActorContext createActorContext(ActorRef actor) {
+ protected MockRaftActorContext createActorContext(ActorRef actor) {
return new MockRaftActorContext("test", getSystem(), actor);
}
protected Object fromSerializableMessage(Object serializable){
return SerializationUtils.fromSerializable(serializable);
}
+
+ protected ByteString toByteString(Map<String, String> state) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try(ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+ oos.writeObject(state);
+ return ByteString.copyFrom(bos.toByteArray());
+ } catch (IOException e) {
+ throw new AssertionError("IOException occurred converting Map to Bytestring", e);
+ }
+ }
+
+ protected void logStart(String name) {
+ LoggerFactory.getLogger(LeaderTest.class).info("Starting " + name);
+ }
}
package org.opendaylight.controller.cluster.raft.behaviors;
+import static org.junit.Assert.assertEquals;
import akka.actor.ActorRef;
import akka.actor.Props;
-import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import org.junit.Assert;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
+import org.opendaylight.controller.cluster.raft.RaftState;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
+import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
import org.opendaylight.controller.cluster.raft.messages.RequestVote;
import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
-
-import static org.junit.Assert.assertEquals;
+import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
public class CandidateTest extends AbstractRaftActorBehaviorTest {
- private final ActorRef candidateActor = getSystem().actorOf(Props.create(
- DoNothingActor.class));
-
- private final ActorRef peerActor1 = getSystem().actorOf(Props.create(
- DoNothingActor.class));
+ private final TestActorRef<MessageCollectorActor> candidateActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("candidate"));
- private final ActorRef peerActor2 = getSystem().actorOf(Props.create(
- DoNothingActor.class));
+ private TestActorRef<MessageCollectorActor>[] peerActors;
- private final ActorRef peerActor3 = getSystem().actorOf(Props.create(
- DoNothingActor.class));
-
- private final ActorRef peerActor4 = getSystem().actorOf(Props.create(
- DoNothingActor.class));
-
- private final Map<String, String> onePeer = new HashMap<>();
- private final Map<String, String> twoPeers = new HashMap<>();
- private final Map<String, String> fourPeers = new HashMap<>();
+ private RaftActorBehavior candidate;
@Before
public void setUp(){
- onePeer.put(peerActor1.path().toString(),
- peerActor1.path().toString());
-
- twoPeers.put(peerActor1.path().toString(),
- peerActor1.path().toString());
- twoPeers.put(peerActor2.path().toString(),
- peerActor2.path().toString());
-
- fourPeers.put(peerActor1.path().toString(),
- peerActor1.path().toString());
- fourPeers.put(peerActor2.path().toString(),
- peerActor2.path().toString());
- fourPeers.put(peerActor3.path().toString(),
- peerActor3.path().toString());
- fourPeers.put(peerActor4.path().toString(),
- peerActor3.path().toString());
+ }
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if(candidate != null) {
+ candidate.close();
+ }
+ super.tearDown();
}
@Test
RaftActorContext raftActorContext = createActorContext();
long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
- new Candidate(raftActorContext);
+ candidate = new Candidate(raftActorContext);
- assertEquals(expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
- assertEquals(raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
+ assertEquals("getCurrentTerm", expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
+ assertEquals("getVotedFor", raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
}
@Test
public void testThatAnElectionTimeoutIsTriggered(){
- new JavaTestKit(getSystem()) {{
-
- new Within(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6)) {
- protected void run() {
-
- Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
- final Boolean out = new ExpectMsg<Boolean>(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6), "ElectionTimeout") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof ElectionTimeout) {
- return true;
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
- }
- };
- }};
+ MockRaftActorContext actorContext = createActorContext();
+ candidate = new Candidate(actorContext);
+
+ MessageCollectorActor.expectFirstMatching(candidateActor, ElectionTimeout.class,
+ actorContext.getConfigParams().getElectionTimeOutInterval().$times(6).toMillis());
}
@Test
public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
RaftActorContext raftActorContext = createActorContext();
- Candidate candidate =
- new Candidate(raftActorContext);
+ candidate = new Candidate(raftActorContext);
- RaftActorBehavior raftBehavior =
+ RaftActorBehavior newBehavior =
candidate.handleMessage(candidateActor, new ElectionTimeout());
- Assert.assertTrue(raftBehavior instanceof Leader);
+ assertEquals("Behavior", RaftState.Leader, newBehavior.state());
}
@Test
- public void testHandleElectionTimeoutWhenThereAreTwoNodesInCluster(){
- MockRaftActorContext raftActorContext =
- (MockRaftActorContext) createActorContext();
- raftActorContext.setPeerAddresses(onePeer);
- Candidate candidate =
- new Candidate(raftActorContext);
-
- RaftActorBehavior raftBehavior =
- candidate.handleMessage(candidateActor, new ElectionTimeout());
+ public void testHandleElectionTimeoutWhenThereAreTwoNodeCluster(){
+ MockRaftActorContext raftActorContext = createActorContext();
+ raftActorContext.setPeerAddresses(setupPeers(1));
+ candidate = new Candidate(raftActorContext);
+
+ candidate = candidate.handleMessage(candidateActor, new ElectionTimeout());
- Assert.assertTrue(raftBehavior instanceof Candidate);
+ assertEquals("Behavior", RaftState.Candidate, candidate.state());
}
@Test
- public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodesInCluster(){
- MockRaftActorContext raftActorContext =
- (MockRaftActorContext) createActorContext();
- raftActorContext.setPeerAddresses(twoPeers);
- Candidate candidate =
- new Candidate(raftActorContext);
-
- RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
+ public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodeCluster(){
+ MockRaftActorContext raftActorContext = createActorContext();
+ raftActorContext.setPeerAddresses(setupPeers(2));
+ candidate = new Candidate(raftActorContext);
- Assert.assertTrue(behaviorOnFirstVote instanceof Leader);
+ candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, true));
+ assertEquals("Behavior", RaftState.Leader, candidate.state());
}
@Test
- public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodesInCluster(){
- MockRaftActorContext raftActorContext =
- (MockRaftActorContext) createActorContext();
- raftActorContext.setPeerAddresses(fourPeers);
- Candidate candidate =
- new Candidate(raftActorContext);
+ public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodeCluster(){
+ MockRaftActorContext raftActorContext = createActorContext();
+ raftActorContext.setPeerAddresses(setupPeers(4));
+ candidate = new Candidate(raftActorContext);
- RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
+ // First peers denies the vote.
+ candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, false));
- RaftActorBehavior behaviorOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
+ assertEquals("Behavior", RaftState.Candidate, candidate.state());
- Assert.assertTrue(behaviorOnFirstVote instanceof Candidate);
- Assert.assertTrue(behaviorOnSecondVote instanceof Leader);
+ candidate = candidate.handleMessage(peerActors[1], new RequestVoteReply(1, true));
- }
+ assertEquals("Behavior", RaftState.Candidate, candidate.state());
- @Test
- public void testResponseToAppendEntriesWithLowerTerm(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
- candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.<ReplicatedLogEntry>emptyList(), 0, -1));
-
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
- }
- };
- }};
+ candidate = candidate.handleMessage(peerActors[2], new RequestVoteReply(1, true));
+
+ assertEquals("Behavior", RaftState.Leader, candidate.state());
}
@Test
- public void testResponseToRequestVoteWithLowerTerm(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
- candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0));
-
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
- }
- };
- }};
+ public void testResponseToHandleAppendEntriesWithLowerTerm() {
+ candidate = new Candidate(createActorContext());
+
+ setupPeers(1);
+ candidate.handleMessage(peerActors[0], new AppendEntries(1, "test", 0, 0,
+ Collections.<ReplicatedLogEntry>emptyList(), 0, -1));
+
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(
+ peerActors[0], AppendEntriesReply.class);
+ assertEquals("isSuccess", false, reply.isSuccess());
+ assertEquals("getTerm", 2, reply.getTerm());
}
@Test
- public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext context = createActorContext(getTestActor());
-
- context.getTermInformation().update(1000, null);
-
- // Once a candidate is created it will immediately increment the current term so after
- // construction the currentTerm should be 1001
- RaftActorBehavior follower = createBehavior(context);
-
- follower.handleMessage(getTestActor(), new RequestVote(1001, "test", 10000, 999));
-
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
- }
- };
- }};
+ public void testResponseToRequestVoteWithLowerTerm() {
+ candidate = new Candidate(createActorContext());
+
+ setupPeers(1);
+ candidate.handleMessage(peerActors[0], new RequestVote(1, "test", 0, 0));
+
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+ peerActors[0], RequestVoteReply.class);
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
+ assertEquals("getTerm", 2, reply.getTerm());
}
@Test
- public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
- new JavaTestKit(getSystem()) {{
+ public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForMatches() {
+ MockRaftActorContext context = createActorContext();
+ context.getTermInformation().update(1000, null);
- new Within(duration("1 seconds")) {
- protected void run() {
+ // Once a candidate is created it will immediately increment the current term so after
+ // construction the currentTerm should be 1001
+ candidate = new Candidate(context);
- RaftActorContext context = createActorContext(getTestActor());
+ setupPeers(1);
+ candidate.handleMessage(peerActors[0], new RequestVote(1001, context.getId(), 10000, 999));
- context.getTermInformation().update(1000, "test");
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+ peerActors[0], RequestVoteReply.class);
+ assertEquals("isVoteGranted", true, reply.isVoteGranted());
+ assertEquals("getTerm", 1001, reply.getTerm());
+ }
+
+ @Test
+ public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForDoesNotMatch() {
+ MockRaftActorContext context = createActorContext();
+ context.getTermInformation().update(1000, null);
- RaftActorBehavior follower = createBehavior(context);
+ // Once a candidate is created it will immediately increment the current term so after
+ // construction the currentTerm should be 1001
+ candidate = new Candidate(context);
- follower.handleMessage(getTestActor(), new RequestVote(1001, "candidate", 10000, 999));
+ setupPeers(1);
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ // RequestVote candidate ID ("candidate2") does not match this candidate's votedFor
+ // (it votes for itself)
+ candidate.handleMessage(peerActors[0], new RequestVote(1001, "candidate2", 10000, 999));
- assertEquals(false, out);
- }
- };
- }};
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+ peerActors[0], RequestVoteReply.class);
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
+ assertEquals("getTerm", 1001, reply.getTerm());
}
- @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
+ @Override
+ protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
return new Candidate(actorContext);
}
- @Override protected RaftActorContext createActorContext() {
- return new MockRaftActorContext("test", getSystem(), candidateActor);
+ @Override protected MockRaftActorContext createActorContext() {
+ return new MockRaftActorContext("candidate", getSystem(), candidateActor);
}
+ private Map<String, String> setupPeers(int count) {
+ Map<String, String> peerMap = new HashMap<>();
+ peerActors = new TestActorRef[count];
+ for(int i = 0; i < count; i++) {
+ peerActors[i] = actorFactory.createTestActor(Props.create(MessageCollectorActor.class),
+ actorFactory.generateActorId("peer"));
+ peerMap.put("peer" + (i+1), peerActors[i].path().toString());
+ }
+ return peerMap;
+ }
+
+ @Override
+ protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
+ ActorRef actorRef, RaftRPC rpc) throws Exception {
+ super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
+ assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor());
+ }
}
package org.opendaylight.controller.cluster.raft.behaviors;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import akka.actor.ActorRef;
import akka.actor.Props;
-import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
import com.google.protobuf.ByteString;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import org.junit.After;
+import org.junit.Assert;
import org.junit.Test;
-import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.Snapshot;
import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot;
import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot;
import org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply;
+import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
import org.opendaylight.controller.cluster.raft.messages.RequestVote;
import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
public class FollowerTest extends AbstractRaftActorBehaviorTest {
- private final ActorRef followerActor = getSystem().actorOf(Props.create(
- DoNothingActor.class));
+ private final TestActorRef<MessageCollectorActor> followerActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("follower"));
+ private final TestActorRef<MessageCollectorActor> leaderActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("leader"));
- @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
+ private RaftActorBehavior follower;
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if(follower != null) {
+ follower.close();
+ }
+
+ super.tearDown();
+ }
+
+ @Override
+ protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
return new Follower(actorContext);
}
- @Override protected RaftActorContext createActorContext() {
+ @Override
+ protected MockRaftActorContext createActorContext() {
return createActorContext(followerActor);
}
- protected RaftActorContext createActorContext(ActorRef actorRef){
- return new MockRaftActorContext("test", getSystem(), actorRef);
+ @Override
+ protected MockRaftActorContext createActorContext(ActorRef actorRef){
+ return new MockRaftActorContext("follower", getSystem(), actorRef);
}
@Test
public void testThatAnElectionTimeoutIsTriggered(){
- new JavaTestKit(getSystem()) {{
-
- new Within(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6)) {
- protected void run() {
-
- Follower follower = new Follower(createActorContext(getTestActor()));
-
- final Boolean out = new ExpectMsg<Boolean>(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6), "ElectionTimeout") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof ElectionTimeout) {
- return true;
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
- }
- };
- }};
+ MockRaftActorContext actorContext = createActorContext();
+ follower = new Follower(actorContext);
+
+ MessageCollectorActor.expectFirstMatching(followerActor, ElectionTimeout.class,
+ actorContext.getConfigParams().getElectionTimeOutInterval().$times(6).toMillis());
}
@Test
public void testHandleElectionTimeout(){
- RaftActorContext raftActorContext = createActorContext();
- Follower follower =
- new Follower(raftActorContext);
+ logStart("testHandleElectionTimeout");
- RaftActorBehavior raftBehavior =
- follower.handleMessage(followerActor, new ElectionTimeout());
+ follower = new Follower(createActorContext());
+
+ RaftActorBehavior raftBehavior = follower.handleMessage(followerActor, new ElectionTimeout());
assertTrue(raftBehavior instanceof Candidate);
}
@Test
public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext context = createActorContext(getTestActor());
+ logStart("testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull");
- context.getTermInformation().update(1000, null);
+ RaftActorContext context = createActorContext();
+ long term = 1000;
+ context.getTermInformation().update(term, null);
- RaftActorBehavior follower = createBehavior(context);
+ follower = createBehavior(context);
- follower.handleMessage(getTestActor(), new RequestVote(1000, "test", 10000, 999));
+ follower.handleMessage(leaderActor, new RequestVote(term, "test", 10000, 999));
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, RequestVoteReply.class);
- assertEquals(true, out);
- }
- };
- }};
+ assertEquals("isVoteGranted", true, reply.isVoteGranted());
+ assertEquals("getTerm", term, reply.getTerm());
}
@Test
public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext context = createActorContext(getTestActor());
+ logStart("testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId");
- context.getTermInformation().update(1000, "test");
+ RaftActorContext context = createActorContext();
+ long term = 1000;
+ context.getTermInformation().update(term, "test");
- RaftActorBehavior follower = createBehavior(context);
+ follower = createBehavior(context);
- follower.handleMessage(getTestActor(), new RequestVote(1000, "candidate", 10000, 999));
+ follower.handleMessage(leaderActor, new RequestVote(term, "candidate", 10000, 999));
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, RequestVoteReply.class);
- assertEquals(false, out);
- }
- };
- }};
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
}
/**
*/
@Test
public void testHandleAppendEntriesWithNewerCommitIndex() throws Exception {
- new JavaTestKit(getSystem()) {{
+ logStart("testHandleAppendEntriesWithNewerCommitIndex");
- RaftActorContext context =
- createActorContext();
+ MockRaftActorContext context = createActorContext();
- context.setLastApplied(100);
- setLastLogEntry((MockRaftActorContext) context, 1, 100,
+ context.setLastApplied(100);
+ setLastLogEntry(context, 1, 100,
new MockRaftActorContext.MockPayload(""));
- ((MockRaftActorContext) context).getReplicatedLog().setSnapshotIndex(99);
+ context.getReplicatedLog().setSnapshotIndex(99);
- List<ReplicatedLogEntry> entries =
- Arrays.asList(
- (ReplicatedLogEntry) new MockRaftActorContext.MockReplicatedLogEntry(2, 101,
- new MockRaftActorContext.MockPayload("foo"))
- );
+ List<ReplicatedLogEntry> entries = Arrays.<ReplicatedLogEntry>asList(
+ newReplicatedLogEntry(2, 101, "foo"));
- // The new commitIndex is 101
- AppendEntries appendEntries =
- new AppendEntries(2, "leader-1", 100, 1, entries, 101, 100);
+ // The new commitIndex is 101
+ AppendEntries appendEntries = new AppendEntries(2, "leader-1", 100, 1, entries, 101, 100);
- RaftActorBehavior raftBehavior =
- createBehavior(context).handleMessage(getRef(), appendEntries);
+ follower = createBehavior(context);
+ follower.handleMessage(leaderActor, appendEntries);
- assertEquals(101L, context.getLastApplied());
-
- }};
+ assertEquals("getLastApplied", 101L, context.getLastApplied());
}
/**
* @throws Exception
*/
@Test
- public void testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm()
- throws Exception {
- new JavaTestKit(getSystem()) {{
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
-
- // First set the receivers term to lower number
- context.getTermInformation().update(95, "test");
-
- // Set the last log entry term for the receiver to be greater than
- // what we will be sending as the prevLogTerm in AppendEntries
- MockRaftActorContext.SimpleReplicatedLog mockReplicatedLog =
- setLastLogEntry(context, 20, 0, new MockRaftActorContext.MockPayload(""));
-
- // AppendEntries is now sent with a bigger term
- // this will set the receivers term to be the same as the sender's term
- AppendEntries appendEntries =
- new AppendEntries(100, "leader-1", 0, 0, null, 101, -1);
+ public void testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm() {
+ logStart("testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm");
- RaftActorBehavior behavior = createBehavior(context);
+ MockRaftActorContext context = createActorContext();
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ // First set the receivers term to lower number
+ context.getTermInformation().update(95, "test");
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ // AppendEntries is now sent with a bigger term
+ // this will set the receivers term to be the same as the sender's term
+ AppendEntries appendEntries = new AppendEntries(100, "leader", 0, 0, null, 101, -1);
- assertEquals(expected, raftBehavior);
+ follower = createBehavior(context);
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
- assertEquals(false, out);
+ Assert.assertSame(follower, newBehavior);
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor,
+ AppendEntriesReply.class);
- }};
+ assertEquals("isSuccess", false, reply.isSuccess());
}
-
-
/**
* This test verifies that when a new AppendEntries message is received with
* new entries and the logs of the sender and receiver match that the new
* @throws Exception
*/
@Test
- public void testHandleAppendEntriesAddNewEntries() throws Exception {
- new JavaTestKit(getSystem()) {{
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
-
- // First set the receivers term to lower number
- context.getTermInformation().update(1, "test");
-
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 1, new MockRaftActorContext.MockPayload("one")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 2, new MockRaftActorContext.MockPayload("two")));
-
- context.setReplicatedLog(log);
-
- // Prepare the entries to be sent with AppendEntries
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 3, new MockRaftActorContext.MockPayload("three")));
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("four")));
-
- // Send appendEntries with the same term as was set on the receiver
- // before the new behavior was created (1 in this case)
- // This will not work for a Candidate because as soon as a Candidate
- // is created it increments the term
- AppendEntries appendEntries =
- new AppendEntries(1, "leader-1", 2, 1, entries, 4, -1);
-
- RaftActorBehavior behavior = createBehavior(context);
-
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
-
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
-
- assertEquals(expected, raftBehavior);
- assertEquals(5, log.last().getIndex() + 1);
- assertNotNull(log.get(3));
- assertNotNull(log.get(4));
-
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
-
-
- }};
- }
+ public void testHandleAppendEntriesAddNewEntries() {
+ logStart("testHandleAppendEntriesAddNewEntries");
+
+ MockRaftActorContext context = createActorContext();
+
+ // First set the receivers term to lower number
+ context.getTermInformation().update(1, "test");
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
+ log.append(newReplicatedLogEntry(1, 0, "zero"));
+ log.append(newReplicatedLogEntry(1, 1, "one"));
+ log.append(newReplicatedLogEntry(1, 2, "two"));
+ context.setReplicatedLog(log);
+
+ // Prepare the entries to be sent with AppendEntries
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(newReplicatedLogEntry(1, 3, "three"));
+ entries.add(newReplicatedLogEntry(1, 4, "four"));
+
+ // Send appendEntries with the same term as was set on the receiver
+ // before the new behavior was created (1 in this case)
+ // This will not work for a Candidate because as soon as a Candidate
+ // is created it increments the term
+ AppendEntries appendEntries = new AppendEntries(1, "leader-1", 2, 1, entries, 4, -1);
+
+ follower = createBehavior(context);
+
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
+
+ Assert.assertSame(follower, newBehavior);
+
+ assertEquals("Next index", 5, log.last().getIndex() + 1);
+ assertEquals("Entry 3", entries.get(0), log.get(3));
+ assertEquals("Entry 4", entries.get(1), log.get(4));
+
+ expectAndVerifyAppendEntriesReply(1, true, context.getId(), 1, 4);
+ }
/**
* This test verifies that when a new AppendEntries message is received with
* new entries and the logs of the sender and receiver are out-of-sync that
* the log is first corrected by removing the out of sync entries from the
* log and then adding in the new entries sent with the AppendEntries message
- *
- * @throws Exception
*/
@Test
- public void testHandleAppendEntriesCorrectReceiverLogEntries()
- throws Exception {
- new JavaTestKit(getSystem()) {{
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
-
- // First set the receivers term to lower number
- context.getTermInformation().update(2, "test");
-
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 1, new MockRaftActorContext.MockPayload("one")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 2, new MockRaftActorContext.MockPayload("two")));
-
- context.setReplicatedLog(log);
-
- // Prepare the entries to be sent with AppendEntries
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(2, 2, new MockRaftActorContext.MockPayload("two-1")));
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(2, 3, new MockRaftActorContext.MockPayload("three")));
-
- // Send appendEntries with the same term as was set on the receiver
- // before the new behavior was created (1 in this case)
- // This will not work for a Candidate because as soon as a Candidate
- // is created it increments the term
- AppendEntries appendEntries =
- new AppendEntries(2, "leader-1", 1, 1, entries, 3, -1);
-
- RaftActorBehavior behavior = createBehavior(context);
-
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
-
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
-
- assertEquals(expected, raftBehavior);
-
- // The entry at index 2 will be found out-of-sync with the leader
- // and will be removed
- // Then the two new entries will be added to the log
- // Thus making the log to have 4 entries
- assertEquals(4, log.last().getIndex() + 1);
- assertNotNull(log.get(2));
-
- assertEquals("one", log.get(1).getData().toString());
-
- // Check that the entry at index 2 has the new data
- assertEquals("two-1", log.get(2).getData().toString());
-
- assertEquals("three", log.get(3).getData().toString());
-
- assertNotNull(log.get(3));
-
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
-
-
- }};
+ public void testHandleAppendEntriesCorrectReceiverLogEntries() {
+ logStart("testHandleAppendEntriesCorrectReceiverLogEntries");
+
+ MockRaftActorContext context = createActorContext();
+
+ // First set the receivers term to lower number
+ context.getTermInformation().update(1, "test");
+
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
+ log.append(newReplicatedLogEntry(1, 0, "zero"));
+ log.append(newReplicatedLogEntry(1, 1, "one"));
+ log.append(newReplicatedLogEntry(1, 2, "two"));
+
+ context.setReplicatedLog(log);
+
+ // Prepare the entries to be sent with AppendEntries
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(newReplicatedLogEntry(2, 2, "two-1"));
+ entries.add(newReplicatedLogEntry(2, 3, "three"));
+
+ // Send appendEntries with the same term as was set on the receiver
+ // before the new behavior was created (1 in this case)
+ // This will not work for a Candidate because as soon as a Candidate
+ // is created it increments the term
+ AppendEntries appendEntries = new AppendEntries(2, "leader", 1, 1, entries, 3, -1);
+
+ follower = createBehavior(context);
+
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
+
+ Assert.assertSame(follower, newBehavior);
+
+ // The entry at index 2 will be found out-of-sync with the leader
+ // and will be removed
+ // Then the two new entries will be added to the log
+ // Thus making the log to have 4 entries
+ assertEquals("Next index", 4, log.last().getIndex() + 1);
+ //assertEquals("Entry 2", entries.get(0), log.get(2));
+
+ assertEquals("Entry 1 data", "one", log.get(1).getData().toString());
+
+ // Check that the entry at index 2 has the new data
+ assertEquals("Entry 2", entries.get(0), log.get(2));
+
+ assertEquals("Entry 3", entries.get(1), log.get(3));
+
+ expectAndVerifyAppendEntriesReply(2, true, context.getId(), 2, 3);
}
@Test
public void testHandleAppendEntriesPreviousLogEntryMissing(){
- new JavaTestKit(getSystem()) {{
+ logStart("testHandleAppendEntriesPreviousLogEntryMissing");
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
+ MockRaftActorContext context = createActorContext();
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 1, new MockRaftActorContext.MockPayload("one")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 2, new MockRaftActorContext.MockPayload("two")));
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
+ log.append(newReplicatedLogEntry(1, 0, "zero"));
+ log.append(newReplicatedLogEntry(1, 1, "one"));
+ log.append(newReplicatedLogEntry(1, 2, "two"));
- context.setReplicatedLog(log);
+ context.setReplicatedLog(log);
- // Prepare the entries to be sent with AppendEntries
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("two-1")));
+ // Prepare the entries to be sent with AppendEntries
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(newReplicatedLogEntry(1, 4, "four"));
- AppendEntries appendEntries =
- new AppendEntries(1, "leader-1", 3, 1, entries, 4, -1);
+ AppendEntries appendEntries = new AppendEntries(1, "leader", 3, 1, entries, 4, -1);
- RaftActorBehavior behavior = createBehavior(context);
+ follower = createBehavior(context);
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ Assert.assertSame(follower, newBehavior);
- assertEquals(expected, raftBehavior);
+ expectAndVerifyAppendEntriesReply(1, false, context.getId(), 1, 2);
+ }
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ @Test
+ public void testHandleAppendEntriesWithExistingLogEntry() {
+ logStart("testHandleAppendEntriesWithExistingLogEntry");
- assertEquals(false, out);
+ MockRaftActorContext context = createActorContext();
- }};
+ context.getTermInformation().update(1, "test");
- }
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
+ log.append(newReplicatedLogEntry(1, 0, "zero"));
+ log.append(newReplicatedLogEntry(1, 1, "one"));
- @Test
- public void testHandleAppendAfterInstallingSnapshot(){
- new JavaTestKit(getSystem()) {{
+ context.setReplicatedLog(log);
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
+ // Send the last entry again.
+ List<ReplicatedLogEntry> entries = Arrays.asList(newReplicatedLogEntry(1, 1, "one"));
+ follower = createBehavior(context);
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
+ follower.handleMessage(leaderActor, new AppendEntries(1, "leader", 0, 1, entries, 1, -1));
- // Set up a log as if it has been snapshotted
- log.setSnapshotIndex(3);
- log.setSnapshotTerm(1);
+ assertEquals("Next index", 2, log.last().getIndex() + 1);
+ assertEquals("Entry 1", entries.get(0), log.get(1));
- context.setReplicatedLog(log);
+ expectAndVerifyAppendEntriesReply(1, true, context.getId(), 1, 1);
- // Prepare the entries to be sent with AppendEntries
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("two-1")));
+ // Send the last entry again and also a new one.
- AppendEntries appendEntries =
- new AppendEntries(1, "leader-1", 3, 1, entries, 4, 3);
+ entries = Arrays.asList(newReplicatedLogEntry(1, 1, "one"), newReplicatedLogEntry(1, 2, "two"));
- RaftActorBehavior behavior = createBehavior(context);
+ leaderActor.underlyingActor().clear();
+ follower.handleMessage(leaderActor, new AppendEntries(1, "leader", 0, 1, entries, 2, -1));
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ assertEquals("Next index", 3, log.last().getIndex() + 1);
+ assertEquals("Entry 1", entries.get(0), log.get(1));
+ assertEquals("Entry 2", entries.get(1), log.get(2));
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ expectAndVerifyAppendEntriesReply(1, true, context.getId(), 1, 2);
+ }
- assertEquals(expected, raftBehavior);
+ @Test
+ public void testHandleAppendAfterInstallingSnapshot(){
+ logStart("testHandleAppendAfterInstallingSnapshot");
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ MockRaftActorContext context = createActorContext();
- assertEquals(true, out);
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
- }};
+ // Set up a log as if it has been snapshotted
+ log.setSnapshotIndex(3);
+ log.setSnapshotTerm(1);
+ context.setReplicatedLog(log);
+
+ // Prepare the entries to be sent with AppendEntries
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(newReplicatedLogEntry(1, 4, "four"));
+
+ AppendEntries appendEntries = new AppendEntries(1, "leader", 3, 1, entries, 4, 3);
+
+ follower = createBehavior(context);
+
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
+
+ Assert.assertSame(follower, newBehavior);
+
+ expectAndVerifyAppendEntriesReply(1, true, context.getId(), 1, 4);
}
*/
@Test
public void testHandleInstallSnapshot() throws Exception {
- JavaTestKit javaTestKit = new JavaTestKit(getSystem()) {{
-
- ActorRef leaderActor = getSystem().actorOf(Props.create(
- MessageCollectorActor.class));
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext(getRef());
-
- Follower follower = (Follower)createBehavior(context);
-
- HashMap<String, String> followerSnapshot = new HashMap<>();
- followerSnapshot.put("1", "A");
- followerSnapshot.put("2", "B");
- followerSnapshot.put("3", "C");
-
- ByteString bsSnapshot = toByteString(followerSnapshot);
- ByteString chunkData = ByteString.EMPTY;
- int offset = 0;
- int snapshotLength = bsSnapshot.size();
- int i = 1;
- int chunkIndex = 1;
-
- do {
- chunkData = getNextChunk(bsSnapshot, offset);
- final InstallSnapshot installSnapshot =
- new InstallSnapshot(1, "leader-1", i, 1,
- chunkData, chunkIndex, 3);
- follower.handleMessage(leaderActor, installSnapshot);
- offset = offset + 50;
- i++;
- chunkIndex++;
- } while ((offset+50) < snapshotLength);
-
- final InstallSnapshot installSnapshot3 = new InstallSnapshot(1, "leader-1", 3, 1, chunkData, chunkIndex, 3);
- follower.handleMessage(leaderActor, installSnapshot3);
-
- String[] matches = new ReceiveWhile<String>(String.class, duration("2 seconds")) {
- @Override
- protected String match(Object o) throws Exception {
- if (o instanceof ApplySnapshot) {
- ApplySnapshot as = (ApplySnapshot)o;
- if (as.getSnapshot().getLastIndex() != installSnapshot3.getLastIncludedIndex()) {
- return "applySnapshot-lastIndex-mismatch";
- }
- if (as.getSnapshot().getLastAppliedTerm() != installSnapshot3.getLastIncludedTerm()) {
- return "applySnapshot-lastAppliedTerm-mismatch";
- }
- if (as.getSnapshot().getLastAppliedIndex() != installSnapshot3.getLastIncludedIndex()) {
- return "applySnapshot-lastAppliedIndex-mismatch";
- }
- if (as.getSnapshot().getLastTerm() != installSnapshot3.getLastIncludedTerm()) {
- return "applySnapshot-lastTerm-mismatch";
- }
- return "applySnapshot";
- }
-
- return "ignoreCase";
- }
- }.get();
-
- // Verify that after a snapshot is successfully applied the collected snapshot chunks is reset to empty
- assertEquals(ByteString.EMPTY, follower.getSnapshotChunksCollected());
-
- String applySnapshotMatch = "";
- for (String reply: matches) {
- if (reply.startsWith("applySnapshot")) {
- applySnapshotMatch = reply;
- }
- }
-
- assertEquals("applySnapshot", applySnapshotMatch);
-
- Object messages = executeLocalOperation(leaderActor, "get-all-messages");
-
- assertNotNull(messages);
- assertTrue(messages instanceof List);
- List<Object> listMessages = (List<Object>) messages;
-
- int installSnapshotReplyReceivedCount = 0;
- for (Object message: listMessages) {
- if (message instanceof InstallSnapshotReply) {
- ++installSnapshotReplyReceivedCount;
- }
- }
+ logStart("testHandleInstallSnapshot");
+
+ MockRaftActorContext context = createActorContext();
+
+ follower = createBehavior(context);
+
+ HashMap<String, String> followerSnapshot = new HashMap<>();
+ followerSnapshot.put("1", "A");
+ followerSnapshot.put("2", "B");
+ followerSnapshot.put("3", "C");
+
+ ByteString bsSnapshot = toByteString(followerSnapshot);
+ int offset = 0;
+ int snapshotLength = bsSnapshot.size();
+ int chunkSize = 50;
+ int totalChunks = (snapshotLength / chunkSize) + ((snapshotLength % chunkSize) > 0 ? 1 : 0);
+ int lastIncludedIndex = 1;
+ int chunkIndex = 1;
+ InstallSnapshot lastInstallSnapshot = null;
+
+ for(int i = 0; i < totalChunks; i++) {
+ ByteString chunkData = getNextChunk(bsSnapshot, offset, chunkSize);
+ lastInstallSnapshot = new InstallSnapshot(1, "leader", lastIncludedIndex, 1,
+ chunkData, chunkIndex, totalChunks);
+ follower.handleMessage(leaderActor, lastInstallSnapshot);
+ offset = offset + 50;
+ lastIncludedIndex++;
+ chunkIndex++;
+ }
- assertEquals(3, installSnapshotReplyReceivedCount);
+ ApplySnapshot applySnapshot = MessageCollectorActor.expectFirstMatching(followerActor,
+ ApplySnapshot.class);
+ Snapshot snapshot = applySnapshot.getSnapshot();
+ assertEquals("getLastIndex", lastInstallSnapshot.getLastIncludedIndex(), snapshot.getLastIndex());
+ assertEquals("getLastIncludedTerm", lastInstallSnapshot.getLastIncludedTerm(),
+ snapshot.getLastAppliedTerm());
+ assertEquals("getLastAppliedIndex", lastInstallSnapshot.getLastIncludedIndex(),
+ snapshot.getLastAppliedIndex());
+ assertEquals("getLastTerm", lastInstallSnapshot.getLastIncludedTerm(), snapshot.getLastTerm());
+ Assert.assertArrayEquals("getState", bsSnapshot.toByteArray(), snapshot.getState());
+
+ List<InstallSnapshotReply> replies = MessageCollectorActor.getAllMatching(
+ leaderActor, InstallSnapshotReply.class);
+ assertEquals("InstallSnapshotReply count", totalChunks, replies.size());
+
+ chunkIndex = 1;
+ for(InstallSnapshotReply reply: replies) {
+ assertEquals("getChunkIndex", chunkIndex++, reply.getChunkIndex());
+ assertEquals("getTerm", 1, reply.getTerm());
+ assertEquals("isSuccess", true, reply.isSuccess());
+ assertEquals("getFollowerId", context.getId(), reply.getFollowerId());
+ }
- }};
+ Assert.assertNull("Expected null SnapshotTracker", ((Follower)follower).getSnapshotTracker());
}
@Test
public void testHandleOutOfSequenceInstallSnapshot() throws Exception {
- JavaTestKit javaTestKit = new JavaTestKit(getSystem()) {
- {
-
- ActorRef leaderActor = getSystem().actorOf(Props.create(
- MessageCollectorActor.class));
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext(getRef());
+ logStart("testHandleOutOfSequenceInstallSnapshot");
- Follower follower = (Follower) createBehavior(context);
+ MockRaftActorContext context = createActorContext();
- HashMap<String, String> followerSnapshot = new HashMap<>();
- followerSnapshot.put("1", "A");
- followerSnapshot.put("2", "B");
- followerSnapshot.put("3", "C");
+ follower = createBehavior(context);
- ByteString bsSnapshot = toByteString(followerSnapshot);
+ HashMap<String, String> followerSnapshot = new HashMap<>();
+ followerSnapshot.put("1", "A");
+ followerSnapshot.put("2", "B");
+ followerSnapshot.put("3", "C");
- final InstallSnapshot installSnapshot = new InstallSnapshot(1, "leader-1", 3, 1, getNextChunk(bsSnapshot, 10), 3, 3);
- follower.handleMessage(leaderActor, installSnapshot);
+ ByteString bsSnapshot = toByteString(followerSnapshot);
- Object messages = executeLocalOperation(leaderActor, "get-all-messages");
+ InstallSnapshot installSnapshot = new InstallSnapshot(1, "leader", 3, 1,
+ getNextChunk(bsSnapshot, 10, 50), 3, 3);
+ follower.handleMessage(leaderActor, installSnapshot);
- assertNotNull(messages);
- assertTrue(messages instanceof List);
- List<Object> listMessages = (List<Object>) messages;
+ InstallSnapshotReply reply = MessageCollectorActor.expectFirstMatching(leaderActor,
+ InstallSnapshotReply.class);
- int installSnapshotReplyReceivedCount = 0;
- for (Object message: listMessages) {
- if (message instanceof InstallSnapshotReply) {
- ++installSnapshotReplyReceivedCount;
- }
- }
+ assertEquals("isSuccess", false, reply.isSuccess());
+ assertEquals("getChunkIndex", -1, reply.getChunkIndex());
+ assertEquals("getTerm", 1, reply.getTerm());
+ assertEquals("getFollowerId", context.getId(), reply.getFollowerId());
- assertEquals(1, installSnapshotReplyReceivedCount);
- InstallSnapshotReply reply = (InstallSnapshotReply) listMessages.get(0);
- assertEquals(false, reply.isSuccess());
- assertEquals(-1, reply.getChunkIndex());
- assertEquals(ByteString.EMPTY, follower.getSnapshotChunksCollected());
-
-
- }};
+ Assert.assertNull("Expected null SnapshotTracker", ((Follower)follower).getSnapshotTracker());
}
- public Object executeLocalOperation(ActorRef actor, Object message) throws Exception {
- return MessageCollectorActor.getAllMessages(actor);
- }
-
- public ByteString getNextChunk (ByteString bs, int offset){
+ public ByteString getNextChunk (ByteString bs, int offset, int chunkSize){
int snapshotLength = bs.size();
int start = offset;
- int size = 50;
- if (50 > snapshotLength) {
+ int size = chunkSize;
+ if (chunkSize > snapshotLength) {
size = snapshotLength;
} else {
- if ((start + 50) > snapshotLength) {
+ if ((start + chunkSize) > snapshotLength) {
size = snapshotLength - start;
}
}
return bs.substring(start, start + size);
}
- private ByteString toByteString(Map<String, String> state) {
- ByteArrayOutputStream b = null;
- ObjectOutputStream o = null;
- try {
- try {
- b = new ByteArrayOutputStream();
- o = new ObjectOutputStream(b);
- o.writeObject(state);
- byte[] snapshotBytes = b.toByteArray();
- return ByteString.copyFrom(snapshotBytes);
- } finally {
- if (o != null) {
- o.flush();
- o.close();
- }
- if (b != null) {
- b.close();
- }
- }
- } catch (IOException e) {
- org.junit.Assert.fail("IOException in converting Hashmap to Bytestring:" + e);
- }
- return null;
+ private void expectAndVerifyAppendEntriesReply(int expTerm, boolean expSuccess,
+ String expFollowerId, long expLogLastTerm, long expLogLastIndex) {
+
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor,
+ AppendEntriesReply.class);
+
+ assertEquals("isSuccess", expSuccess, reply.isSuccess());
+ assertEquals("getTerm", expTerm, reply.getTerm());
+ assertEquals("getFollowerId", expFollowerId, reply.getFollowerId());
+ assertEquals("getLogLastTerm", expLogLastTerm, reply.getLogLastTerm());
+ assertEquals("getLogLastIndex", expLogLastIndex, reply.getLogLastIndex());
+ }
+
+ private ReplicatedLogEntry newReplicatedLogEntry(long term, long index, String data) {
+ return new MockRaftActorContext.MockReplicatedLogEntry(term, index,
+ new MockRaftActorContext.MockPayload(data));
+ }
+
+ @Override
+ protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
+ ActorRef actorRef, RaftRPC rpc) throws Exception {
+ super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
+
+ String expVotedFor = RequestVote.class.isInstance(rpc) ? ((RequestVote)rpc).getCandidateId() : null;
+ assertEquals("New votedFor", expVotedFor, actorContext.getTermInformation().getVotedFor());
+ }
+
+ @Override
+ protected void handleAppendEntriesAddSameEntryToLogReply(TestActorRef<MessageCollectorActor> replyActor)
+ throws Exception {
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(replyActor, AppendEntriesReply.class);
+ assertEquals("isSuccess", true, reply.isSuccess());
}
}
*/
package org.opendaylight.controller.cluster.raft.behaviors;
+import static org.junit.Assert.assertEquals;
import akka.actor.ActorRef;
import akka.actor.Props;
-import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
import java.util.HashMap;
import java.util.Map;
+import org.junit.After;
import org.junit.Test;
+import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftState;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
+
+public class IsolatedLeaderTest extends AbstractLeaderTest {
+
+ private final TestActorRef<MessageCollectorActor> leaderActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("leader"));
-public class IsolatedLeaderTest extends AbstractRaftActorBehaviorTest {
+ private final TestActorRef<MessageCollectorActor> senderActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("sender"));
- private ActorRef leaderActor =
- getSystem().actorOf(Props.create(DoNothingActor.class));
+ private AbstractLeader isolatedLeader;
- private ActorRef senderActor =
- getSystem().actorOf(Props.create(DoNothingActor.class));
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if(isolatedLeader != null) {
+ isolatedLeader.close();
+ }
+
+ super.tearDown();
+ }
@Override
- protected RaftActorBehavior createBehavior(
- RaftActorContext actorContext) {
- return new Leader(actorContext);
+ protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
+ return new IsolatedLeader(actorContext);
}
@Override
- protected RaftActorContext createActorContext() {
+ protected MockRaftActorContext createActorContext() {
return createActorContext(leaderActor);
}
+ @Override
+ protected MockRaftActorContext createActorContext(ActorRef actor) {
+ DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
+ configParams.setElectionTimeoutFactor(100000);
+ MockRaftActorContext context = new MockRaftActorContext("isolated-leader", getSystem(), actor);
+ context.setConfigParams(configParams);
+ return context;
+ }
@Test
- public void testHandleMessageWithThreeMembers() {
- new JavaTestKit(getSystem()) {{
- String followerAddress1 = "akka://test/user/$a";
- String followerAddress2 = "akka://test/user/$b";
-
- MockRaftActorContext leaderActorContext = (MockRaftActorContext) createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1", followerAddress1);
- peerAddresses.put("follower-2", followerAddress2);
- leaderActorContext.setPeerAddresses(peerAddresses);
-
- IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext);
- assertTrue(isolatedLeader.state() == RaftState.IsolatedLeader);
-
- // in a 3 node cluster, even if 1 follower is returns a reply, the isolatedLeader is not isolated
- RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
+ public void testHandleMessageWithThreeMembers() throws Exception {
+ String followerAddress1 = "akka://test/user/$a";
+ String followerAddress2 = "akka://test/user/$b";
+
+ MockRaftActorContext leaderActorContext = createActorContext();
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put("follower-1", followerAddress1);
+ peerAddresses.put("follower-2", followerAddress2);
+ leaderActorContext.setPeerAddresses(peerAddresses);
+
+ isolatedLeader = new IsolatedLeader(leaderActorContext);
+ assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state());
+
+ // in a 3 node cluster, even if 1 follower is returns a reply, the isolatedLeader is not isolated
+ RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() - 1, isolatedLeader.lastTerm() - 1));
+ isolatedLeader.lastIndex() - 1, isolatedLeader.lastTerm() - 1));
+
+ assertEquals("Raft state", RaftState.Leader, behavior.state());
- assertEquals(RaftState.Leader, behavior.state());
+ isolatedLeader.close();
+ isolatedLeader = (AbstractLeader) behavior;
- behavior = isolatedLeader.handleMessage(senderActor,
+ behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-2", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+ isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
- assertEquals(RaftState.Leader, behavior.state());
- }};
+ assertEquals("Raft state", RaftState.Leader, behavior.state());
}
@Test
- public void testHandleMessageWithFiveMembers() {
- new JavaTestKit(getSystem()) {{
-
- String followerAddress1 = "akka://test/user/$a";
- String followerAddress2 = "akka://test/user/$b";
- String followerAddress3 = "akka://test/user/$c";
- String followerAddress4 = "akka://test/user/$d";
-
- MockRaftActorContext leaderActorContext = (MockRaftActorContext) createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1", followerAddress1);
- peerAddresses.put("follower-2", followerAddress2);
- peerAddresses.put("follower-3", followerAddress3);
- peerAddresses.put("follower-4", followerAddress4);
- leaderActorContext.setPeerAddresses(peerAddresses);
-
- IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext);
- assertEquals(RaftState.IsolatedLeader, isolatedLeader.state());
-
- // in a 5 member cluster, atleast 2 followers need to be active and return a reply
- RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
+ public void testHandleMessageWithFiveMembers() throws Exception {
+ String followerAddress1 = "akka://test/user/$a";
+ String followerAddress2 = "akka://test/user/$b";
+ String followerAddress3 = "akka://test/user/$c";
+ String followerAddress4 = "akka://test/user/$d";
+
+ MockRaftActorContext leaderActorContext = createActorContext();
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put("follower-1", followerAddress1);
+ peerAddresses.put("follower-2", followerAddress2);
+ peerAddresses.put("follower-3", followerAddress3);
+ peerAddresses.put("follower-4", followerAddress4);
+ leaderActorContext.setPeerAddresses(peerAddresses);
+
+ isolatedLeader = new IsolatedLeader(leaderActorContext);
+ assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state());
+
+ // in a 5 member cluster, atleast 2 followers need to be active and return a reply
+ RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+ isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
- assertEquals(RaftState.IsolatedLeader, behavior.state());
+ assertEquals("Raft state", RaftState.IsolatedLeader, behavior.state());
- behavior = isolatedLeader.handleMessage(senderActor,
+ behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-2", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+ isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+
+ assertEquals("Raft state", RaftState.Leader, behavior.state());
- assertEquals(RaftState.Leader, behavior.state());
+ isolatedLeader.close();
+ isolatedLeader = (AbstractLeader) behavior;
- behavior = isolatedLeader.handleMessage(senderActor,
+ behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-3", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+ isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
- assertEquals(RaftState.Leader, behavior.state());
- }};
+ assertEquals("Raft state", RaftState.Leader, behavior.state());
}
@Test
- public void testHandleMessageFromAnotherLeader() {
- new JavaTestKit(getSystem()) {{
- String followerAddress1 = "akka://test/user/$a";
- String followerAddress2 = "akka://test/user/$b";
-
- MockRaftActorContext leaderActorContext = (MockRaftActorContext) createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1", followerAddress1);
- peerAddresses.put("follower-2", followerAddress2);
- leaderActorContext.setPeerAddresses(peerAddresses);
-
- IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext);
- assertTrue(isolatedLeader.state() == RaftState.IsolatedLeader);
-
- // if an append-entries reply is received by the isolated-leader, and that reply
- // has a term > than its own term, then IsolatedLeader switches to Follower
- // bowing itself to another leader in the cluster
- RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
+ public void testHandleMessageFromAnotherLeader() throws Exception {
+ String followerAddress1 = "akka://test/user/$a";
+ String followerAddress2 = "akka://test/user/$b";
+
+ MockRaftActorContext leaderActorContext = createActorContext();
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put("follower-1", followerAddress1);
+ peerAddresses.put("follower-2", followerAddress2);
+ leaderActorContext.setPeerAddresses(peerAddresses);
+
+ isolatedLeader = new IsolatedLeader(leaderActorContext);
+ assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state());
+
+ // if an append-entries reply is received by the isolated-leader, and that reply
+ // has a term > than its own term, then IsolatedLeader switches to Follower
+ // bowing itself to another leader in the cluster
+ RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() + 1, true,
- isolatedLeader.lastIndex() + 1, isolatedLeader.lastTerm() + 1));
+ isolatedLeader.lastIndex() + 1, isolatedLeader.lastTerm() + 1));
- assertEquals(RaftState.Follower, behavior.state());
- }};
+ assertEquals("Raft state", RaftState.Follower, behavior.state());
+ behavior.close();
}
}
import akka.testkit.JavaTestKit;
import akka.testkit.TestActorRef;
import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.protobuf.ByteString;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
import org.opendaylight.controller.cluster.raft.base.messages.Replicate;
import org.opendaylight.controller.cluster.raft.base.messages.SendHeartBeat;
import org.opendaylight.controller.cluster.raft.base.messages.SendInstallSnapshot;
+import org.opendaylight.controller.cluster.raft.behaviors.AbstractLeader.FollowerToSnapshot;
import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot;
import org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply;
+import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.raft.utils.ForwardMessageToBehaviorActor;
import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
import org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages;
import scala.concurrent.duration.FiniteDuration;
-public class LeaderTest extends AbstractRaftActorBehaviorTest {
+public class LeaderTest extends AbstractLeaderTest {
- private final ActorRef leaderActor =
- getSystem().actorOf(Props.create(DoNothingActor.class));
- private final ActorRef senderActor =
- getSystem().actorOf(Props.create(DoNothingActor.class));
+ static final String FOLLOWER_ID = "follower";
+
+ private final TestActorRef<ForwardMessageToBehaviorActor> leaderActor = actorFactory.createTestActor(
+ Props.create(ForwardMessageToBehaviorActor.class), actorFactory.generateActorId("leader"));
+
+ private final TestActorRef<ForwardMessageToBehaviorActor> followerActor = actorFactory.createTestActor(
+ Props.create(ForwardMessageToBehaviorActor.class), actorFactory.generateActorId("follower"));
+
+ private Leader leader;
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if(leader != null) {
+ leader.close();
+ }
+
+ super.tearDown();
+ }
@Test
public void testHandleMessageForUnknownMessage() throws Exception {
- new JavaTestKit(getSystem()) {{
- Leader leader =
- new Leader(createActorContext());
+ logStart("testHandleMessageForUnknownMessage");
- // handle message should return the Leader state when it receives an
- // unknown message
- RaftActorBehavior behavior = leader.handleMessage(senderActor, "foo");
- Assert.assertTrue(behavior instanceof Leader);
- }};
+ leader = new Leader(createActorContext());
+
+ // handle message should return the Leader state when it receives an
+ // unknown message
+ RaftActorBehavior behavior = leader.handleMessage(followerActor, "foo");
+ Assert.assertTrue(behavior instanceof Leader);
}
@Test
- public void testThatLeaderSendsAHeartbeatMessageToAllFollowers() {
- new JavaTestKit(getSystem()) {{
- new Within(duration("1 seconds")) {
- @Override
- protected void run() {
- ActorRef followerActor = getTestActor();
+ public void testThatLeaderSendsAHeartbeatMessageToAllFollowers() throws Exception {
+ logStart("testThatLeaderSendsAHeartbeatMessageToAllFollowers");
- MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext();
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- Map<String, String> peerAddresses = new HashMap<>();
+ long term = 1;
+ actorContext.getTermInformation().update(term, "");
- String followerId = "follower";
- peerAddresses.put(followerId, followerActor.path().toString());
+ leader = new Leader(actorContext);
- actorContext.setPeerAddresses(peerAddresses);
+ // Leader should send an immediate heartbeat with no entries as follower is inactive.
+ long lastIndex = actorContext.getReplicatedLog().lastIndex();
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
+ assertEquals("getTerm", term, appendEntries.getTerm());
+ assertEquals("getPrevLogIndex", -1, appendEntries.getPrevLogIndex());
+ assertEquals("getPrevLogTerm", -1, appendEntries.getPrevLogTerm());
+ assertEquals("Entries size", 0, appendEntries.getEntries().size());
- long term = 1;
- actorContext.getTermInformation().update(term, "");
+ // The follower would normally reply - simulate that explicitly here.
+ leader.handleMessage(followerActor, new AppendEntriesReply(
+ FOLLOWER_ID, term, true, lastIndex - 1, term));
+ assertEquals("isFollowerActive", true, leader.getFollower(FOLLOWER_ID).isFollowerActive());
- Leader leader = new Leader(actorContext);
+ followerActor.underlyingActor().clear();
- // Leader should send an immediate heartbeat with no entries as follower is inactive.
- long lastIndex = actorContext.getReplicatedLog().lastIndex();
- AppendEntries appendEntries = expectMsgClass(duration("5 seconds"), AppendEntries.class);
- assertEquals("getTerm", term, appendEntries.getTerm());
- assertEquals("getPrevLogIndex", -1, appendEntries.getPrevLogIndex());
- assertEquals("getPrevLogTerm", -1, appendEntries.getPrevLogTerm());
- assertEquals("Entries size", 0, appendEntries.getEntries().size());
+ // Sleep for the heartbeat interval so AppendEntries is sent.
+ Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().
+ getHeartBeatInterval().toMillis(), TimeUnit.MILLISECONDS);
- // The follower would normally reply - simulate that explicitly here.
- leader.handleMessage(followerActor, new AppendEntriesReply(
- followerId, term, true, lastIndex - 1, term));
- assertEquals("isFollowerActive", true, leader.getFollower(followerId).isFollowerActive());
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- // Sleep for the heartbeat interval so AppendEntries is sent.
- Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().
- getHeartBeatInterval().toMillis(), TimeUnit.MILLISECONDS);
+ appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
+ assertEquals("getPrevLogIndex", lastIndex - 1, appendEntries.getPrevLogIndex());
+ assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
+ assertEquals("Entries size", 1, appendEntries.getEntries().size());
+ assertEquals("Entry getIndex", lastIndex, appendEntries.getEntries().get(0).getIndex());
+ assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
+ }
- leader.handleMessage(senderActor, new SendHeartBeat());
+ @Test
+ public void testHandleReplicateMessageSendAppendEntriesToFollower() throws Exception {
+ logStart("testHandleReplicateMessageSendAppendEntriesToFollower");
- appendEntries = expectMsgClass(duration("5 seconds"), AppendEntries.class);
- assertEquals("getPrevLogIndex", lastIndex - 1, appendEntries.getPrevLogIndex());
- assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
- assertEquals("Entries size", 1, appendEntries.getEntries().size());
- assertEquals("Entry getIndex", lastIndex, appendEntries.getEntries().get(0).getIndex());
- assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
- }
- };
- }};
+ MockRaftActorContext actorContext = createActorContextWithFollower();
+
+ long term = 1;
+ actorContext.getTermInformation().update(term, "");
+
+ leader = new Leader(actorContext);
+
+ // Leader will send an immediate heartbeat - ignore it.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
+
+ // The follower would normally reply - simulate that explicitly here.
+ long lastIndex = actorContext.getReplicatedLog().lastIndex();
+ leader.handleMessage(followerActor, new AppendEntriesReply(
+ FOLLOWER_ID, term, true, lastIndex, term));
+ assertEquals("isFollowerActive", true, leader.getFollower(FOLLOWER_ID).isFollowerActive());
+
+ followerActor.underlyingActor().clear();
+
+ MockRaftActorContext.MockPayload payload = new MockRaftActorContext.MockPayload("foo");
+ MockRaftActorContext.MockReplicatedLogEntry newEntry = new MockRaftActorContext.MockReplicatedLogEntry(
+ 1, lastIndex + 1, payload);
+ actorContext.getReplicatedLog().append(newEntry);
+ RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor,
+ new Replicate(null, null, newEntry));
+
+ // State should not change
+ assertTrue(raftBehavior instanceof Leader);
+
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
+ assertEquals("getPrevLogIndex", lastIndex, appendEntries.getPrevLogIndex());
+ assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
+ assertEquals("Entries size", 1, appendEntries.getEntries().size());
+ assertEquals("Entry getIndex", lastIndex + 1, appendEntries.getEntries().get(0).getIndex());
+ assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
+ assertEquals("Entry payload", payload, appendEntries.getEntries().get(0).getData());
}
@Test
- public void testHandleReplicateMessageSendAppendEntriesToFollower() {
- new JavaTestKit(getSystem()) {{
- new Within(duration("1 seconds")) {
- @Override
- protected void run() {
- ActorRef followerActor = getTestActor();
+ public void testHandleReplicateMessageWhenThereAreNoFollowers() throws Exception {
+ logStart("testHandleReplicateMessageWhenThereAreNoFollowers");
- MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext();
+ MockRaftActorContext actorContext = createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
+ leader = new Leader(actorContext);
- String followerId = "follower";
- peerAddresses.put(followerId, followerActor.path().toString());
+ actorContext.setLastApplied(0);
- actorContext.setPeerAddresses(peerAddresses);
+ long newLogIndex = actorContext.getReplicatedLog().lastIndex() + 1;
+ long term = actorContext.getTermInformation().getCurrentTerm();
+ MockRaftActorContext.MockReplicatedLogEntry newEntry = new MockRaftActorContext.MockReplicatedLogEntry(
+ term, newLogIndex, new MockRaftActorContext.MockPayload("foo"));
- long term = 1;
- actorContext.getTermInformation().update(term, "");
+ actorContext.getReplicatedLog().append(newEntry);
- Leader leader = new Leader(actorContext);
+ RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor,
+ new Replicate(leaderActor, "state-id", newEntry));
- // Leader will send an immediate heartbeat - ignore it.
- expectMsgClass(duration("5 seconds"), AppendEntries.class);
+ // State should not change
+ assertTrue(raftBehavior instanceof Leader);
- // The follower would normally reply - simulate that explicitly here.
- long lastIndex = actorContext.getReplicatedLog().lastIndex();
- leader.handleMessage(followerActor, new AppendEntriesReply(
- followerId, term, true, lastIndex, term));
- assertEquals("isFollowerActive", true, leader.getFollower(followerId).isFollowerActive());
+ assertEquals("getCommitIndex", newLogIndex, actorContext.getCommitIndex());
- MockRaftActorContext.MockPayload payload = new MockRaftActorContext.MockPayload("foo");
- MockRaftActorContext.MockReplicatedLogEntry newEntry = new MockRaftActorContext.MockReplicatedLogEntry(
- 1, lastIndex + 1, payload);
- actorContext.getReplicatedLog().append(newEntry);
- RaftActorBehavior raftBehavior = leader.handleMessage(senderActor,
- new Replicate(null, null, newEntry));
+ // We should get 2 ApplyState messages - 1 for new log entry and 1 for the previous
+ // one since lastApplied state is 0.
+ List<ApplyState> applyStateList = MessageCollectorActor.getAllMatching(
+ leaderActor, ApplyState.class);
+ assertEquals("ApplyState count", newLogIndex, applyStateList.size());
- // State should not change
- assertTrue(raftBehavior instanceof Leader);
-
- AppendEntries appendEntries = expectMsgClass(duration("5 seconds"), AppendEntries.class);
- assertEquals("getPrevLogIndex", lastIndex, appendEntries.getPrevLogIndex());
- assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
- assertEquals("Entries size", 1, appendEntries.getEntries().size());
- assertEquals("Entry getIndex", lastIndex + 1, appendEntries.getEntries().get(0).getIndex());
- assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
- assertEquals("Entry payload", payload, appendEntries.getEntries().get(0).getData());
- }
- };
- }};
- }
+ for(int i = 0; i <= newLogIndex - 1; i++ ) {
+ ApplyState applyState = applyStateList.get(i);
+ assertEquals("getIndex", i + 1, applyState.getReplicatedLogEntry().getIndex());
+ assertEquals("getTerm", term, applyState.getReplicatedLogEntry().getTerm());
+ }
- @Test
- public void testHandleReplicateMessageWhenThereAreNoFollowers() {
- new JavaTestKit(getSystem()) {{
- new Within(duration("1 seconds")) {
- @Override
- protected void run() {
-
- ActorRef raftActor = getTestActor();
-
- MockRaftActorContext actorContext =
- new MockRaftActorContext("test", getSystem(), raftActor);
-
- actorContext.getReplicatedLog().removeFrom(0);
-
- actorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 2, 1)
- .build());
-
- Leader leader = new Leader(actorContext);
- RaftActorBehavior raftBehavior = leader
- .handleMessage(senderActor, new Replicate(null, "state-id",actorContext.getReplicatedLog().get(1)));
-
- // State should not change
- assertTrue(raftBehavior instanceof Leader);
-
- assertEquals(1, actorContext.getCommitIndex());
-
- final String out =
- new ExpectMsg<String>(duration("1 seconds"),
- "match hint") {
- // do not put code outside this method, will run afterwards
- @Override
- protected String match(Object in) {
- if (in instanceof ApplyState) {
- if (((ApplyState) in).getIdentifier().equals("state-id")) {
- return "match";
- }
- return null;
- } else {
- throw noMatch();
- }
- }
- }.get(); // this extracts the received message
-
- assertEquals("match", out);
-
- }
- };
- }};
+ ApplyState last = applyStateList.get((int) newLogIndex - 1);
+ assertEquals("getData", newEntry.getData(), last.getReplicatedLogEntry().getData());
+ assertEquals("getIdentifier", "state-id", last.getIdentifier());
}
@Test
public void testSendAppendEntriesOnAnInProgressInstallSnapshot() throws Exception {
- new JavaTestKit(getSystem()) {{
- ActorRef followerActor = getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testSendAppendEntriesOnAnInProgressInstallSnapshot");
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
-
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext(leaderActor);
- actorContext.setPeerAddresses(peerAddresses);
-
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
-
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
-
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
-
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.setCommitIndex(followersLastIndex);
- //set follower timeout to 2 mins, helps during debugging
- actorContext.setConfigParams(new MockConfigParamsImpl(120000L, 10));
-
- MockLeader leader = new MockLeader(actorContext);
-
- // new entry
- ReplicatedLogImplEntry entry =
- new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
- new MockRaftActorContext.MockPayload("D"));
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- //update follower timestamp
- leader.markFollowerActive(followerActor.path().toString());
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
- leader.createFollowerToSnapshot(followerActor.path().toString(), bs);
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
- //send first chunk and no InstallSnapshotReply received yet
- leader.getFollowerToSnapshot().getNextChunk();
- leader.getFollowerToSnapshot().incrementChunkIndex();
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int newEntryIndex = 4;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
- TimeUnit.MILLISECONDS);
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.setCommitIndex(followersLastIndex);
+ //set follower timeout to 2 mins, helps during debugging
+ actorContext.setConfigParams(new MockConfigParamsImpl(120000L, 10));
- leader.handleMessage(leaderActor, new SendHeartBeat());
+ leader = new Leader(actorContext);
- AppendEntries aeproto = MessageCollectorActor.getFirstMatching(
- followerActor, AppendEntries.class);
+ // new entry
+ ReplicatedLogImplEntry entry =
+ new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
+ new MockRaftActorContext.MockPayload("D"));
- assertNotNull("AppendEntries should be sent even if InstallSnapshotReply is not " +
- "received", aeproto);
+ //update follower timestamp
+ leader.markFollowerActive(FOLLOWER_ID);
- AppendEntries ae = (AppendEntries) SerializationUtils.fromSerializable(aeproto);
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
+ FollowerToSnapshot fts = leader.new FollowerToSnapshot(bs);
+ leader.setFollowerSnapshot(FOLLOWER_ID, fts);
- assertTrue("AppendEntries should be sent with empty entries", ae.getEntries().isEmpty());
+ //send first chunk and no InstallSnapshotReply received yet
+ fts.getNextChunk();
+ fts.incrementChunkIndex();
- //InstallSnapshotReply received
- leader.getFollowerToSnapshot().markSendStatus(true);
+ Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
+ TimeUnit.MILLISECONDS);
- leader.handleMessage(senderActor, new SendHeartBeat());
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- InstallSnapshotMessages.InstallSnapshot isproto = MessageCollectorActor.getFirstMatching(followerActor,
- InstallSnapshot.SERIALIZABLE_CLASS);
+ AppendEntries aeproto = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- assertNotNull("Installsnapshot should get called for sending the next chunk of snapshot",
- isproto);
+ AppendEntries ae = (AppendEntries) SerializationUtils.fromSerializable(aeproto);
- InstallSnapshot is = (InstallSnapshot) SerializationUtils.fromSerializable(isproto);
+ assertTrue("AppendEntries should be sent with empty entries", ae.getEntries().isEmpty());
- assertEquals(snapshotIndex, is.getLastIncludedIndex());
+ //InstallSnapshotReply received
+ fts.markSendStatus(true);
- }};
+ leader.handleMessage(leaderActor, new SendHeartBeat());
+
+ InstallSnapshotMessages.InstallSnapshot isproto = MessageCollectorActor.expectFirstMatching(followerActor,
+ InstallSnapshot.SERIALIZABLE_CLASS);
+
+ InstallSnapshot is = (InstallSnapshot) SerializationUtils.fromSerializable(isproto);
+
+ assertEquals(snapshotIndex, is.getLastIncludedIndex());
}
@Test
- public void testSendAppendEntriesSnapshotScenario() {
- new JavaTestKit(getSystem()) {{
-
- ActorRef followerActor = getTestActor();
+ public void testSendAppendEntriesSnapshotScenario() throws Exception {
+ logStart("testSendAppendEntriesSnapshotScenario");
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext(getRef());
- actorContext.setPeerAddresses(peerAddresses);
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int newEntryIndex = 4;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.setCommitIndex(followersLastIndex);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.setCommitIndex(followersLastIndex);
+ leader = new Leader(actorContext);
- Leader leader = new Leader(actorContext);
+ // Leader will send an immediate heartbeat - ignore it.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- // new entry
- ReplicatedLogImplEntry entry =
+ // new entry
+ ReplicatedLogImplEntry entry =
new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
- new MockRaftActorContext.MockPayload("D"));
+ new MockRaftActorContext.MockPayload("D"));
- //update follower timestamp
- leader.markFollowerActive(followerActor.path().toString());
+ //update follower timestamp
+ leader.markFollowerActive(FOLLOWER_ID);
- Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
- TimeUnit.MILLISECONDS);
+ // this should invoke a sendinstallsnapshot as followersLastIndex < snapshotIndex
+ RaftActorBehavior raftBehavior = leader.handleMessage(
+ leaderActor, new Replicate(null, "state-id", entry));
- // this should invoke a sendinstallsnapshot as followersLastIndex < snapshotIndex
- RaftActorBehavior raftBehavior = leader.handleMessage(
- senderActor, new Replicate(null, "state-id", entry));
-
- assertTrue(raftBehavior instanceof Leader);
-
- // we might receive some heartbeat messages, so wait till we get CaptureSnapshot
- Boolean[] matches = new ReceiveWhile<Boolean>(Boolean.class, duration("2 seconds")) {
- @Override
- protected Boolean match(Object o) throws Exception {
- if (o instanceof CaptureSnapshot) {
- return true;
- }
- return false;
- }
- }.get();
-
- boolean captureSnapshot = false;
- for (Boolean b: matches) {
- captureSnapshot = b | captureSnapshot;
- }
+ assertTrue(raftBehavior instanceof Leader);
- assertTrue(captureSnapshot);
- }};
+ MessageCollectorActor.expectFirstMatching(leaderActor, CaptureSnapshot.class);
}
@Test
public void testInitiateInstallSnapshot() throws Exception {
- new JavaTestKit(getSystem()) {{
-
- ActorRef leaderActor = getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testInitiateInstallSnapshot");
- ActorRef followerActor = getTestActor();
-
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(), followerActor.path().toString());
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext(leaderActor);
- actorContext.setPeerAddresses(peerAddresses);
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int newEntryIndex = 4;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.setLastApplied(3);
+ actorContext.setCommitIndex(followersLastIndex);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.setLastApplied(3);
- actorContext.setCommitIndex(followersLastIndex);
+ leader = new Leader(actorContext);
- Leader leader = new Leader(actorContext);
- // set the snapshot as absent and check if capture-snapshot is invoked.
- leader.setSnapshot(Optional.<ByteString>absent());
+ // Leader will send an immediate heartbeat - ignore it.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- // new entry
- ReplicatedLogImplEntry entry = new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
- new MockRaftActorContext.MockPayload("D"));
+ // set the snapshot as absent and check if capture-snapshot is invoked.
+ leader.setSnapshot(Optional.<ByteString>absent());
- actorContext.getReplicatedLog().append(entry);
+ // new entry
+ ReplicatedLogImplEntry entry = new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
+ new MockRaftActorContext.MockPayload("D"));
- //update follower timestamp
- leader.markFollowerActive(followerActor.path().toString());
+ actorContext.getReplicatedLog().append(entry);
- RaftActorBehavior raftBehavior = leader.handleMessage(
- senderActor, new Replicate(null, "state-id", entry));
+ //update follower timestamp
+ leader.markFollowerActive(FOLLOWER_ID);
- CaptureSnapshot cs = MessageCollectorActor.
- getFirstMatching(leaderActor, CaptureSnapshot.class);
+ leader.handleMessage(leaderActor, new Replicate(null, "state-id", entry));
- assertNotNull(cs);
+ CaptureSnapshot cs = MessageCollectorActor.expectFirstMatching(leaderActor, CaptureSnapshot.class);
- assertTrue(cs.isInstallSnapshotInitiated());
- assertEquals(3, cs.getLastAppliedIndex());
- assertEquals(1, cs.getLastAppliedTerm());
- assertEquals(4, cs.getLastIndex());
- assertEquals(2, cs.getLastTerm());
+ assertTrue(cs.isInstallSnapshotInitiated());
+ assertEquals(3, cs.getLastAppliedIndex());
+ assertEquals(1, cs.getLastAppliedTerm());
+ assertEquals(4, cs.getLastIndex());
+ assertEquals(2, cs.getLastTerm());
- // if an initiate is started again when first is in progress, it shouldnt initiate Capture
- leader.handleMessage(senderActor, new Replicate(null, "state-id", entry));
- List<Object> captureSnapshots = MessageCollectorActor.getAllMatching(leaderActor, CaptureSnapshot.class);
- assertEquals("CaptureSnapshot should not get invoked when initiate is in progress", 1, captureSnapshots.size());
+ // if an initiate is started again when first is in progress, it shouldnt initiate Capture
+ leader.handleMessage(leaderActor, new Replicate(null, "state-id", entry));
- }};
+ List<CaptureSnapshot> captureSnapshots = MessageCollectorActor.getAllMatching(leaderActor, CaptureSnapshot.class);
+ assertEquals("CaptureSnapshot should not get invoked when initiate is in progress", 1, captureSnapshots.size());
}
@Test
- public void testInstallSnapshot() {
- new JavaTestKit(getSystem()) {{
+ public void testInstallSnapshot() throws Exception {
+ logStart("testInstallSnapshot");
- ActorRef followerActor = getTestActor();
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
- actorContext.setPeerAddresses(peerAddresses);
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
+ actorContext.setCommitIndex(followersLastIndex);
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
+ leader = new Leader(actorContext);
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ // Ignore initial heartbeat.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- actorContext.setCommitIndex(followersLastIndex);
+ RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor,
+ new SendInstallSnapshot(toByteString(leadersSnapshot)));
- Leader leader = new Leader(actorContext);
+ assertTrue(raftBehavior instanceof Leader);
- // Ignore initial heartbeat.
- expectMsgClass(duration("5 seconds"), AppendEntries.class);
+ // check if installsnapshot gets called with the correct values.
- // new entry
- ReplicatedLogImplEntry entry =
- new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
- new MockRaftActorContext.MockPayload("D"));
+ InstallSnapshot installSnapshot = (InstallSnapshot) SerializationUtils.fromSerializable(
+ MessageCollectorActor.expectFirstMatching(followerActor, InstallSnapshotMessages.InstallSnapshot.class));
- RaftActorBehavior raftBehavior = leader.handleMessage(senderActor,
- new SendInstallSnapshot(toByteString(leadersSnapshot)));
+ assertNotNull(installSnapshot.getData());
+ assertEquals(snapshotIndex, installSnapshot.getLastIncludedIndex());
+ assertEquals(snapshotTerm, installSnapshot.getLastIncludedTerm());
- assertTrue(raftBehavior instanceof Leader);
-
- // check if installsnapshot gets called with the correct values.
- final String out =
- new ExpectMsg<String>(duration("1 seconds"), "match hint") {
- // do not put code outside this method, will run afterwards
- @Override
- protected String match(Object in) {
- if (in instanceof InstallSnapshotMessages.InstallSnapshot) {
- InstallSnapshot is = (InstallSnapshot)
- SerializationUtils.fromSerializable(in);
- if (is.getData() == null) {
- return "InstallSnapshot data is null";
- }
- if (is.getLastIncludedIndex() != snapshotIndex) {
- return is.getLastIncludedIndex() + "!=" + snapshotIndex;
- }
- if (is.getLastIncludedTerm() != snapshotTerm) {
- return is.getLastIncludedTerm() + "!=" + snapshotTerm;
- }
- if (is.getTerm() == currentTerm) {
- return is.getTerm() + "!=" + currentTerm;
- }
-
- return "match";
-
- } else {
- return "message mismatch:" + in.getClass();
- }
- }
- }.get(); // this extracts the received message
-
- assertEquals("match", out);
- }};
+ assertEquals(currentTerm, installSnapshot.getTerm());
}
@Test
- public void testHandleInstallSnapshotReplyLastChunk() {
- new JavaTestKit(getSystem()) {{
+ public void testHandleInstallSnapshotReplyLastChunk() throws Exception {
+ logStart("testHandleInstallSnapshotReplyLastChunk");
- ActorRef followerActor = getTestActor();
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
-
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
-
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
- actorContext.setPeerAddresses(peerAddresses);
- actorContext.setCommitIndex(followersLastIndex);
-
- MockLeader leader = new MockLeader(actorContext);
-
- // Ignore initial heartbeat.
- expectMsgClass(duration("5 seconds"), AppendEntries.class);
-
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
-
- // set the snapshot variables in replicatedlog
-
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
-
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
- leader.createFollowerToSnapshot(followerActor.path().toString(), bs);
- while(!leader.getFollowerToSnapshot().isLastChunk(leader.getFollowerToSnapshot().getChunkIndex())) {
- leader.getFollowerToSnapshot().getNextChunk();
- leader.getFollowerToSnapshot().incrementChunkIndex();
- }
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
+ actorContext.setCommitIndex(followersLastIndex);
- RaftActorBehavior raftBehavior = leader.handleMessage(senderActor,
- new InstallSnapshotReply(currentTerm, followerActor.path().toString(),
- leader.getFollowerToSnapshot().getChunkIndex(), true));
+ leader = new Leader(actorContext);
- assertTrue(raftBehavior instanceof Leader);
+ // Ignore initial heartbeat.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- assertEquals(0, leader.followerSnapshotSize());
- assertEquals(1, leader.followerLogSize());
- assertNotNull(leader.getFollower(followerActor.path().toString()));
- FollowerLogInformation fli = leader.getFollower(followerActor.path().toString());
- assertEquals(snapshotIndex, fli.getMatchIndex());
- assertEquals(snapshotIndex, fli.getMatchIndex());
- assertEquals(snapshotIndex + 1, fli.getNextIndex());
- }};
- }
- @Test
- public void testSendSnapshotfromInstallSnapshotReply() throws Exception {
- new JavaTestKit(getSystem()) {{
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- TestActorRef<MessageCollectorActor> followerActor =
- TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower-reply");
+ // set the snapshot variables in replicatedlog
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-reply",
- followerActor.path().toString());
-
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
-
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
- DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl(){
- @Override
- public int getSnapshotChunkSize() {
- return 50;
- }
- };
- configParams.setHeartBeatInterval(new FiniteDuration(9, TimeUnit.SECONDS));
- configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- actorContext.setConfigParams(configParams);
- actorContext.setPeerAddresses(peerAddresses);
- actorContext.setCommitIndex(followersLastIndex);
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
+ FollowerToSnapshot fts = leader.new FollowerToSnapshot(bs);
+ leader.setFollowerSnapshot(FOLLOWER_ID, fts);
+ while(!fts.isLastChunk(fts.getChunkIndex())) {
+ fts.getNextChunk();
+ fts.incrementChunkIndex();
+ }
- MockLeader leader = new MockLeader(actorContext);
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ RaftActorBehavior raftBehavior = leader.handleMessage(followerActor,
+ new InstallSnapshotReply(currentTerm, FOLLOWER_ID, fts.getChunkIndex(), true));
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
+ assertTrue(raftBehavior instanceof Leader);
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
+ assertEquals(0, leader.followerSnapshotSize());
+ assertEquals(1, leader.followerLogSize());
+ FollowerLogInformation fli = leader.getFollower(FOLLOWER_ID);
+ assertNotNull(fli);
+ assertEquals(snapshotIndex, fli.getMatchIndex());
+ assertEquals(snapshotIndex, fli.getMatchIndex());
+ assertEquals(snapshotIndex + 1, fli.getNextIndex());
+ }
- leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
+ @Test
+ public void testSendSnapshotfromInstallSnapshotReply() throws Exception {
+ logStart("testSendSnapshotfromInstallSnapshotReply");
- List<Object> objectList = MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- assertEquals(1, objectList.size());
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- Object o = objectList.get(0);
- assertTrue(o instanceof InstallSnapshotMessages.InstallSnapshot);
+ DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl(){
+ @Override
+ public int getSnapshotChunkSize() {
+ return 50;
+ }
+ };
+ configParams.setHeartBeatInterval(new FiniteDuration(9, TimeUnit.SECONDS));
+ configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
- InstallSnapshotMessages.InstallSnapshot installSnapshot = (InstallSnapshotMessages.InstallSnapshot) o;
+ actorContext.setConfigParams(configParams);
+ actorContext.setCommitIndex(followersLastIndex);
- assertEquals(1, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
+ leader = new Leader(actorContext);
- leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
- "follower-reply", installSnapshot.getChunkIndex(), true));
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- objectList = MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- assertEquals(2, objectList.size());
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
- installSnapshot = (InstallSnapshotMessages.InstallSnapshot) objectList.get(1);
+ leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
- leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
- "follower-reply", installSnapshot.getChunkIndex(), true));
+ InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- objectList = MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ assertEquals(1, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
- assertEquals(3, objectList.size());
+ followerActor.underlyingActor().clear();
+ leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
+ FOLLOWER_ID, installSnapshot.getChunkIndex(), true));
- installSnapshot = (InstallSnapshotMessages.InstallSnapshot) objectList.get(2);
+ installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- // Send snapshot reply one more time and make sure that a new snapshot message should not be sent to follower
- leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
- "follower-reply", installSnapshot.getChunkIndex(), true));
+ assertEquals(2, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
- objectList = MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ followerActor.underlyingActor().clear();
+ leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
+ FOLLOWER_ID, installSnapshot.getChunkIndex(), true));
- // Count should still stay at 3
- assertEquals(3, objectList.size());
- }};
+ installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
+
+ // Send snapshot reply one more time and make sure that a new snapshot message should not be sent to follower
+ followerActor.underlyingActor().clear();
+ leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
+ FOLLOWER_ID, installSnapshot.getChunkIndex(), true));
+
+ installSnapshot = MessageCollectorActor.getFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
+
+ Assert.assertNull(installSnapshot);
}
@Test
public void testHandleInstallSnapshotReplyWithInvalidChunkIndex() throws Exception{
- new JavaTestKit(getSystem()) {{
-
- TestActorRef<MessageCollectorActor> followerActor =
- TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower");
-
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
+ logStart("testHandleInstallSnapshotReplyWithInvalidChunkIndex");
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- actorContext.setConfigParams(new DefaultConfigParamsImpl(){
- @Override
- public int getSnapshotChunkSize() {
- return 50;
- }
- });
- actorContext.setPeerAddresses(peerAddresses);
- actorContext.setCommitIndex(followersLastIndex);
+ actorContext.setConfigParams(new DefaultConfigParamsImpl(){
+ @Override
+ public int getSnapshotChunkSize() {
+ return 50;
+ }
+ });
- MockLeader leader = new MockLeader(actorContext);
+ actorContext.setCommitIndex(followersLastIndex);
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ leader = new Leader(actorContext);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
- MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
- InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.getFirstMatching(
- followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertNotNull(installSnapshot);
+ InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertEquals(1, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
+ assertEquals(1, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
- followerActor.underlyingActor().clear();
+ followerActor.underlyingActor().clear();
- leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
- followerActor.path().toString(), -1, false));
+ leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
+ FOLLOWER_ID, -1, false));
- Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
+ Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
TimeUnit.MILLISECONDS);
- leader.handleMessage(leaderActor, new SendHeartBeat());
-
- installSnapshot = MessageCollectorActor.getFirstMatching(
- followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertNotNull(installSnapshot);
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- assertEquals(1, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
+ installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- followerActor.tell(PoisonPill.getInstance(), getRef());
- }};
+ assertEquals(1, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
}
@Test
public void testHandleSnapshotSendsPreviousChunksHashCodeWhenSendingNextChunk() throws Exception {
- new JavaTestKit(getSystem()) {
- {
- TestActorRef<MessageCollectorActor> followerActor =
- TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower-chunk");
-
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
+ logStart("testHandleSnapshotSendsPreviousChunksHashCodeWhenSendingNextChunk");
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- actorContext.setConfigParams(new DefaultConfigParamsImpl() {
- @Override
- public int getSnapshotChunkSize() {
- return 50;
- }
- });
- actorContext.setPeerAddresses(peerAddresses);
- actorContext.setCommitIndex(followersLastIndex);
+ actorContext.setConfigParams(new DefaultConfigParamsImpl() {
+ @Override
+ public int getSnapshotChunkSize() {
+ return 50;
+ }
+ });
- MockLeader leader = new MockLeader(actorContext);
+ actorContext.setCommitIndex(followersLastIndex);
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ leader = new Leader(actorContext);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
- InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.getFirstMatching(
- followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertNotNull(installSnapshot);
+ leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
- assertEquals(1, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
- assertEquals(AbstractLeader.INITIAL_LAST_CHUNK_HASH_CODE, installSnapshot.getLastChunkHashCode());
+ InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- int hashCode = installSnapshot.getData().hashCode();
+ assertEquals(1, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
+ assertEquals(AbstractLeader.INITIAL_LAST_CHUNK_HASH_CODE, installSnapshot.getLastChunkHashCode());
- followerActor.underlyingActor().clear();
+ int hashCode = installSnapshot.getData().hashCode();
- leader.handleMessage(followerActor, new InstallSnapshotReply(installSnapshot.getTerm(),followerActor.path().toString(),1,true ));
+ followerActor.underlyingActor().clear();
- installSnapshot = MessageCollectorActor.getFirstMatching(
- followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertNotNull(installSnapshot);
+ leader.handleMessage(followerActor, new InstallSnapshotReply(installSnapshot.getTerm(),
+ FOLLOWER_ID, 1, true));
- assertEquals(2, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
- assertEquals(hashCode, installSnapshot.getLastChunkHashCode());
+ installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- followerActor.tell(PoisonPill.getInstance(), getRef());
- }};
+ assertEquals(2, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
+ assertEquals(hashCode, installSnapshot.getLastChunkHashCode());
}
@Test
public void testFollowerToSnapshotLogic() {
+ logStart("testFollowerToSnapshotLogic");
- MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext();
+ MockRaftActorContext actorContext = createActorContext();
actorContext.setConfigParams(new DefaultConfigParamsImpl() {
@Override
}
});
- MockLeader leader = new MockLeader(actorContext);
+ leader = new Leader(actorContext);
Map<String, String> leadersSnapshot = new HashMap<>();
leadersSnapshot.put("1", "A");
ByteString bs = toByteString(leadersSnapshot);
byte[] barray = bs.toByteArray();
- leader.createFollowerToSnapshot("followerId", bs);
+ FollowerToSnapshot fts = leader.new FollowerToSnapshot(bs);
+ leader.setFollowerSnapshot(FOLLOWER_ID, fts);
+
assertEquals(bs.size(), barray.length);
int chunkIndex=0;
j = barray.length;
}
- ByteString chunk = leader.getFollowerToSnapshot().getNextChunk();
+ ByteString chunk = fts.getNextChunk();
assertEquals("bytestring size not matching for chunk:"+ chunkIndex, j-i, chunk.size());
- assertEquals("chunkindex not matching", chunkIndex, leader.getFollowerToSnapshot().getChunkIndex());
+ assertEquals("chunkindex not matching", chunkIndex, fts.getChunkIndex());
- leader.getFollowerToSnapshot().markSendStatus(true);
- if (!leader.getFollowerToSnapshot().isLastChunk(chunkIndex)) {
- leader.getFollowerToSnapshot().incrementChunkIndex();
+ fts.markSendStatus(true);
+ if (!fts.isLastChunk(chunkIndex)) {
+ fts.incrementChunkIndex();
}
}
- assertEquals("totalChunks not matching", chunkIndex, leader.getFollowerToSnapshot().getTotalChunks());
+ assertEquals("totalChunks not matching", chunkIndex, fts.getTotalChunks());
}
-
@Override protected RaftActorBehavior createBehavior(
RaftActorContext actorContext) {
return new Leader(actorContext);
}
- @Override protected RaftActorContext createActorContext() {
+ @Override
+ protected MockRaftActorContext createActorContext() {
return createActorContext(leaderActor);
}
@Override
- protected RaftActorContext createActorContext(ActorRef actorRef) {
+ protected MockRaftActorContext createActorContext(ActorRef actorRef) {
+ return createActorContext("leader", actorRef);
+ }
+
+ private MockRaftActorContext createActorContextWithFollower() {
+ MockRaftActorContext actorContext = createActorContext();
+ actorContext.setPeerAddresses(ImmutableMap.<String, String>builder().put(FOLLOWER_ID,
+ followerActor.path().toString()).build());
+ return actorContext;
+ }
+
+ private MockRaftActorContext createActorContext(String id, ActorRef actorRef) {
DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
configParams.setHeartBeatInterval(new FiniteDuration(50, TimeUnit.MILLISECONDS));
configParams.setElectionTimeoutFactor(100000);
- MockRaftActorContext context = new MockRaftActorContext("test", getSystem(), actorRef);
+ MockRaftActorContext context = new MockRaftActorContext(id, getSystem(), actorRef);
context.setConfigParams(configParams);
return context;
}
- private ByteString toByteString(Map<String, String> state) {
- ByteArrayOutputStream b = null;
- ObjectOutputStream o = null;
- try {
- try {
- b = new ByteArrayOutputStream();
- o = new ObjectOutputStream(b);
- o.writeObject(state);
- byte[] snapshotBytes = b.toByteArray();
- return ByteString.copyFrom(snapshotBytes);
- } finally {
- if (o != null) {
- o.flush();
- o.close();
- }
- if (b != null) {
- b.close();
- }
- }
- } catch (IOException e) {
- Assert.fail("IOException in converting Hashmap to Bytestring:" + e);
- }
- return null;
- }
-
- public static class ForwardMessageToBehaviorActor extends MessageCollectorActor {
- AbstractRaftActorBehavior behavior;
-
- @Override public void onReceive(Object message) throws Exception {
- if(behavior != null) {
- behavior.handleMessage(sender(), message);
- }
-
- super.onReceive(message);
- }
-
- public static Props props() {
- return Props.create(ForwardMessageToBehaviorActor.class);
- }
- }
-
@Test
public void testLeaderCreatedWithCommitIndexLessThanLastIndex() throws Exception {
- new JavaTestKit(getSystem()) {{
- TestActorRef<ForwardMessageToBehaviorActor> leaderActor = TestActorRef.create(getSystem(),
- Props.create(ForwardMessageToBehaviorActor.class));
+ logStart("testLeaderCreatedWithCommitIndexLessThanLastIndex");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContextWithFollower();
- TestActorRef<ForwardMessageToBehaviorActor> followerActor = TestActorRef.create(getSystem(),
- ForwardMessageToBehaviorActor.props());
+ MockRaftActorContext followerActorContext = createActorContext(FOLLOWER_ID, followerActor);
- MockRaftActorContext followerActorContext =
- new MockRaftActorContext("follower", getSystem(), followerActor);
+ Follower follower = new Follower(followerActorContext);
+ followerActor.underlyingActor().setBehavior(follower);
- Follower follower = new Follower(followerActorContext);
- followerActor.underlyingActor().behavior = follower;
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put(FOLLOWER_ID, followerActor.path().toString());
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower", followerActor.path().toString());
+ leaderActorContext.setPeerAddresses(peerAddresses);
- leaderActorContext.setPeerAddresses(peerAddresses);
+ leaderActorContext.getReplicatedLog().removeFrom(0);
- leaderActorContext.getReplicatedLog().removeFrom(0);
+ //create 3 entries
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- //create 3 entries
- leaderActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ leaderActorContext.setCommitIndex(1);
- leaderActorContext.setCommitIndex(1);
+ followerActorContext.getReplicatedLog().removeFrom(0);
- followerActorContext.getReplicatedLog().removeFrom(0);
+ // follower too has the exact same log entries and has the same commit index
+ followerActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- // follower too has the exact same log entries and has the same commit index
- followerActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ followerActorContext.setCommitIndex(1);
- followerActorContext.setCommitIndex(1);
+ leader = new Leader(leaderActorContext);
- Leader leader = new Leader(leaderActorContext);
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- AppendEntries appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ assertEquals(1, appendEntries.getLeaderCommit());
+ assertEquals(0, appendEntries.getEntries().size());
+ assertEquals(0, appendEntries.getPrevLogIndex());
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(0, appendEntries.getEntries().size());
- assertEquals(0, appendEntries.getPrevLogIndex());
+ AppendEntriesReply appendEntriesReply = MessageCollectorActor.expectFirstMatching(
+ leaderActor, AppendEntriesReply.class);
- AppendEntriesReply appendEntriesReply = MessageCollectorActor.getFirstMatching(
- leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
+ assertEquals(2, appendEntriesReply.getLogLastIndex());
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
- assertEquals(2, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
+ // follower returns its next index
+ assertEquals(2, appendEntriesReply.getLogLastIndex());
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
- // follower returns its next index
- assertEquals(2, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
- }};
+ follower.close();
}
-
@Test
public void testLeaderCreatedWithCommitIndexLessThanFollowersCommitIndex() throws Exception {
- new JavaTestKit(getSystem()) {{
- TestActorRef<ForwardMessageToBehaviorActor> leaderActor = TestActorRef.create(getSystem(),
- Props.create(ForwardMessageToBehaviorActor.class));
+ logStart("testLeaderCreatedWithCommitIndexLessThanFollowersCommitIndex");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContext();
- TestActorRef<ForwardMessageToBehaviorActor> followerActor = TestActorRef.create(getSystem(),
- ForwardMessageToBehaviorActor.props());
+ MockRaftActorContext followerActorContext = createActorContext(FOLLOWER_ID, followerActor);
- MockRaftActorContext followerActorContext =
- new MockRaftActorContext("follower", getSystem(), followerActor);
+ Follower follower = new Follower(followerActorContext);
+ followerActor.underlyingActor().setBehavior(follower);
- Follower follower = new Follower(followerActorContext);
- followerActor.underlyingActor().behavior = follower;
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put(FOLLOWER_ID, followerActor.path().toString());
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower", followerActor.path().toString());
+ leaderActorContext.setPeerAddresses(peerAddresses);
- leaderActorContext.setPeerAddresses(peerAddresses);
+ leaderActorContext.getReplicatedLog().removeFrom(0);
- leaderActorContext.getReplicatedLog().removeFrom(0);
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- leaderActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ leaderActorContext.setCommitIndex(1);
- leaderActorContext.setCommitIndex(1);
+ followerActorContext.getReplicatedLog().removeFrom(0);
- followerActorContext.getReplicatedLog().removeFrom(0);
+ followerActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- followerActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ // follower has the same log entries but its commit index > leaders commit index
+ followerActorContext.setCommitIndex(2);
- // follower has the same log entries but its commit index > leaders commit index
- followerActorContext.setCommitIndex(2);
+ leader = new Leader(leaderActorContext);
- Leader leader = new Leader(leaderActorContext);
+ // Initial heartbeat
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- // Initial heartbeat
- AppendEntries appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ assertEquals(1, appendEntries.getLeaderCommit());
+ assertEquals(0, appendEntries.getEntries().size());
+ assertEquals(0, appendEntries.getPrevLogIndex());
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(0, appendEntries.getEntries().size());
- assertEquals(0, appendEntries.getPrevLogIndex());
+ AppendEntriesReply appendEntriesReply = MessageCollectorActor.expectFirstMatching(
+ leaderActor, AppendEntriesReply.class);
- AppendEntriesReply appendEntriesReply = MessageCollectorActor.getFirstMatching(
- leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
+ assertEquals(2, appendEntriesReply.getLogLastIndex());
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
- assertEquals(2, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
+ leaderActor.underlyingActor().setBehavior(follower);
+ leader.handleMessage(followerActor, appendEntriesReply);
- leaderActor.underlyingActor().behavior = leader;
- leader.handleMessage(followerActor, appendEntriesReply);
+ leaderActor.underlyingActor().clear();
+ followerActor.underlyingActor().clear();
- leaderActor.underlyingActor().clear();
- followerActor.underlyingActor().clear();
+ Uninterruptibles.sleepUninterruptibly(leaderActorContext.getConfigParams().getHeartBeatInterval().toMillis(),
+ TimeUnit.MILLISECONDS);
- Uninterruptibles.sleepUninterruptibly(leaderActorContext.getConfigParams().getHeartBeatInterval().toMillis(),
- TimeUnit.MILLISECONDS);
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- leader.handleMessage(leaderActor, new SendHeartBeat());
+ appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ assertEquals(2, appendEntries.getLeaderCommit());
+ assertEquals(0, appendEntries.getEntries().size());
+ assertEquals(2, appendEntries.getPrevLogIndex());
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(0, appendEntries.getEntries().size());
- assertEquals(2, appendEntries.getPrevLogIndex());
+ appendEntriesReply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
- appendEntriesReply = MessageCollectorActor.getFirstMatching(leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
+ assertEquals(2, appendEntriesReply.getLogLastIndex());
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
- assertEquals(2, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
+ assertEquals(2, followerActorContext.getCommitIndex());
- assertEquals(1, followerActorContext.getCommitIndex());
- }};
+ follower.close();
}
@Test
public void testHandleAppendEntriesReplyFailure(){
- new JavaTestKit(getSystem()) {
- {
-
- ActorRef leaderActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testHandleAppendEntriesReplyFailure");
- ActorRef followerActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ MockRaftActorContext leaderActorContext = createActorContextWithFollower();
+ leader = new Leader(leaderActorContext);
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ // Send initial heartbeat reply with last index.
+ leader.handleAppendEntriesReply(followerActor, new AppendEntriesReply(FOLLOWER_ID, 1, true, 10, 1));
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1",
- followerActor.path().toString());
+ FollowerLogInformation followerInfo = leader.getFollower(FOLLOWER_ID);
+ assertEquals("getNextIndex", 11, followerInfo.getNextIndex());
- leaderActorContext.setPeerAddresses(peerAddresses);
+ AppendEntriesReply reply = new AppendEntriesReply(FOLLOWER_ID, 1, false, 10, 1);
- Leader leader = new Leader(leaderActorContext);
+ RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
- AppendEntriesReply reply = new AppendEntriesReply("follower-1", 1, false, 10, 1);
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
- RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
-
- assertEquals(RaftState.Leader, raftActorBehavior.state());
-
- }};
+ assertEquals("getNextIndex", 10, followerInfo.getNextIndex());
}
@Test
public void testHandleAppendEntriesReplySuccess() throws Exception {
- new JavaTestKit(getSystem()) {
- {
-
- ActorRef leaderActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testHandleAppendEntriesReplySuccess");
- ActorRef followerActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ MockRaftActorContext leaderActorContext = createActorContextWithFollower();
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ leaderActorContext.setCommitIndex(1);
+ leaderActorContext.setLastApplied(1);
+ leaderActorContext.getTermInformation().update(1, "leader");
- leaderActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ leader = new Leader(leaderActorContext);
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1",
- followerActor.path().toString());
+ AppendEntriesReply reply = new AppendEntriesReply(FOLLOWER_ID, 1, true, 2, 1);
- leaderActorContext.setPeerAddresses(peerAddresses);
- leaderActorContext.setCommitIndex(1);
- leaderActorContext.setLastApplied(1);
- leaderActorContext.getTermInformation().update(1, "leader");
+ RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
- Leader leader = new Leader(leaderActorContext);
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
- AppendEntriesReply reply = new AppendEntriesReply("follower-1", 1, true, 2, 1);
+ assertEquals(2, leaderActorContext.getCommitIndex());
- RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
+ ApplyLogEntries applyLogEntries = MessageCollectorActor.expectFirstMatching(
+ leaderActor, ApplyLogEntries.class);
- assertEquals(RaftState.Leader, raftActorBehavior.state());
+ assertEquals(2, leaderActorContext.getLastApplied());
- assertEquals(2, leaderActorContext.getCommitIndex());
+ assertEquals(2, applyLogEntries.getToIndex());
- ApplyLogEntries applyLogEntries =
- MessageCollectorActor.getFirstMatching(leaderActor,
- ApplyLogEntries.class);
+ List<ApplyState> applyStateList = MessageCollectorActor.getAllMatching(leaderActor,
+ ApplyState.class);
- assertNotNull(applyLogEntries);
+ assertEquals(1,applyStateList.size());
- assertEquals(2, leaderActorContext.getLastApplied());
+ ApplyState applyState = applyStateList.get(0);
- assertEquals(2, applyLogEntries.getToIndex());
-
- List<Object> applyStateList = MessageCollectorActor.getAllMatching(leaderActor,
- ApplyState.class);
-
- assertEquals(1,applyStateList.size());
-
- ApplyState applyState = (ApplyState) applyStateList.get(0);
-
- assertEquals(2, applyState.getReplicatedLogEntry().getIndex());
-
- }};
+ assertEquals(2, applyState.getReplicatedLogEntry().getIndex());
}
@Test
public void testHandleAppendEntriesReplyUnknownFollower(){
- new JavaTestKit(getSystem()) {
- {
-
- ActorRef leaderActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testHandleAppendEntriesReplyUnknownFollower");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContext();
- Leader leader = new Leader(leaderActorContext);
+ leader = new Leader(leaderActorContext);
- AppendEntriesReply reply = new AppendEntriesReply("follower-1", 1, false, 10, 1);
+ AppendEntriesReply reply = new AppendEntriesReply("unkown-follower", 1, false, 10, 1);
- RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(getRef(), reply);
+ RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
- assertEquals(RaftState.Leader, raftActorBehavior.state());
-
- }};
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
}
@Test
public void testHandleRequestVoteReply(){
- new JavaTestKit(getSystem()) {
- {
-
- ActorRef leaderActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testHandleRequestVoteReply");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContext();
- Leader leader = new Leader(leaderActorContext);
+ leader = new Leader(leaderActorContext);
- RaftActorBehavior raftActorBehavior = leader.handleRequestVoteReply(getRef(), new RequestVoteReply(1, true));
+ // Should be a no-op.
+ RaftActorBehavior raftActorBehavior = leader.handleRequestVoteReply(followerActor,
+ new RequestVoteReply(1, true));
- assertEquals(RaftState.Leader, raftActorBehavior.state());
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
- raftActorBehavior = leader.handleRequestVoteReply(getRef(), new RequestVoteReply(1, false));
+ raftActorBehavior = leader.handleRequestVoteReply(followerActor, new RequestVoteReply(1, false));
- assertEquals(RaftState.Leader, raftActorBehavior.state());
- }};
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
}
@Test
public void testIsolatedLeaderCheckNoFollowers() {
- new JavaTestKit(getSystem()) {{
- ActorRef leaderActor = getTestActor();
+ logStart("testIsolatedLeaderCheckNoFollowers");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
- leaderActorContext.setPeerAddresses(peerAddresses);
-
- Leader leader = new Leader(leaderActorContext);
- RaftActorBehavior behavior = leader.handleMessage(leaderActor, new IsolatedLeaderCheck());
- Assert.assertTrue(behavior instanceof Leader);
- }};
+ leader = new Leader(leaderActorContext);
+ RaftActorBehavior behavior = leader.handleMessage(leaderActor, new IsolatedLeaderCheck());
+ Assert.assertTrue(behavior instanceof Leader);
}
@Test
public void testIsolatedLeaderCheckTwoFollowers() throws Exception {
+ logStart("testIsolatedLeaderCheckTwoFollowers");
+
new JavaTestKit(getSystem()) {{
ActorRef followerActor1 = getTestActor();
ActorRef followerActor2 = getTestActor();
- MockRaftActorContext leaderActorContext = (MockRaftActorContext) createActorContext();
+ MockRaftActorContext leaderActorContext = createActorContext();
Map<String, String> peerAddresses = new HashMap<>();
peerAddresses.put("follower-1", followerActor1.path().toString());
leaderActorContext.setPeerAddresses(peerAddresses);
- Leader leader = new Leader(leaderActorContext);
- leader.stopIsolatedLeaderCheckSchedule();
+ leader = new Leader(leaderActorContext);
leader.markFollowerActive("follower-1");
leader.markFollowerActive("follower-2");
behavior = leader.handleMessage(leaderActor, new IsolatedLeaderCheck());
Assert.assertTrue("Behavior not instance of IsolatedLeader when majority followers are inactive",
behavior instanceof IsolatedLeader);
-
}};
}
@Test
public void testAppendEntryCallAtEndofAppendEntryReply() throws Exception {
- new JavaTestKit(getSystem()) {{
- TestActorRef<MessageCollectorActor> leaderActor = TestActorRef.create(getSystem(),
- Props.create(MessageCollectorActor.class));
+ logStart("testAppendEntryCallAtEndofAppendEntryReply");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
-
- DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
- //configParams.setHeartBeatInterval(new FiniteDuration(9, TimeUnit.SECONDS));
- configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
+ MockRaftActorContext leaderActorContext = createActorContextWithFollower();
- leaderActorContext.setConfigParams(configParams);
+ DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
+ //configParams.setHeartBeatInterval(new FiniteDuration(9, TimeUnit.SECONDS));
+ configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
- TestActorRef<ForwardMessageToBehaviorActor> followerActor = TestActorRef.create(getSystem(),
- ForwardMessageToBehaviorActor.props());
+ leaderActorContext.setConfigParams(configParams);
- MockRaftActorContext followerActorContext =
- new MockRaftActorContext("follower-reply", getSystem(), followerActor);
+ MockRaftActorContext followerActorContext = createActorContext(FOLLOWER_ID, followerActor);
- followerActorContext.setConfigParams(configParams);
+ followerActorContext.setConfigParams(configParams);
- Follower follower = new Follower(followerActorContext);
- followerActor.underlyingActor().behavior = follower;
+ Follower follower = new Follower(followerActorContext);
+ followerActor.underlyingActor().setBehavior(follower);
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-reply",
- followerActor.path().toString());
+ leaderActorContext.getReplicatedLog().removeFrom(0);
+ leaderActorContext.setCommitIndex(-1);
+ leaderActorContext.setLastApplied(-1);
- leaderActorContext.setPeerAddresses(peerAddresses);
+ followerActorContext.getReplicatedLog().removeFrom(0);
+ followerActorContext.setCommitIndex(-1);
+ followerActorContext.setLastApplied(-1);
- leaderActorContext.getReplicatedLog().removeFrom(0);
- leaderActorContext.setCommitIndex(-1);
- leaderActorContext.setLastApplied(-1);
+ leader = new Leader(leaderActorContext);
- followerActorContext.getReplicatedLog().removeFrom(0);
- followerActorContext.setCommitIndex(-1);
- followerActorContext.setLastApplied(-1);
+ AppendEntriesReply appendEntriesReply = MessageCollectorActor.expectFirstMatching(
+ leaderActor, AppendEntriesReply.class);
- Leader leader = new Leader(leaderActorContext);
+ leader.handleMessage(followerActor, appendEntriesReply);
- AppendEntriesReply appendEntriesReply = MessageCollectorActor.getFirstMatching(
- leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
- System.out.println("appendEntriesReply: "+appendEntriesReply);
- leader.handleMessage(followerActor, appendEntriesReply);
+ // Clear initial heartbeat messages
- // Clear initial heartbeat messages
+ leaderActor.underlyingActor().clear();
+ followerActor.underlyingActor().clear();
- leaderActor.underlyingActor().clear();
- followerActor.underlyingActor().clear();
+ // create 3 entries
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ leaderActorContext.setCommitIndex(1);
+ leaderActorContext.setLastApplied(1);
- // create 3 entries
- leaderActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- leaderActorContext.setCommitIndex(1);
- leaderActorContext.setLastApplied(1);
+ Uninterruptibles.sleepUninterruptibly(leaderActorContext.getConfigParams().getHeartBeatInterval().toMillis(),
+ TimeUnit.MILLISECONDS);
- Uninterruptibles.sleepUninterruptibly(leaderActorContext.getConfigParams().getHeartBeatInterval().toMillis(),
- TimeUnit.MILLISECONDS);
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- leader.handleMessage(leaderActor, new SendHeartBeat());
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- AppendEntries appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ // Should send first log entry
+ assertEquals(1, appendEntries.getLeaderCommit());
+ assertEquals(0, appendEntries.getEntries().get(0).getIndex());
+ assertEquals(-1, appendEntries.getPrevLogIndex());
- // Should send first log entry
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(0, appendEntries.getEntries().get(0).getIndex());
- assertEquals(-1, appendEntries.getPrevLogIndex());
+ appendEntriesReply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
- appendEntriesReply = MessageCollectorActor.getFirstMatching(leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
+ assertEquals(0, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
- assertEquals(0, appendEntriesReply.getLogLastIndex());
+ followerActor.underlyingActor().clear();
- followerActor.underlyingActor().clear();
+ leader.handleAppendEntriesReply(followerActor, appendEntriesReply);
- leader.handleAppendEntriesReply(followerActor, appendEntriesReply);
+ appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ // Should send second log entry
+ assertEquals(1, appendEntries.getLeaderCommit());
+ assertEquals(1, appendEntries.getEntries().get(0).getIndex());
- // Should send second log entry
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(1, appendEntries.getEntries().get(0).getIndex());
- }};
+ follower.close();
}
- class MockLeader extends Leader {
+ @Test
+ public void testLaggingFollowerStarvation() throws Exception {
+ logStart("testLaggingFollowerStarvation");
+ new JavaTestKit(getSystem()) {{
+ String leaderActorId = actorFactory.generateActorId("leader");
+ String follower1ActorId = actorFactory.generateActorId("follower");
+ String follower2ActorId = actorFactory.generateActorId("follower");
- FollowerToSnapshot fts;
+ TestActorRef<ForwardMessageToBehaviorActor> leaderActor =
+ actorFactory.createTestActor(ForwardMessageToBehaviorActor.props(), leaderActorId);
+ ActorRef follower1Actor = actorFactory.createActor(MessageCollectorActor.props(), follower1ActorId);
+ ActorRef follower2Actor = actorFactory.createActor(MessageCollectorActor.props(), follower2ActorId);
- public MockLeader(RaftActorContext context){
- super(context);
- }
+ MockRaftActorContext leaderActorContext =
+ new MockRaftActorContext(leaderActorId, getSystem(), leaderActor);
- public FollowerToSnapshot getFollowerToSnapshot() {
- return fts;
- }
+ DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
+ configParams.setHeartBeatInterval(new FiniteDuration(200, TimeUnit.MILLISECONDS));
+ configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
- public void createFollowerToSnapshot(String followerId, ByteString bs ) {
- fts = new FollowerToSnapshot(bs);
- setFollowerSnapshot(followerId, fts);
- }
+ leaderActorContext.setConfigParams(configParams);
+
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(1,5,1).build());
+
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put(follower1ActorId,
+ follower1Actor.path().toString());
+ peerAddresses.put(follower2ActorId,
+ follower2Actor.path().toString());
+
+ leaderActorContext.setPeerAddresses(peerAddresses);
+ leaderActorContext.getTermInformation().update(1, leaderActorId);
+
+ RaftActorBehavior leader = createBehavior(leaderActorContext);
+
+ leaderActor.underlyingActor().setBehavior(leader);
+
+ for(int i=1;i<6;i++) {
+ // Each AppendEntriesReply could end up rescheduling the heartbeat (without the fix for bug 2733)
+ RaftActorBehavior newBehavior = leader.handleMessage(follower1Actor, new AppendEntriesReply(follower1ActorId, 1, true, i, 1));
+ assertTrue(newBehavior == leader);
+ Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS);
+ }
+
+ // Check if the leader has been receiving SendHeartbeat messages despite getting AppendEntriesReply
+ List<SendHeartBeat> heartbeats = MessageCollectorActor.getAllMatching(leaderActor, SendHeartBeat.class);
+
+ assertTrue(String.format("%s heartbeat(s) is less than expected", heartbeats.size()),
+ heartbeats.size() > 1);
+
+ // Check if follower-2 got AppendEntries during this time and was not starved
+ List<AppendEntries> appendEntries = MessageCollectorActor.getAllMatching(follower2Actor, AppendEntries.class);
+
+ assertTrue(String.format("%s append entries is less than expected", appendEntries.size()),
+ appendEntries.size() > 1);
+
+ }};
+ }
+
+ @Override
+ protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
+ ActorRef actorRef, RaftRPC rpc) throws Exception {
+ super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
+ assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor());
}
private class MockConfigParamsImpl extends DefaultConfigParamsImpl {
--- /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.cluster.raft.utils;
+
+import akka.actor.Props;
+import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
+
+public class ForwardMessageToBehaviorActor extends MessageCollectorActor {
+ private RaftActorBehavior behavior;
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if(behavior != null) {
+ behavior.handleMessage(sender(), message);
+ }
+
+ super.onReceive(message);
+ }
+
+ public static Props props() {
+ return Props.create(ForwardMessageToBehaviorActor.class);
+ }
+
+ public void setBehavior(RaftActorBehavior behavior){
+ this.behavior = behavior;
+ }
+}
+
package org.opendaylight.controller.cluster.raft.utils;
import akka.actor.ActorRef;
+import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.pattern.Patterns;
import akka.util.Timeout;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import org.junit.Assert;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
* @return
*/
public static <T> T getFirstMatching(ActorRef actor, Class<T> clazz) throws Exception {
- for(int i = 0; i < 50; i++) {
- List<Object> allMessages = getAllMessages(actor);
+ List<Object> allMessages = getAllMessages(actor);
- for(Object message : allMessages){
- if(message.getClass().equals(clazz)){
- return (T) message;
- }
+ for(Object message : allMessages){
+ if(message.getClass().equals(clazz)){
+ return (T) message;
}
+ }
+
+ return null;
+ }
- Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
+ public static <T> T expectFirstMatching(ActorRef actor, Class<T> clazz) {
+ return expectFirstMatching(actor, clazz, 5000);
+ }
+
+ public static <T> T expectFirstMatching(ActorRef actor, Class<T> clazz, long timeout) {
+ int count = (int) (timeout / 50);
+ for(int i = 0; i < count; i++) {
+ try {
+ T message = getFirstMatching(actor, clazz);
+ if(message != null) {
+ return message;
+ }
+ } catch (Exception e) {}
+
+ Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
}
+ Assert.fail("Did not receive message of type " + clazz);
return null;
}
- public static List<Object> getAllMatching(ActorRef actor, Class<?> clazz) throws Exception {
+ public static <T> List<T> getAllMatching(ActorRef actor, Class<T> clazz) throws Exception {
List<Object> allMessages = getAllMessages(actor);
- List<Object> output = Lists.newArrayList();
+ List<T> output = Lists.newArrayList();
for(Object message : allMessages){
if(message.getClass().equals(clazz)){
- output.add(message);
+ output.add((T) message);
}
}
throw new TimeoutException("Actor not ready in time.");
}
+
+ public static Props props() {
+ return Props.create(MessageCollectorActor.class);
+ }
}
org.slf4j.simpleLogger.logFile=System.out
org.slf4j.simpleLogger.showShortLogName=true
org.slf4j.simpleLogger.levelInBrackets=true
-org.slf4j.simpleLogger.org.opendaylight.controller.cluster.raft=trace
\ No newline at end of file
+org.slf4j.simpleLogger.log.org.opendaylight.controller.cluster.raft=trace
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html.
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+
+/**
+ * Modified Data Object.
+ *
+ * Represents modification of Data Object.
+ *
+ */
+public interface DataObjectModification<T extends DataObject> extends Identifiable<PathArgument> {
+
+ enum ModificationType {
+ /**
+ *
+ * Child node (direct or indirect) was modified.
+ *
+ */
+ SUBTREE_MODIFIED,
+ /**
+ *
+ * Node was explicitly created / overwritten.
+ *
+ */
+ WRITE,
+ /**
+ *
+ * Node was deleted.
+ *
+ */
+ DELETE
+ }
+
+ @Override
+ PathArgument getIdentifier();
+
+ /**
+ * Returns type of modified object.
+ *
+ * @return type of modified object.
+ */
+ @Nonnull Class<T> getDataType();
+
+ /**
+ *
+ * Returns type of modification
+ *
+ * @return type Type of performed modification.
+ */
+ @Nonnull ModificationType getModificationType();
+
+ /**
+ * Returns after state of top level container.
+ *
+ * @param root Class representing data container
+ * @return State of object after modification. Null if subtree is not present.
+ */
+ @Nullable T getDataAfter();
+
+ /**
+ * Returns unmodifiable collection of modified direct children.
+ *
+ * @return unmodifiable collection of modified direct children.
+ */
+ @Nonnull Collection<DataObjectModification<? extends DataObject>> getModifiedChildren();
+
+
+}
--- /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.md.sal.binding.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+
+/**
+ * Interface implemented by classes interested in receiving notifications about
+ * data tree changes. This interface differs from {@link DataChangeListener}
+ * in that it provides a cursor-based view of the change, which has potentially
+ * lower overhead and allow more flexible consumption of change event.
+ */
+public interface DataTreeChangeListener extends EventListener {
+ /**
+ * Invoked when there was data change for the supplied path, which was used
+ * to register this listener.
+ *
+ * <p>
+ * This method may be also invoked during registration of the listener if
+ * there is any pre-existing data in the conceptual data tree for supplied
+ * path. This initial event will contain all pre-existing data as created.
+ *
+ * <p>
+ * A data change event may be triggered spuriously, e.g. such that data before
+ * and after compare as equal. Implementations of this interface are expected
+ * to recover from such events. Event producers are expected to exert reasonable
+ * effort to suppress such events.
+ *
+ * In other words, it is completely acceptable to observe
+ * a {@link DataObjectModification}, while the state observed before and
+ * after- data items compare as equal.
+ *
+ * @param changes Collection of change events, may not be null or empty.
+ */
+ void onDataTreeChanged(@Nonnull Collection<DataTreeModification> changes);
+}
--- /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.md.sal.binding.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMService} which allows users to register for changes to a
+ * subtree.
+ */
+public interface DataTreeChangeService extends BindingService {
+ /**
+ * Registers a {@link DataTreeChangeListener} to receive
+ * notifications when data changes under a given path in the conceptual data
+ * tree.
+ * <p>
+ * You are able to register for notifications for any node or subtree
+ * which can be represented using {@link DataTreeIdentifier}.
+ * <p>
+ *
+ * You are able to register for data change notifications for a subtree or leaf
+ * even if it does not exist. You will receive notification once that node is
+ * created.
+ * <p>
+ * If there is any pre-existing data in the data tree for the path for which you are
+ * registering, you will receive an initial data change event, which will
+ * contain all pre-existing data, marked as created.
+ *
+ * <p>
+ * This method returns a {@link ListenerRegistration} object. To
+ * "unregister" your listener for changes call the {@link ListenerRegistration#close()}
+ * method on the returned object.
+ * <p>
+ * You MUST explicitly unregister your listener when you no longer want to receive
+ * notifications. This is especially true in OSGi environments, where failure to
+ * do so during bundle shutdown can lead to stale listeners being still registered.
+ *
+ * @param treeId
+ * Data tree identifier of the subtree which should be watched for
+ * changes.
+ * @param listener
+ * Listener instance which is being registered
+ * @return Listener registration object, which may be used to unregister
+ * your listener using {@link ListenerRegistration#close()} to stop
+ * delivery of change events.
+ */
+ @Nonnull <L extends DataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(@Nonnull DataTreeIdentifier treeId, @Nonnull L listener);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A unique identifier for a particular subtree. It is composed of the logical
+ * data store type and the instance identifier of the root node.
+ */
+public final class DataTreeIdentifier implements Immutable, Path<DataTreeIdentifier>, Serializable {
+ private static final long serialVersionUID = 1L;
+ private final InstanceIdentifier<?> rootIdentifier;
+ private final LogicalDatastoreType datastoreType;
+
+ public DataTreeIdentifier(final LogicalDatastoreType datastoreType, final InstanceIdentifier<?> rootIdentifier) {
+ this.datastoreType = Preconditions.checkNotNull(datastoreType);
+ this.rootIdentifier = Preconditions.checkNotNull(rootIdentifier);
+ }
+
+ /**
+ * Return the logical data store type.
+ *
+ * @return Logical data store type. Guaranteed to be non-null.
+ */
+ public @Nonnull LogicalDatastoreType getDatastoreType() {
+ return datastoreType;
+ }
+
+ /**
+ * Return the {@link YangInstanceIdentifier} of the root node.
+ *
+ * @return Instance identifier corresponding to the root node.
+ */
+ public @Nonnull InstanceIdentifier<?> getRootIdentifier() {
+ return rootIdentifier;
+ }
+
+ @Override
+ public boolean contains(final DataTreeIdentifier other) {
+ return datastoreType == other.datastoreType && rootIdentifier.contains(other.rootIdentifier);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + datastoreType.hashCode();
+ result = prime * result + rootIdentifier.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof DataTreeIdentifier)) {
+ return false;
+ }
+ DataTreeIdentifier other = (DataTreeIdentifier) obj;
+ if (datastoreType != other.datastoreType) {
+ return false;
+ }
+ return rootIdentifier.equals(other.rootIdentifier);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html.
+ */
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Represent root of modification.
+ *
+ * @author Tony Tkacik <ttkacik@cisco.com>
+ *
+ */
+public interface DataTreeModification {
+
+ /**
+ * Get the modification root path. This is the path of the root node
+ * relative to the root of InstanceIdentifier namespace.
+ *
+ * @return absolute path of the root node
+ */
+ @Nonnull DataTreeIdentifier getRootPath();
+
+ /**
+ * Get the modification root node.
+ *
+ * @return modification root node
+ */
+ @Nonnull DataObjectModification<? extends DataObject> getRootNode();
+
+}
<type>test-jar</type>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-service</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-native</artifactId>
<artifactId>yang-parser-impl</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-test-model</artifactId>
- </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-test-model</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>opendaylight-l2-types</artifactId>
+ </dependency>
</dependencies>
<build>
<plugins>
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
-/*
+/**
* FIXME: THis test should be moved to sal-binding-broker and rewriten
* to use new DataBroker API
*/
@SuppressWarnings("deprecation")
public class ConcurrentImplicitCreateTest extends AbstractDataServiceTest {
- private static final NodeKey NODE_FOO_KEY = new NodeKey(new NodeId("foo"));
- private static final NodeKey NODE_BAR_KEY = new NodeKey(new NodeId("foo"));
- private static InstanceIdentifier<Nodes> NODES_PATH = InstanceIdentifier.builder(Nodes.class).build();
- private static InstanceIdentifier<Node> NODE_FOO_PATH = NODES_PATH.child(Node.class, NODE_FOO_KEY);
- private static InstanceIdentifier<Node> NODE_BAR_PATH = NODES_PATH.child(Node.class, NODE_FOO_KEY);
+ private static final TopLevelListKey FOO_KEY = new TopLevelListKey("foo");
+ private static final TopLevelListKey BAR_KEY = new TopLevelListKey("bar");
+ private static InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.builder(Top.class).build();
+ private static InstanceIdentifier<TopLevelList> FOO_PATH = TOP_PATH.child(TopLevelList.class, FOO_KEY);
+ private static InstanceIdentifier<TopLevelList> BAR_PATH = TOP_PATH.child(TopLevelList.class, BAR_KEY);
@Test
public void testConcurrentCreate() throws InterruptedException, ExecutionException {
DataModificationTransaction fooTx = baDataService.beginTransaction();
DataModificationTransaction barTx = baDataService.beginTransaction();
- fooTx.putOperationalData(NODE_FOO_PATH, new NodeBuilder().setKey(NODE_FOO_KEY).build());
- barTx.putOperationalData(NODE_BAR_PATH, new NodeBuilder().setKey(NODE_BAR_KEY).build());
+ fooTx.putOperationalData(FOO_PATH, new TopLevelListBuilder().setKey(FOO_KEY).build());
+ barTx.putOperationalData(BAR_PATH, new TopLevelListBuilder().setKey(BAR_KEY).build());
Future<RpcResult<TransactionStatus>> fooFuture = fooTx.commit();
Future<RpcResult<TransactionStatus>> barFuture = barTx.commit();
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import com.google.common.util.concurrent.SettableFuture;
-/*
- * FIXME: THis test should be moved to compat test-suite and rewriten
- * to use sal-test-model
+/**
+ * FIXME: THis test should be moved to compat test-suite
*/
@SuppressWarnings("deprecation")
public class WildcardedDataChangeListenerTest extends AbstractDataServiceTest {
- private static final NodeKey NODE_0_KEY = new NodeKey(new NodeId("test:0"));
- private static final NodeKey NODE_1_KEY = new NodeKey(new NodeId("test:1"));
+ private static final TopLevelListKey TOP_LEVEL_LIST_0_KEY = new TopLevelListKey("test:0");
+ private static final TopLevelListKey TOP_LEVEL_LIST_1_KEY = new TopLevelListKey("test:1");
- public static final InstanceIdentifier<Flow> DEEP_WILDCARDED_PATH = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class) //
- .augmentation(FlowCapableNode.class) //
- .child(Table.class) //
- .child(Flow.class) //
+ protected static final InstanceIdentifier<ListViaUses> DEEP_WILDCARDED_PATH = InstanceIdentifier
+ .builder(Top.class)
+ .child(TopLevelList.class) //
+ .augmentation(TreeComplexUsesAugment.class) //
+ .child(ListViaUses.class) //
.build();
- private static final TableKey TABLE_0_KEY = new TableKey((short) 0);
- private static final TableFeaturesKey TABLE_FEATURES_KEY = new TableFeaturesKey((short) 0);
-
- private static final InstanceIdentifier<Table> NODE_0_TABLE_PATH = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class, NODE_0_KEY) //
- .augmentation(FlowCapableNode.class) //
- .child(Table.class, TABLE_0_KEY) //
+ private static final InstanceIdentifier<TreeComplexUsesAugment> NODE_0_TCU_PATH = InstanceIdentifier
+ .builder(Top.class)
+ .child(TopLevelList.class, TOP_LEVEL_LIST_0_KEY) //
+ .augmentation(TreeComplexUsesAugment.class) //
.build();
- private static final InstanceIdentifier<Table> NODE_1_TABLE_PATH = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class, NODE_1_KEY) //
- .augmentation(FlowCapableNode.class) //
- .child(Table.class, TABLE_0_KEY) //
+ private static final InstanceIdentifier<TreeComplexUsesAugment> NODE_1_TCU_PATH = InstanceIdentifier
+ .builder(Top.class)
+ .child(TopLevelList.class, TOP_LEVEL_LIST_1_KEY) //
+ .augmentation(TreeComplexUsesAugment.class) //
.build();
- private static final FlowKey FLOW_KEY = new FlowKey(new FlowId("test"));
- private static final InstanceIdentifier<Flow> NODE_0_FLOW_PATH = NODE_0_TABLE_PATH.child(Flow.class, FLOW_KEY);
+ private static final ListViaUsesKey LIST_VIA_USES_KEY = new ListViaUsesKey("test");
+
+ private static final InstanceIdentifier<ListViaUses> NODE_0_LVU_PATH = NODE_0_TCU_PATH.child(ListViaUses.class, LIST_VIA_USES_KEY);
- private static final InstanceIdentifier<Flow> NODE_1_FLOW_PATH = NODE_1_TABLE_PATH.child(Flow.class, FLOW_KEY);
+ private static final InstanceIdentifier<ListViaUses> NODE_1_LVU_PATH = NODE_1_TCU_PATH.child(ListViaUses.class, LIST_VIA_USES_KEY);
- private static final InstanceIdentifier<TableFeatures> NODE_0_TABLE_FEATURES_PATH =
- NODE_0_TABLE_PATH.child(TableFeatures.class, TABLE_FEATURES_KEY);
+ private static final InstanceIdentifier<ContainerWithUses> NODE_0_CWU_PATH =
+ NODE_0_TCU_PATH.child(ContainerWithUses.class);
- private static final TableFeatures TABLE_FEATURES = new TableFeaturesBuilder()//
- .setKey(TABLE_FEATURES_KEY) //
- .setName("Foo") //
- .setMaxEntries(1000L) //
+ private static final ContainerWithUses CWU= new ContainerWithUsesBuilder()//
+ .setLeafFromGrouping("some container value") //
.build();
- private static final Flow FLOW = new FlowBuilder() //
- .setKey(FLOW_KEY) //
- .setBarrier(true) //
- .setStrict(true) //
+ private static final ListViaUses LVU = new ListViaUsesBuilder() //
+ .setKey(LIST_VIA_USES_KEY) //
+ .setName("john")
.build();
@Test
- public void testSepareteWrites() throws InterruptedException, TimeoutException, ExecutionException {
+ public void testSeparateWrites() throws InterruptedException, TimeoutException, ExecutionException {
DataProviderService dataBroker = testContext.getBindingDataBroker();
final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> eventFuture = SettableFuture.create();
dataBroker.registerDataChangeListener(DEEP_WILDCARDED_PATH, new DataChangeListener() {
-
@Override
public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
eventFuture.set(dataChangeEvent);
});
DataModificationTransaction transaction = dataBroker.beginTransaction();
- transaction.putOperationalData(NODE_0_TABLE_FEATURES_PATH, TABLE_FEATURES);
- transaction.putOperationalData(NODE_0_FLOW_PATH, FLOW);
- transaction.putOperationalData(NODE_1_FLOW_PATH, FLOW);
+ transaction.putOperationalData(NODE_0_CWU_PATH, CWU);
+ transaction.putOperationalData(NODE_0_LVU_PATH, LVU);
+ transaction.putOperationalData(NODE_1_LVU_PATH, LVU);
transaction.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> event = eventFuture.get(1000, TimeUnit.MILLISECONDS);
final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> eventFuture = SettableFuture.create();
dataBroker.registerDataChangeListener(DEEP_WILDCARDED_PATH, new DataChangeListener() {
-
@Override
public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
eventFuture.set(dataChangeEvent);
}
});
- DataModificationTransaction tableTx = dataBroker.beginTransaction();
- tableTx.putOperationalData(NODE_0_TABLE_FEATURES_PATH, TABLE_FEATURES);
- tableTx.commit().get();
+ DataModificationTransaction cwuTx = dataBroker.beginTransaction();
+ cwuTx.putOperationalData(NODE_0_CWU_PATH, CWU);
+ cwuTx.commit().get();
assertFalse(eventFuture.isDone());
- DataModificationTransaction flowTx = dataBroker.beginTransaction();
+ DataModificationTransaction lvuTx = dataBroker.beginTransaction();
- Table table = new TableBuilder() //
- .setKey(TABLE_0_KEY) //
- .setFlow(Collections.singletonList(FLOW)) //
- .build();
+ TreeComplexUsesAugment tcua = new TreeComplexUsesAugmentBuilder()
+ .setListViaUses(Collections.singletonList(LVU)).build();
- flowTx.putOperationalData(NODE_0_TABLE_PATH, table);
- flowTx.putOperationalData(NODE_1_FLOW_PATH, FLOW);
- flowTx.commit().get();
+ lvuTx.putOperationalData(NODE_0_TCU_PATH, tcua);
+ lvuTx.putOperationalData(NODE_1_LVU_PATH, LVU);
+ lvuTx.commit().get();
validateEvent(eventFuture.get(1000, TimeUnit.MILLISECONDS));
}
// We wrote initial state NODE_0_FLOW
DataModificationTransaction transaction = dataBroker.beginTransaction();
- transaction.putOperationalData(NODE_0_FLOW_PATH, FLOW);
+ transaction.putOperationalData(NODE_0_LVU_PATH, LVU);
transaction.commit().get();
// We registered DataChangeListener
assertFalse(eventFuture.isDone());
DataModificationTransaction secondTx = dataBroker.beginTransaction();
- secondTx.putOperationalData(NODE_0_FLOW_PATH, FLOW);
- secondTx.putOperationalData(NODE_1_FLOW_PATH, FLOW);
+ secondTx.putOperationalData(NODE_0_LVU_PATH, LVU);
+ secondTx.putOperationalData(NODE_1_LVU_PATH, LVU);
secondTx.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> event = (eventFuture.get(1000, TimeUnit.MILLISECONDS));
assertNotNull(event);
// Data change should contains NODE_1 Flow - which was added
- assertTrue(event.getCreatedOperationalData().containsKey(NODE_1_FLOW_PATH));
+ assertTrue(event.getCreatedOperationalData().containsKey(NODE_1_LVU_PATH));
// Data change must not containe NODE_0 Flow which was replaced with same value.
- assertFalse(event.getUpdatedOperationalData().containsKey(NODE_0_FLOW_PATH));
+ assertFalse(event.getUpdatedOperationalData().containsKey(NODE_0_LVU_PATH));
}
private static void validateEvent(final DataChangeEvent<InstanceIdentifier<?>, DataObject> event) {
assertNotNull(event);
- assertTrue(event.getCreatedOperationalData().containsKey(NODE_1_FLOW_PATH));
- assertTrue(event.getCreatedOperationalData().containsKey(NODE_0_FLOW_PATH));
- assertFalse(event.getCreatedOperationalData().containsKey(NODE_0_TABLE_FEATURES_PATH));
+ assertTrue(event.getCreatedOperationalData().containsKey(NODE_1_LVU_PATH));
+ assertTrue(event.getCreatedOperationalData().containsKey(NODE_0_LVU_PATH));
+ assertFalse(event.getCreatedOperationalData().containsKey(NODE_0_CWU_PATH));
}
}
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
@SuppressWarnings("deprecation")
public class DOMCodecBug02Test extends AbstractDataServiceTest {
- private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+ private static final InstanceIdentifier<Top> TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
.toInstance();
/**
.submit(new Callable<Future<RpcResult<TransactionStatus>>>() {
@Override
public Future<RpcResult<TransactionStatus>> call() throws Exception {
- NodesBuilder nodesBuilder = new NodesBuilder();
- nodesBuilder.setNode(Collections.<Node> emptyList());
+ TopBuilder topBuilder = new TopBuilder();
+ topBuilder.setTopLevelList(Collections.<TopLevelList> emptyList());
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.putOperationalData(NODES_INSTANCE_ID_BA, nodesBuilder.build());
+ transaction.putOperationalData(TOP_INSTANCE_ID_BA, topBuilder.build());
return transaction.commit();
}
});
RpcResult<TransactionStatus> result = future.get().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Nodes nodes = checkForNodes();
- assertNotNull(nodes);
+ Top top = checkForTop();
+ assertNotNull(top);
}
- private Nodes checkForNodes() {
- return (Nodes) baDataService.readOperationalData(NODES_INSTANCE_ID_BA);
+ private Top checkForTop() {
+ return (Top) baDataService.readOperationalData(TOP_INSTANCE_ID_BA);
}
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActions;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.supported.actions.ActionType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.supported.actions.ActionTypeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.SupportType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.CustomEnum;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.Cont2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.Cont2Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.cont2.Contlist1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.cont2.Contlist1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
@SuppressWarnings("deprecation")
public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataChangeListener {
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final String NODE_ID = "openflow:1";
+ private static final QName TOP_LEVEL_LIST_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final String TOP_LEVEL_LIST_NAME = "tll:foo";
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TOP_LEVEL_LIST_NAME);
- private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
- NODE_ID);
+ private static final Map<QName, Object> TLL_KEY_BI = Collections.<QName, Object> singletonMap(TOP_LEVEL_LIST_NAME_QNAME,
+ TOP_LEVEL_LIST_NAME);
- private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+ private static final InstanceIdentifier<Top> TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
.toInstance();
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = NODES_INSTANCE_ID_BA.child(Node.class, NODE_KEY);
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = TOP_INSTANCE_ID_BA.child(TopLevelList.class, TLL_KEY);
- private static final InstanceIdentifier<SupportedActions> SUPPORTED_ACTIONS_INSTANCE_ID_BA = //
- NODES_INSTANCE_ID_BA.builder() //
- .child(Node.class, NODE_KEY) //
- .augmentation(FlowCapableNode.class) //
- .child(SupportedActions.class)
+ private static final InstanceIdentifier<Cont2> CONT2_INSTANCE_ID_BA = //
+ TOP_INSTANCE_ID_BA.builder() //
+ .child(TopLevelList.class, TLL_KEY) //
+ .augmentation(TllComplexAugment.class) //
+ .child(Cont2.class)
.toInstance();
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier NODE_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier TLL_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
.toInstance();
- private static final QName SUPPORTED_ACTIONS_QNAME = QName.create(FlowCapableNode.QNAME, SupportedActions.QNAME.getLocalName());
+ private static final QName CONT2_QNAME = QName.create(TllComplexAugment.QNAME, Cont2.QNAME.getLocalName());
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier SUPPORTED_ACTIONS_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier CONT2_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
- .node(SUPPORTED_ACTIONS_QNAME) //
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
+ .node(CONT2_QNAME) //
.toInstance();
private final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> receivedChangeEvent = SettableFuture.create();
public void testAugmentSerialization() throws Exception {
- baDataService.registerDataChangeListener(NODES_INSTANCE_ID_BA, this);
+ baDataService.registerDataChangeListener(TOP_INSTANCE_ID_BA, this);
- NodeBuilder nodeBuilder = new NodeBuilder();
- nodeBuilder.setId(new NodeId(NODE_ID));
- nodeBuilder.setKey(NODE_KEY);
+ TopLevelListBuilder tllBuilder = new TopLevelListBuilder();
+ tllBuilder.setKey(TLL_KEY);
DataModificationTransaction transaction = baDataService.beginTransaction();
- FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
- fnub.setHardware("Hardware Foo");
- fnub.setManufacturer("Manufacturer Foo");
- fnub.setSerialNumber("Serial Foo");
- fnub.setDescription("Description Foo");
- fnub.setSoftware("JUnit emulated");
- FlowCapableNode fnu = fnub.build();
- nodeBuilder.addAugmentation(FlowCapableNode.class, fnu);
- Node original = nodeBuilder.build();
- transaction.putOperationalData(NODE_INSTANCE_ID_BA, original);
+ TllComplexAugmentBuilder tllcab = new TllComplexAugmentBuilder();
+ tllcab.setAttrStr1("Hardware Foo");
+ tllcab.setAttrStr2("Manufacturer Foo");
+ tllcab.setAttrStr3("Serial Foo");
+ tllcab.setAttrStr4("Description Foo");
+ TllComplexAugment tlca = tllcab.build();
+ tllBuilder.addAugmentation(TllComplexAugment.class, tlca);
+ TopLevelList original = tllBuilder.build();
+ transaction.putOperationalData(TLL_INSTANCE_ID_BA, original);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = receivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
assertNotNull(potential);
- verifyNodes((Nodes) potential.getUpdatedOperationalSubtree(),original);
- assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
- Nodes nodes = checkForNodes();
- verifyNodes(nodes,original);
+ verifyTll((Top) potential.getUpdatedOperationalSubtree(),original);
+ assertBindingIndependentVersion(TLL_INSTANCE_ID_BI);
+ Top top = checkForTop();
+ verifyTll(top,original);
testAddingNodeConnector();
- testNodeRemove();
+ testTllRemove();
}
public void testAugmentNestedSerialization() throws Exception {
DataModificationTransaction transaction = baDataService.beginTransaction();
- SupportedActionsBuilder actions = new SupportedActionsBuilder();
- ActionTypeBuilder action = new ActionTypeBuilder();
- action.setAction("foo-action");
- action.setSupportState(SupportType.Native);
- List<ActionType> actionTypes = Collections.singletonList(action.build());
- actions.setActionType(actionTypes );
+ Cont2Builder cont2b = new Cont2Builder();
+ Contlist1Builder cl1b = new Contlist1Builder();
+ cl1b.setAttrStr("foo-action");
+ cl1b.setAttrEnum(CustomEnum.Type1);
+ List<Contlist1> contlists = Collections.singletonList(cl1b.build());
+ cont2b.setContlist1(contlists);
- transaction.putOperationalData(SUPPORTED_ACTIONS_INSTANCE_ID_BA, actions.build());
+ transaction.putOperationalData(CONT2_INSTANCE_ID_BA, cont2b.build());
RpcResult<TransactionStatus> putResult = transaction.commit().get();
assertNotNull(putResult);
assertEquals(TransactionStatus.COMMITED, putResult.getResult());
- SupportedActions readedTable = (SupportedActions) baDataService.readOperationalData(SUPPORTED_ACTIONS_INSTANCE_ID_BA);
+ Cont2 readedTable = (Cont2) baDataService.readOperationalData(CONT2_INSTANCE_ID_BA);
assertNotNull(readedTable);
- CompositeNode biSupportedActions = biDataService.readOperationalData(SUPPORTED_ACTIONS_INSTANCE_ID_BI);
+ CompositeNode biSupportedActions = biDataService.readOperationalData(CONT2_INSTANCE_ID_BI);
assertNotNull(biSupportedActions);
}
private void testAddingNodeConnector() throws Exception {
-
- NodeConnectorId ncId = new NodeConnectorId("openflow:1:bar");
- NodeConnectorKey nodeKey = new NodeConnectorKey(ncId );
- InstanceIdentifier<NodeConnector> ncInstanceId = NODE_INSTANCE_ID_BA.child(NodeConnector.class, nodeKey);
- NodeConnectorBuilder ncBuilder = new NodeConnectorBuilder();
- ncBuilder.setId(ncId);
- ncBuilder.setKey(nodeKey);
- NodeConnector connector = ncBuilder.build();
+ NestedListKey nlKey = new NestedListKey("test:0:0");
+ InstanceIdentifier<NestedList> ncInstanceId = TLL_INSTANCE_ID_BA.child(NestedList.class, nlKey);
+ NestedListBuilder nlBuilder = new NestedListBuilder();
+ nlBuilder.setKey(nlKey);
+ NestedList nestedList = nlBuilder.build();
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.putOperationalData(ncInstanceId, connector);
+ transaction.putOperationalData(ncInstanceId, nestedList);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNotNull(node);
- assertNotNull(node.getNodeConnector());
- assertFalse(node.getNodeConnector().isEmpty());
- NodeConnector readedNc = node.getNodeConnector().get(0);
- assertNotNull(readedNc);
+ TopLevelList tll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNotNull(tll);
+ assertNotNull(tll.getNestedList());
+ assertFalse(tll.getNestedList().isEmpty());
+ NestedList readedNl = tll.getNestedList().get(0);
+ assertNotNull(readedNl);
}
- private void testNodeRemove() throws Exception {
+ private void testTllRemove() throws Exception {
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.removeOperationalData(NODE_INSTANCE_ID_BA);
+ transaction.removeOperationalData(TLL_INSTANCE_ID_BA);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNull(node);
+ TopLevelList tll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNull(tll);
}
- private void verifyNodes(final Nodes nodes,final Node original) {
- assertNotNull(nodes);
- assertNotNull(nodes.getNode());
- assertEquals(1, nodes.getNode().size());
- Node readedNode = nodes.getNode().get(0);
- assertEquals(original.getId(), readedNode.getId());
+ private void verifyTll(final Top top,final TopLevelList original) {
+ assertNotNull(top);
+ assertNotNull(top.getTopLevelList());
+ assertEquals(1, top.getTopLevelList().size());
+ TopLevelList readedNode = top.getTopLevelList().get(0);
+ assertEquals(original.getName(), readedNode.getName());
assertEquals(original.getKey(), readedNode.getKey());
- FlowCapableNode fnu = original.getAugmentation(FlowCapableNode.class);
- FlowCapableNode readedAugment = readedNode.getAugmentation(FlowCapableNode.class);
+ TllComplexAugment fnu = original.getAugmentation(TllComplexAugment.class);
+ TllComplexAugment readedAugment = readedNode.getAugmentation(TllComplexAugment.class);
assertNotNull(fnu);
- assertEquals(fnu.getDescription(), readedAugment.getDescription());
- assertEquals(fnu.getSerialNumber(), readedAugment.getSerialNumber());
+ assertEquals(fnu.getAttrStr2(), readedAugment.getAttrStr2());
+ assertEquals(fnu.getAttrStr3(), readedAugment.getAttrStr3());
}
assertNotNull(node);
}
- private Nodes checkForNodes() {
- return (Nodes) baDataService.readOperationalData(NODES_INSTANCE_ID_BA);
+ private Top checkForTop() {
+ return (Top) baDataService.readOperationalData(TOP_INSTANCE_ID_BA);
}
@Override
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsDataBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatisticsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.List11SimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.List11SimpleAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@SuppressWarnings("deprecation")
public class DeleteNestedAugmentationListenParentTest extends AbstractDataServiceTest {
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId("foo"));
+ private static final TopLevelListKey FOO_KEY = new TopLevelListKey("foo");
- private static final TableKey TABLE_KEY = new TableKey((short) 0);
+ private static final List1Key LIST1_KEY = new List1Key("one");
- private static final FlowKey FLOW_KEY = new FlowKey(new FlowId("100"));
+ private static final List11Key LIST11_KEY = new List11Key(100);
- private static final InstanceIdentifier<FlowCapableNode> NODE_AUGMENT_PATH = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class,NODE_KEY)
- .augmentation(FlowCapableNode.class)
+ private static final InstanceIdentifier<TllComplexAugment> TLL_COMPLEX_AUGMENT_PATH = InstanceIdentifier.builder(Top.class)
+ .child(TopLevelList.class,FOO_KEY)
+ .augmentation(TllComplexAugment.class)
.build();
- private static final InstanceIdentifier<Flow> FLOW_PATH = NODE_AUGMENT_PATH.builder()
- .child(Table.class,TABLE_KEY)
- .child(Flow.class,FLOW_KEY)
+ private static final InstanceIdentifier<List11> LIST11_PATH = TLL_COMPLEX_AUGMENT_PATH.builder()
+ .child(List1.class,LIST1_KEY)
+ .child(List11.class,LIST11_KEY)
.build();
public void deleteChildListenParent() throws InterruptedException, ExecutionException {
DataModificationTransaction initTx = baDataService.beginTransaction();
- initTx.putOperationalData(FLOW_PATH, flow());
+ initTx.putOperationalData(LIST11_PATH, createList11());
initTx.commit().get();
final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> event = SettableFuture.create();
- baDataService.registerDataChangeListener(FLOW_PATH, new DataChangeListener() {
+ baDataService.registerDataChangeListener(LIST11_PATH, new DataChangeListener() {
@Override
public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
});
DataModificationTransaction deleteTx = baDataService.beginTransaction();
- deleteTx.removeOperationalData(FLOW_PATH.augmentation(FlowStatisticsData.class));
+ deleteTx.removeOperationalData(LIST11_PATH.augmentation(List11SimpleAugment.class));
deleteTx.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedEvent = event.get();
- assertFalse(receivedEvent.getRemovedOperationalData().contains(NODE_AUGMENT_PATH));
+ assertFalse(receivedEvent.getRemovedOperationalData().contains(TLL_COMPLEX_AUGMENT_PATH));
}
- private Flow flow() {
- FlowBuilder builder = new FlowBuilder()
- .setKey(FLOW_KEY)
- .addAugmentation(FlowStatisticsData.class,new FlowStatisticsDataBuilder()
- .setFlowStatistics(new FlowStatisticsBuilder().build())
- .build())
- .setBarrier(true)
- .setMatch(new MatchBuilder()
- .build())
- ;
+ private List11 createList11() {
+ List11Builder builder = new List11Builder()
+ .setKey(LIST11_KEY)
+ .addAugmentation(List11SimpleAugment.class,new List11SimpleAugmentBuilder()
+ .setAttrStr2("bad").build())
+ .setAttrStr("good");
return builder.build();
}
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
import org.junit.Test;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PopMplsActionCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.pop.mpls.action._case.PopMplsActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.BitFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
@SuppressWarnings("deprecation")
public class FlagsSerializationTest extends AbstractDataServiceTest {
-
- private static final String FLOW_ID = "1234";
- private static final short TABLE_ID = (short)0;
- private static final String NODE_ID = "node:1";
-
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final FlowKey FLOW_KEY = new FlowKey(new FlowId(FLOW_ID));
- private static final TableKey TABLE_KEY = new TableKey(TABLE_ID);
-
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
-
- private static final InstanceIdentifier<? extends DataObject> FLOW_INSTANCE_ID_BA = //
- NODE_INSTANCE_ID_BA.builder() //
- .augmentation(FlowCapableNode.class)
- .child(Table.class,TABLE_KEY)
- .child(Flow.class, FLOW_KEY) //
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey("foo");
+ private static final List11Key LIST11_KEY = new List11Key(1234);
+ private static final List1Key LIST1_KEY = new List1Key("1");
+
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
+
+ private static final InstanceIdentifier<? extends DataObject> LIST11_INSTANCE_ID_BA = //
+ TLL_INSTANCE_ID_BA.builder() //
+ .augmentation(TllComplexAugment.class)
+ .child(List1.class,LIST1_KEY)
+ .child(List11.class, LIST11_KEY) //
.toInstance();
- private static final QName FLOW_FLAGS_QNAME = QName.create(Flow.QNAME, "flags");
+ private static final QName LIST11_FLAGS_QNAME = QName.create(List11.QNAME, "flags");
@Test
public void testIndirectGeneration() throws Exception {
- FlowModFlags checkOverlapFlags = new FlowModFlags(true,false,false,false,false);
- ImmutableSet<String> domCheckOverlapFlags = ImmutableSet.<String>of("CHECK_OVERLAP");
+ BitFlags checkOverlapFlags = new BitFlags(true,false,false,false,false);
+ ImmutableSet<String> domCheckOverlapFlags = ImmutableSet.<String>of("FLAG_FIVE");
testFlags(checkOverlapFlags,domCheckOverlapFlags);
- FlowModFlags allFalseFlags = new FlowModFlags(false,false,false,false,false);
+ BitFlags allFalseFlags = new BitFlags(false,false,false,false,false);
ImmutableSet<String> domAllFalseFlags = ImmutableSet.<String>of();
testFlags(allFalseFlags,domAllFalseFlags);
- FlowModFlags allTrueFlags = new FlowModFlags(true,true,true,true,true);
- ImmutableSet<String> domAllTrueFlags = ImmutableSet.<String>of("CHECK_OVERLAP","NO_BYT_COUNTS", "NO_PKT_COUNTS", "RESET_COUNTS", "SEND_FLOW_REM");
+ BitFlags allTrueFlags = new BitFlags(true,true,true,true,true);
+ ImmutableSet<String> domAllTrueFlags = ImmutableSet.<String>of("FLAG_ONE","FLAG_TWO","FLAG_THREE","FLAG_FOUR","FLAG_FIVE");
testFlags(allTrueFlags,domAllTrueFlags);
testFlags(null,null);
}
- private void testFlags(final FlowModFlags flagsToTest, final ImmutableSet<String> domFlags) throws Exception {
- Flow flow = createFlow(flagsToTest);
- assertNotNull(flow);
+ private void testFlags(final BitFlags flagsToTest, final ImmutableSet<String> domFlags) throws Exception {
+ List11 list11 = createList11(flagsToTest);
+ assertNotNull(list11);
- CompositeNode domFlow = biDataService.readConfigurationData(mappingService.toDataDom(FLOW_INSTANCE_ID_BA));
+ CompositeNode domList11 = biDataService.readConfigurationData(mappingService.toDataDom(LIST11_INSTANCE_ID_BA));
- assertNotNull(domFlow);
- org.opendaylight.yangtools.yang.data.api.Node<?> readedFlags = domFlow.getFirstSimpleByName(FLOW_FLAGS_QNAME);
+ assertNotNull(domList11);
+ org.opendaylight.yangtools.yang.data.api.Node<?> readedFlags = domList11.getFirstSimpleByName(LIST11_FLAGS_QNAME);
if(domFlags != null) {
assertNotNull(readedFlags);
} else {
assertNull(readedFlags);
}
- assertEquals(flagsToTest, flow.getFlags());
+ assertEquals(flagsToTest, list11.getFlags());
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.removeConfigurationData(FLOW_INSTANCE_ID_BA);
+ transaction.removeConfigurationData(LIST11_INSTANCE_ID_BA);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
}
- private Flow createFlow(final FlowModFlags flagsToTest) throws Exception {
+ private List11 createList11(final BitFlags flagsToTest) throws Exception {
DataModificationTransaction modification = baDataService.beginTransaction();
- FlowBuilder flow = new FlowBuilder();
- MatchBuilder match = new MatchBuilder();
- VlanMatchBuilder vlanBuilder = new VlanMatchBuilder();
- VlanIdBuilder vlanIdBuilder = new VlanIdBuilder();
- VlanId vlanId = new VlanId(10);
- vlanBuilder.setVlanId(vlanIdBuilder.setVlanId(vlanId).build());
- match.setVlanMatch(vlanBuilder.build());
-
- flow.setKey(FLOW_KEY);
- flow.setMatch(match.build());
-
- flow.setFlags(flagsToTest);
-
- InstructionsBuilder instructions = new InstructionsBuilder();
- InstructionBuilder instruction = new InstructionBuilder();
-
- instruction.setOrder(10);
- ApplyActionsBuilder applyActions = new ApplyActionsBuilder();
- List<Action> actionList = new ArrayList<>();
- PopMplsActionBuilder popMplsAction = new PopMplsActionBuilder();
- popMplsAction.setEthernetType(34);
- actionList.add(new ActionBuilder().setAction(new PopMplsActionCaseBuilder().setPopMplsAction(popMplsAction.build()).build()).setOrder(10).build());
-
- applyActions.setAction(actionList );
-
- instruction.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions.build()).build());
+ List11Builder list11b = new List11Builder();
+ list11b.setKey(LIST11_KEY);
+ list11b.setAttrStr("list:1:1");
- List<Instruction> instructionList = Collections.<Instruction>singletonList(instruction.build());
- instructions.setInstruction(instructionList );
+ list11b.setFlags(flagsToTest);
- flow.setInstructions(instructions.build());
- modification.putConfigurationData(FLOW_INSTANCE_ID_BA, flow.build());
+ modification.putConfigurationData(LIST11_INSTANCE_ID_BA, list11b.build());
RpcResult<TransactionStatus> ret = modification.commit().get();
assertNotNull(ret);
assertEquals(TransactionStatus.COMMITED, ret.getResult());
- return (Flow) baDataService.readConfigurationData(FLOW_INSTANCE_ID_BA);
+ return (List11) baDataService.readConfigurationData(LIST11_INSTANCE_ID_BA);
}
}
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.NestedListSimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.NestedListSimpleAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
@SuppressWarnings("deprecation")
public class PutAugmentationTest extends AbstractDataServiceTest implements DataChangeListener {
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final String NODE_ID = "openflow:1";
+ private static final QName TLL_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final String TLL_NAME = "foo";
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
- private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
- NODE_ID);
+ private static final Map<QName, Object> TLL_KEY_BI = Collections.<QName, Object> singletonMap(TLL_NAME_QNAME,
+ TLL_NAME);
- private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+ private static final InstanceIdentifier<Top> TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
.toInstance();
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = //
- NODES_INSTANCE_ID_BA.builder() //
- .child(Node.class, NODE_KEY).toInstance();
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = //
+ TOP_INSTANCE_ID_BA.builder() //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
- private static final InstanceIdentifier<FlowCapableNode> ALL_FLOW_CAPABLE_NODES = //
- NODES_INSTANCE_ID_BA.builder() //
- .child(Node.class) //
- .augmentation(FlowCapableNode.class) //
+ private static final InstanceIdentifier<TllComplexAugment> ALL_TCA = //
+ TOP_INSTANCE_ID_BA.builder() //
+ .child(TopLevelList.class) //
+ .augmentation(TllComplexAugment.class) //
.build();
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier NODE_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier TLL_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
.toInstance();
- private static final InstanceIdentifier<FlowCapableNode> FLOW_AUGMENTATION_PATH =
- NODE_INSTANCE_ID_BA.builder() //
- .augmentation(FlowCapableNode.class) //
+ private static final InstanceIdentifier<TllComplexAugment> TCA_AUGMENTATION_PATH =
+ TLL_INSTANCE_ID_BA.builder() //
+ .augmentation(TllComplexAugment.class) //
.build();
private SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> lastReceivedChangeEvent;
@Ignore
public void putNodeAndAugmentation() throws Exception {
lastReceivedChangeEvent = SettableFuture.create();
- baDataService.registerDataChangeListener(ALL_FLOW_CAPABLE_NODES, this);
+ baDataService.registerDataChangeListener(ALL_TCA, this);
- NodeBuilder nodeBuilder = new NodeBuilder();
- nodeBuilder.setId(new NodeId(NODE_ID));
- nodeBuilder.setKey(NODE_KEY);
+ TopLevelListBuilder nodeBuilder = new TopLevelListBuilder();
+ nodeBuilder.setKey(TLL_KEY);
DataModificationTransaction baseTransaction = baDataService.beginTransaction();
- baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build());
+ baseTransaction.putOperationalData(TLL_INSTANCE_ID_BA, nodeBuilder.build());
RpcResult<TransactionStatus> result = baseTransaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNotNull(node);
- assertEquals(NODE_KEY, node.getKey());
-
- FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
- fnub.setHardware("Hardware Foo");
- fnub.setManufacturer("Manufacturer Foo");
- fnub.setSerialNumber("Serial Foo");
- fnub.setDescription("Description Foo");
- fnub.setSoftware("JUnit emulated");
- FlowCapableNode fnu = fnub.build();
- InstanceIdentifier<FlowCapableNode> augmentIdentifier = NODE_INSTANCE_ID_BA
- .augmentation(FlowCapableNode.class);
+ TopLevelList tll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNotNull(tll);
+ assertEquals(TLL_KEY, tll.getKey());
+
+ TllComplexAugmentBuilder tcab = new TllComplexAugmentBuilder();
+ tcab.setAttrStr1("FooFoo");
+ tcab.setAttrStr2("BarBar");
+ TllComplexAugment tca = tcab.build();
+ InstanceIdentifier<TreeComplexUsesAugment> augmentIdentifier = TLL_INSTANCE_ID_BA
+ .augmentation(TreeComplexUsesAugment.class);
DataModificationTransaction augmentedTransaction = baDataService.beginTransaction();
- augmentedTransaction.putOperationalData(augmentIdentifier, fnu);
+ augmentedTransaction.putOperationalData(augmentIdentifier, tca);
lastReceivedChangeEvent = SettableFuture.create();
DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
assertNotNull(potential);
- assertTrue(potential.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+ assertTrue(potential.getCreatedOperationalData().containsKey(TCA_AUGMENTATION_PATH));
lastReceivedChangeEvent = SettableFuture.create();
- Node augmentedNode = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNotNull(node);
- assertEquals(NODE_KEY, augmentedNode.getKey());
+ TopLevelList augmentedTll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNotNull(tll);
+ assertEquals(TLL_KEY, augmentedTll.getKey());
System.out.println("Before assertion");
- assertNotNull(augmentedNode.getAugmentation(FlowCapableNode.class));
- FlowCapableNode readedAugmentation = augmentedNode.getAugmentation(FlowCapableNode.class);
- assertEquals(fnu.getDescription(), readedAugmentation.getDescription());
- assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
- testNodeRemove();
- assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
+ assertNotNull(augmentedTll.getAugmentation(TllComplexAugment.class));
+ TllComplexAugment readedAugmentation = augmentedTll.getAugmentation(TllComplexAugment.class);
+ assertEquals(tca.getAttrStr2(), readedAugmentation.getAttrStr2());
+ assertBindingIndependentVersion(TLL_INSTANCE_ID_BI);
+ testTllRemove();
+ assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(TCA_AUGMENTATION_PATH));
}
@Test
@Ignore
public void putNodeWithAugmentation() throws Exception {
lastReceivedChangeEvent = SettableFuture.create();
- baDataService.registerDataChangeListener(ALL_FLOW_CAPABLE_NODES, this);
-
- NodeBuilder nodeBuilder = new NodeBuilder();
- nodeBuilder.setId(new NodeId(NODE_ID));
- nodeBuilder.setKey(NODE_KEY);
- FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
- fnub.setHardware("Hardware Foo");
- fnub.setManufacturer("Manufacturer Foo");
- fnub.setSerialNumber("Serial Foo");
- fnub.setDescription("Description Foo");
- fnub.setSoftware("JUnit emulated");
- FlowCapableNode fnu = fnub.build();
-
- nodeBuilder.addAugmentation(FlowCapableNode.class, fnu);
+ baDataService.registerDataChangeListener(ALL_TCA, this);
+
+ TopLevelListBuilder nodeBuilder = new TopLevelListBuilder();
+ nodeBuilder.setKey(TLL_KEY);
+ TllComplexAugmentBuilder tcab = new TllComplexAugmentBuilder();
+ tcab.setAttrStr1("FooFoo");
+ tcab.setAttrStr2("BarBar");
+ TllComplexAugment tca = tcab.build();
+
+ nodeBuilder.addAugmentation(TreeComplexUsesAugment.class, tca);
DataModificationTransaction baseTransaction = baDataService.beginTransaction();
- baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build());
+ baseTransaction.putOperationalData(TLL_INSTANCE_ID_BA, nodeBuilder.build());
RpcResult<TransactionStatus> result = baseTransaction.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
assertNotNull(potential);
- assertTrue(potential.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+ assertTrue(potential.getCreatedOperationalData().containsKey(TCA_AUGMENTATION_PATH));
lastReceivedChangeEvent = SettableFuture.create();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- FlowCapableNode readedAugmentation = (FlowCapableNode) baDataService.readOperationalData(
- NODE_INSTANCE_ID_BA.augmentation(FlowCapableNode.class));
+ TllComplexAugment readedAugmentation = (TllComplexAugment) baDataService.readOperationalData(
+ TLL_INSTANCE_ID_BA.augmentation(TllComplexAugment.class));
assertNotNull(readedAugmentation);
- assertEquals(fnu.getHardware(), readedAugmentation.getHardware());
+ assertEquals(tca.getAttrStr1(), readedAugmentation.getAttrStr1());
testPutNodeConnectorWithAugmentation();
lastReceivedChangeEvent = SettableFuture.create();
- testNodeRemove();
+ testTllRemove();
- assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
+ assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(TCA_AUGMENTATION_PATH));
}
private void testPutNodeConnectorWithAugmentation() throws Exception {
- NodeConnectorKey ncKey = new NodeConnectorKey(new NodeConnectorId("test:0:0"));
- InstanceIdentifier<NodeConnector> ncPath = NODE_INSTANCE_ID_BA
- .child(NodeConnector.class, ncKey);
- InstanceIdentifier<FlowCapableNodeConnector> ncAugmentPath = ncPath
- .augmentation(FlowCapableNodeConnector.class);
+ NestedListKey ncKey = new NestedListKey("test:0:0");
+ InstanceIdentifier<NestedList> ncPath = TLL_INSTANCE_ID_BA
+ .child(NestedList.class, ncKey);
+ InstanceIdentifier<NestedListSimpleAugment> ncAugmentPath = ncPath
+ .augmentation(NestedListSimpleAugment.class);
- NodeConnectorBuilder nc = new NodeConnectorBuilder();
+ NestedListBuilder nc = new NestedListBuilder();
nc.setKey(ncKey);
- FlowCapableNodeConnectorBuilder fncb = new FlowCapableNodeConnectorBuilder();
- fncb.setName("Baz");
- nc.addAugmentation(FlowCapableNodeConnector.class, fncb.build());
+ NestedListSimpleAugmentBuilder fncb = new NestedListSimpleAugmentBuilder();
+ fncb.setType("Baz");
+ nc.addAugmentation(NestedListSimpleAugment.class, fncb.build());
DataModificationTransaction baseTransaction = baDataService.beginTransaction();
baseTransaction.putOperationalData(ncPath, nc.build());
RpcResult<TransactionStatus> result = baseTransaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- FlowCapableNodeConnector readedAugmentation = (FlowCapableNodeConnector) baDataService
+ NestedListSimpleAugment readedAugmentation = (NestedListSimpleAugment) baDataService
.readOperationalData(ncAugmentPath);
assertNotNull(readedAugmentation);
- assertEquals(fncb.getName(), readedAugmentation.getName());
+ assertEquals(fncb.getType(), readedAugmentation.getType());
}
- private void testNodeRemove() throws Exception {
+ private void testTllRemove() throws Exception {
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.removeOperationalData(NODE_INSTANCE_ID_BA);
+ transaction.removeOperationalData(TLL_INSTANCE_ID_BA);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNull(node);
+ TopLevelList tll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNull(tll);
}
- private void assertBindingIndependentVersion(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier nodeId) {
- CompositeNode node = biDataService.readOperationalData(nodeId);
- assertNotNull(node);
+ private void assertBindingIndependentVersion(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier tllId) {
+ CompositeNode tll = biDataService.readOperationalData(tllId);
+ assertNotNull(tll);
}
@Override
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@SuppressWarnings("deprecation")
public class WriteParentListenAugmentTest extends AbstractDataServiceTest {
- private static final String NODE_ID = "node:1";
+ private static final String TLL_NAME = "foo";
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
- private static final InstanceIdentifier<FlowCapableNode> AUGMENT_WILDCARDED_PATH = InstanceIdentifier
- .builder(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class).toInstance();
+ private static final InstanceIdentifier<TreeComplexUsesAugment> AUGMENT_WILDCARDED_PATH = InstanceIdentifier
+ .builder(Top.class).child(TopLevelList.class).augmentation(TreeComplexUsesAugment.class).toInstance();
- private static final InstanceIdentifier<FlowCapableNode> AUGMENT_NODE_PATH = InstanceIdentifier
- .builder(Nodes.class).child(Node.class, NODE_KEY).augmentation(FlowCapableNode.class).toInstance();
+ private static final InstanceIdentifier<TreeComplexUsesAugment> AUGMENT_TLL_PATH = InstanceIdentifier
+ .builder(Top.class).child(TopLevelList.class, TLL_KEY).augmentation(TreeComplexUsesAugment.class).toInstance();
@Test
public void writeNodeListenAugment() throws Exception {
DataModificationTransaction modification = baDataService.beginTransaction();
- Node node = new NodeBuilder() //
- .setKey(NODE_KEY) //
- .addAugmentation(FlowCapableNode.class, flowCapableNode("one")).build();
- modification.putOperationalData(NODE_INSTANCE_ID_BA, node);
+ TopLevelList tll = new TopLevelListBuilder() //
+ .setKey(TLL_KEY) //
+ .addAugmentation(TreeComplexUsesAugment.class, treeComplexUsesAugment("one")).build();
+ modification.putOperationalData(TLL_INSTANCE_ID_BA, tll);
modification.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedEvent = event.get(1000, TimeUnit.MILLISECONDS);
- assertTrue(receivedEvent.getCreatedOperationalData().containsKey(AUGMENT_NODE_PATH));
+ assertTrue(receivedEvent.getCreatedOperationalData().containsKey(AUGMENT_TLL_PATH));
dclRegistration.close();
DataModificationTransaction mod2 = baDataService.beginTransaction();
- mod2.putOperationalData(AUGMENT_NODE_PATH, flowCapableNode("two"));
+ mod2.putOperationalData(AUGMENT_TLL_PATH, treeComplexUsesAugment("two"));
mod2.commit().get();
- FlowCapableNode readedAug = (FlowCapableNode) baDataService.readOperationalData(AUGMENT_NODE_PATH);
- assertEquals("two", readedAug.getDescription());
+ TreeComplexUsesAugment readedAug = (TreeComplexUsesAugment) baDataService.readOperationalData(AUGMENT_TLL_PATH);
+ assertEquals("two", readedAug.getContainerWithUses().getLeafFromGrouping());
}
- private FlowCapableNode flowCapableNode(final String description) {
- return new FlowCapableNodeBuilder() //
- .setDescription(description) //
+ private TreeComplexUsesAugment treeComplexUsesAugment(final String value) {
+ return new TreeComplexUsesAugmentBuilder() //
+ .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping(value).build()) //
.build();
}
}
\ No newline at end of file
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
@SuppressWarnings("deprecation")
public class WriteParentReadChildTest extends AbstractDataServiceTest {
- private static final String FLOW_ID = "1234";
- private static final short TABLE_ID = (short) 0;
- private static final String NODE_ID = "node:1";
+ private static final int LIST11_ID = 1234;
+ private static final String LIST1_NAME = "bar";
+ private static final String TLL_NAME = "foo";
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final FlowKey FLOW_KEY = new FlowKey(new FlowId(FLOW_ID));
- private static final TableKey TABLE_KEY = new TableKey(TABLE_ID);
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
+ private static final List11Key LIST11_KEY = new List11Key(LIST11_ID);
+ private static final List1Key LIST1_KEY = new List1Key(LIST1_NAME);
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
- private static final InstanceIdentifier<Table> TABLE_INSTANCE_ID_BA = //
- NODE_INSTANCE_ID_BA.builder() //
- .augmentation(FlowCapableNode.class).child(Table.class, TABLE_KEY).build();
+ private static final InstanceIdentifier<List1> LIST1_INSTANCE_ID_BA = //
+ TLL_INSTANCE_ID_BA.builder() //
+ .augmentation(TllComplexAugment.class).child(List1.class, LIST1_KEY).build();
- private static final InstanceIdentifier<? extends DataObject> FLOW_INSTANCE_ID_BA = //
- TABLE_INSTANCE_ID_BA.child(Flow.class, FLOW_KEY);
+ private static final InstanceIdentifier<? extends DataObject> LIST11_INSTANCE_ID_BA = //
+ LIST1_INSTANCE_ID_BA.child(List11.class, LIST11_KEY);
/**
*
* The scenario tests writing parent node, which also contains child items
* @throws Exception
*/
@Test
- public void writeTableReadFlow() throws Exception {
+ public void writeParentReadChild() throws Exception {
DataModificationTransaction modification = baDataService.beginTransaction();
- Flow flow = new FlowBuilder() //
- .setKey(FLOW_KEY) //
- .setMatch(new MatchBuilder() //
- .setVlanMatch(new VlanMatchBuilder() //
- .setVlanId(new VlanIdBuilder() //
- .setVlanId(new VlanId(10)) //
- .build()) //
- .build()) //
- .build()) //
- .setInstructions(new InstructionsBuilder() //
- .setInstruction(ImmutableList.<Instruction>builder() //
- .build()) //
- .build()) //
+ List11 list11 = new List11Builder() //
+ .setKey(LIST11_KEY) //
+ .setAttrStr("primary")
.build();
- Table table = new TableBuilder()
- .setKey(TABLE_KEY)
- .setFlow(ImmutableList.of(flow))
+ List1 list1 = new List1Builder()
+ .setKey(LIST1_KEY)
+ .setList11(ImmutableList.of(list11))
.build();
- modification.putConfigurationData(TABLE_INSTANCE_ID_BA, table);
+ modification.putConfigurationData(LIST1_INSTANCE_ID_BA, list1);
RpcResult<TransactionStatus> ret = modification.commit().get();
assertNotNull(ret);
assertEquals(TransactionStatus.COMMITED, ret.getResult());
- DataObject readedTable = baDataService.readConfigurationData(TABLE_INSTANCE_ID_BA);
- assertNotNull("Readed table should not be nul.", readedTable);
- assertTrue(readedTable instanceof Table);
+ DataObject readList1 = baDataService.readConfigurationData(LIST1_INSTANCE_ID_BA);
+ assertNotNull("Readed table should not be nul.", readList1);
+ assertTrue(readList1 instanceof List1);
- DataObject readedFlow = baDataService.readConfigurationData(FLOW_INSTANCE_ID_BA);
- assertNotNull("Readed flow should not be null.",readedFlow);
- assertTrue(readedFlow instanceof Flow);
- assertEquals(flow, readedFlow);
+ DataObject readList11 = baDataService.readConfigurationData(LIST11_INSTANCE_ID_BA);
+ assertNotNull("Readed flow should not be null.",readList11);
+ assertTrue(readList11 instanceof List11);
+ assertEquals(list11, readList11);
}
}
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
-// FIXME: Migrate to use new Data Broker APIs
+/**
+ * FIXME: Migrate to use new Data Broker APIs
+ */
@SuppressWarnings("deprecation")
public class BrokerIntegrationTest extends AbstractDataServiceTest {
+ private static final TopLevelListKey TLL_FOO_KEY = new TopLevelListKey("foo");
+ private static final TopLevelListKey TLL_BAR_KEY = new TopLevelListKey("bar");
+ private static final TopLevelListKey TLL_BAZ_KEY = new TopLevelListKey("baz");
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.builder(Top.class).build();
+ private static final InstanceIdentifier<TopLevelList> FOO_PATH = TOP_PATH.child(TopLevelList.class, TLL_FOO_KEY);
+ private static final InstanceIdentifier<TopLevelList> BAR_PATH = TOP_PATH.child(TopLevelList.class, TLL_BAR_KEY);
+ private static final InstanceIdentifier<TopLevelList> BAZ_PATH = TOP_PATH.child(TopLevelList.class, TLL_BAZ_KEY);
+
@Test
public void simpleModifyOperation() throws Exception {
- NodeRef node1 = createNodeRef("0");
- DataObject node = baDataService.readConfigurationData(node1.getValue());
- assertNull(node);
- Node nodeData1 = createNode("0");
+ DataObject tllFoo = baDataService.readConfigurationData(FOO_PATH);
+ assertNull(tllFoo);
+ TopLevelList tllFooData = createTll(TLL_FOO_KEY);
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.putConfigurationData(node1.getValue(), nodeData1);
+ transaction.putConfigurationData(FOO_PATH, tllFooData);
Future<RpcResult<TransactionStatus>> commitResult = transaction.commit();
assertNotNull(commitResult);
assertNotNull(result.getResult());
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node readedData = (Node) baDataService.readConfigurationData(node1.getValue());
+ TopLevelList readedData = (TopLevelList) baDataService.readConfigurationData(FOO_PATH);
assertNotNull(readedData);
- assertEquals(nodeData1.getKey(), readedData.getKey());
+ assertEquals(tllFooData.getKey(), readedData.getKey());
- NodeRef nodeFoo = createNodeRef("foo");
- NodeRef nodeBar = createNodeRef("bar");
- Node nodeFooData = createNode("foo");
- Node nodeBarData = createNode("bar");
+ TopLevelList nodeBarData = createTll(TLL_BAR_KEY);
+ TopLevelList nodeBazData = createTll(TLL_BAZ_KEY);
DataModificationTransaction insertMoreTr = baDataService.beginTransaction();
- insertMoreTr.putConfigurationData(nodeFoo.getValue(), nodeFooData);
- insertMoreTr.putConfigurationData(nodeBar.getValue(), nodeBarData);
+ insertMoreTr.putConfigurationData(BAR_PATH, nodeBarData);
+ insertMoreTr.putConfigurationData(BAZ_PATH, nodeBazData);
RpcResult<TransactionStatus> result2 = insertMoreTr.commit().get();
assertNotNull(result2);
assertNotNull(result2.getResult());
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Nodes allNodes = (Nodes) baDataService.readConfigurationData(InstanceIdentifier.builder(Nodes.class)
- .toInstance());
- assertNotNull(allNodes);
- assertNotNull(allNodes.getNode());
- assertEquals(3, allNodes.getNode().size());
+ Top top = (Top) baDataService.readConfigurationData(TOP_PATH);
+ assertNotNull(top);
+ assertNotNull(top.getTopLevelList());
+ assertEquals(3, top.getTopLevelList().size());
/**
* We create transaction no 2
* We remove node 1
*
*/
- removalTransaction.removeConfigurationData(node1.getValue());
+ removalTransaction.removeConfigurationData(BAR_PATH);
/**
* We commit transaction
assertNotNull(result3.getResult());
assertEquals(TransactionStatus.COMMITED, result2.getResult());
- DataObject readedData2 = baDataService.readConfigurationData(node1.getValue());
+ DataObject readedData2 = baDataService.readConfigurationData(BAR_PATH);
assertNull(readedData2);
}
- private static NodeRef createNodeRef(final String string) {
- NodeKey key = new NodeKey(new NodeId(string));
- InstanceIdentifier<Node> path = InstanceIdentifier.builder(Nodes.class).child(Node.class, key)
- .toInstance();
- return new NodeRef(path);
- }
-
- private static Node createNode(final String string) {
- NodeBuilder ret = new NodeBuilder();
- ret.setId(new NodeId(string));
- ret.setKey(new NodeKey(ret.getId()));
+ private static TopLevelList createTll(final TopLevelListKey key) {
+ TopLevelListBuilder ret = new TopLevelListBuilder();
+ ret.setKey(key);
return ret.build();
}
}
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpVersion;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DecNwTtlCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.dec.nw.ttl._case.DecNwTtl;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.dec.nw.ttl._case.DecNwTtlBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.IpMatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List12Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List12Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.SettableFuture;
-// FIXME: Migrate to use new Data Broker APIs
+/**
+ * FIXME: Migrate to use new Data Broker APIs
+ */
@SuppressWarnings("deprecation")
public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest {
- private static final Logger LOG = LoggerFactory.getLogger(ChangeOriginatedInDomBrokerTest.class);
-
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final QName FLOW_ID_QNAME = QName.create(Flow.QNAME, "id");
- private static final QName TABLE_ID_QNAME = QName.create(Table.QNAME, "id");
+ protected static final Logger LOG = LoggerFactory.getLogger(ChangeOriginatedInDomBrokerTest.class);
- private static final String NODE_ID = "node:1";
- private static final FlowId FLOW_ID = new FlowId("1234");
- private static final Short TABLE_ID = Short.valueOf((short) 0);
+ private static final QName TLL_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final QName LIST1_ATTR_STR_QNAME = QName.create(List1.QNAME, "attr-str");
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final FlowKey FLOW_KEY = new FlowKey(FLOW_ID);
+ private static final String TLL_NAME = "1";
+ private static final int LIST11_ATTR_INT = 1234;
+ private static final String LIST1_ATTR_STR = "foo:foo";
- private final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> modificationCapture = SettableFuture.create();
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
+ private static final List1Key LIST1_KEY = new List1Key(LIST1_ATTR_STR);
+ private static final List11Key LIST11_KEY = new List11Key(LIST11_ATTR_INT);
- private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
- NODE_ID);
+ protected final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> modificationCapture = SettableFuture.create();
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
+ private static final Map<QName, Object> TLL_KEY_BI = Collections.<QName, Object> singletonMap(TLL_NAME_QNAME,
+ TLL_NAME);
- private static final Map<QName, Object> FLOW_KEY_BI = //
- ImmutableMap.<QName, Object> of(FLOW_ID_QNAME, FLOW_ID.getValue());
+ private static final InstanceIdentifier<TopLevelList> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
- private static final Map<QName, Object> TABLE_KEY_BI = //
- ImmutableMap.<QName, Object> of(TABLE_ID_QNAME, TABLE_ID);;
+ private static final Map<QName, Object> LIST1_KEY_BI = //
+ ImmutableMap.<QName, Object> of(LIST1_ATTR_STR_QNAME, LIST1_ATTR_STR);;
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier FLOW_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier LIST1_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
- .nodeWithKey(Table.QNAME, TABLE_KEY_BI) //
- .nodeWithKey(Flow.QNAME, FLOW_KEY_BI) //
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
+ .nodeWithKey(List1.QNAME, LIST1_KEY_BI) //
.toInstance();
- private static final TableKey TABLE_KEY_BA = new TableKey((short) 0);
- private static final InstanceIdentifier<Flow> FLOWS_PATH_BA = //
+ private static final InstanceIdentifier<List1> LIST1_PATH_BA = //
NODE_INSTANCE_ID_BA.builder() //
- .augmentation(FlowCapableNode.class) //
- .child(Table.class, TABLE_KEY_BA) //
- .child(Flow.class) //
+ .augmentation(TllComplexAugment.class) //
+ .child(List1.class, LIST1_KEY) //
.toInstance();
- private static final InstanceIdentifier<Flow> FLOW_INSTANCE_ID_BA = //
- FLOWS_PATH_BA.firstIdentifierOf(Table.class).child(Flow.class, FLOW_KEY);
-
@Test
public void simpleModifyOperation() throws Exception {
- assertNull(biDataService.readConfigurationData(FLOW_INSTANCE_ID_BI));
+ assertNull(biDataService.readConfigurationData(LIST1_INSTANCE_ID_BI));
registerChangeListener();
- CompositeNode domflow = createTestFlow();
+ CompositeNode domflow = createTestList1();
DataModificationTransaction biTransaction = biDataService.beginTransaction();
- biTransaction.putConfigurationData(FLOW_INSTANCE_ID_BI, domflow);
+ biTransaction.putConfigurationData(LIST1_INSTANCE_ID_BI, domflow);
RpcResult<TransactionStatus> biResult = biTransaction.commit().get();
assertEquals(TransactionStatus.COMMITED, biResult.getResult());
DataChangeEvent<InstanceIdentifier<?>, DataObject> event = modificationCapture.get(1000,TimeUnit.MILLISECONDS);
assertNotNull(event);
LOG.info("Created Configuration :{}",event.getCreatedConfigurationData());
- Flow flow = (Flow) event.getCreatedConfigurationData().get(FLOW_INSTANCE_ID_BA);
- assertNotNull(flow);
- assertNotNull(flow.getMatch());
+ List1 list1 = (List1) event.getCreatedConfigurationData().get(LIST1_PATH_BA);
+ assertNotNull(list1);
+ assertNotNull(list1.getAttrStr());
+ assertNotNull(list1.getList11());
+ assertNotNull(list1.getList12());
assertEquals(TransactionStatus.COMMITED, biResult.getResult());
}
private void registerChangeListener() {
- baDataService.registerDataChangeListener(FLOWS_PATH_BA, new DataChangeListener() {
+ baDataService.registerDataChangeListener(LIST1_PATH_BA, new DataChangeListener() {
@Override
public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
});
}
- private CompositeNode createTestFlow() {
- FlowBuilder flow = new FlowBuilder();
- flow.setKey(FLOW_KEY);
- Short tableId = 0;
- flow.setTableId(tableId);
- MatchBuilder match = new MatchBuilder();
- match.setIpMatch(new IpMatchBuilder().setIpProto(IpVersion.Ipv4).build());
- Ipv4MatchBuilder ipv4Match = new Ipv4MatchBuilder();
- // ipv4Match.setIpv4Destination(new Ipv4Prefix(cliInput.get(4)));
- Ipv4Prefix prefix = new Ipv4Prefix("10.0.0.1/24");
- ipv4Match.setIpv4Destination(prefix);
- Ipv4Match i4m = ipv4Match.build();
- match.setLayer3Match(i4m);
- flow.setMatch(match.build());
-
-
-
- // Create a drop action
- /*
- * Note: We are mishandling drop actions DropAction dropAction = new
- * DropActionBuilder().build(); ActionBuilder ab = new ActionBuilder();
- * ab.setAction(dropAction);
- */
-
- DecNwTtl decNwTtl = new DecNwTtlBuilder().build();
- ActionBuilder ab = new ActionBuilder();
- ActionKey actionKey = new ActionKey(0);
- ab.setKey(actionKey );
- ab.setAction(new DecNwTtlCaseBuilder().setDecNwTtl(decNwTtl).build());
-
- // Add our drop action to a list
- List<Action> actionList = new ArrayList<Action>();
- actionList.add(ab.build());
-
- // Create an Apply Action
- ApplyActionsBuilder aab = new ApplyActionsBuilder();
- aab.setAction(actionList);
-
- // Wrap our Apply Action in an Instruction
- InstructionBuilder ib = new InstructionBuilder();
- ib.setOrder(0);
- ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
-
- // Put our Instruction in a list of Instructions
- InstructionsBuilder isb = new InstructionsBuilder();
- List<Instruction> instructions = new ArrayList<Instruction>();
- instructions.add(ib.build());
- isb.setInstruction(instructions);
-
- // Add our instructions to the flow
- flow.setInstructions(isb.build());
-
- flow.setPriority(2);
- flow.setFlowName("Foo Name");
- CompositeNode domFlow = mappingService.toDataDom(flow.build());
- return domFlow;
+ private CompositeNode createTestList1() {
+ List1Builder l1b = new List1Builder();
+ List11Builder l11b = new List11Builder();
+ List12Builder l12b = new List12Builder();
+ l11b.setKey(LIST11_KEY);
+ l11b.setAttrStr("foo:foo:foo");
+ l12b.setKey(new List12Key(321));
+ l12b.setAttrStr("foo:foo:bar");
+ l1b.setKey(LIST1_KEY);
+ l1b.setList11(ImmutableList.of(l11b.build()));
+ l1b.setList12(ImmutableList.of(l12b.build()));
+ CompositeNode domList1 = mappingService.toDataDom(l1b.build());
+ return domList1;
}
}
*/
package org.opendaylight.controller.sal.binding.test.connect.dom;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
-import java.math.BigInteger;
import java.util.Collections;
import java.util.Map;
import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupStatistics;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.statistics.GroupStatistics;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.List11SimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.top.top.level.list.list1.list1._1.Cont;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@SuppressWarnings("deprecation")
public class CrossBrokerMountPointTest {
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final String NODE_ID = "node:1";
+ private static final QName TLL_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final String TLL_NAME = "foo:1";
+
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+ private static final Map<QName, Object> TLL_KEY_BI = Collections.<QName, Object> singletonMap(TLL_NAME_QNAME,
+ TLL_NAME);
- private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
- NODE_ID);
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).build();
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
- private static GroupKey GROUP_KEY = new GroupKey(new GroupId(0L));
+ private static final List1Key LIST1_KEY = new List1Key("foo");
+ private static final List11Key LIST11_KEY = new List11Key(1);
- private static final InstanceIdentifier<GroupStatistics> GROUP_STATISTICS_ID_BA = NODE_INSTANCE_ID_BA
- .builder().augmentation(FlowCapableNode.class) //
- .child(Group.class, GROUP_KEY) //
- .augmentation(NodeGroupStatistics.class) //
- .child(GroupStatistics.class) //
- .toInstance();
+ private static final InstanceIdentifier<Cont> AUG_CONT_ID_BA = TLL_INSTANCE_ID_BA
+ .builder().augmentation(TllComplexAugment.class) //
+ .child(List1.class, LIST1_KEY) //
+ .child(List11.class, LIST11_KEY) //
+ .augmentation(List11SimpleAugment.class) //
+ .child(Cont.class) //
+ .build();
- private static final QName AUGMENTED_GROUP_STATISTICS = QName.create(NodeGroupStatistics.QNAME,
- GroupStatistics.QNAME.getLocalName());
+ private static final QName AUG_CONT = QName.create(List11.QNAME,
+ Cont.QNAME.getLocalName());
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier NODE_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier TLL_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
- .toInstance();
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
+ .build();
private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier GROUP_STATISTICS_ID_BI = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
//
- .builder(NODE_INSTANCE_ID_BI)
- .nodeWithKey(QName.create(FlowCapableNode.QNAME, "group"), QName.create(FlowCapableNode.QNAME, "group-id"),
- 0L).node(AUGMENTED_GROUP_STATISTICS).toInstance();
+ .builder(TLL_INSTANCE_ID_BI)
+ .nodeWithKey(QName.create(TllComplexAugment.QNAME, "list1"), QName.create(TllComplexAugment.QNAME, "attr-str"),
+ LIST1_KEY.getAttrStr())
+ .nodeWithKey(QName.create(TllComplexAugment.QNAME, "list1-1"), QName.create(TllComplexAugment.QNAME, "attr-int"),
+ LIST11_KEY.getAttrInt())
+ .node(AUG_CONT).build();
private BindingTestContext testContext;
private MountProviderService bindingMountPointService;
@Test
public void testMountPoint() {
- testContext.getBindingDataBroker().readOperationalData(NODE_INSTANCE_ID_BA);
+ testContext.getBindingDataBroker().readOperationalData(TLL_INSTANCE_ID_BA);
- MountProvisionInstance domMountPoint = domMountPointService.createMountPoint(NODE_INSTANCE_ID_BI);
+ MountProvisionInstance domMountPoint = domMountPointService.createMountPoint(TLL_INSTANCE_ID_BI);
assertNotNull(domMountPoint);
- MountProviderInstance bindingMountPoint = bindingMountPointService.getMountPoint(NODE_INSTANCE_ID_BA);
+ MountProviderInstance bindingMountPoint = bindingMountPointService.getMountPoint(TLL_INSTANCE_ID_BA);
assertNotNull(bindingMountPoint);
- final BigInteger packetCount = BigInteger.valueOf(500L);
+ final Integer attrIntalue = 500;
DataReader<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, CompositeNode> simpleReader = new DataReader<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, CompositeNode>() {
if (arg0.equals(GROUP_STATISTICS_ID_BI)) {
ImmutableCompositeNode data = ImmutableCompositeNode
.builder()
- .setQName(AUGMENTED_GROUP_STATISTICS)
- .addLeaf(QName.create(AUGMENTED_GROUP_STATISTICS, "packet-count"), packetCount) //
- .toInstance();
+ .setQName(AUG_CONT)
+ .addLeaf(QName.create(AUG_CONT, "attr-int"), attrIntalue) //
+ .build();
return data;
}
}
};
- domMountPoint.registerOperationalReader(NODE_INSTANCE_ID_BI, simpleReader);
+ domMountPoint.registerOperationalReader(TLL_INSTANCE_ID_BI, simpleReader);
- GroupStatistics data = (GroupStatistics) bindingMountPoint.readOperationalData(GROUP_STATISTICS_ID_BA);
+ Cont data = (Cont) bindingMountPoint.readOperationalData(AUG_CONT_ID_BA);
assertNotNull(data);
- assertEquals(packetCount,data.getPacketCount().getValue());
+ assertEquals(attrIntalue ,data.getAttrInt());
}
}
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
-import java.math.BigInteger;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.Future;
-
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeFlowRemoved;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.OpendaylightOfMigrationTestModelService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.TestContext;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.Future;
public class CrossBrokerRpcTest {
- protected RpcProviderRegistry baRpcRegistry;
- protected RpcProvisionRegistry biRpcRegistry;
+ protected RpcProviderRegistry providerRegistry;
+ protected RpcProvisionRegistry provisionRegistry;
private BindingTestContext testContext;
private RpcImplementation biRpcInvoker;
- private MessageCapturingFlowService flowService;
+ private MessageCapturingFlowService knockService;
- public static final NodeId NODE_A = new NodeId("a");
- public static final NodeId NODE_B = new NodeId("b");
- public static final NodeId NODE_C = new NodeId("c");
- public static final NodeId NODE_D = new NodeId("d");
+ public static final TopLevelListKey NODE_A = new TopLevelListKey("a");
+ public static final TopLevelListKey NODE_B = new TopLevelListKey("b");
+ public static final TopLevelListKey NODE_C = new TopLevelListKey("c");
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final QName ADD_FLOW_QNAME = QName.create(NodeFlowRemoved.QNAME, "add-flow");
+ private static final QName NODE_ID_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final QName KNOCK_KNOCK_QNAME = QName.create(KnockKnockOutput.QNAME, "knock-knock");
- public static final InstanceIdentifier<Node> BA_NODE_A_ID = createBANodeIdentifier(NODE_A);
- public static final InstanceIdentifier<Node> BA_NODE_B_ID = createBANodeIdentifier(NODE_B);
- public static final InstanceIdentifier<Node> BA_NODE_C_ID = createBANodeIdentifier(NODE_C);
- public static final InstanceIdentifier<Node> BA_NODE_D_ID = createBANodeIdentifier(NODE_D);
+ public static final InstanceIdentifier<Top> NODES_PATH = InstanceIdentifier.builder(Top.class).build();
+ public static final InstanceIdentifier<TopLevelList> BA_NODE_A_ID = NODES_PATH.child(TopLevelList.class, NODE_A);
+ public static final InstanceIdentifier<TopLevelList> BA_NODE_B_ID = NODES_PATH.child(TopLevelList.class, NODE_B);
+ public static final InstanceIdentifier<TopLevelList> BA_NODE_C_ID = NODES_PATH.child(TopLevelList.class, NODE_C);
- public static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_NODE_A_ID = createBINodeIdentifier(NODE_A);
- public static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_NODE_B_ID = createBINodeIdentifier(NODE_B);
public static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_NODE_C_ID = createBINodeIdentifier(NODE_C);
- public static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_NODE_D_ID = createBINodeIdentifier(NODE_D);
-
@Before
testContext = testFactory.getTestContext();
testContext.start();
- baRpcRegistry = testContext.getBindingRpcRegistry();
- biRpcRegistry = testContext.getDomRpcRegistry();
+ providerRegistry = testContext.getBindingRpcRegistry();
+ provisionRegistry = testContext.getDomRpcRegistry();
biRpcInvoker = testContext.getDomRpcInvoker();
- assertNotNull(baRpcRegistry);
- assertNotNull(biRpcRegistry);
+ assertNotNull(providerRegistry);
+ assertNotNull(provisionRegistry);
- flowService = MessageCapturingFlowService.create(baRpcRegistry);
+ knockService = MessageCapturingFlowService.create(providerRegistry);
}
@Test
public void bindingRoutedRpcProvider_DomInvokerTest() throws Exception {
- flowService//
- .registerPath(NodeContext.class, BA_NODE_A_ID) //
- .registerPath(NodeContext.class, BA_NODE_B_ID) //
- .setAddFlowResult(addFlowResult(true, 10));
+ knockService//
+ .registerPath(TestContext.class, BA_NODE_A_ID) //
+ .registerPath(TestContext.class, BA_NODE_B_ID) //
+ .setKnockKnockResult(knockResult(true, "open"));
- SalFlowService baFlowInvoker = baRpcRegistry.getRpcService(SalFlowService.class);
- assertNotSame(flowService, baFlowInvoker);
+ OpendaylightOfMigrationTestModelService baKnockInvoker =
+ providerRegistry.getRpcService(OpendaylightOfMigrationTestModelService.class);
+ assertNotSame(knockService, baKnockInvoker);
- AddFlowInput addFlowA = addFlow(BA_NODE_A_ID) //
- .setPriority(100).setBarrier(true).build();
+ KnockKnockInput knockKnockA = knockKnock(BA_NODE_A_ID) //
+ .setQuestion("who's there?").build();
- CompositeNode addFlowDom = toDomRpc(ADD_FLOW_QNAME, addFlowA);
- assertNotNull(addFlowDom);
- RpcResult<CompositeNode> domResult = biRpcInvoker.invokeRpc(ADD_FLOW_QNAME, addFlowDom).get();
+ CompositeNode knockKnockDom = toDomRpc(KNOCK_KNOCK_QNAME, knockKnockA);
+ assertNotNull(knockKnockDom);
+ RpcResult<CompositeNode> domResult = biRpcInvoker.invokeRpc(KNOCK_KNOCK_QNAME, knockKnockDom).get();
assertNotNull(domResult);
assertTrue("DOM result is successful.", domResult.isSuccessful());
- assertTrue("Bidning Add Flow RPC was captured.", flowService.getReceivedAddFlows().containsKey(BA_NODE_A_ID));
- assertEquals(addFlowA, flowService.getReceivedAddFlows().get(BA_NODE_A_ID).iterator().next());
+ assertTrue("Bidning Add Flow RPC was captured.", knockService.getReceivedKnocks().containsKey(BA_NODE_A_ID));
+ assertEquals(knockKnockA, knockService.getReceivedKnocks().get(BA_NODE_A_ID).iterator().next());
}
@Test
public void bindingRpcInvoker_DomRoutedProviderTest() throws Exception {
- AddFlowOutputBuilder builder = new AddFlowOutputBuilder();
- builder.setTransactionId(new TransactionId(BigInteger.valueOf(10)));
- final AddFlowOutput output = builder.build();
- org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration registration = biRpcRegistry.addRoutedRpcImplementation(ADD_FLOW_QNAME, new RpcImplementation() {
+ KnockKnockOutputBuilder builder = new KnockKnockOutputBuilder();
+ builder.setAnswer("open");
+ final KnockKnockOutput output = builder.build();
+ org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration registration = provisionRegistry.addRoutedRpcImplementation(KNOCK_KNOCK_QNAME, new RpcImplementation() {
@Override
public Set<QName> getSupportedRpcs() {
- return ImmutableSet.of(ADD_FLOW_QNAME);
+ return ImmutableSet.of(KNOCK_KNOCK_QNAME);
}
@Override
return Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success(result).build());
}
});
- registration.registerPath(NodeContext.QNAME, BI_NODE_C_ID);
+ registration.registerPath(TestContext.QNAME, BI_NODE_C_ID);
+
- SalFlowService baFlowInvoker = baRpcRegistry.getRpcService(SalFlowService.class);
- Future<RpcResult<AddFlowOutput>> baResult = baFlowInvoker.addFlow(addFlow(BA_NODE_C_ID).setPriority(500).build());
+ OpendaylightOfMigrationTestModelService baKnockInvoker =
+ providerRegistry.getRpcService(OpendaylightOfMigrationTestModelService.class);
+ Future<RpcResult<KnockKnockOutput>> baResult = baKnockInvoker.knockKnock((knockKnock(BA_NODE_C_ID).setQuestion("Who's there?").build()));
assertNotNull(baResult);
- assertEquals(output,baResult.get().getResult());
+ assertEquals(output, baResult.get().getResult());
}
private CompositeNode toDomRpcInput(DataObject addFlowA) {
testContext.close();
}
- private static InstanceIdentifier<Node> createBANodeIdentifier(NodeId node) {
- return InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(node)).toInstance();
- }
-
- private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBINodeIdentifier(NodeId node) {
- return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder().node(Nodes.QNAME)
- .nodeWithKey(Node.QNAME, NODE_ID_QNAME, node.getValue()).toInstance();
+ private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBINodeIdentifier(TopLevelListKey listKey) {
+ return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder().node(Top.QNAME)
+ .nodeWithKey(TopLevelList.QNAME, NODE_ID_QNAME, listKey.getName()).toInstance();
}
- private Future<RpcResult<AddFlowOutput>> addFlowResult(boolean success, long xid) {
- AddFlowOutput output = new AddFlowOutputBuilder() //
- .setTransactionId(new TransactionId(BigInteger.valueOf(xid))).build();
- RpcResult<AddFlowOutput> result = RpcResultBuilder.<AddFlowOutput>status(success).withResult(output).build();
+ private Future<RpcResult<KnockKnockOutput>> knockResult(boolean success, String answer) {
+ KnockKnockOutput output = new KnockKnockOutputBuilder() //
+ .setAnswer(answer).build();
+ RpcResult<KnockKnockOutput> result = RpcResultBuilder.<KnockKnockOutput>status(success).withResult(output).build();
return Futures.immediateFuture(result);
}
- private static AddFlowInputBuilder addFlow(InstanceIdentifier<Node> nodeId) {
- AddFlowInputBuilder builder = new AddFlowInputBuilder();
- builder.setNode(new NodeRef(nodeId));
+ private static KnockKnockInputBuilder knockKnock(InstanceIdentifier<TopLevelList> listId) {
+ KnockKnockInputBuilder builder = new KnockKnockInputBuilder();
+ builder.setKnockerId(listId);
return builder;
}
- private CompositeNode toDomRpc(QName rpcName, AddFlowInput addFlowA) {
+ private CompositeNode toDomRpc(QName rpcName, KnockKnockInput knockInput) {
return new CompositeNodeTOImpl(rpcName, null,
- Collections.<org.opendaylight.yangtools.yang.data.api.Node<?>> singletonList(toDomRpcInput(addFlowA)));
+ Collections.<org.opendaylight.yangtools.yang.data.api.Node<?>>singletonList(toDomRpcInput(knockInput)));
}
}
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.OpendaylightTestRpcServiceService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
import org.opendaylight.yangtools.yang.common.QName;
private final static QName RPC_NAME = QName.create(RPC_SERVICE_NAMESPACE,
REVISION_DATE, "rock-the-house");
- private static final NodeId MOUNT_NODE = new NodeId("id");
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
+ private static final String TLL_NAME = "id";
+ private static final QName TLL_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
- private static final InstanceIdentifier<Node> BA_MOUNT_ID = createBANodeIdentifier(MOUNT_NODE);
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_MOUNT_ID = createBINodeIdentifier(MOUNT_NODE);
+ private static final InstanceIdentifier<TopLevelList> BA_MOUNT_ID = createBATllIdentifier(TLL_NAME);
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_MOUNT_ID = createBITllIdentifier(TLL_NAME);
private BindingTestContext testContext;
private MountProvisionService domMountPointService;
schemaContext = mountSchemaContext;
}
- private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBINodeIdentifier(
- final NodeId mountNode) {
+ private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBITllIdentifier(
+ final String mount) {
return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
- .builder().node(Nodes.QNAME)
- .nodeWithKey(Node.QNAME, NODE_ID_QNAME, mountNode.getValue())
+ .builder().node(Top.QNAME)
+ .nodeWithKey(TopLevelList.QNAME, TLL_NAME_QNAME, mount)
.toInstance();
}
- private static InstanceIdentifier<Node> createBANodeIdentifier(
- final NodeId mountNode) {
- return InstanceIdentifier.builder(Nodes.class)
- .child(Node.class, new NodeKey(mountNode)).toInstance();
+ private static InstanceIdentifier<TopLevelList> createBATllIdentifier(
+ final String mount) {
+ return InstanceIdentifier.builder(Top.class)
+ .child(TopLevelList.class, new TopLevelListKey(mount)).toInstance();
}
@SuppressWarnings("deprecation")
import java.util.concurrent.Future;
-import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.OpendaylightOfMigrationTestModelService;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
-public class MessageCapturingFlowService implements SalFlowService, AutoCloseable {
-
- private Future<RpcResult<AddFlowOutput>> addFlowResult;
- private Future<RpcResult<RemoveFlowOutput>> removeFlowResult;
- private Future<RpcResult<UpdateFlowOutput>> updateFlowResult;
-
- private final Multimap<InstanceIdentifier<?>, AddFlowInput> receivedAddFlows = HashMultimap.create();
- private final Multimap<InstanceIdentifier<?>, RemoveFlowInput> receivedRemoveFlows = HashMultimap.create();
- private final Multimap<InstanceIdentifier<?>, UpdateFlowInput> receivedUpdateFlows = HashMultimap.create();
- private RoutedRpcRegistration<SalFlowService> registration;
-
- @Override
- public Future<RpcResult<AddFlowOutput>> addFlow(AddFlowInput arg0) {
- receivedAddFlows.put(arg0.getNode().getValue(), arg0);
- return addFlowResult;
- }
-
- @Override
- public Future<RpcResult<RemoveFlowOutput>> removeFlow(RemoveFlowInput arg0) {
- receivedRemoveFlows.put(arg0.getNode().getValue(), arg0);
- return removeFlowResult;
- }
-
- @Override
- public Future<RpcResult<UpdateFlowOutput>> updateFlow(UpdateFlowInput arg0) {
- receivedUpdateFlows.put(arg0.getNode().getValue(), arg0);
- return updateFlowResult;
- }
-
- public Future<RpcResult<AddFlowOutput>> getAddFlowResult() {
- return addFlowResult;
- }
+public class MessageCapturingFlowService implements OpendaylightOfMigrationTestModelService, AutoCloseable {
- public MessageCapturingFlowService setAddFlowResult(Future<RpcResult<AddFlowOutput>> addFlowResult) {
- this.addFlowResult = addFlowResult;
- return this;
- }
+ private Future<RpcResult<KnockKnockOutput>> knockKnockResult;
- public Future<RpcResult<RemoveFlowOutput>> getRemoveFlowResult() {
- return removeFlowResult;
- }
-
- public MessageCapturingFlowService setRemoveFlowResult(Future<RpcResult<RemoveFlowOutput>> removeFlowResult) {
- this.removeFlowResult = removeFlowResult;
- return this;
- }
+ private final Multimap<InstanceIdentifier<?>, KnockKnockInput> receivedKnocks = HashMultimap.create();
+ private RoutedRpcRegistration<OpendaylightOfMigrationTestModelService> registration;
- public Future<RpcResult<UpdateFlowOutput>> getUpdateFlowResult() {
- return updateFlowResult;
+ public Future<RpcResult<KnockKnockOutput>> getKnockKnockResult() {
+ return knockKnockResult;
}
- public MessageCapturingFlowService setUpdateFlowResult(Future<RpcResult<UpdateFlowOutput>> updateFlowResult) {
- this.updateFlowResult = updateFlowResult;
+ public MessageCapturingFlowService setKnockKnockResult(Future<RpcResult<KnockKnockOutput>> kkOutput) {
+ this.knockKnockResult = kkOutput;
return this;
}
- public Multimap<InstanceIdentifier<?>, AddFlowInput> getReceivedAddFlows() {
- return receivedAddFlows;
- }
-
- public Multimap<InstanceIdentifier<?>, RemoveFlowInput> getReceivedRemoveFlows() {
- return receivedRemoveFlows;
- }
-
- public Multimap<InstanceIdentifier<?>, UpdateFlowInput> getReceivedUpdateFlows() {
- return receivedUpdateFlows;
+ public Multimap<InstanceIdentifier<?>, KnockKnockInput> getReceivedKnocks() {
+ return receivedKnocks;
}
public MessageCapturingFlowService registerTo(RpcProviderRegistry registry) {
- registration = registry.addRoutedRpcImplementation(SalFlowService.class, this);
+ registration = registry.addRoutedRpcImplementation(OpendaylightOfMigrationTestModelService.class, this);
assertNotNull(registration);
return this;
}
return ret;
}
+ @Override
+ public Future<RpcResult<KnockKnockOutput>> knockKnock(KnockKnockInput input) {
+ receivedKnocks.put(input.getKnockerId(), input);
+ return knockKnockResult;
+ }
+
}
loggers = ["akka.event.slf4j.Slf4jLogger"]
actor {
-
provider = "akka.cluster.ClusterActorRefProvider"
serializers {
- java = "akka.serialization.JavaSerializer"
- proto = "akka.remote.serialization.ProtobufSerializer"
- }
+ java = "akka.serialization.JavaSerializer"
+ proto = "akka.remote.serialization.ProtobufSerializer"
+ }
+
+ serialization-bindings {
+ "com.google.protobuf.Message" = proto
+ }
- serialization-bindings {
- "com.google.protobuf.Message" = proto
+ default-dispatcher {
+ # Setting throughput to 1 makes the dispatcher fair. It processes 1 message from
+ # the mailbox before moving on to the next mailbox
+ throughput = 1
+ }
- }
+ default-mailbox {
+ # When not using a BalancingDispatcher it is recommended that we use the SingleConsumerOnlyUnboundedMailbox
+ # as it is the most efficient for multiple producer/single consumer use cases
+ mailbox-type="akka.dispatch.SingleConsumerOnlyUnboundedMailbox"
+ }
}
remote {
log-remote-lifecycle-events = off
import java.util.Iterator;
import java.util.Map;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
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.PathArgument;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
normalizedPath), e);
}
}
-
- // Write Augmentation data resolution
- if (legacyData.getValue().size() == 1) {
- final DataNormalizationOperation<?> potentialOp;
-
- try {
- final QName childType = legacyData.getValue().get(0).getNodeType();
- potentialOp = currentOp.getChild(childType);
- } catch (DataNormalizationException e) {
- throw new IllegalArgumentException(String.format("Failed to get child operation for %s", legacyData), e);
- }
-
- if (potentialOp.getIdentifier() instanceof AugmentationIdentifier) {
- currentOp = potentialOp;
- normalizedPath = normalizedPath.node(potentialOp.getIdentifier());
- }
- }
-
Preconditions.checkArgument(currentOp != null,
"Instance Identifier %s does not reference correct schema Node.", normalizedPath);
return new AbstractMap.SimpleEntry<YangInstanceIdentifier, NormalizedNode<?, ?>>(normalizedPath,
YangInstanceIdentifier.create(Lists.newArrayList(new NodeIdentifier(TEST_QNAME), new NodeIdentifier(
OUTER_CONTAINER_QNAME))), outerContBuilder.toInstance()));
- verifyNormalizedInstanceIdentifier(normalizedNodeEntry.getKey(), TEST_QNAME, OUTER_CONTAINER_QNAME,
- Sets.newHashSet(AUGMENTED_LEAF_QNAME));
-
- verifyNormalizedNode(normalizedNodeEntry.getValue(), expAugmentation);
}
@Test
package org.opendaylight.controller.cluster.datastore;
import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.AbstractFuture;
import com.google.common.util.concurrent.AbstractListeningExecutorService;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
@Override
public CheckedFuture<Void, TransactionCommitFailedException> submit(DOMDataWriteTransaction transaction,
- Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
+ Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
Preconditions.checkArgument(cohorts != null, "Cohorts must not be null.");
LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
- final int cohortSize = Iterables.size(cohorts);
final AsyncNotifyingSettableFuture clientSubmitFuture =
new AsyncNotifyingSettableFuture(clientFutureCallbackExecutor);
- doCanCommit(clientSubmitFuture, transaction, cohorts, cohortSize);
+ doCanCommit(clientSubmitFuture, transaction, cohorts);
return MappingCheckedFuture.create(clientSubmitFuture,
TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
private void doCanCommit(final AsyncNotifyingSettableFuture clientSubmitFuture,
final DOMDataWriteTransaction transaction,
- final Iterable<DOMStoreThreePhaseCommitCohort> cohorts, final int cohortSize) {
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
final long startTime = System.nanoTime();
// Not using Futures.allAsList here to avoid its internal overhead.
- final AtomicInteger remaining = new AtomicInteger(cohortSize);
+ final AtomicInteger remaining = new AtomicInteger(cohorts.size());
FutureCallback<Boolean> futureCallback = new FutureCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
if (result == null || !result) {
- handleException(clientSubmitFuture, transaction, cohorts, cohortSize,
+ handleException(clientSubmitFuture, transaction, cohorts,
CAN_COMMIT, TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER,
new TransactionCommitFailedException(
"Can Commit failed, no detailed cause available."));
} else {
if(remaining.decrementAndGet() == 0) {
// All cohorts completed successfully - we can move on to the preCommit phase
- doPreCommit(startTime, clientSubmitFuture, transaction, cohorts, cohortSize);
+ doPreCommit(startTime, clientSubmitFuture, transaction, cohorts);
}
}
}
@Override
public void onFailure(Throwable t) {
- handleException(clientSubmitFuture, transaction, cohorts, cohortSize, CAN_COMMIT,
+ handleException(clientSubmitFuture, transaction, cohorts, CAN_COMMIT,
TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER, t);
}
};
private void doPreCommit(final long startTime, final AsyncNotifyingSettableFuture clientSubmitFuture,
final DOMDataWriteTransaction transaction,
- final Iterable<DOMStoreThreePhaseCommitCohort> cohorts, final int cohortSize) {
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
// Not using Futures.allAsList here to avoid its internal overhead.
- final AtomicInteger remaining = new AtomicInteger(cohortSize);
+ final AtomicInteger remaining = new AtomicInteger(cohorts.size());
FutureCallback<Void> futureCallback = new FutureCallback<Void>() {
@Override
public void onSuccess(Void notUsed) {
if(remaining.decrementAndGet() == 0) {
// All cohorts completed successfully - we can move on to the commit phase
- doCommit(startTime, clientSubmitFuture, transaction, cohorts, cohortSize);
+ doCommit(startTime, clientSubmitFuture, transaction, cohorts);
}
}
@Override
public void onFailure(Throwable t) {
- handleException(clientSubmitFuture, transaction, cohorts, cohortSize, PRE_COMMIT,
+ handleException(clientSubmitFuture, transaction, cohorts, PRE_COMMIT,
TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER, t);
}
};
private void doCommit(final long startTime, final AsyncNotifyingSettableFuture clientSubmitFuture,
final DOMDataWriteTransaction transaction,
- final Iterable<DOMStoreThreePhaseCommitCohort> cohorts, final int cohortSize) {
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
// Not using Futures.allAsList here to avoid its internal overhead.
- final AtomicInteger remaining = new AtomicInteger(cohortSize);
+ final AtomicInteger remaining = new AtomicInteger(cohorts.size());
FutureCallback<Void> futureCallback = new FutureCallback<Void>() {
@Override
public void onSuccess(Void notUsed) {
@Override
public void onFailure(Throwable t) {
- handleException(clientSubmitFuture, transaction, cohorts, cohortSize, COMMIT,
+ handleException(clientSubmitFuture, transaction, cohorts, COMMIT,
TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER, t);
}
};
private void handleException(final AsyncNotifyingSettableFuture clientSubmitFuture,
final DOMDataWriteTransaction transaction,
- final Iterable<DOMStoreThreePhaseCommitCohort> cohorts, int cohortSize,
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts,
final String phase, final TransactionCommitFailedExceptionMapper exMapper,
final Throwable t) {
// Transaction failed - tell all cohorts to abort.
@SuppressWarnings("unchecked")
- ListenableFuture<Void>[] canCommitFutures = new ListenableFuture[cohortSize];
+ ListenableFuture<Void>[] canCommitFutures = new ListenableFuture[cohorts.size()];
int i = 0;
for(DOMStoreThreePhaseCommitCohort cohort: cohorts) {
canCommitFutures[i++] = cohort.abort();
import akka.actor.ActorSelection;
import akka.actor.PoisonPill;
import akka.dispatch.OnComplete;
+import com.google.common.annotations.VisibleForTesting;
import org.opendaylight.controller.cluster.datastore.exceptions.LocalShardNotFoundException;
import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistration;
import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.annotations.VisibleForTesting;
import scala.concurrent.Future;
/**
public void init(final YangInstanceIdentifier path, final AsyncDataBroker.DataChangeScope scope) {
dataChangeListenerActor = actorContext.getActorSystem().actorOf(
- DataChangeListener.props(listener));
+ DataChangeListener.props(listener).withDispatcher(actorContext.getNotificationDispatcherPath()));
Future<ActorRef> findFuture = actorContext.findLocalShardAsync(shardName);
findFuture.onComplete(new OnComplete<ActorRef>() {
doRegistration(shard, path, scope);
}
}
- }, actorContext.getActorSystem().dispatcher());
+ }, actorContext.getClientDispatcher());
}
private void doRegistration(ActorRef shard, final YangInstanceIdentifier path,
reply.getListenerRegistrationPath()));
}
}
- }, actorContext.getActorSystem().dispatcher());
+ }, actorContext.getClientDispatcher());
}
@Override
import org.opendaylight.controller.cluster.datastore.identifiers.ShardManagerIdentifier;
import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory;
import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.cluster.datastore.utils.Dispatchers;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
LOG.info("Creating ShardManager : {}", shardManagerId);
+ String shardDispatcher =
+ new Dispatchers(actorSystem.dispatchers()).getDispatcherPath(Dispatchers.DispatcherType.Shard);
+
actorContext = new ActorContext(actorSystem, actorSystem.actorOf(
ShardManager.props(cluster, configuration, datastoreContext)
- .withMailbox(ActorContext.MAILBOX), shardManagerId ),
+ .withDispatcher(shardDispatcher).withMailbox(ActorContext.MAILBOX), shardManagerId ),
cluster, configuration, datastoreContext);
}
import org.opendaylight.controller.cluster.datastore.modification.Modification;
import org.opendaylight.controller.cluster.datastore.modification.ModificationPayload;
import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
+import org.opendaylight.controller.cluster.datastore.utils.Dispatchers;
+import org.opendaylight.controller.cluster.datastore.utils.MessageTracker;
import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils;
import org.opendaylight.controller.cluster.notifications.RoleChangeNotifier;
import org.opendaylight.controller.cluster.raft.RaftActor;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload;
import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload;
import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionFactory;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
private final Optional<ActorRef> roleChangeNotifier;
+ private final MessageTracker appendEntriesReplyTracker;
+
/**
* Coordinates persistence recovery on startup.
*/
private final Map<String, DOMStoreTransactionChain> transactionChains = new HashMap<>();
+ private final String txnDispatcherPath;
+
protected Shard(final ShardIdentifier name, final Map<ShardIdentifier, String> peerAddresses,
final DatastoreContext datastoreContext, final SchemaContext schemaContext) {
super(name.toString(), mapPeerAddresses(peerAddresses),
this.name = name;
this.datastoreContext = datastoreContext;
this.schemaContext = schemaContext;
- this.dataPersistenceProvider = (datastoreContext.isPersistent()) ? new PersistentDataProvider() : new NonPersistentRaftDataProvider();
+ this.dataPersistenceProvider = (datastoreContext.isPersistent())
+ ? new PersistentDataProvider() : new NonPersistentRaftDataProvider();
+ this.txnDispatcherPath = new Dispatchers(context().system().dispatchers())
+ .getDispatcherPath(Dispatchers.DispatcherType.Transaction);
+
LOG.info("Shard created : {}, persistent : {}", name, datastoreContext.isPersistent());
// create a notifier actor for each cluster member
roleChangeNotifier = createRoleChangeNotifier(name.toString());
+
+ appendEntriesReplyTracker = new MessageTracker(AppendEntriesReply.class,
+ getRaftActorContext().getConfigParams().getIsolatedCheckIntervalInMillis());
}
private static Map<String, String> mapPeerAddresses(
onRecoveryComplete();
} else {
super.onReceiveRecover(message);
+ if(LOG.isTraceEnabled()) {
+ appendEntriesReplyTracker.begin();
+ }
}
}
@Override
public void onReceiveCommand(final Object message) throws Exception {
- if (message.getClass().equals(CreateTransaction.SERIALIZABLE_CLASS)) {
- handleCreateTransaction(message);
- } else if(message instanceof ForwardedReadyTransaction) {
- handleForwardedReadyTransaction((ForwardedReadyTransaction)message);
- } else if(message.getClass().equals(CanCommitTransaction.SERIALIZABLE_CLASS)) {
- handleCanCommitTransaction(CanCommitTransaction.fromSerializable(message));
- } else if(message.getClass().equals(CommitTransaction.SERIALIZABLE_CLASS)) {
- handleCommitTransaction(CommitTransaction.fromSerializable(message));
- } else if(message.getClass().equals(AbortTransaction.SERIALIZABLE_CLASS)) {
- handleAbortTransaction(AbortTransaction.fromSerializable(message));
- } else if (message.getClass().equals(CloseTransactionChain.SERIALIZABLE_CLASS)){
- closeTransactionChain(CloseTransactionChain.fromSerializable(message));
- } else if (message instanceof RegisterChangeListener) {
- registerChangeListener((RegisterChangeListener) message);
- } else if (message instanceof UpdateSchemaContext) {
- updateSchemaContext((UpdateSchemaContext) message);
- } else if (message instanceof PeerAddressResolved) {
- PeerAddressResolved resolved = (PeerAddressResolved) message;
- setPeerAddress(resolved.getPeerId().toString(),
- resolved.getPeerAddress());
- } else if(message.equals(TX_COMMIT_TIMEOUT_CHECK_MESSAGE)) {
- handleTransactionCommitTimeoutCheck();
- } else {
- super.onReceiveCommand(message);
+
+ MessageTracker.Context context = appendEntriesReplyTracker.received(message);
+
+ if(context.error().isPresent()){
+ LOG.trace("{} : AppendEntriesReply failed to arrive at the expected interval {}", persistenceId(),
+ context.error());
+ }
+
+ try {
+ if (message.getClass().equals(CreateTransaction.SERIALIZABLE_CLASS)) {
+ handleCreateTransaction(message);
+ } else if (message instanceof ForwardedReadyTransaction) {
+ handleForwardedReadyTransaction((ForwardedReadyTransaction) message);
+ } else if (message.getClass().equals(CanCommitTransaction.SERIALIZABLE_CLASS)) {
+ handleCanCommitTransaction(CanCommitTransaction.fromSerializable(message));
+ } else if (message.getClass().equals(CommitTransaction.SERIALIZABLE_CLASS)) {
+ handleCommitTransaction(CommitTransaction.fromSerializable(message));
+ } else if (message.getClass().equals(AbortTransaction.SERIALIZABLE_CLASS)) {
+ handleAbortTransaction(AbortTransaction.fromSerializable(message));
+ } else if (message.getClass().equals(CloseTransactionChain.SERIALIZABLE_CLASS)) {
+ closeTransactionChain(CloseTransactionChain.fromSerializable(message));
+ } else if (message instanceof RegisterChangeListener) {
+ registerChangeListener((RegisterChangeListener) message);
+ } else if (message instanceof UpdateSchemaContext) {
+ updateSchemaContext((UpdateSchemaContext) message);
+ } else if (message instanceof PeerAddressResolved) {
+ PeerAddressResolved resolved = (PeerAddressResolved) message;
+ setPeerAddress(resolved.getPeerId().toString(),
+ resolved.getPeerAddress());
+ } else if (message.equals(TX_COMMIT_TIMEOUT_CHECK_MESSAGE)) {
+ handleTransactionCommitTimeoutCheck();
+ } else {
+ super.onReceiveCommand(message);
+ }
+ } finally {
+ context.done();
}
}
shardMBean.incrementReadOnlyTransactionCount();
- return getContext().actorOf(
- ShardTransaction.props(factory.newReadOnlyTransaction(), getSelf(),
- schemaContext,datastoreContext, shardMBean,
- transactionId.getRemoteTransactionId(), clientVersion),
- transactionId.toString());
+ return createShardTransaction(factory.newReadOnlyTransaction(), transactionId, clientVersion);
} else if (transactionType == TransactionProxy.TransactionType.READ_WRITE.ordinal()) {
shardMBean.incrementReadWriteTransactionCount();
- return getContext().actorOf(
- ShardTransaction.props(factory.newReadWriteTransaction(), getSelf(),
- schemaContext, datastoreContext, shardMBean,
- transactionId.getRemoteTransactionId(), clientVersion),
- transactionId.toString());
-
+ return createShardTransaction(factory.newReadWriteTransaction(), transactionId, clientVersion);
} else if (transactionType == TransactionProxy.TransactionType.WRITE_ONLY.ordinal()) {
shardMBean.incrementWriteOnlyTransactionCount();
- return getContext().actorOf(
- ShardTransaction.props(factory.newWriteOnlyTransaction(), getSelf(),
- schemaContext, datastoreContext, shardMBean,
- transactionId.getRemoteTransactionId(), clientVersion),
- transactionId.toString());
+ return createShardTransaction(factory.newWriteOnlyTransaction(), transactionId, clientVersion);
} else {
throw new IllegalArgumentException(
"Shard="+name + ":CreateTransaction message has unidentified transaction type="
}
}
+ private ActorRef createShardTransaction(DOMStoreTransaction transaction, ShardTransactionIdentifier transactionId,
+ short clientVersion){
+ return getContext().actorOf(
+ ShardTransaction.props(transaction, getSelf(),
+ schemaContext, datastoreContext, shardMBean,
+ transactionId.getRemoteTransactionId(), clientVersion)
+ .withDispatcher(txnDispatcherPath),
+ transactionId.toString());
+
+ }
+
private void createTransaction(CreateTransaction createTransaction) {
try {
ActorRef transactionActor = createTransaction(createTransaction.getTransactionType(),
import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound;
import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
+import org.opendaylight.controller.cluster.datastore.utils.Dispatchers;
import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
private final Configuration configuration;
+ private final String shardDispatcherPath;
+
private ShardManagerInfoMBean mBean;
private final DatastoreContext datastoreContext;
this.datastoreContext = datastoreContext;
this.dataPersistenceProvider = createDataPersistenceProvider(datastoreContext.isPersistent());
this.type = datastoreContext.getDataStoreType();
+ this.shardDispatcherPath =
+ new Dispatchers(context().system().dispatchers()).getDispatcherPath(Dispatchers.DispatcherType.Shard);
// Subscribe this actor to cluster member events
cluster.subscribeToMemberEvents(getSelf());
for (ShardInformation info : localShards.values()) {
if (info.getActor() == null) {
info.setActor(getContext().actorOf(Shard.props(info.getShardId(),
- info.getPeerAddresses(), datastoreContext, schemaContext),
- info.getShardId().toString()));
+ info.getPeerAddresses(), datastoreContext, schemaContext)
+ .withDispatcher(shardDispatcherPath), info.getShardId().toString()));
} else {
info.getActor().tell(message, getSelf());
}
private Future<Void> buildCohortList() {
Future<Iterable<ActorSelection>> combinedFutures = Futures.sequence(cohortFutures,
- actorContext.getActorSystem().dispatcher());
+ actorContext.getClientDispatcher());
return combinedFutures.transform(new AbstractFunction1<Iterable<ActorSelection>, Void>() {
@Override
}
return null;
}
- }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getActorSystem().dispatcher());
+ }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getClientDispatcher());
}
@Override
finishCanCommit(returnFuture);
}
}
- }, actorContext.getActorSystem().dispatcher());
+ }, actorContext.getClientDispatcher());
return returnFuture;
}
}
returnFuture.set(Boolean.valueOf(result));
}
- }, actorContext.getActorSystem().dispatcher());
+ }, actorContext.getClientDispatcher());
}
private Future<Iterable<Object>> invokeCohorts(Object message) {
futureList.add(actorContext.executeOperationAsync(cohort, message, actorContext.getTransactionCommitOperationTimeout()));
}
- return Futures.sequence(futureList, actorContext.getActorSystem().dispatcher());
+ return Futures.sequence(futureList, actorContext.getClientDispatcher());
}
@Override
propagateException, returnFuture, callback);
}
}
- }, actorContext.getActorSystem().dispatcher());
+ }, actorContext.getClientDispatcher());
}
return returnFuture;
callback.success();
}
}
- }, actorContext.getActorSystem().dispatcher());
+ }, actorContext.getClientDispatcher());
}
@VisibleForTesting
timerContext.stop();
Snapshot timerSnapshot = commitTimer.getSnapshot();
- double allowedLatencyInNanos = timerSnapshot.get98thPercentile();
+ double allowedLatencyInNanos = timerSnapshot.get95thPercentile();
long commitTimeoutInSeconds = actorContext.getDatastoreContext()
.getShardTransactionCommitTimeoutInSeconds();
}
private Future<Object> completeOperation(Future<Object> operationFuture){
- operationFuture.onComplete(this.operationCompleter, actorContext.getActorSystem().dispatcher());
+ operationFuture.onComplete(this.operationCompleter, actorContext.getClientDispatcher());
return operationFuture;
}
futureList.add(replyFuture);
Future<Iterable<Object>> combinedFutures = akka.dispatch.Futures.sequence(futureList,
- actorContext.getActorSystem().dispatcher());
+ actorContext.getClientDispatcher());
// Transform the combined Future into a Future that returns the cohort actor path from
// the ReadyTransactionReply. That's the end result of the ready operation.
serializedReadyReply.getClass()));
}
}
- }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getActorSystem().dispatcher());
+ }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getClientDispatcher());
}
@Override
Future<Iterable<Object>> combinedFutures = akka.dispatch.Futures.sequence(
Lists.newArrayList(recordedOperationFutures),
- actorContext.getActorSystem().dispatcher());
+ actorContext.getClientDispatcher());
OnComplete<Iterable<Object>> onComplete = new OnComplete<Iterable<Object>>() {
@Override
}
};
- combinedFutures.onComplete(onComplete, actorContext.getActorSystem().dispatcher());
+ combinedFutures.onComplete(onComplete, actorContext.getClientDispatcher());
}
}
Future<Object> readFuture = executeOperationAsync(new ReadData(path));
- readFuture.onComplete(onComplete, actorContext.getActorSystem().dispatcher());
+ readFuture.onComplete(onComplete, actorContext.getClientDispatcher());
}
@Override
Future<Iterable<Object>> combinedFutures = akka.dispatch.Futures.sequence(
Lists.newArrayList(recordedOperationFutures),
- actorContext.getActorSystem().dispatcher());
+ actorContext.getClientDispatcher());
OnComplete<Iterable<Object>> onComplete = new OnComplete<Iterable<Object>>() {
@Override
public void onComplete(Throwable failure, Iterable<Object> notUsed)
}
};
- combinedFutures.onComplete(onComplete, actorContext.getActorSystem().dispatcher());
+ combinedFutures.onComplete(onComplete, actorContext.getClientDispatcher());
}
}
Future<Object> future = executeOperationAsync(new DataExists(path));
- future.onComplete(onComplete, actorContext.getActorSystem().dispatcher());
+ future.onComplete(onComplete, actorContext.getClientDispatcher());
}
}
newTxFutureCallback.setPrimaryShard(primaryShard);
}
}
- }, actorContext.getActorSystem().dispatcher());
+ }, actorContext.getClientDispatcher());
}
return txFutureCallback;
TransactionProxy.this.transactionType.ordinal(),
getTransactionChainId()).toSerializable());
- createTxFuture.onComplete(this, actorContext.getActorSystem().dispatcher());
+ createTxFuture.onComplete(this, actorContext.getClientDispatcher());
}
@Override
public void run() {
tryCreateTransaction();
}
- }, actorContext.getActorSystem().dispatcher());
+ }, actorContext.getClientDispatcher());
return;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.concurrent.Await;
+import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
private final JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).inDomain(DOMAIN).build();
private final int transactionOutstandingOperationLimit;
private final Timeout transactionCommitOperationTimeout;
+ private final Dispatchers dispatchers;
private volatile SchemaContext schemaContext;
this.configuration = configuration;
this.datastoreContext = datastoreContext;
this.txRateLimiter = RateLimiter.create(datastoreContext.getTransactionCreationInitialRateLimit());
+ this.dispatchers = new Dispatchers(actorSystem.dispatchers());
operationDuration = Duration.create(datastoreContext.getOperationTimeoutInSeconds(), TimeUnit.SECONDS);
operationTimeout = new Timeout(operationDuration);
transactionOutstandingOperationLimit = new CommonConfig(this.getActorSystem().settings().config()).getMailBoxCapacity();
jmxReporter.start();
+
}
public DatastoreContext getDatastoreContext() {
throw new UnknownMessageException(String.format(
"FindPrimary returned unkown response: %s", response));
}
- }, FIND_PRIMARY_FAILURE_TRANSFORMER, getActorSystem().dispatcher());
+ }, FIND_PRIMARY_FAILURE_TRANSFORMER, getClientDispatcher());
}
/**
throw new UnknownMessageException(String.format(
"FindLocalShard returned unkown response: %s", response));
}
- }, getActorSystem().dispatcher());
+ }, getClientDispatcher());
}
private String findPrimaryPathOrNull(String shardName) {
return transactionCommitOperationTimeout;
}
+ /**
+ * An akka dispatcher that is meant to be used when processing ask Futures which were triggered by client
+ * code on the datastore
+ * @return
+ */
+ public ExecutionContext getClientDispatcher() {
+ return this.dispatchers.getDispatcher(Dispatchers.DispatcherType.Client);
+ }
+
+ public String getNotificationDispatcherPath(){
+ return this.dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Notification);
+ }
}
--- /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.cluster.datastore.utils;
+
+import com.google.common.base.Preconditions;
+import scala.concurrent.ExecutionContext;
+
+public class Dispatchers {
+ public static final String DEFAULT_DISPATCHER_PATH = "akka.actor.default-dispatcher";
+ public static final String CLIENT_DISPATCHER_PATH = "client-dispatcher";
+ public static final String TXN_DISPATCHER_PATH = "txn-dispatcher";
+ public static final String SHARD_DISPATCHER_PATH = "shard-dispatcher";
+ public static final String NOTIFICATION_DISPATCHER_PATH = "notification-dispatcher";
+
+ private final akka.dispatch.Dispatchers dispatchers;
+
+ public static enum DispatcherType {
+ Client(CLIENT_DISPATCHER_PATH),
+ Transaction(TXN_DISPATCHER_PATH),
+ Shard(SHARD_DISPATCHER_PATH),
+ Notification(NOTIFICATION_DISPATCHER_PATH);
+
+ private final String path;
+ private DispatcherType(String path){
+ this.path = path;
+ }
+ private String path(akka.dispatch.Dispatchers dispatchers){
+ if(dispatchers.hasDispatcher(path)){
+ return path;
+ }
+ return DEFAULT_DISPATCHER_PATH;
+ }
+
+ private ExecutionContext dispatcher(akka.dispatch.Dispatchers dispatchers){
+ if(dispatchers.hasDispatcher(path)){
+ return dispatchers.lookup(path);
+ }
+ return dispatchers.defaultGlobalDispatcher();
+ }
+ }
+
+ public Dispatchers(akka.dispatch.Dispatchers dispatchers){
+ Preconditions.checkNotNull(dispatchers, "dispatchers should not be null");
+ this.dispatchers = dispatchers;
+ }
+
+ public ExecutionContext getDispatcher(DispatcherType dispatcherType){
+ return dispatcherType.dispatcher(this.dispatchers);
+ }
+
+ public String getDispatcherPath(DispatcherType dispatcherType){
+ return dispatcherType.path(this.dispatchers);
+ }
+}
--- /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.cluster.datastore.utils;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * MessageTracker is a diagnostic utility class to be used for figuring out why a certain message which was
+ * expected to arrive in a given time interval does not arrive. It attempts to keep track of all the messages that
+ * received between the arrival of two instances of the same message and the amount of time it took to process each
+ * of those messages.
+ * <br/>
+ * Usage of the API is as follows,
+ * <pre>
+ *
+ * // Track the Foo class, Here we expect to see a message of type Foo come in every 10 millis
+ * MessageTracker tracker = new MessageTracker(Foo.class, 10);
+ *
+ * // Begin the tracking process. If this is not called then calling received and done on the resultant Context
+ * // will do nothing
+ * tracker.begin();
+ *
+ * .....
+ *
+ * MessageTracker.Context context = tracker.received(message);
+ *
+ * if(context.error().isPresent()){
+ * LOG.error("{}", context.error().get());
+ * }
+ *
+ * // Some custom processing
+ * process(message);
+ *
+ * context.done();
+ *
+ * </pre>
+ */
+public class MessageTracker {
+
+ private static final Context NO_OP_CONTEXT = new NoOpContext();
+
+ private final Class expectedMessageClass;
+
+ private final long expectedArrivalInterval;
+
+ private final List<MessageProcessingTime> messagesSinceLastExpectedMessage = new LinkedList<>();
+
+ private Stopwatch expectedMessageWatch;
+
+ private boolean enabled = false;
+
+ private Object lastExpectedMessage;
+
+ private Object currentMessage;
+
+ private final CurrentMessageContext currentMessageContext = new CurrentMessageContext();
+
+ /**
+ *
+ * @param expectedMessageClass The class of the message to track
+ * @param expectedArrivalIntervalInMillis The expected arrival interval between two instances of the expected
+ * message
+ */
+ public MessageTracker(Class expectedMessageClass, long expectedArrivalIntervalInMillis){
+ this.expectedMessageClass = expectedMessageClass;
+ this.expectedArrivalInterval = expectedArrivalIntervalInMillis;
+ }
+
+ public void begin(){
+ if(enabled) {
+ return;
+ }
+ enabled = true;
+ expectedMessageWatch = Stopwatch.createStarted();
+ }
+
+ public Context received(Object message){
+ if(!enabled) {
+ return NO_OP_CONTEXT;
+ }
+ this.currentMessage = message;
+ if(expectedMessageClass.isInstance(message)){
+ long actualElapsedTime = expectedMessageWatch.elapsed(TimeUnit.MILLISECONDS);
+ if(actualElapsedTime > expectedArrivalInterval){
+ return new ErrorContext(message, Optional.of(new FailedExpectation(lastExpectedMessage, message,
+ ImmutableList.copyOf(messagesSinceLastExpectedMessage), expectedArrivalInterval,
+ actualElapsedTime)));
+ }
+ this.lastExpectedMessage = message;
+ this.messagesSinceLastExpectedMessage.clear();
+ }
+
+ currentMessageContext.reset();
+ return currentMessageContext;
+ }
+
+ private void processed(Object message, long messageElapseTimeInNanos){
+ if(!enabled) {
+ return;
+ }
+ if(!expectedMessageClass.isInstance(message)){
+ this.messagesSinceLastExpectedMessage.add(new MessageProcessingTime(message.getClass(), messageElapseTimeInNanos));
+ }
+ }
+
+ public List<MessageProcessingTime> getMessagesSinceLastExpectedMessage(){
+ return ImmutableList.copyOf(this.messagesSinceLastExpectedMessage);
+ }
+
+ public static class MessageProcessingTime {
+ private final Class messageClass;
+ private final long elapsedTimeInNanos;
+
+ MessageProcessingTime(Class messageClass, long elapsedTimeInNanos){
+ this.messageClass = messageClass;
+ this.elapsedTimeInNanos = elapsedTimeInNanos;
+ }
+
+ @Override
+ public String toString() {
+ return "MessageProcessingTime{" +
+ "messageClass=" + messageClass.getSimpleName() +
+ ", elapsedTimeInMillis=" + TimeUnit.NANOSECONDS.toMillis(elapsedTimeInNanos) +
+ '}';
+ }
+
+ public Class getMessageClass() {
+ return messageClass;
+ }
+
+ public long getElapsedTimeInNanos() {
+ return elapsedTimeInNanos;
+ }
+ }
+
+ public interface Error {
+ Object getLastExpectedMessage();
+ Object getCurrentExpectedMessage();
+ List<MessageProcessingTime> getMessageProcessingTimesSinceLastExpectedMessage();
+ }
+
+ private class FailedExpectation implements Error {
+
+ private final Object lastExpectedMessage;
+ private final Object currentExpectedMessage;
+ private final List<MessageProcessingTime> messagesSinceLastExpectedMessage;
+ private final long expectedTimeInMillis;
+ private final long actualTimeInMillis;
+
+ public FailedExpectation(Object lastExpectedMessage, Object message, List<MessageProcessingTime> messagesSinceLastExpectedMessage, long expectedTimeInMillis, long actualTimeInMillis) {
+ this.lastExpectedMessage = lastExpectedMessage;
+ this.currentExpectedMessage = message;
+ this.messagesSinceLastExpectedMessage = messagesSinceLastExpectedMessage;
+ this.expectedTimeInMillis = expectedTimeInMillis;
+ this.actualTimeInMillis = actualTimeInMillis;
+ }
+
+ public Object getLastExpectedMessage() {
+ return lastExpectedMessage;
+ }
+
+ public Object getCurrentExpectedMessage() {
+ return currentExpectedMessage;
+ }
+
+ public List<MessageProcessingTime> getMessageProcessingTimesSinceLastExpectedMessage() {
+ return messagesSinceLastExpectedMessage;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("\n> Last Expected Message = " + lastExpectedMessage);
+ builder.append("\n> Current Expected Message = " + currentExpectedMessage);
+ builder.append("\n> Expected time in between messages = " + expectedTimeInMillis);
+ builder.append("\n> Actual time in between messages = " + actualTimeInMillis);
+ for (MessageProcessingTime time : messagesSinceLastExpectedMessage) {
+ builder.append("\n\t> ").append(time.toString());
+ }
+ return builder.toString();
+ }
+
+ }
+
+ public interface Context {
+ Context done();
+ Optional<? extends Error> error();
+ }
+
+ private static class NoOpContext implements Context {
+
+ @Override
+ public Context done() {
+ return this;
+ }
+
+ @Override
+ public Optional<Error> error() {
+ return Optional.absent();
+ }
+ }
+
+ private class CurrentMessageContext implements Context {
+ Stopwatch stopwatch = Stopwatch.createStarted();
+ boolean done = true;
+
+ public void reset(){
+ Preconditions.checkState(done);
+ done = false;
+ stopwatch.reset().start();
+ }
+
+ @Override
+ public Context done() {
+ processed(currentMessage, stopwatch.elapsed(TimeUnit.NANOSECONDS));
+ done = true;
+ return this;
+ }
+
+ @Override
+ public Optional<? extends Error> error() {
+ return Optional.absent();
+ }
+ }
+
+ private class ErrorContext implements Context {
+ Object message;
+ private final Optional<? extends Error> error;
+ Stopwatch stopwatch;
+
+ ErrorContext(Object message, Optional<? extends Error> error){
+ this.message = message;
+ this.error = error;
+ this.stopwatch = Stopwatch.createStarted();
+ }
+
+ @Override
+ public Context done(){
+ processed(message, this.stopwatch.elapsed(TimeUnit.NANOSECONDS));
+ this.stopwatch.stop();
+ return this;
+ }
+
+ @Override
+ public Optional<? extends Error> error() {
+ return error;
+ }
+ }
+}
import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.cluster.datastore.utils.Dispatchers;
import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
doReturn(mockActor).when(mockActorSystem).actorOf(any(Props.class));
ExecutionContextExecutor executor = ExecutionContexts.fromExecutor(
MoreExecutors.sameThreadExecutor());
- doReturn(executor).when(mockActorSystem).dispatcher();
+
ActorContext actorContext = mock(ActorContext.class);
+ doReturn(executor).when(actorContext).getClientDispatcher();
+
String shardName = "shard-1";
final DataChangeListenerRegistrationProxy proxy = new DataChangeListenerRegistrationProxy(
shardName, actorContext, mockListener);
shardName, actorContext, mockListener);
doReturn(DatastoreContext.newBuilder().build()).when(actorContext).getDatastoreContext();
+ doReturn(getSystem().dispatchers().defaultGlobalDispatcher()).when(actorContext).getClientDispatcher();
doReturn(getSystem()).when(actorContext).getActorSystem();
+ doReturn(Dispatchers.DEFAULT_DISPATCHER_PATH).when(actorContext).getNotificationDispatcherPath();
doReturn(getSystem().actorSelection(getRef().path())).
when(actorContext).actorSelection(getRef().path());
doReturn(duration("5 seconds")).when(actorContext).getOperationDuration();
assertEquals(DatastoreContext.DEFAULT_SHARD_LEADER_ELECTION_TIMEOUT, build.getShardLeaderElectionTimeout());
assertEquals(DatastoreContext.DEFAULT_PERSISTENT, build.isPersistent());
assertEquals(DatastoreContext.DEFAULT_CONFIGURATION_READER, build.getConfigurationReader());
- assertEquals(DatastoreContext.DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS, build.getShardRaftConfig().getIsolatedCheckInterval().length());
+ assertEquals(DatastoreContext.DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS, build.getShardRaftConfig().getIsolatedCheckIntervalInMillis());
assertEquals(DatastoreContext.DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE, build.getShardRaftConfig().getSnapshotDataThresholdPercentage());
assertEquals(DatastoreContext.DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR, build.getShardRaftConfig().getElectionTimeoutFactor());
assertEquals(DatastoreContext.DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT, build.getTransactionCreationInitialRateLimit());
MockitoAnnotations.initMocks(this);
doReturn(getSystem()).when(actorContext).getActorSystem();
+ doReturn(getSystem().dispatchers().defaultGlobalDispatcher()).when(actorContext).getClientDispatcher();
doReturn(datastoreContext).when(actorContext).getDatastoreContext();
doReturn(100).when(datastoreContext).getShardTransactionCommitTimeoutInSeconds();
doReturn(commitTimer).when(actorContext).getOperationTimer("commit");
doReturn(commitTimerContext).when(commitTimer).time();
doReturn(commitSnapshot).when(commitTimer).getSnapshot();
- doReturn(TimeUnit.MILLISECONDS.toNanos(2000) * 1.0).when(commitSnapshot).get98thPercentile();
+ doReturn(TimeUnit.MILLISECONDS.toNanos(2000) * 1.0).when(commitSnapshot).get95thPercentile();
doReturn(10.0).when(actorContext).getTxCreationLimit();
}
DatastoreContext dataStoreContext = DatastoreContext.newBuilder().operationTimeoutInSeconds(2).build();
doReturn(getSystem()).when(mockActorContext).getActorSystem();
+ doReturn(getSystem().dispatchers().defaultGlobalDispatcher()).when(mockActorContext).getClientDispatcher();
doReturn(memberName).when(mockActorContext).getCurrentMemberName();
doReturn(schemaContext).when(mockActorContext).getSchemaContext();
doReturn(mockClusterWrapper).when(mockActorContext).getClusterWrapper();
package org.opendaylight.controller.cluster.datastore.utils;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import akka.actor.ActorRef;
import akka.actor.ActorSelection;
+import akka.actor.ActorSystem;
import akka.actor.Address;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.japi.Creator;
import akka.testkit.JavaTestKit;
import com.google.common.base.Optional;
+import com.typesafe.config.ConfigFactory;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.time.StopWatch;
import org.junit.Test;
assertTrue("did not take as much time as expected", watch.getTime() > 1000);
}
+
+ @Test
+ public void testClientDispatcherIsGlobalDispatcher(){
+
+ DatastoreContext mockDataStoreContext = mock(DatastoreContext.class);
+
+ doReturn(155L).when(mockDataStoreContext).getTransactionCreationInitialRateLimit();
+ doReturn("config").when(mockDataStoreContext).getDataStoreType();
+
+ ActorContext actorContext =
+ new ActorContext(getSystem(), mock(ActorRef.class), mock(ClusterWrapper.class),
+ mock(Configuration.class), mockDataStoreContext);
+
+ assertEquals(getSystem().dispatchers().defaultGlobalDispatcher(), actorContext.getClientDispatcher());
+
+ }
+
+ @Test
+ public void testClientDispatcherIsNotGlobalDispatcher(){
+
+ DatastoreContext mockDataStoreContext = mock(DatastoreContext.class);
+
+ doReturn(155L).when(mockDataStoreContext).getTransactionCreationInitialRateLimit();
+ doReturn("config").when(mockDataStoreContext).getDataStoreType();
+
+ ActorSystem actorSystem = ActorSystem.create("with-custom-dispatchers", ConfigFactory.load("application-with-custom-dispatchers.conf"));
+
+ ActorContext actorContext =
+ new ActorContext(actorSystem, mock(ActorRef.class), mock(ClusterWrapper.class),
+ mock(Configuration.class), mockDataStoreContext);
+
+ assertNotEquals(actorSystem.dispatchers().defaultGlobalDispatcher(), actorContext.getClientDispatcher());
+
+ actorSystem.shutdown();
+
+ }
+
}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import akka.dispatch.MessageDispatcher;
+import org.junit.Test;
+
+public class DispatchersTest {
+
+ @Test
+ public void testGetDefaultDispatcherPath(){
+ akka.dispatch.Dispatchers mockDispatchers = mock(akka.dispatch.Dispatchers.class);
+ doReturn(false).when(mockDispatchers).hasDispatcher(anyString());
+ Dispatchers dispatchers = new Dispatchers(mockDispatchers);
+
+ for(Dispatchers.DispatcherType type : Dispatchers.DispatcherType.values()) {
+ assertEquals(Dispatchers.DEFAULT_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(type));
+ }
+
+ }
+
+ @Test
+ public void testGetDefaultDispatcher(){
+ akka.dispatch.Dispatchers mockDispatchers = mock(akka.dispatch.Dispatchers.class);
+ MessageDispatcher mockGlobalDispatcher = mock(MessageDispatcher.class);
+ doReturn(false).when(mockDispatchers).hasDispatcher(anyString());
+ doReturn(mockGlobalDispatcher).when(mockDispatchers).defaultGlobalDispatcher();
+ Dispatchers dispatchers = new Dispatchers(mockDispatchers);
+
+ for(Dispatchers.DispatcherType type : Dispatchers.DispatcherType.values()) {
+ assertEquals(mockGlobalDispatcher,
+ dispatchers.getDispatcher(type));
+ }
+
+ }
+
+ @Test
+ public void testGetDispatcherPath(){
+ akka.dispatch.Dispatchers mockDispatchers = mock(akka.dispatch.Dispatchers.class);
+ doReturn(true).when(mockDispatchers).hasDispatcher(anyString());
+ Dispatchers dispatchers = new Dispatchers(mockDispatchers);
+
+ assertEquals(Dispatchers.CLIENT_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Client));
+
+ assertEquals(Dispatchers.TXN_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Transaction));
+
+ assertEquals(Dispatchers.SHARD_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Shard));
+
+ assertEquals(Dispatchers.NOTIFICATION_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Notification));
+
+ }
+
+ @Test
+ public void testGetDispatcher(){
+ akka.dispatch.Dispatchers mockDispatchers = mock(akka.dispatch.Dispatchers.class);
+ MessageDispatcher mockDispatcher = mock(MessageDispatcher.class);
+ doReturn(true).when(mockDispatchers).hasDispatcher(anyString());
+ doReturn(mockDispatcher).when(mockDispatchers).lookup(anyString());
+ Dispatchers dispatchers = new Dispatchers(mockDispatchers);
+
+ assertEquals(Dispatchers.CLIENT_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Client));
+
+ assertEquals(Dispatchers.TXN_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Transaction));
+
+ assertEquals(Dispatchers.SHARD_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Shard));
+
+ assertEquals(Dispatchers.NOTIFICATION_DISPATCHER_PATH,
+ dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Notification));
+
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MessageTrackerTest {
+
+ private final Logger LOG = LoggerFactory.getLogger(getClass());
+
+ private class Foo {}
+
+ @Test
+ public void testNoTracking(){
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+
+ MessageTracker.Context context1 = messageTracker.received(new Foo());
+ context1.done();
+
+ Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS);
+
+ MessageTracker.Context context2 = messageTracker.received(new Foo());
+ context2.done();
+
+ }
+
+ @Test
+ public void testFailedExpectationOnTracking(){
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+ messageTracker.begin();
+
+ MessageTracker.Context context1 = messageTracker.received(new Foo());
+ context1.done();
+
+ Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS);
+
+ MessageTracker.Context context2 = messageTracker.received(new Foo());
+ Assert.assertEquals(true, context2.error().isPresent());
+ Assert.assertEquals(0, context2.error().get().getMessageProcessingTimesSinceLastExpectedMessage().size());
+
+ }
+
+ @Test
+ public void testFailedExpectationOnTrackingWithMessagesInBetween(){
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+ messageTracker.begin();
+
+ MessageTracker.Context context1 = messageTracker.received(new Foo());
+ context1.done();
+
+ messageTracker.received("A").done();
+ messageTracker.received(Long.valueOf(10)).done();
+ MessageTracker.Context c = messageTracker.received(Integer.valueOf(100));
+
+ Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS);
+
+ c.done();
+
+ MessageTracker.Context context2 = messageTracker.received(new Foo());
+
+ Assert.assertEquals(true, context2.error().isPresent());
+
+ MessageTracker.Error error = context2.error().get();
+
+ List<MessageTracker.MessageProcessingTime> messageProcessingTimes =
+ error.getMessageProcessingTimesSinceLastExpectedMessage();
+
+ Assert.assertEquals(3, messageProcessingTimes.size());
+
+ Assert.assertEquals(String.class, messageProcessingTimes.get(0).getMessageClass());
+ Assert.assertEquals(Long.class, messageProcessingTimes.get(1).getMessageClass());
+ Assert.assertEquals(Integer.class, messageProcessingTimes.get(2).getMessageClass());
+ Assert.assertTrue(messageProcessingTimes.get(2).getElapsedTimeInNanos() > TimeUnit.MILLISECONDS.toNanos(10));
+ Assert.assertEquals(Foo.class, error.getLastExpectedMessage().getClass());
+ Assert.assertEquals(Foo.class, error.getCurrentExpectedMessage().getClass());
+
+ LOG.error("An error occurred : {}" , error);
+
+ }
+
+
+ @Test
+ public void testMetExpectationOnTracking(){
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+ messageTracker.begin();
+
+ MessageTracker.Context context1 = messageTracker.received(new Foo());
+ context1.done();
+
+ Uninterruptibles.sleepUninterruptibly(1, TimeUnit.MILLISECONDS);
+
+ MessageTracker.Context context2 = messageTracker.received(new Foo());
+ Assert.assertEquals(false, context2.error().isPresent());
+
+ }
+
+ @Test
+ public void testIllegalStateExceptionWhenDoneIsNotCalledWhileTracking(){
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+ messageTracker.begin();
+
+ messageTracker.received(new Foo());
+
+ try {
+ messageTracker.received(new Foo());
+ fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e){
+
+ }
+ }
+
+ @Test
+ public void testNoIllegalStateExceptionWhenDoneIsNotCalledWhileNotTracking(){
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+
+ messageTracker.received(new Foo());
+ messageTracker.received(new Foo());
+ }
+
+ @Test
+ public void testDelayInFirstExpectedMessageArrival(){
+
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+ messageTracker.begin();
+
+ Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS);
+
+ MessageTracker.Context context = messageTracker.received(new Foo());
+
+ Assert.assertEquals(true, context.error().isPresent());
+
+ MessageTracker.Error error = context.error().get();
+
+ Assert.assertEquals(null, error.getLastExpectedMessage());
+ Assert.assertEquals(Foo.class, error.getCurrentExpectedMessage().getClass());
+
+ String errorString = error.toString();
+ Assert.assertTrue(errorString.contains("Last Expected Message = null"));
+
+ LOG.error("An error occurred : {}", error);
+ }
+
+ @Test
+ public void testCallingBeginDoesNotResetWatch(){
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+ messageTracker.begin();
+
+ Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS);
+
+ messageTracker.begin();
+
+ MessageTracker.Context context = messageTracker.received(new Foo());
+
+ Assert.assertEquals(true, context.error().isPresent());
+
+ }
+
+ @Test
+ public void testMessagesSinceLastExpectedMessage(){
+
+ MessageTracker messageTracker = new MessageTracker(Foo.class, 10);
+ messageTracker.begin();
+
+ MessageTracker.Context context1 = messageTracker.received(Integer.valueOf(45)).done();
+
+ Assert.assertEquals(false, context1.error().isPresent());
+
+ MessageTracker.Context context2 = messageTracker.received(Long.valueOf(45)).done();
+
+ Assert.assertEquals(false, context2.error().isPresent());
+
+ List<MessageTracker.MessageProcessingTime> processingTimeList =
+ messageTracker.getMessagesSinceLastExpectedMessage();
+
+ Assert.assertEquals(2, processingTimeList.size());
+
+ assertEquals(Integer.class, processingTimeList.get(0).getMessageClass());
+ assertEquals(Long.class, processingTimeList.get(1).getMessageClass());
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+akka {
+ persistence.snapshot-store.plugin = "in-memory-snapshot-store"
+ persistence.journal.plugin = "in-memory-journal"
+
+ loggers = ["akka.testkit.TestEventListener", "akka.event.slf4j.Slf4jLogger"]
+
+ actor {
+ serializers {
+ java = "akka.serialization.JavaSerializer"
+ proto = "akka.remote.serialization.ProtobufSerializer"
+ }
+
+ serialization-bindings {
+ "org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification" = java
+ "com.google.protobuf.Message" = proto
+
+ }
+ }
+}
+
+in-memory-journal {
+ class = "org.opendaylight.controller.cluster.datastore.utils.InMemoryJournal"
+}
+
+in-memory-snapshot-store {
+ # Class name of the plugin.
+ class = "org.opendaylight.controller.cluster.datastore.utils.InMemorySnapshotStore"
+ # Dispatcher for the plugin actor.
+ plugin-dispatcher = "akka.persistence.dispatchers.default-plugin-dispatcher"
+}
+
+bounded-mailbox {
+ mailbox-type = "org.opendaylight.controller.cluster.common.actor.MeteredBoundedMailbox"
+ mailbox-capacity = 1000
+ mailbox-push-timeout-time = 100ms
+}
+
+client-dispatcher {
+ # Dispatcher is the name of the event-based dispatcher
+ type = Dispatcher
+ # What kind of ExecutionService to use
+ executor = "fork-join-executor"
+ # Configuration for the fork join pool
+ fork-join-executor {
+ # Min number of threads to cap factor-based parallelism number to
+ parallelism-min = 2
+ # Parallelism (threads) ... ceil(available processors * factor)
+ parallelism-factor = 2.0
+ # Max number of threads to cap factor-based parallelism number to
+ parallelism-max = 10
+ }
+ # Throughput defines the maximum number of messages to be
+ # processed per actor before the thread jumps to the next actor.
+ # Set to 1 for as fair as possible.
+ throughput = 100
+}
+
+transaction-dispatcher {
+ # Dispatcher is the name of the event-based dispatcher
+ type = Dispatcher
+ # What kind of ExecutionService to use
+ executor = "fork-join-executor"
+ # Configuration for the fork join pool
+ fork-join-executor {
+ # Min number of threads to cap factor-based parallelism number to
+ parallelism-min = 2
+ # Parallelism (threads) ... ceil(available processors * factor)
+ parallelism-factor = 2.0
+ # Max number of threads to cap factor-based parallelism number to
+ parallelism-max = 10
+ }
+ # Throughput defines the maximum number of messages to be
+ # processed per actor before the thread jumps to the next actor.
+ # Set to 1 for as fair as possible.
+ throughput = 100
+}
+
+shard-dispatcher {
+ # Dispatcher is the name of the event-based dispatcher
+ type = Dispatcher
+ # What kind of ExecutionService to use
+ executor = "fork-join-executor"
+ # Configuration for the fork join pool
+ fork-join-executor {
+ # Min number of threads to cap factor-based parallelism number to
+ parallelism-min = 2
+ # Parallelism (threads) ... ceil(available processors * factor)
+ parallelism-factor = 2.0
+ # Max number of threads to cap factor-based parallelism number to
+ parallelism-max = 10
+ }
+ # Throughput defines the maximum number of messages to be
+ # processed per actor before the thread jumps to the next actor.
+ # Set to 1 for as fair as possible.
+ throughput = 100
+}
+
+notification-dispatcher {
+ # Dispatcher is the name of the event-based dispatcher
+ type = Dispatcher
+ # What kind of ExecutionService to use
+ executor = "fork-join-executor"
+ # Configuration for the fork join pool
+ fork-join-executor {
+ # Min number of threads to cap factor-based parallelism number to
+ parallelism-min = 2
+ # Parallelism (threads) ... ceil(available processors * factor)
+ parallelism-factor = 2.0
+ # Max number of threads to cap factor-based parallelism number to
+ parallelism-max = 10
+ }
+ # Throughput defines the maximum number of messages to be
+ # processed per actor before the thread jumps to the next actor.
+ # Set to 1 for as fair as possible.
+ throughput = 100
+}
\ No newline at end of file
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-test-model</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
import com.google.common.base.Preconditions;
import java.io.Serializable;
+import java.util.Iterator;
import javax.annotation.Nonnull;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
/**
* A unique identifier for a particular subtree. It is composed of the logical
* data store type and the instance identifier of the root node.
*/
-public final class DOMDataTreeIdentifier implements Immutable, Path<DOMDataTreeIdentifier>, Serializable {
+public final class DOMDataTreeIdentifier implements Immutable, Path<DOMDataTreeIdentifier>, Serializable, Comparable<DOMDataTreeIdentifier> {
private static final long serialVersionUID = 1L;
private final YangInstanceIdentifier rootIdentifier;
private final LogicalDatastoreType datastoreType;
}
return rootIdentifier.equals(other.rootIdentifier);
}
+
+ @Override
+ public int compareTo(final DOMDataTreeIdentifier o) {
+ int i = datastoreType.compareTo(o.datastoreType);
+ if (i != 0) {
+ return i;
+ }
+
+ final Iterator<PathArgument> mi = rootIdentifier.getPathArguments().iterator();
+ final Iterator<PathArgument> oi = o.rootIdentifier.getPathArguments().iterator();
+
+ while (mi.hasNext()) {
+ if (!oi.hasNext()) {
+ return 1;
+ }
+
+ final PathArgument ma = mi.next();
+ final PathArgument oa = oi.next();
+ i = ma.compareTo(oa);
+ if (i != 0) {
+ return i;
+ }
+ }
+
+ return oi.hasNext() ? -1 : 0;
+ }
}
--- /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.md.sal.dom.api;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Failure reported when a data tree is no longer accessible.
+ */
+public class DOMDataTreeInaccessibleException extends DOMDataTreeListeningException {
+ private static final long serialVersionUID = 1L;
+ private final DOMDataTreeIdentifier treeIdentifier;
+
+ public DOMDataTreeInaccessibleException(final DOMDataTreeIdentifier treeIdentifier, final String message) {
+ super(message);
+ this.treeIdentifier = Preconditions.checkNotNull(treeIdentifier);
+ }
+
+ public DOMDataTreeInaccessibleException(final DOMDataTreeIdentifier treeIdentifier, final String message, final Throwable cause) {
+ super(message);
+ this.treeIdentifier = Preconditions.checkNotNull(treeIdentifier);
+ }
+
+ public final DOMDataTreeIdentifier getTreeIdentifier() {
+ return treeIdentifier;
+ }
+}
--- /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.md.sal.dom.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Interface implemented by data consumers, e.g. processes wanting to act on data
+ * after it has been introduced to the conceptual data tree.
+ */
+public interface DOMDataTreeListener extends EventListener {
+ /**
+ * Invoked whenever one or more registered subtrees change. The logical changes are reported,
+ * as well as the roll up of new state for all subscribed subtrees.
+ *
+ * @param changes The set of changes being reported. Each subscribed subtree may be present
+ * at most once.
+ * @param subtrees Per-subtree state as visible after the reported changes have been applied.
+ * This includes all the subtrees this listener is subscribed to, even those
+ * which have not changed.
+ */
+ void onDataTreeChanged(@Nonnull Collection<DataTreeCandidate> changes, @Nonnull Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> subtrees);
+
+ /**
+ * Invoked when a subtree listening failure occurs. This can be triggered, for example, when
+ * a connection to external subtree source is broken. The listener will not receive any other
+ * callbacks, but its registration still needs to be closed to prevent resource leak.
+ *
+ * @param cause Collection of failure causes, may not be null or empty.
+ */
+ void onDataTreeFailed(@Nonnull Collection<DOMDataTreeListeningException> causes);
+}
--- /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.md.sal.dom.api;
+
+/**
+ * Base exception for various causes why and {@link DOMDataTreeListener}
+ * may be terminated by the {@link DOMDataTreeService} implementation.
+ */
+public class DOMDataTreeListeningException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeListeningException(final String message) {
+ super(message);
+ }
+
+ public DOMDataTreeListeningException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /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.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Exception thrown when a loop is detected in the way {@link DOMDataTreeListener}
+ * and {@link DOMDataTreeProducer} instances would be connected.
+ */
+public class DOMDataTreeLoopException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeLoopException(final @Nonnull String message) {
+ super(message);
+ }
+
+ public DOMDataTreeLoopException(final @Nonnull String message, final @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+}
--- /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.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+
+/**
+ * A data producer context. It allows transactions to be submitted to the subtrees
+ * specified at instantiation time. At any given time there may be a single transaction
+ * open. It needs to be either submitted or cancelled before another one can be open.
+ * Once a transaction is submitted, it will proceed to be committed asynchronously.
+ *
+ * Each instance has an upper bound on the number of transactions which can be in-flight,
+ * once that capacity is exceeded, an attempt to create a new transaction will block
+ * until some transactions complete.
+ *
+ * Each {@link DOMDataTreeProducer} can be in two logical states, bound and unbound,
+ * which define the lifecycle rules for when is it legal to create and submit transactions
+ * in relationship with {@link DOMDataTreeListener} callbacks.
+ *
+ * When a producer is first created, it is unbound. In this state the producer can be
+ * accessed by any application thread to allocate or submit transactions, as long as
+ * the 'single open transaction' rule is maintained. The producer and any transaction
+ * object MUST NOT be accessed, directly or indirectly, from a {@link DOMDataTreeListener}
+ * callback.
+ *
+ * When a producer is referenced in a call to {@link DOMDataTreeService#registerListener(DOMDataTreeListener, java.util.Collection, boolean, java.util.Collection)},
+ * an attempt will be made to bind the producer to the specified {@link DOMDataTreeListener}.
+ * Such an attempt will fail the producer is already bound, or it has an open transaction.
+ * Once bound, the producer can only be accessed from within the {@link DOMDataTreeListener}
+ * callback on that particular instance. Any transaction which is not submitted by the
+ * time the callback returns will be implicitly cancelled. A producer becomes unbound
+ * when the listener it is bound to becomes unregistered.
+ */
+public interface DOMDataTreeProducer extends DOMDataTreeProducerFactory, AutoCloseable {
+ /**
+ * Allocate a new open transaction on this producer. Any and all transactions
+ * previously allocated must have been either submitted or cancelled by the
+ * time this method is invoked.
+ *
+ * @param barrier Indicates whether this transaction should be a barrier. A barrier
+ * transaction is processed separately from any preceding transactions.
+ * Non-barrier transactions may be merged and processed in a batch,
+ * such that any observers see the modifications contained in them as
+ * if the modifications were made in a single transaction.
+ * @return A new {@link DOMDataWriteTransaction}
+ * @throws {@link IllegalStateException} if a previous transaction was not closed.
+ * @throws {@link IllegalThreadStateException} if the calling thread context does not
+ * match the lifecycle rules enforced by the producer state (e.g. bound or unbound).
+ * This exception is thrown on a best effort basis and programs should not rely
+ * on it for correct operation.
+ */
+ @Nonnull DOMDataWriteTransaction createTransaction(boolean isolated);
+
+ /**
+ * {@inheritDoc}
+ *
+ * When invoked on a {@link DOMDataTreeProducer}, this method has additional restrictions.
+ * There may not be an open transaction from this producer. The method needs to be
+ * invoked in appropriate context, e.g. bound or unbound.
+ *
+ * Specified subtrees must be accessible by this producer. Accessible means they are a subset
+ * of the subtrees specified when the producer is instantiated. The set is further reduced as
+ * child producers are instantiated -- if you create a producer for /a and then a child for
+ * /a/b, /a/b is not accessible from the first producer.
+ *
+ * Once this method returns successfully, this (parent) producer loses the ability to
+ * access the specified paths until the resulting (child) producer is shut down.
+ *
+ * @throws {@link IllegalStateException} if there is an open transaction
+ * @throws {@link IllegalArgumentException} if subtrees contains a subtree which is not
+ * accessible by this producer
+ * @throws {@link IllegalThreadStateException} if the calling thread context does not
+ * match the lifecycle rules enforced by the producer state (e.g. bound or unbound).
+ * This exception is thrown on a best effort basis and programs should not rely
+ * on it for correct operation.
+ */
+ @Override
+ @Nonnull DOMDataTreeProducer createProducer(@Nonnull Collection<DOMDataTreeIdentifier> subtrees);
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DOMDataTreeProducerBusyException when there is an open transaction.
+ */
+ @Override
+ void close() throws DOMDataTreeProducerException;
+}
--- /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.md.sal.dom.api;
+
+/**
+ * Exception indicating that the {@link DOMDataTreeProducer} has an open user
+ * transaction and cannot be closed.
+ */
+public class DOMDataTreeProducerBusyException extends DOMDataTreeProducerException {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeProducerBusyException(final String message) {
+ super(message);
+ }
+
+ public DOMDataTreeProducerBusyException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /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.md.sal.dom.api;
+
+/**
+ * Base exception for all exceptions related to {@link DOMDataTreeProducer}s.
+ */
+public class DOMDataTreeProducerException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeProducerException(final String message) {
+ super(message);
+ }
+
+ public DOMDataTreeProducerException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /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.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+
+/**
+ * Base source of {@link DOMDataTreeProducer}s. This interface is usually not used directly,
+ * but rather through one of its sub-interfaces.
+ */
+public interface DOMDataTreeProducerFactory {
+ /**
+ * Create a producer, which is able to access to a set of trees.
+ *
+ * @param subtrees The collection of subtrees the resulting producer should have access to.
+ * @return A {@link DOMDataTreeProducer} instance.
+ * @throws {@link IllegalArgumentException} if subtrees is empty.
+ */
+ @Nonnull DOMDataTreeProducer createProducer(@Nonnull Collection<DOMDataTreeIdentifier> subtrees);
+}
--- /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.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMService} providing access to the conceptual data tree. Interactions
+ * with the data tree are split into data producers and consumers (listeners). Each
+ * of them operate on a set of subtrees, which need to be declared at instantiation time.
+ *
+ * Returned instances are not thread-safe and expected to be used by a single thread
+ * at a time. Furthermore, producers may not be accessed from consumer callbacks
+ * unless they were specified when the listener is registered.
+ *
+ * The service maintains a loop-free topology of producers and consumers. What this means
+ * is that a consumer is not allowed to access a producer, which affects any of the
+ * subtrees it is subscribed to. This restriction is in place to ensure the system does
+ * not go into a feedback loop, where it is impossible to block either a producer or
+ * a consumer without accumulating excess work in the backlog stemming from its previous
+ * activity.
+ */
+public interface DOMDataTreeService extends DOMDataTreeProducerFactory, DOMService {
+ /**
+ * Register a {@link DOMDataTreeListener} instance. Once registered, the listener
+ * will start receiving changes on the selected subtrees. If the listener cannot
+ * keep up with the rate of changes, and allowRxMerges is set to true, this service
+ * is free to merge the changes, so that a smaller number of them will be reported,
+ * possibly hiding some data transitions (like flaps).
+ *
+ * If the listener wants to write into any producer, that producer has to be mentioned
+ * in the call to this method. Those producers will be bound exclusively to the
+ * registration, so that accessing them outside of this listener's callback will trigger
+ * an error. Any producers mentioned must be idle, e.g. they may not have an open
+ * transaction at the time this method is invoked.
+ *
+ * Each listener instance can be registered at most once. Implementations of this
+ * interface have to guarantee that the listener's methods will not be invoked
+ * concurrently from multiple threads.
+ *
+ * @param listener {@link DOMDataTreeListener} that is being registered
+ * @param subtrees Conceptual subtree identifier of subtrees which should be monitored
+ * for changes. May not be null or empty.
+ * @param allowRxMerges True if the backend may perform ingress state compression.
+ * @param producers {@link DOMDataTreeProducer} instances to bind to the listener.
+ * @return A listener registration. Once closed, the listener will no longer be
+ * invoked and the producers will be unbound.
+ * @throws IllegalArgumentException if subtrees is empty or the listener is already bound
+ * @throws DOMDataTreeLoopException if the registration of the listener to the specified
+ * subtrees with specified producers would form a
+ * feedback loop
+ */
+ @Nonnull <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(@Nonnull T listener,
+ @Nonnull Collection<DOMDataTreeIdentifier> subtrees, boolean allowRxMerges, @Nonnull Collection<DOMDataTreeProducer> producers);
+}
--- /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.md.sal.dom.api;
+
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+
+/**
+ * A single shard of the conceptual data tree. This interface defines the basic notifications
+ * a shard can receive. Each shard implementation is expected to also implement some of the
+ * datastore-level APIs. Which interfaces are required depends on the {@link DOMDataTreeShardingService}
+ * implementation.
+ */
+public interface DOMDataTreeShard extends EventListener {
+ /**
+ * Invoked whenever a child is getting attached as a more specific prefix under this shard.
+ *
+ * @param prefix Child's prefix
+ * @param child Child shard
+ */
+ void onChildAttached(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull DOMDataTreeShard child);
+
+ /**
+ * Invoked whenever a child is getting detached as a more specific prefix under this shard.
+ *
+ * @param prefix Child's prefix
+ * @param child Child shard
+ */
+ void onChildDetached(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull DOMDataTreeShard child);
+}
--- /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.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Exception thrown when an attempt to attach a conflicting shard to the global
+ * table.
+ */
+public class DOMDataTreeShardingConflictException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeShardingConflictException(final @Nonnull String message) {
+ super(message);
+ }
+
+ public DOMDataTreeShardingConflictException(final @Nonnull String message, final @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+}
--- /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.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMService} providing access to details on how the conceptual data tree
+ * is distributed among providers (also known as shards). Each shard is tied to a
+ * single {@link DOMDataTreeIdentifier}. Based on those data tree identifiers, the
+ * shards are organized in a tree, where there is a logical parent/child relationship.
+ *
+ * It is not allowed to attach two shards to the same data tree identifier, which means
+ * the mapping of each piece of information has an unambiguous home. When accessing
+ * the information, the shard with the longest matching data tree identifier is used,
+ * which is why this interface treats it is a prefix.
+ *
+ * Whenever a parent/child relationship is changed, the parent is notified, so it can
+ * understand that a logical child has been attached.
+ */
+public interface DOMDataTreeShardingService extends DOMService {
+ /**
+ * Register a shard as responsible for a particular subtree prefix.
+ *
+ * @param prefix Data tree identifier, may not be null.
+ * @param shard Responsible shard instance
+ * @return A registration. To remove the shard's binding, close the registration.
+ * @throws DOMDataTreeShardingConflictException if the prefix is already bound
+ */
+ @Nonnull <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull T shard) throws DOMDataTreeShardingConflictException;
+}
--- /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.md.sal.dom.api;
+
+import static org.junit.Assert.assertNotNull;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.net.URI;
+import java.util.Collections;
+import javax.annotation.Nonnull;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+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.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+
+/**
+ * Abstract test suite demonstrating various access patterns on how a {@link DOMDataTreeService}
+ * can be used.
+ */
+public abstract class AbstractDOMDataTreeServiceTestSuite {
+ protected static final QNameModule TEST_MODULE = QNameModule.create(URI.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:store"), null);
+
+ protected static final YangInstanceIdentifier UNORDERED_CONTAINER_IID = YangInstanceIdentifier.create(
+ new NodeIdentifier(QName.create(TEST_MODULE, "lists")),
+ new NodeIdentifier(QName.create(TEST_MODULE, "unordered-container")));
+ protected static final DOMDataTreeIdentifier UNORDERED_CONTAINER_TREE = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, UNORDERED_CONTAINER_IID);
+
+ /**
+ * Return a reference to the service used in this test. The instance
+ * needs to be reused within the same test and must be isolated between
+ * tests.
+ *
+ * @return {@link DOMDataTreeService} instance.
+ */
+ protected abstract @Nonnull DOMDataTreeService service();
+
+ /**
+ * A simple unbound producer. It write some basic things into the data store based on the
+ * test model.
+ * @throws DOMDataTreeProducerException
+ * @throws TransactionCommitFailedException
+ */
+ @Test
+ public final void testBasicProducer() throws DOMDataTreeProducerException, TransactionCommitFailedException {
+ // Create a producer. It is an AutoCloseable resource, hence the try-with pattern
+ try (final DOMDataTreeProducer prod = service().createProducer(Collections.singleton(UNORDERED_CONTAINER_TREE))) {
+ assertNotNull(prod);
+
+ final DOMDataWriteTransaction tx = prod.createTransaction(true);
+ assertNotNull(tx);
+
+ tx.put(LogicalDatastoreType.OPERATIONAL, UNORDERED_CONTAINER_IID, ImmutableContainerNodeBuilder.create().build());
+
+ final CheckedFuture<Void, TransactionCommitFailedException> f = tx.submit();
+ assertNotNull(f);
+
+ f.checkedGet();
+ }
+ }
+
+ // TODO: simple listener
+}
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import java.util.Map.Entry;
* the Future fails with a {@link TransactionCommitFailedException}.
*/
protected abstract CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
- final Iterable<DOMStoreThreePhaseCommitCohort> cohorts);
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts);
/**
* Creates a new composite read-only transaction
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
-import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
};
private static final Logger LOG = LoggerFactory.getLogger(CommitCoordinationTask.class);
- private final Iterable<DOMStoreThreePhaseCommitCohort> cohorts;
+ private final Collection<DOMStoreThreePhaseCommitCohort> cohorts;
private final DurationStatisticsTracker commitStatTracker;
private final DOMDataWriteTransaction tx;
- private final int cohortSize;
public CommitCoordinationTask(final DOMDataWriteTransaction transaction,
- final Iterable<DOMStoreThreePhaseCommitCohort> cohorts,
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts,
final DurationStatisticsTracker commitStatTracker) {
this.tx = Preconditions.checkNotNull(transaction, "transaction must not be null");
this.cohorts = Preconditions.checkNotNull(cohorts, "cohorts must not be null");
this.commitStatTracker = commitStatTracker;
- this.cohortSize = Iterables.size(cohorts);
}
@Override
*
*/
private ListenableFuture<?>[] canCommitAll() {
- final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohortSize];
+ final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
int i = 0;
for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
ops[i++] = cohort.canCommit();
*
*/
private ListenableFuture<?>[] preCommitAll() {
- final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohortSize];
+ final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
int i = 0;
for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
ops[i++] = cohort.preCommit();
* @return List of all cohorts futures from can commit phase.
*/
private ListenableFuture<?>[] commitAll() {
- final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohortSize];
+ final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
int i = 0;
for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
ops[i++] = cohort.commit();
*/
private ListenableFuture<Void> abortAsyncAll() {
- final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohortSize];
+ final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
int i = 0;
for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
ops[i++] = cohort.abort();
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
+import java.util.Collection;
import java.util.Map;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLong;
@Override
public CheckedFuture<Void, TransactionCommitFailedException> submit(
- final DOMDataWriteTransaction transaction, final Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
+ final DOMDataWriteTransaction transaction, final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
checkNotFailed();
checkNotClosed();
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.Collection;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@Override
protected CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
- final Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
Preconditions.checkArgument(cohorts != null, "Cohorts must not be null.");
LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
--- /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.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+
+final class ShardRegistration<T extends DOMDataTreeShard> extends AbstractListenerRegistration<T> {
+ private final DOMDataTreeIdentifier prefix;
+ private final ShardedDOMDataTree tree;
+
+ protected ShardRegistration(final ShardedDOMDataTree tree, final DOMDataTreeIdentifier prefix, final T shard) {
+ super(shard);
+ this.tree = Preconditions.checkNotNull(tree);
+ this.prefix = Preconditions.checkNotNull(prefix);
+ }
+
+ DOMDataTreeIdentifier getPrefix() {
+ return prefix;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ tree.removeShard(this);
+ }
+}
--- /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.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShardingConflictException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShardingService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTreeShardingService {
+ private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTree.class);
+ private final Map<LogicalDatastoreType, ShardingTableEntry> shardingTables = new EnumMap<>(LogicalDatastoreType.class);
+ @GuardedBy("this")
+ private final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> idToProducer = new TreeMap<>();
+
+ @GuardedBy("this")
+ private ShardingTableEntry lookupShard(final DOMDataTreeIdentifier prefix) {
+ final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+ if (t == null) {
+ return null;
+ }
+
+ return t.lookup(prefix.getRootIdentifier());
+ }
+
+ @GuardedBy("this")
+ private void storeShard(final DOMDataTreeIdentifier prefix, final ShardRegistration<?> reg) {
+ ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+ if (t == null) {
+ t = new ShardingTableEntry();
+ shardingTables.put(prefix.getDatastoreType(), t);
+ }
+
+ t.store(prefix.getRootIdentifier(), reg);
+ }
+
+ void removeShard(final ShardRegistration<?> reg) {
+ final DOMDataTreeIdentifier prefix = reg.getPrefix();
+ final ShardRegistration<?> parentReg;
+
+ synchronized (this) {
+ final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+ if (t == null) {
+ LOG.warn("Shard registration {} points to non-existent table", reg);
+ return;
+ }
+
+ t.remove(prefix.getRootIdentifier());
+ parentReg = lookupShard(prefix).getRegistration();
+
+ /*
+ * FIXME: adjust all producers. This is tricky, as we need different locking strategy,
+ * simply because we risk AB/BA deadlock with a producer being split off from
+ * a producer.
+ *
+ */
+ }
+
+ if (parentReg != null) {
+ parentReg.getInstance().onChildDetached(prefix, reg.getInstance());
+ }
+ }
+
+ @Override
+ public <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(final DOMDataTreeIdentifier prefix, final T shard) throws DOMDataTreeShardingConflictException {
+ final ShardRegistration<T> reg;
+ final ShardRegistration<?> parentReg;
+
+ synchronized (this) {
+ /*
+ * Lookup the parent shard (e.g. the one which currently matches the prefix),
+ * and if it exists, check if its registration prefix does not collide with
+ * this registration.
+ */
+ final ShardingTableEntry parent = lookupShard(prefix);
+ parentReg = parent.getRegistration();
+ if (parentReg != null && prefix.equals(parentReg.getPrefix())) {
+ throw new DOMDataTreeShardingConflictException(String.format("Prefix %s is already occupied by shard {}", prefix, parentReg.getInstance()));
+ }
+
+ // FIXME: wrap the shard in a proper adaptor based on implemented interface
+
+ reg = new ShardRegistration<T>(this, prefix, shard);
+
+ storeShard(prefix, reg);
+
+ // FIXME: update any producers/registrations
+ }
+
+ // Notify the parent shard
+ if (parentReg != null) {
+ parentReg.getInstance().onChildAttached(prefix, shard);
+ }
+
+ return reg;
+ }
+
+ @GuardedBy("this")
+ private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) {
+ for (Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : idToProducer.entrySet()) {
+ if (e.getKey().contains(subtree)) {
+ return e.getValue();
+ }
+ }
+
+ return null;
+ }
+
+ synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) {
+ for (DOMDataTreeIdentifier s : producer.getSubtrees()) {
+ DOMDataTreeProducer r = idToProducer.remove(s);
+ if (!producer.equals(r)) {
+ LOG.error("Removed producer %s on subtree %s while removing %s", r, s, producer);
+ }
+ }
+ }
+
+ @GuardedBy("this")
+ private DOMDataTreeProducer createProducer(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+ // Record the producer's attachment points
+ final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, shardMap);
+ for (DOMDataTreeIdentifier s : shardMap.keySet()) {
+ idToProducer.put(s, ret);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
+ Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty");
+
+ final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
+ for (DOMDataTreeIdentifier s : subtrees) {
+ // Attempting to create a disconnected producer -- all subtrees have to be unclaimed
+ final DOMDataTreeProducer producer = findProducer(s);
+ Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", s, producer);
+
+ shardMap.put(s, lookupShard(s).getRegistration().getInstance());
+ }
+
+ return createProducer(shardMap);
+ }
+
+ synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent, final Collection<DOMDataTreeIdentifier> subtrees) {
+ Preconditions.checkNotNull(parent);
+
+ final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
+ for (DOMDataTreeIdentifier s : subtrees) {
+ shardMap.put(s, lookupShard(s).getRegistration().getInstance());
+ }
+
+ return createProducer(shardMap);
+ }
+
+ @Override
+ public synchronized <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener, final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges, final Collection<DOMDataTreeProducer> producers) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
--- /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.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducerBusyException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducerException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
+ private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducer.class);
+ private final BiMap<DOMDataTreeShard, DOMStoreTransactionChain> shardToChain;
+ private final Map<DOMDataTreeIdentifier, DOMDataTreeShard> idToShard;
+ private final ShardedDOMDataTree dataTree;
+
+ @GuardedBy("this")
+ private Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children = Collections.emptyMap();
+ @GuardedBy("this")
+ private DOMDataWriteTransaction openTx;
+ @GuardedBy("this")
+ private boolean closed;
+
+ ShardedDOMDataTreeProducer(final ShardedDOMDataTree dataTree, final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap, final Set<DOMDataTreeShard> shards) {
+ this.dataTree = Preconditions.checkNotNull(dataTree);
+
+ // Create shard -> chain map
+ final Builder<DOMDataTreeShard, DOMStoreTransactionChain> cb = ImmutableBiMap.builder();
+ final Queue<Exception> es = new LinkedList<>();
+
+ for (DOMDataTreeShard s : shards) {
+ if (s instanceof DOMStore) {
+ try {
+ final DOMStoreTransactionChain c = ((DOMStore)s).createTransactionChain();
+ LOG.trace("Using DOMStore chain {} to access shard {}", c, s);
+ cb.put(s, c);
+ } catch (Exception e) {
+ LOG.error("Failed to instantiate chain for shard {}", s, e);
+ es.add(e);
+ }
+ } else {
+ LOG.error("Unhandled shard instance type {}", s.getClass());
+ }
+ }
+ this.shardToChain = cb.build();
+
+ // An error was encountered, close chains and report the error
+ if (shardToChain.size() != shards.size()) {
+ for (DOMStoreTransactionChain c : shardToChain.values()) {
+ try {
+ c.close();
+ } catch (Exception e) {
+ LOG.warn("Exception raised while closing chain %s", c, e);
+ }
+ }
+
+ final IllegalStateException e = new IllegalStateException("Failed to completely allocate contexts", es.poll());
+ while (!es.isEmpty()) {
+ e.addSuppressed(es.poll());
+ }
+
+ throw e;
+ }
+
+ idToShard = ImmutableMap.copyOf(shardMap);
+ }
+
+ @Override
+ public synchronized DOMDataWriteTransaction createTransaction(final boolean isolated) {
+ Preconditions.checkState(!closed, "Producer is already closed");
+ Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
+
+ // Allocate backing transactions
+ final Map<DOMDataTreeShard, DOMStoreWriteTransaction> shardToTx = new HashMap<>();
+ for (Entry<DOMDataTreeShard, DOMStoreTransactionChain> e : shardToChain.entrySet()) {
+ shardToTx.put(e.getKey(), e.getValue().newWriteOnlyTransaction());
+ }
+
+ // Create the ID->transaction map
+ final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMStoreWriteTransaction> b = ImmutableMap.builder();
+ for (Entry<DOMDataTreeIdentifier, DOMDataTreeShard> e : idToShard.entrySet()) {
+ b.put(e.getKey(), shardToTx.get(e.getValue()));
+ }
+
+ final ShardedDOMDataWriteTransaction ret = new ShardedDOMDataWriteTransaction(this, b.build());
+ openTx = ret;
+ return ret;
+ }
+
+ @GuardedBy("this")
+ private boolean haveSubtree(final DOMDataTreeIdentifier subtree) {
+ for (DOMDataTreeIdentifier i : idToShard.keySet()) {
+ if (i.contains(subtree)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @GuardedBy("this")
+ private DOMDataTreeProducer lookupChild(final DOMDataTreeIdentifier s) {
+ for (Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : children.entrySet()) {
+ if (e.getKey().contains(s)) {
+ return e.getValue();
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
+ Preconditions.checkState(!closed, "Producer is already closed");
+ Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
+
+ for (DOMDataTreeIdentifier s : subtrees) {
+ // Check if the subtree was visible at any time
+ if (!haveSubtree(s)) {
+ throw new IllegalArgumentException(String.format("Subtree %s was never available in producer %s", s, this));
+ }
+
+ // Check if the subtree has not been delegated to a child
+ final DOMDataTreeProducer child = lookupChild(s);
+ Preconditions.checkArgument(child == null, "Subtree %s is delegated to child producer %s", s, child);
+
+ // Check if part of the requested subtree is not delegated to a child.
+ for (DOMDataTreeIdentifier c : children.keySet()) {
+ if (s.contains(c)) {
+ throw new IllegalArgumentException(String.format("Subtree %s cannot be delegated as it is superset of already-delegated %s", s, c));
+ }
+ }
+ }
+
+ final DOMDataTreeProducer ret = dataTree.createProducer(this, subtrees);
+ final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMDataTreeProducer> cb = ImmutableMap.builder();
+ cb.putAll(children);
+ for (DOMDataTreeIdentifier s : subtrees) {
+ cb.put(s, ret);
+ }
+
+ children = cb.build();
+ return ret;
+ }
+
+ @Override
+ public synchronized void close() throws DOMDataTreeProducerException {
+ if (!closed) {
+ if (openTx != null) {
+ throw new DOMDataTreeProducerBusyException(String.format("Transaction %s is still open", openTx));
+ }
+
+ closed = true;
+ dataTree.destroyProducer(this);
+ }
+ }
+
+ static DOMDataTreeProducer create(final ShardedDOMDataTree dataTree, final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+ /*
+ * FIXME: we do not allow multiple multiple shards in a producer because we do not implement the
+ * synchronization primitives yet
+ */
+ final Set<DOMDataTreeShard> shards = ImmutableSet.copyOf(shardMap.values());
+ if (shards.size() > 1) {
+ throw new UnsupportedOperationException("Cross-shard producers are not supported yet");
+ }
+
+ return new ShardedDOMDataTreeProducer(dataTree, shardMap, shards);
+ }
+
+ Set<DOMDataTreeIdentifier> getSubtrees() {
+ return idToShard.keySet();
+ }
+
+ synchronized void cancelTransaction(final ShardedDOMDataWriteTransaction transaction) {
+ if (!openTx.equals(transaction)) {
+ LOG.warn("Transaction {} is not open in producer {}", transaction, this);
+ return;
+ }
+
+ LOG.debug("Transaction {} cancelled", transaction);
+ openTx = null;
+ }
+}
--- /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.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.NotThreadSafe;
+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.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NotThreadSafe
+final class ShardedDOMDataWriteTransaction implements DOMDataWriteTransaction {
+ private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataWriteTransaction.class);
+ private static final AtomicLong COUNTER = new AtomicLong();
+ private final Map<DOMDataTreeIdentifier, DOMStoreWriteTransaction> idToTransaction;
+ private final ShardedDOMDataTreeProducer producer;
+ private final String identifier;
+ @GuardedBy("this")
+ private boolean closed = false;
+
+ ShardedDOMDataWriteTransaction(final ShardedDOMDataTreeProducer producer, final Map<DOMDataTreeIdentifier, DOMStoreWriteTransaction> idToTransaction) {
+ this.producer = Preconditions.checkNotNull(producer);
+ this.idToTransaction = Preconditions.checkNotNull(idToTransaction);
+ this.identifier = "SHARDED-DOM-" + COUNTER.getAndIncrement();
+ }
+
+ // FIXME: use atomic operations
+ @GuardedBy("this")
+ private DOMStoreWriteTransaction lookup(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ final DOMDataTreeIdentifier id = new DOMDataTreeIdentifier(store, path);
+
+ for (Entry<DOMDataTreeIdentifier, DOMStoreWriteTransaction> e : idToTransaction.entrySet()) {
+ if (e.getKey().contains(id)) {
+ return e.getValue();
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("Path %s is not acessible from transaction %s", id, this));
+ }
+
+ @Override
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public synchronized boolean cancel() {
+ if (closed) {
+ return false;
+ }
+
+ LOG.debug("Cancelling transaction {}", identifier);
+ for (DOMStoreWriteTransaction tx : ImmutableSet.copyOf(idToTransaction.values())) {
+ tx.close();
+ }
+
+ closed = true;
+ producer.cancelTransaction(this);
+ return true;
+ }
+
+ @Override
+ public synchronized CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ Preconditions.checkState(!closed, "Transaction %s is already closed", identifier);
+
+ final Set<DOMStoreWriteTransaction> txns = ImmutableSet.copyOf(idToTransaction.values());
+ final List<DOMStoreThreePhaseCommitCohort> cohorts = new ArrayList<>(txns.size());
+ for (DOMStoreWriteTransaction tx : txns) {
+ cohorts.add(tx.ready());
+ }
+
+ try {
+ return Futures.immediateCheckedFuture(new CommitCoordinationTask(this, cohorts, null).call());
+ } catch (TransactionCommitFailedException e) {
+ return Futures.immediateFailedCheckedFuture(e);
+ }
+ }
+
+ @Override
+ @Deprecated
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+ }
+
+ @Override
+ public synchronized void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ lookup(store, path).delete(path);
+ }
+
+ @Override
+ public synchronized void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ lookup(store, path).write(path, data);
+ }
+
+ @Override
+ public synchronized void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ lookup(store, path).merge(path, data);
+ }
+}
--- /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.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ShardingTableEntry implements Identifiable<PathArgument> {
+ private static final Logger LOG = LoggerFactory.getLogger(ShardingTableEntry.class);
+ private final Map<PathArgument, ShardingTableEntry> children = Collections.emptyMap();
+ private final PathArgument identifier;
+ private ShardRegistration<?> registration;
+
+ ShardingTableEntry() {
+ identifier = null;
+ }
+
+ ShardingTableEntry(final PathArgument identifier) {
+ this.identifier = Preconditions.checkNotNull(identifier);
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ public ShardRegistration<?> getRegistration() {
+ return registration;
+ }
+
+ ShardingTableEntry lookup(final YangInstanceIdentifier id) {
+ final Iterator<PathArgument> it = id.getPathArguments().iterator();
+ ShardingTableEntry entry = this;
+
+ while (it.hasNext()) {
+ final PathArgument a = it.next();
+ final ShardingTableEntry child = entry.children.get(a);
+ if (child == null) {
+ LOG.debug("Lookup of {} stopped at {}", id, a);
+ break;
+ }
+
+ entry = child;
+ }
+
+ return entry;
+ }
+
+ void store(final YangInstanceIdentifier id, final ShardRegistration<?> reg) {
+ final Iterator<PathArgument> it = id.getPathArguments().iterator();
+ ShardingTableEntry entry = this;
+
+ while (it.hasNext()) {
+ final PathArgument a = it.next();
+ ShardingTableEntry child = entry.children.get(a);
+ if (child == null) {
+ child = new ShardingTableEntry(a);
+ entry.children.put(a, child);
+ }
+ }
+
+ Preconditions.checkState(entry.registration == null);
+ entry.registration = reg;
+ }
+
+ private boolean remove(final Iterator<PathArgument> it) {
+ if (it.hasNext()) {
+ final PathArgument arg = it.next();
+ final ShardingTableEntry child = children.get(arg);
+ if (child != null) {
+ if (child.remove(it)) {
+ children.remove(arg);
+ }
+ } else {
+ LOG.warn("Cannot remove non-existent child {}", arg);
+ }
+ }
+
+ return registration == null && children.isEmpty();
+ }
+
+ void remove(final YangInstanceIdentifier id) {
+ this.remove(id.getPathArguments().iterator());
+ }
+}
@Before
public void setupStore() {
- InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
- InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.newDirectExecutorService());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.newDirectExecutorService());
schemaContext = TestModel.createTestContext();
operStore.onGlobalContextUpdated(schemaContext);
public void setupStore() {
InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
- MoreExecutors.sameThreadExecutor());
+ MoreExecutors.newDirectExecutorService());
InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
- MoreExecutors.sameThreadExecutor());
+ MoreExecutors.newDirectExecutorService());
schemaContext = TestModel.createTestContext();
operStore.onGlobalContextUpdated(schemaContext);
@Before
public void setupStore() {
- InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
- InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.newDirectExecutorService());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.newDirectExecutorService());
schemaContext = TestModel.createTestContext();
operStore.onGlobalContextUpdated(schemaContext);
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
</dependencies>
<build>
<plugins>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
- <version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
- <version>1.7.7</version>
</dependency>
</dependencies>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
- <version>1.5</version>
<executions>
<execution>
<phase>package</phase>
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.japi.Creator;
+import com.google.common.base.Stopwatch;
+import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
private final Logger LOG = LoggerFactory.getLogger(DummyShard.class);
private long lastMessageIndex = -1;
private long lastMessageSize = 0;
+ private Stopwatch appendEntriesWatch;
public DummyShard(Configuration configuration, String followerId) {
this.configuration = configuration;
}
protected void handleAppendEntries(AppendEntries req) throws InterruptedException {
-
LOG.info("{} - Received AppendEntries message : leader term = {}, index = {}, prevLogIndex = {}, size = {}",
followerId, req.getTerm(),req.getLeaderCommit(), req.getPrevLogIndex(), req.getEntries().size());
+ if(appendEntriesWatch != null){
+ long elapsed = appendEntriesWatch.elapsed(TimeUnit.SECONDS);
+ if(elapsed >= 5){
+ LOG.error("More than 5 seconds since last append entry, elapsed Time = {} seconds" +
+ ", leaderCommit = {}, prevLogIndex = {}, size = {}",
+ elapsed, req.getLeaderCommit(), req.getPrevLogIndex(), req.getEntries().size());
+ }
+ appendEntriesWatch.reset().start();
+ } else {
+ appendEntriesWatch = Stopwatch.createStarted();
+ }
+
if(lastMessageIndex == req.getLeaderCommit() && req.getEntries().size() > 0 && lastMessageSize > 0){
LOG.error("{} - Duplicate message with leaderCommit = {} prevLogIndex = {} received", followerId, req.getLeaderCommit(), req.getPrevLogIndex());
}
org.slf4j.simpleLogger.logFile=System.out
org.slf4j.simpleLogger.showShortLogName=true
org.slf4j.simpleLogger.levelInBrackets=true
-org.slf4j.simpleLogger.defaultLogLevel=info
\ No newline at end of file
+org.slf4j.simpleLogger.defaultLogLevel=error
\ No newline at end of file
@Before
public void setupStore() {
- domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
+ domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
schemaContext = TestModel.createTestContext();
domStore.onGlobalContextUpdated(schemaContext);
}
@Before
public void setupStore() {
- domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
+ domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
loadSchemas(RockTheHouseInput.class);
}
package org.opendaylight.controller.md.sal.dom.store.impl;
-import java.util.concurrent.ExecutorService;
-
import com.google.common.util.concurrent.ForwardingExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutorService;
/**
* A forwarding Executor used by unit tests for DataChangeListener notifications
public class TestDCLExecutorService extends ForwardingExecutorService {
// Start with a same thread executor to avoid timing issues during test setup.
- private volatile ExecutorService currentExecutor = MoreExecutors.sameThreadExecutor();
+ private volatile ExecutorService currentExecutor = MoreExecutors.newDirectExecutorService();
// The real executor to use when test setup is complete.
private final ExecutorService postSetupExecutor;
- public TestDCLExecutorService( ExecutorService postSetupExecutor ) {
+ public TestDCLExecutorService( final ExecutorService postSetupExecutor ) {
this.postSetupExecutor = postSetupExecutor;
}
new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
final NetconfDevice device =
- new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, new NetconfMessageTransformer(), true);
+ new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, new NetconfMessageTransformer(), getReconnectOnChangedSchema());
final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device);
@Override
public void onRemoteSessionDown() {
+ notificationHandler.onRemoteSchemaDown();
+
salFacade.onDeviceDisconnected();
for (final SchemaSourceRegistration<? extends SchemaSourceRepresentation> sourceRegistration : sourceRegistrations) {
sourceRegistration.close();
logger.warn("{}: Netconf device provides additional yang models not reported in hello message capabilities: {}",
id, providedSourcesNotRequired);
logger.warn("{}: Adding provided but not required sources as required to prevent failures", id);
+ logger.debug("{}: Netconf device reported in hello: {}", id, requiredSources);
requiredSources.addAll(providedSourcesNotRequired);
}
synchronized void handleNotification(final NetconfMessage notification) {
if(passNotifications) {
- passNotification(messageTransformer.toNotification(notification));
+ passNotification(transformNotification(notification));
} else {
queueNotification(notification);
}
passNotifications = true;
for (final NetconfMessage cachedNotification : queue) {
- passNotification(messageTransformer.toNotification(cachedNotification));
+ passNotification(transformNotification(cachedNotification));
}
queue.clear();
}
+ private CompositeNode transformNotification(final NetconfMessage cachedNotification) {
+ final CompositeNode parsedNotification = messageTransformer.toNotification(cachedNotification);
+ Preconditions.checkNotNull(parsedNotification, "%s: Unable to parse received notification: %s", id, cachedNotification);
+ return parsedNotification;
+ }
+
private void queueNotification(final NetconfMessage notification) {
Preconditions.checkState(passNotifications == false);
private synchronized void passNotification(final CompositeNode parsedNotification) {
logger.debug("{}: Forwarding notification {}", id, parsedNotification);
- Preconditions.checkNotNull(parsedNotification);
if(filter == null || filter.filterNotification(parsedNotification).isPresent()) {
salFacade.onNotification(parsedNotification);
this.filter = filter;
}
+ synchronized void onRemoteSchemaDown() {
+ queue.clear();
+ passNotifications = false;
+ }
+
static interface NotificationFilter {
Optional<CompositeNode> filterNotification(CompositeNode notification);
--- /dev/null
+module opendaylight-of-migration-test-model {
+
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:of-migration-test-model";
+ prefix of-migration-test;
+
+ import opendaylight-mdsal-list-test {prefix test;}
+ import yang-ext {prefix ext;}
+ import opendaylight-mdsal-augment-test {prefix aug;}
+ import opendaylight-test-routed-rpc {prefix routed;}
+
+ description
+ "This module contains a collection of YANG definitions used for
+ test cases that used to depend on flow model.";
+
+ revision 2015-02-10 {
+ }
+
+ typedef bit-flags {
+ type bits {
+ bit FLAG_ONE;
+ bit FLAG_TWO;
+ bit FLAG_THREE;
+ bit FLAG_FOUR;
+ bit FLAG_FIVE;
+ }
+ }
+
+ typedef custom-enum {
+ type enumeration {
+ enum type1;
+ enum type2;
+ enum type3;
+ }
+ }
+
+ grouping enum-grouping {
+ leaf attr-enum {
+ type custom-enum;
+ }
+ }
+
+ grouping aug-grouping {
+ container cont1 {
+ leaf attr-str {
+ type string;
+ }
+ }
+
+ container cont2 {
+ list contlist1 {
+ key "attr-str";
+
+ leaf attr-str {
+ type string;
+ }
+
+ uses enum-grouping;
+ }
+ }
+
+ leaf attr-str1 {
+ type string;
+ }
+
+ leaf attr-str2 {
+ type string;
+ }
+
+ leaf attr-str3 {
+ type string;
+ }
+
+ leaf attr-str4 {
+ type string;
+ }
+
+ list list1 {
+ key "attr-str";
+ leaf attr-str {
+ type string;
+ }
+
+ list list1-1 {
+ key "attr-int";
+ leaf attr-int {
+ type int32;
+ }
+
+ leaf attr-str {
+ type string;
+ }
+
+ leaf flags {
+ type bit-flags;
+ }
+ }
+
+ list list1-2 {
+ key "attr-int";
+ leaf attr-int {
+ type int32;
+ }
+
+ leaf attr-str {
+ type string;
+ }
+ }
+ }
+ }
+
+ augment "/test:top/test:top-level-list" {
+ ext:augment-identifier tll-complex-augment;
+ uses aug-grouping;
+ }
+
+ augment "/test:top/test:top-level-list/list1/list1-1" {
+ ext:augment-identifier list11-simple-augment;
+
+ leaf attr-str2 {
+ type string;
+ }
+
+ container cont {
+ leaf attr-int {
+ type int32;
+ }
+ }
+ }
+
+ augment "/test:top/test:top-level-list/test:nested-list/" {
+ ext:augment-identifier nested-list-simple-augment;
+
+ leaf type {
+ type string;
+ }
+ }
+
+ rpc knock-knock {
+ input {
+ leaf knocker-id {
+ ext:context-reference routed:test-context;
+ type instance-identifier;
+ }
+
+ leaf question {
+ type string;
+ }
+ }
+
+ output {
+ leaf answer {
+ type string;
+ }
+ }
+ }
+}
import java.util.Map.Entry;
import javax.management.ObjectName;
import javax.management.openmbean.OpenType;
-import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.BeanReader;
import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
private final Map<String, AttributeIfc> yangToAttrConfig;
private final String nullableDummyContainerName;
private final Map<String, AttributeIfc> jmxToAttrConfig;
- private final ConfigRegistryClient configRegistryClient;
+ private final BeanReader configRegistryClient;
- public InstanceConfig(ConfigRegistryClient configRegistryClient, Map<String, AttributeIfc> yangNamesToAttributes,
+ public InstanceConfig(BeanReader configRegistryClient, Map<String, AttributeIfc> yangNamesToAttributes,
String nullableDummyContainerName) {
this.yangToAttrConfig = yangNamesToAttributes;
TransactionProvider transactionProvider) {
switch (source) {
case running:
- return new RunningDatastoreQueryStrategy();
+ return new RunningDatastoreQueryStrategy(transactionProvider);
case candidate:
return new CandidateDatastoreQueryStrategy(transactionProvider);
default:
import javax.management.InstanceNotFoundException;
import javax.management.ObjectName;
import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.util.BeanReader;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.config.util.ConfigTransactionClient;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
public static Map<String/* Namespace from yang file */,
Map<String /* Name of module entry from yang file */, ModuleConfig>> transformMbeToModuleConfigs
- (final ConfigRegistryClient configRegistryClient, Map<String/* Namespace from yang file */,
+ (final BeanReader configRegistryClient, Map<String/* Namespace from yang file */,
Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> mBeanEntries) {
Map<String, Map<String, ModuleConfig>> namespaceToModuleNameToModuleConfig = Maps.newHashMap();
@Override
protected Element handleWithNoSubsequentOperations(Document document, XmlElement xml) throws NetconfDocumentedException {
-
EditConfigXmlParser.EditConfigExecution editConfigExecution;
Config cfg = getConfigMapping(getConfigRegistryClient(), yangStoreSnapshot);
editConfigExecution = editConfigXmlParser.fromXml(xml, cfg);
import java.util.Set;
import javax.management.ObjectName;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreContext;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.exception.UnexpectedElementException;
import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException;
public class Get extends AbstractConfigNetconfOperation {
+ private final TransactionProvider transactionProvider;
private final YangStoreContext yangStoreSnapshot;
private static final Logger LOG = LoggerFactory.getLogger(Get.class);
- public Get(YangStoreContext yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+ public Get(final TransactionProvider transactionProvider, YangStoreContext yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
String netconfSessionIdForReporting) {
super(configRegistryClient, netconfSessionIdForReporting);
+ this.transactionProvider = transactionProvider;
this.yangStoreSnapshot = yangStoreSnapshot;
}
protected Element handleWithNoSubsequentOperations(Document document, XmlElement xml) throws NetconfDocumentedException {
checkXml(xml);
- final Set<ObjectName> runtimeBeans = getConfigRegistryClient().lookupRuntimeBeans();
+ final ObjectName testTransaction = transactionProvider.getOrCreateReadTransaction();
+ final ConfigTransactionClient registryClient = getConfigRegistryClient().getConfigTransactionClient(testTransaction);
- //Transaction provider required only for candidate datastore
- final Set<ObjectName> configBeans = Datastore.getInstanceQueryStrategy(Datastore.running, null)
- .queryInstances(getConfigRegistryClient());
+ try {
+ // Runtime beans are not parts of transactions and have to be queried against the central registry
+ final Set<ObjectName> runtimeBeans = getConfigRegistryClient().lookupRuntimeBeans();
- final Map<String, Map<String, ModuleRuntime>> moduleRuntimes = createModuleRuntimes(getConfigRegistryClient(),
- yangStoreSnapshot.getModuleMXBeanEntryMap());
- final Map<String, Map<String, ModuleConfig>> moduleConfigs = EditConfig.transformMbeToModuleConfigs(
- getConfigRegistryClient(), yangStoreSnapshot.getModuleMXBeanEntryMap());
+ final Set<ObjectName> configBeans = Datastore.getInstanceQueryStrategy(Datastore.running, transactionProvider)
+ .queryInstances(getConfigRegistryClient());
- final Runtime runtime = new Runtime(moduleRuntimes, moduleConfigs);
+ final Map<String, Map<String, ModuleRuntime>> moduleRuntimes = createModuleRuntimes(getConfigRegistryClient(),
+ yangStoreSnapshot.getModuleMXBeanEntryMap());
+ final Map<String, Map<String, ModuleConfig>> moduleConfigs = EditConfig.transformMbeToModuleConfigs(
+ registryClient, yangStoreSnapshot.getModuleMXBeanEntryMap());
- final Element element = runtime.toXml(runtimeBeans, configBeans, document);
+ final Runtime runtime = new Runtime(moduleRuntimes, moduleConfigs);
- LOG.trace("{} operation successful", XmlNetconfConstants.GET);
+ final Element element = runtime.toXml(runtimeBeans, configBeans, document);
- return element;
+ LOG.trace("{} operation successful", XmlNetconfConstants.GET);
+
+ return element;
+ } finally {
+ transactionProvider.closeReadTransaction();
+ }
}
}
private Element getResponseInternal(final Document document, final ConfigRegistryClient configRegistryClient,
final Datastore source) {
- Element dataElement = XmlUtil.createElement(document, XmlNetconfConstants.DATA_KEY, Optional.<String>absent());
- final Set<ObjectName> instances = Datastore.getInstanceQueryStrategy(source, this.transactionProvider)
- .queryInstances(configRegistryClient);
- final Config configMapping = new Config(EditConfig.transformMbeToModuleConfigs(configRegistryClient,
- yangStoreSnapshot.getModuleMXBeanEntryMap()));
-
-
- ObjectName on = transactionProvider.getOrCreateTransaction();
- ConfigTransactionClient ta = configRegistryClient.getConfigTransactionClient(on);
-
- ServiceRegistryWrapper serviceTracker = new ServiceRegistryWrapper(ta);
- dataElement = configMapping.toXml(instances, this.maybeNamespace, document, dataElement, serviceTracker);
-
- LOG.trace("{} operation successful", GET_CONFIG);
-
- return dataElement;
+ final ConfigTransactionClient registryClient;
+ // Read current state from a transaction, if running is source, then start new transaction just for reading
+ // in case of candidate, get current transaction representing candidate
+ if(source == Datastore.running) {
+ final ObjectName readTx = transactionProvider.getOrCreateReadTransaction();
+ registryClient = getConfigRegistryClient().getConfigTransactionClient(readTx);
+ } else {
+ registryClient = getConfigRegistryClient().getConfigTransactionClient(transactionProvider.getOrCreateTransaction());
+ }
+
+ try {
+ Element dataElement = XmlUtil.createElement(document, XmlNetconfConstants.DATA_KEY, Optional.<String>absent());
+ final Set<ObjectName> instances = Datastore.getInstanceQueryStrategy(source, this.transactionProvider)
+ .queryInstances(configRegistryClient);
+
+ final Config configMapping = new Config(EditConfig.transformMbeToModuleConfigs(registryClient,
+ yangStoreSnapshot.getModuleMXBeanEntryMap()));
+
+ ServiceRegistryWrapper serviceTracker = new ServiceRegistryWrapper(registryClient);
+ dataElement = configMapping.toXml(instances, this.maybeNamespace, document, dataElement, serviceTracker);
+
+ LOG.trace("{} operation successful", GET_CONFIG);
+
+ return dataElement;
+ } finally {
+ if(source == Datastore.running) {
+ transactionProvider.closeReadTransaction();
+ }
+ }
}
@Override
import java.util.Set;
import javax.management.ObjectName;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
public class RunningDatastoreQueryStrategy implements DatastoreQueryStrategy {
+ private final TransactionProvider transactionProvider;
+
+ public RunningDatastoreQueryStrategy(TransactionProvider transactionProvider) {
+ this.transactionProvider = transactionProvider;
+ }
+
@Override
public Set<ObjectName> queryInstances(ConfigRegistryClient configRegistryClient) {
- return configRegistryClient.lookupConfigBeans();
+ ObjectName on = transactionProvider.getOrCreateReadTransaction();
+ ConfigTransactionClient proxy = configRegistryClient.getConfigTransactionClient(on);
+ return proxy.lookupConfigBeans();
}
}
ops.add(new Commit(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
ops.add(new Lock(netconfSessionIdForReporting));
ops.add(new UnLock(netconfSessionIdForReporting));
- ops.add(new Get(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting));
+ ops.add(new Get(transactionProvider, yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting));
ops.add(new DiscardChanges(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
ops.add(new Validate(transactionProvider, configRegistryClient, netconfSessionIdForReporting));
ops.add(new RuntimeRpc(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting));
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
+import com.google.common.base.Optional;
import java.lang.management.ManagementFactory;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
import javax.management.MBeanServer;
import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.yangtools.yang.model.api.Module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public NetconfOperationServiceImpl createService(String netconfSessionIdForReporting) {
return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting);
}
+
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return setupCapabilities(yangStoreService);
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ return yangStoreService.registerCapabilityListener(listener);
+ }
+
+ public static Set<Capability> setupCapabilities(final YangStoreContext yangStoreSnapshot) {
+ Set<Capability> capabilities = new HashSet<>();
+ // [RFC6241] 8.3. Candidate Configuration Capability
+ capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
+
+ // TODO rollback on error not supported EditConfigXmlParser:100
+ // [RFC6241] 8.5. Rollback-on-Error Capability
+ // capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
+
+ Set<Module> modules = yangStoreSnapshot.getModules();
+ for (Module module : modules) {
+ capabilities.add(new YangStoreCapability(module, yangStoreSnapshot.getModuleSource(module)));
+ }
+
+ return capabilities;
+ }
+
+ private static class BasicCapability implements Capability {
+
+ private final String capability;
+
+ private BasicCapability(final String capability) {
+ this.capability = capability;
+ }
+
+ @Override
+ public String getCapabilityUri() {
+ return capability;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Collection<String> getLocation() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return capability;
+ }
+ }
+
+ static final class YangStoreCapability extends BasicCapability {
+
+ private final String content;
+ private final String revision;
+ private final String moduleName;
+ private final String moduleNamespace;
+
+ public YangStoreCapability(final Module module, final String moduleContent) {
+ super(toCapabilityURI(module));
+ this.content = moduleContent;
+ this.moduleName = module.getName();
+ this.moduleNamespace = module.getNamespace().toString();
+ this.revision = Util.writeDate(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.of(content);
+ }
+
+ private static String toCapabilityURI(final Module module) {
+ return String.valueOf(module.getNamespace()) + "?module="
+ + module.getName() + "&revision=" + Util.writeDate(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(moduleName);
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(moduleNamespace);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(revision);
+ }
+ }
}
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
-import com.google.common.base.Optional;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Set;
import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
-import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.yangtools.yang.model.api.Module;
/**
* Manages life cycle of {@link YangStoreContext}.
private final NetconfOperationProvider operationProvider;
private final TransactionProvider transactionProvider;
- private final YangStoreService yangStoreService;
public NetconfOperationServiceImpl(final YangStoreService yangStoreService, final ConfigRegistryJMXClient jmxClient,
final String netconfSessionIdForReporting) {
- this.yangStoreService = yangStoreService;
-
transactionProvider = new TransactionProvider(jmxClient, netconfSessionIdForReporting);
operationProvider = new NetconfOperationProvider(yangStoreService, jmxClient, transactionProvider,
netconfSessionIdForReporting);
}
- @Override
- public void close() {
- transactionProvider.close();
- }
-
- @Override
- public Set<Capability> getCapabilities() {
- return setupCapabilities(yangStoreService);
- }
-
@Override
public Set<NetconfOperation> getNetconfOperations() {
return operationProvider.getOperations();
}
- private static Set<Capability> setupCapabilities(final YangStoreContext yangStoreSnapshot) {
- Set<Capability> capabilities = new HashSet<>();
- // [RFC6241] 8.3. Candidate Configuration Capability
- capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
-
- // TODO rollback on error not supported EditConfigXmlParser:100
- // [RFC6241] 8.5. Rollback-on-Error Capability
- // capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
-
- Set<Module> modules = yangStoreSnapshot.getModules();
- for (Module module : modules) {
- capabilities.add(new YangStoreCapability(module, yangStoreSnapshot.getModuleSource(module)));
- }
-
- return capabilities;
- }
-
- private static class BasicCapability implements Capability {
-
- private final String capability;
-
- private BasicCapability(final String capability) {
- this.capability = capability;
- }
-
- @Override
- public String getCapabilityUri() {
- return capability;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.absent();
- }
-
- @Override
- public Collection<String> getLocation() {
- return Collections.emptyList();
- }
-
- @Override
- public String toString() {
- return capability;
- }
+ @Override
+ public void close() {
+ transactionProvider.close();
}
- private static final class YangStoreCapability extends BasicCapability {
-
- private final String content;
- private final String revision;
- private final String moduleName;
- private final String moduleNamespace;
-
- public YangStoreCapability(final Module module, final String moduleContent) {
- super(toCapabilityURI(module));
- this.content = moduleContent;
- this.moduleName = module.getName();
- this.moduleNamespace = module.getNamespace().toString();
- this.revision = Util.writeDate(module.getRevision());
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.of(content);
- }
-
- private static String toCapabilityURI(final Module module) {
- return String.valueOf(module.getNamespace()) + "?module="
- + module.getName() + "&revision=" + Util.writeDate(module.getRevision());
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.of(moduleName);
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.of(moduleNamespace);
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.of(revision);
- }
- }
}
import com.google.common.collect.Sets;
import java.lang.ref.SoftReference;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.netconf.notifications.BaseNetconfNotificationListener;
import org.opendaylight.controller.netconf.notifications.BaseNotificationPublisherRegistration;
import org.opendaylight.controller.netconf.notifications.NetconfNotificationCollector;
}
});
+ private final Set<CapabilityListener> listeners = Collections.synchronizedSet(new HashSet<CapabilityListener>());
+
public YangStoreService(final SchemaContextProvider schemaContextProvider, final BundleContext context) {
this(schemaContextProvider, new NotificationCollectorTracker(context));
}
notificationExecutor.submit(new CapabilityChangeNotifier(previous));
}
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ if(ref.get() == null || ref.get().get() == null) {
+ getYangStoreSnapshot();
+ }
+
+ this.listeners.add(listener);
+ listener.onCapabilitiesAdded(NetconfOperationServiceFactoryImpl.setupCapabilities(ref.get().get()));
+
+ return new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ YangStoreService.this.listeners.remove(listener);
+ }
+ };
+ }
+
+ private static final Function<Module, Capability> MODULE_TO_CAPABILITY = new Function<Module, Capability>() {
+ @Override
+ public Capability apply(final Module module) {
+ return new NetconfOperationServiceFactoryImpl.YangStoreCapability(module, module.getSource());
+ }
+ };
+
private final class CapabilityChangeNotifier implements Runnable {
+
private final YangStoreSnapshot previous;
public CapabilityChangeNotifier(final YangStoreSnapshot previous) {
final YangStoreContext current = getYangStoreSnapshot();
if(current.equals(previous) == false) {
- notificationPublisher.onCapabilityChanged(computeDiff(previous, current));
+ final Sets.SetView<Module> removed = Sets.difference(previous.getModules(), current.getModules());
+ final Sets.SetView<Module> added = Sets.difference(current.getModules(), previous.getModules());
+
+ // Notify notification manager
+ notificationPublisher.onCapabilityChanged(computeDiff(removed, added));
+
+ // Notify direct capability listener TODO would it not be better if the capability listeners went through notification manager ?
+ for (final CapabilityListener listener : listeners) {
+ listener.onCapabilitiesAdded(Sets.newHashSet(Collections2.transform(added, MODULE_TO_CAPABILITY)));
+ }
+ for (final CapabilityListener listener : listeners) {
+ listener.onCapabilitiesRemoved(Sets.newHashSet(Collections2.transform(removed, MODULE_TO_CAPABILITY)));
+ }
}
}
}
private static final Function<Module, Uri> MODULE_TO_URI = new Function<Module, Uri>() {
@Override
public Uri apply(final Module input) {
- final QName qName = QName.cachedReference(QName.create(input.getQNameModule(), input.getName()));
- return new Uri(qName.toString());
+ return new Uri(new NetconfOperationServiceFactoryImpl.YangStoreCapability(input, input.getSource()).getCapabilityUri());
}
};
- static NetconfCapabilityChange computeDiff(final YangStoreContext previous, final YangStoreContext current) {
- final Sets.SetView<Module> removed = Sets.difference(previous.getModules(), current.getModules());
- final Sets.SetView<Module> added = Sets.difference(current.getModules(), previous.getModules());
-
+ static NetconfCapabilityChange computeDiff(final Sets.SetView<Module> removed, final Sets.SetView<Module> added) {
final NetconfCapabilityChangeBuilder netconfCapabilityChangeBuilder = new NetconfCapabilityChangeBuilder();
netconfCapabilityChangeBuilder.setChangedBy(new ChangedByBuilder().setServerOrUser(new ServerBuilder().setServer(true).build()).build());
netconfCapabilityChangeBuilder.setDeletedCapability(Lists.newArrayList(Collections2.transform(removed, MODULE_TO_URI)));
private final ConfigRegistryClient configRegistryClient;
private final String netconfSessionIdForReporting;
- private ObjectName transaction;
+ private ObjectName candidateTx;
+ private ObjectName readTx;
private final List<ObjectName> allOpenedTransactions = new ArrayList<>();
private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No transaction found for session ";
public synchronized Optional<ObjectName> getTransaction() {
- if (transaction == null){
+ if (candidateTx == null){
return Optional.absent();
}
// Transaction was already closed somehow
- if (!isStillOpenTransaction(transaction)) {
- LOG.warn("Fixing illegal state: transaction {} was closed in {}", transaction,
+ if (!isStillOpenTransaction(candidateTx)) {
+ LOG.warn("Fixing illegal state: transaction {} was closed in {}", candidateTx,
netconfSessionIdForReporting);
- transaction = null;
+ candidateTx = null;
return Optional.absent();
}
- return Optional.of(transaction);
+ return Optional.of(candidateTx);
+ }
+
+ public synchronized Optional<ObjectName> getReadTransaction() {
+
+ if (readTx == null){
+ return Optional.absent();
+ }
+
+ // Transaction was already closed somehow
+ if (!isStillOpenTransaction(readTx)) {
+ LOG.warn("Fixing illegal state: transaction {} was closed in {}", readTx,
+ netconfSessionIdForReporting);
+ readTx = null;
+ return Optional.absent();
+ }
+ return Optional.of(readTx);
}
private boolean isStillOpenTransaction(ObjectName transaction) {
if (ta.isPresent()) {
return ta.get();
}
- transaction = configRegistryClient.beginConfig();
- allOpenedTransactions.add(transaction);
- return transaction;
+ candidateTx = configRegistryClient.beginConfig();
+ allOpenedTransactions.add(candidateTx);
+ return candidateTx;
+ }
+
+ public synchronized ObjectName getOrCreateReadTransaction() {
+ Optional<ObjectName> ta = getReadTransaction();
+
+ if (ta.isPresent()) {
+ return ta.get();
+ }
+ readTx = configRegistryClient.beginConfig();
+ allOpenedTransactions.add(readTx);
+ return readTx;
}
/**
try {
CommitStatus status = configRegistryClient.commitConfig(taON);
// clean up
- allOpenedTransactions.remove(transaction);
- transaction = null;
+ allOpenedTransactions.remove(candidateTx);
+ candidateTx = null;
return status;
} catch (ValidationException validationException) {
// no clean up: user can reconfigure and recover this transaction
ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
transactionClient.abortConfig();
- allOpenedTransactions.remove(transaction);
- transaction = null;
+ allOpenedTransactions.remove(candidateTx);
+ candidateTx = null;
+ }
+
+ public synchronized void closeReadTransaction() {
+ LOG.debug("Closing read transaction");
+ Optional<ObjectName> taON = getReadTransaction();
+ Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
+
+ ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
+ transactionClient.abortConfig();
+ allOpenedTransactions.remove(readTx);
+ readTx = null;
}
public synchronized void abortTestTransaction(ObjectName testTx) {
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreService;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
+import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshotImpl;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
@Mock
NetconfOperationRouter netconfOperationRouter;
@Mock
- NetconfOperationServiceSnapshotImpl netconfOperationServiceSnapshot;
+ AggregatedNetconfOperationServiceFactory netconfOperationServiceSnapshot;
+ @Mock
+ private AutoCloseable sessionCloseable;
private TransactionProvider transactionProvider;
doReturn(getMbes()).when(this.yangStoreSnapshot).getModuleMXBeanEntryMap();
doReturn(getModules()).when(this.yangStoreSnapshot).getModules();
- doNothing().when(netconfOperationServiceSnapshot).close();
this.factory = new NetconfTestImplModuleFactory();
this.factory2 = new DepTestImplModuleFactory();
this.factory3 = new IdentityTestModuleFactory();
factory4 = new TestImplModuleFactory();
-
+ doNothing().when(sessionCloseable).close();
super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, this.factory, this.factory2,
this.factory3, factory4));
edit("netconfMessages/editConfig_none.xml");
closeSession();
- verify(netconfOperationServiceSnapshot).close();
+ verify(sessionCloseable).close();
verifyNoMoreInteractions(netconfOperationRouter);
verifyNoMoreInteractions(netconfOperationServiceSnapshot);
}
private void closeSession() throws NetconfDocumentedException, ParserConfigurationException, SAXException,
IOException {
- DefaultCloseSession closeOp = new DefaultCloseSession(NETCONF_SESSION_ID, netconfOperationServiceSnapshot);
+ DefaultCloseSession closeOp = new DefaultCloseSession(NETCONF_SESSION_ID, sessionCloseable);
executeOp(closeOp, "netconfMessages/closeSession.xml");
}
}
private Document get() throws NetconfDocumentedException, ParserConfigurationException, SAXException, IOException {
- Get getOp = new Get(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID);
+ Get getOp = new Get(transactionProvider, yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID);
return executeOp(getOp, "netconfMessages/get.xml");
}
import org.opendaylight.controller.config.persist.api.ConfigPusher;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
} catch(RuntimeException e) {
throw new NotEnoughCapabilitiesException("Netconf service not stable for " + idForReporting, e);
}
- Set<String> notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, serviceCandidate);
+ Set<String> notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, configNetconfConnector);
if (notFoundDiff.isEmpty()) {
return serviceCandidate;
} else {
serviceCandidate.close();
LOG.trace("Netconf server did not provide required capabilities for {} ", idForReporting,
"Expected but not found: {}, all expected {}, current {}",
- notFoundDiff, expectedCapabilities, serviceCandidate.getCapabilities()
+ notFoundDiff, expectedCapabilities, configNetconfConnector.getCapabilities()
);
throw new NotEnoughCapabilitiesException("Not enough capabilities for " + idForReporting + ". Expected but not found: " + notFoundDiff);
}
}
- private static Set<String> computeNotFoundCapabilities(Set<String> expectedCapabilities, NetconfOperationService serviceCandidate) {
+ private static Set<String> computeNotFoundCapabilities(Set<String> expectedCapabilities, NetconfOperationServiceFactory serviceCandidate) {
Collection<String> actual = Collections2.transform(serviceCandidate.getCapabilities(), new Function<Capability, String>() {
@Override
public String apply(@Nonnull final Capability input) {
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.management.MBeanServer;
import org.opendaylight.controller.config.persist.api.ConfigPusher;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.persist.impl.ConfigPusherImpl;
import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator;
InnerCustomizer innerCustomizer = new InnerCustomizer(configs, maxWaitForCapabilitiesMillis,
conflictingVersionTimeoutMillis, persisterAggregator);
OuterCustomizer outerCustomizer = new OuterCustomizer(context, innerCustomizer);
- new ServiceTracker<>(context, NetconfOperationProvider.class, outerCustomizer).open();
+ new ServiceTracker<>(context, NetconfOperationServiceFactory.class, outerCustomizer).open();
}
private long getConflictingVersionTimeoutMillis(PropertiesProviderBaseImpl propertiesProvider) {
")";
}
- class OuterCustomizer implements ServiceTrackerCustomizer<NetconfOperationProvider, NetconfOperationProvider> {
+ class OuterCustomizer implements ServiceTrackerCustomizer<NetconfOperationServiceFactory, NetconfOperationServiceFactory> {
private final BundleContext context;
private final InnerCustomizer innerCustomizer;
}
@Override
- public NetconfOperationProvider addingService(ServiceReference<NetconfOperationProvider> reference) {
+ public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
LOG.trace("Got OuterCustomizer.addingService {}", reference);
// JMX was registered, track config-netconf-connector
Filter filter;
}
@Override
- public void modifiedService(ServiceReference<NetconfOperationProvider> reference, NetconfOperationProvider service) {
+ public void modifiedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
}
@Override
- public void removedService(ServiceReference<NetconfOperationProvider> reference, NetconfOperationProvider service) {
+ public void removedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
}
}
private final List<ConfigSnapshotHolder> configs;
private final PersisterAggregator persisterAggregator;
private final long maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis;
-
+ // This inner customizer has its filter to find the right operation service, but it gets triggered after any
+ // operation service appears. This means that it could start pushing thread up to N times (N = number of operation services spawned in OSGi)
+ private final AtomicBoolean alreadyStarted = new AtomicBoolean(false);
InnerCustomizer(List<ConfigSnapshotHolder> configs, long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis,
PersisterAggregator persisterAggregator) {
@Override
public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
+ if(alreadyStarted.compareAndSet(false, true) == false) {
+ //Prevents multiple calls to this method spawning multiple pushing threads
+ return reference.getBundle().getBundleContext().getService(reference);
+ }
LOG.trace("Got InnerCustomizer.addingService {}", reference);
NetconfOperationServiceFactory service = reference.getBundle().getBundleContext().getService(reference);
@Override
public void modifiedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
+ LOG.trace("Got InnerCustomizer.modifiedService {}", reference);
}
@Override
public void removedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
+ LOG.trace("Got InnerCustomizer.removedService {}", reference);
}
}
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
private TestingExceptionHandler handler;
- private void setUpContextAndStartPersister(String requiredCapability) throws Exception {
+ private void setUpContextAndStartPersister(String requiredCapability, final NetconfOperationService conflictingService) throws Exception {
DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability;
ctx = new MockedBundleContext(1000, 1000);
+ doReturn(getConflictingService()).when(ctx.serviceFactory).createService(anyString());
configPersisterActivator = new ConfigPersisterActivator();
configPersisterActivator.start(ctx.getBundleContext());
}
@Test
public void testPersisterNotAllCapabilitiesProvided() throws Exception {
- setUpContextAndStartPersister("required-cap");
+ setUpContextAndStartPersister("required-cap", getConflictingService());
Thread.sleep(2000);
handler.assertException(IllegalStateException.class, "Max wait for capabilities reached.Not enough capabilities " +
"for <data><config-snapshot/></data>. Expected but not found: [required-cap]");
@Test
public void testPersisterSuccessfulPush() throws Exception {
- setUpContextAndStartPersister("cap1");
+ setUpContextAndStartPersister("cap1", getConflictingService());
NetconfOperationService service = getWorkingService(getOKDocument());
doReturn(service).when(ctx.serviceFactory).createService(anyString());
Thread.sleep(2000);
public NetconfOperationService getWorkingService(Document document) throws SAXException, IOException, NetconfDocumentedException {
NetconfOperationService service = mock(NetconfOperationService.class);
Capability capability = mock(Capability.class);
- doReturn(Sets.newHashSet(capability)).when(service).getCapabilities();
+// doReturn(Sets.newHashSet(capability)).when(service).getCapabilities();
doReturn("cap1").when(capability).getCapabilityUri();
@Test
public void testPersisterConflictingVersionException() throws Exception {
- setUpContextAndStartPersister("cap1");
+ setUpContextAndStartPersister("cap1", getConflictingService());
- doReturn(getConflictingService()).when(ctx.serviceFactory).createService(anyString());
Thread.sleep(2000);
handler.assertException(IllegalStateException.class, "Max wait for conflicting version stabilization timeout");
}
@Test
public void testSuccessConflictingVersionException() throws Exception {
- setUpContextAndStartPersister("cap1");
+ setUpContextAndStartPersister("cap1", getConflictingService());
doReturn(getConflictingService()).when(ctx.serviceFactory).createService(anyString());
Thread.sleep(500);
// working service:
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
+import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.persist.impl.DummyAdapter;
doReturn(null).when(context).getProperty(anyString());
initContext(maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis);
- String outerFilterString = "(objectClass=org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider)";
+ String outerFilterString = "(objectClass=org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory)";
doReturn(outerFilter).when(context).createFilter(outerFilterString);
doNothing().when(context).addServiceListener(any(ServiceListener.class), eq(outerFilterString));
ServiceReference<?>[] toBeReturned = {serviceReference};
- doReturn(toBeReturned).when(context).getServiceReferences(NetconfOperationProvider.class.getName(), null);
+ doReturn(toBeReturned).when(context).getServiceReferences(NetconfOperationServiceFactory.class.getName(), null);
String innerFilterString = "innerfilter";
doReturn(innerFilterString).when(outerFilter).toString();
doReturn(bundle).when(serviceReference).getBundle();
doReturn(context).when(bundle).getBundleContext();
doReturn("").when(serviceReference).toString();
+ doReturn("context").when(context).toString();
doReturn(serviceFactory).when(context).getService(any(ServiceReference.class));
doReturn(service).when(serviceFactory).createService(anyString());
- doReturn(Collections.emptySet()).when(service).getCapabilities();
+ final Capability cap = mock(Capability.class);
+ doReturn("cap1").when(cap).getCapabilityUri();
+ doReturn(Collections.singleton(cap)).when(serviceFactory).getCapabilities();
doNothing().when(service).close();
doReturn("serviceFactoryMock").when(serviceFactory).toString();
import ietf-yang-types {
prefix yang;
+ revision-date "2010-09-24";
}
+
import ietf-inet-types {
prefix inet;
}
@Override
public java.lang.AutoCloseable createInstance() {
- return new MdsalNetconfOperationServiceFactory(getRootSchemaServiceDependency(), getDomBrokerDependency());
+ final MdsalNetconfOperationServiceFactory mdsalNetconfOperationServiceFactory = new MdsalNetconfOperationServiceFactory(getRootSchemaServiceDependency(), getDomBrokerDependency()) {
+ @Override
+ public void close() throws Exception {
+ super.close();
+ getMapperAggregatorDependency().onRemoveNetconfOperationServiceFactory(this);
+ }
+ };
+ getMapperAggregatorDependency().onAddNetconfOperationServiceFactory(mdsalNetconfOperationServiceFactory);
+ return mdsalNetconfOperationServiceFactory;
}
}
package org.opendaylight.controller.netconf.mdsal.connector;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public class CurrentSchemaContext implements SchemaContextListener, AutoCloseable {
final AtomicReference<SchemaContext> currentContext = new AtomicReference<SchemaContext>();
private final ListenerRegistration<SchemaContextListener> schemaContextListenerListenerRegistration;
+ private final Set<CapabilityListener> listeners = Collections.synchronizedSet(Sets.<CapabilityListener>newHashSet());
public SchemaContext getCurrentContext() {
Preconditions.checkState(currentContext.get() != null, "Current context not received");
@Override
public void onGlobalContextUpdated(final SchemaContext schemaContext) {
currentContext.set(schemaContext);
+ // FIXME is notifying all the listeners from this callback wise ?
+ final Set<Capability> addedCaps = MdsalNetconfOperationServiceFactory.transformCapabilities(currentContext.get());
+ for (final CapabilityListener listener : listeners) {
+ listener.onCapabilitiesAdded(addedCaps);
+ }
}
@Override
public void close() throws Exception {
+ listeners.clear();
schemaContextListenerListenerRegistration.close();
currentContext.set(null);
}
+
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ listener.onCapabilitiesAdded(MdsalNetconfOperationServiceFactory.transformCapabilities(currentContext.get()));
+ listeners.add(listener);
+ return new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ listeners.remove(listener);
+ }
+ };
+ }
}
\ No newline at end of file
package org.opendaylight.controller.netconf.mdsal.connector;
-import com.google.common.base.Optional;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Set;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public class MdsalNetconfOperationService implements NetconfOperationService {
- private static final Logger LOG = LoggerFactory.getLogger(MdsalNetconfOperationService.class);
-
- private final CurrentSchemaContext schemaContext;
- private final String netconfSessionIdForReporting;
private final OperationProvider operationProvider;
public MdsalNetconfOperationService(final CurrentSchemaContext schemaContext, final String netconfSessionIdForReporting,
final DOMDataBroker dataBroker) {
- this.schemaContext = schemaContext;
- // TODO schema contexts are different in data broker and the one we receive here ... the one received here should be updated same way as broker is
- this.netconfSessionIdForReporting = netconfSessionIdForReporting;
this.operationProvider = new OperationProvider(netconfSessionIdForReporting, schemaContext, dataBroker);
}
}
- // TODO does this get called dynamically ?
- @Override
- public Set<Capability> getCapabilities() {
- final Set<Capability> capabilities = new HashSet<>();
- // [RFC6241] 8.3. Candidate Configuration Capability
- capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
-
- final SchemaContext currentContext = schemaContext.getCurrentContext();
- final Set<Module> modules = currentContext.getModules();
- for (final Module module : modules) {
- if(currentContext.getModuleSource(module).isPresent()) {
- capabilities.add(new YangStoreCapability(module, currentContext.getModuleSource(module).get()));
- } else {
- LOG.warn("Missing source for module {}. This module will not be available from netconf server for session {}",
- module, netconfSessionIdForReporting);
- }
- }
-
- return capabilities;
- }
-
@Override
public Set<NetconfOperation> getNetconfOperations() {
return operationProvider.getOperations();
}
- // TODO reuse from netconf impl
- private static class BasicCapability implements Capability {
-
- private final String capability;
-
- private BasicCapability(final String capability) {
- this.capability = capability;
- }
-
- @Override
- public String getCapabilityUri() {
- return capability;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.absent();
- }
-
- @Override
- public Collection<String> getLocation() {
- return Collections.emptyList();
- }
-
- @Override
- public String toString() {
- return capability;
- }
- }
-
- private static final class YangStoreCapability extends BasicCapability {
-
- private final String content;
- private final String revision;
- private final String moduleName;
- private final String moduleNamespace;
-
- public YangStoreCapability(final Module module, final String moduleContent) {
- super(toCapabilityURI(module));
- this.content = moduleContent;
- this.moduleName = module.getName();
- this.moduleNamespace = module.getNamespace().toString();
- this.revision = SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.of(content);
- }
-
- private static String toCapabilityURI(final Module module) {
- return String.valueOf(module.getNamespace()) + "?module="
- + module.getName() + "&revision=" + SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.of(moduleName);
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.of(moduleNamespace);
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.of(revision);
- }
- }
}
package org.opendaylight.controller.netconf.mdsal.connector;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class MdsalNetconfOperationServiceFactory implements NetconfOperationServiceFactory, AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(MdsalNetconfOperationServiceFactory.class);
+
private final DOMDataBroker dataBroker;
private final CurrentSchemaContext currentSchemaContext;
public void close() throws Exception {
currentSchemaContext.close();
}
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return transformCapabilities(currentSchemaContext.getCurrentContext());
+ }
+
+ static Set<Capability> transformCapabilities(final SchemaContext currentContext1) {
+ final Set<Capability> capabilities = new HashSet<>();
+ // [RFC6241] 8.3. Candidate Configuration Capability
+ capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
+
+ final SchemaContext currentContext = currentContext1;
+ final Set<Module> modules = currentContext.getModules();
+ for (final Module module : modules) {
+ if(currentContext.getModuleSource(module).isPresent()) {
+ capabilities.add(new YangStoreCapability(module, currentContext.getModuleSource(module).get()));
+ } else {
+ LOG.warn("Missing source for module {}. This module will not be available from netconf server",
+ module);
+ }
+ }
+
+ return capabilities;
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ return currentSchemaContext.registerCapabilityListener(listener);
+ }
+
+ private static class BasicCapability implements Capability {
+
+ private final String capability;
+
+ private BasicCapability(final String capability) {
+ this.capability = capability;
+ }
+
+ @Override
+ public String getCapabilityUri() {
+ return capability;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Collection<String> getLocation() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return capability;
+ }
+ }
+
+ private static final class YangStoreCapability extends BasicCapability {
+
+ private final String content;
+ private final String revision;
+ private final String moduleName;
+ private final String moduleNamespace;
+
+ public YangStoreCapability(final Module module, final String moduleContent) {
+ super(toCapabilityURI(module));
+ this.content = moduleContent;
+ this.moduleName = module.getName();
+ this.moduleNamespace = module.getNamespace().toString();
+ this.revision = SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.of(content);
+ }
+
+ private static String toCapabilityURI(final Module module) {
+ return String.valueOf(module.getNamespace()) + "?module="
+ + module.getName() + "&revision=" + SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(moduleName);
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(moduleNamespace);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(revision);
+ }
+ }
}
package org.opendaylight.controller.netconf.mdsal.connector.ops.get;
import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import java.io.IOException;
}
protected static final class GetConfigExecution {
- private final Datastore datastore;
+ private final Optional<Datastore> datastore;
- public GetConfigExecution(final Datastore datastore) {
+ public GetConfigExecution(final Optional<Datastore> datastore) {
this.datastore = datastore;
}
- public Datastore getDatastore() {
+ public Optional<Datastore> getDatastore() {
return datastore;
}
throw new NetconfDocumentedException("Incorrect RPC: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
}
- final Datastore sourceDatastore;
+ final Optional<Datastore> sourceDatastore;
try {
sourceDatastore = parseSource(xml);
} catch (final NetconfDocumentedException e) {
return new GetConfigExecution(sourceDatastore);
}
- private static Datastore parseSource(final XmlElement xml) throws NetconfDocumentedException {
- final Datastore sourceDatastore;
- final XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY,
+ private static Optional<Datastore> parseSource(final XmlElement xml) throws NetconfDocumentedException {
+ final Optional<XmlElement> sourceElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SOURCE_KEY,
XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
- final String sourceParsed = sourceElement.getOnlyChildElement().getName();
- sourceDatastore = Datastore.valueOf(sourceParsed);
- return sourceDatastore;
+ return sourceElement.isPresent() ?
+ Optional.of(Datastore.valueOf(sourceElement.get().getOnlyChildElement().getName())) : Optional.<Datastore>absent();
}
private static void validateInputRpc(final XmlElement xml, String operationName) throws NetconfDocumentedException{
}
final YangInstanceIdentifier dataRoot = ROOT;
- DOMDataReadWriteTransaction rwTx = getTransaction(getConfigExecution.getDatastore());
+ DOMDataReadWriteTransaction rwTx = getTransaction(Datastore.running);
try {
final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = rwTx.read(LogicalDatastoreType.OPERATIONAL, dataRoot).checkedGet();
- if (getConfigExecution.getDatastore() == Datastore.running) {
- transactionProvider.abortRunningTransaction(rwTx);
- rwTx = null;
- }
+ transactionProvider.abortRunningTransaction(rwTx);
return (Element) transformNormalizedNode(document, normalizedNodeOptional.get(), dataRoot);
} catch (ReadFailedException e) {
LOG.warn("Unable to read data: {}", dataRoot, e);
package org.opendaylight.controller.netconf.mdsal.connector.ops.get;
import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
}
final YangInstanceIdentifier dataRoot = ROOT;
- DOMDataReadWriteTransaction rwTx = getTransaction(getConfigExecution.getDatastore());
+ // Proper exception should be thrown
+ Preconditions.checkState(getConfigExecution.getDatastore().isPresent(), "Source element missing from request");
+
+ DOMDataReadWriteTransaction rwTx = getTransaction(getConfigExecution.getDatastore().get());
try {
final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = rwTx.read(LogicalDatastoreType.CONFIGURATION, dataRoot).checkedGet();
- if (getConfigExecution.getDatastore() == Datastore.running) {
+ if (getConfigExecution.getDatastore().get() == Datastore.running) {
transactionProvider.abortRunningTransaction(rwTx);
rwTx = null;
}
}
}
}
+
+ container mapper-aggregator {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity nnm:netconf-mapper-registry;
+ }
+ }
+ }
}
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>mdsal-netconf-monitoring</artifactId>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </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.mdsal.monitoring;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+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.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class MonitoringToMdsalWriter implements AutoCloseable, NetconfMonitoringService.MonitoringListener, BindingAwareProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MonitoringToMdsalWriter.class);
+
+ private final NetconfMonitoringService serverMonitoringDependency;
+ private DataBroker dataBroker;
+
+ public MonitoringToMdsalWriter(final NetconfMonitoringService serverMonitoringDependency) {
+ this.serverMonitoringDependency = serverMonitoringDependency;
+ }
+
+ @Override
+ public void close() {
+ final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+ tx.delete(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(NetconfState.class));
+ final CheckedFuture<Void, TransactionCommitFailedException> submit = tx.submit();
+
+ Futures.addCallback(submit, new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(final Void aVoid) {
+ LOG.debug("Netconf state cleared successfully");
+ }
+
+ @Override
+ public void onFailure(final Throwable throwable) {
+ LOG.warn("Unable to clear netconf state", throwable);
+ }
+ });
+ }
+
+ @Override
+ public void onStateChanged(final NetconfState state) {
+ Preconditions.checkState(dataBroker != null);
+ final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+ tx.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(NetconfState.class), state);
+ // FIXME first attempt (right after we register to binding broker) always fails
+ // Is it due to the fact that we are writing from the onSessionInitiated callback ?
+ final CheckedFuture<Void, TransactionCommitFailedException> submit = tx.submit();
+
+ Futures.addCallback(submit, new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(final Void aVoid) {
+ LOG.debug("Netconf state updated successfully");
+ }
+
+ @Override
+ public void onFailure(final Throwable throwable) {
+ LOG.warn("Unable to update netconf state", throwable);
+ }
+ });
+ }
+
+ @Override
+ public void onSessionInitiated(final BindingAwareBroker.ProviderContext providerContext) {
+ dataBroker = providerContext.getSALService(DataBroker.class);
+ serverMonitoringDependency.registerListener(this);
+ }
+}
--- /dev/null
+package org.opendaylight.controller.config.yang.netconf.mdsal.monitoring;
+
+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.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.monitoring.GetSchema;
+import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
+
+public class NetconfMdsalMonitoringMapperModule extends org.opendaylight.controller.config.yang.netconf.mdsal.monitoring.AbstractNetconfMdsalMonitoringMapperModule {
+ public NetconfMdsalMonitoringMapperModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfMdsalMonitoringMapperModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.netconf.mdsal.monitoring.NetconfMdsalMonitoringMapperModule oldModule, final 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 NetconfMonitoringService serverMonitoringDependency = getServerMonitoringDependency();
+
+ final MonitoringToMdsalWriter monitoringToMdsalWriter = new MonitoringToMdsalWriter(serverMonitoringDependency);
+ getBindingAwareBrokerDependency().registerProvider(monitoringToMdsalWriter);
+
+ final MdSalMonitoringMapperFactory mdSalMonitoringMapperFactory = new MdSalMonitoringMapperFactory(new MdsalMonitoringMapper(serverMonitoringDependency)) {
+ @Override
+ public void close() {
+ super.close();
+ monitoringToMdsalWriter.close();
+ getAggregatorDependency().onRemoveNetconfOperationServiceFactory(this);
+ }
+ };
+
+ getAggregatorDependency().onAddNetconfOperationServiceFactory(mdSalMonitoringMapperFactory);
+ return mdSalMonitoringMapperFactory;
+
+ }
+
+ // FIXME almost exactly same code as in netconf-monitoring, refactor
+ private static class MdSalMonitoringMapperFactory implements NetconfOperationServiceFactory, AutoCloseable {
+
+ private final NetconfOperationService operationService;
+
+ private static final Set<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
+
+ @Override
+ public String getCapabilityUri() {
+ return MonitoringConstants.URI;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(MonitoringConstants.NAMESPACE);
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(MonitoringConstants.MODULE_NAME);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(MonitoringConstants.MODULE_REVISION);
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Collection<String> getLocation() {
+ return Collections.emptyList();
+ }
+ });
+
+ private static final AutoCloseable AUTO_CLOSEABLE = new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+ };
+
+ private final List<CapabilityListener> listeners = new ArrayList<>();
+
+ public MdSalMonitoringMapperFactory(final NetconfOperationService operationService) {
+ this.operationService = operationService;
+ }
+
+ @Override
+ public NetconfOperationService createService(final String netconfSessionIdForReporting) {
+ return operationService;
+ }
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return CAPABILITIES;
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ listener.onCapabilitiesAdded(getCapabilities());
+ listeners.add(listener);
+ return AUTO_CLOSEABLE;
+ }
+
+ @Override
+ public void close() {
+ for (final CapabilityListener listener : listeners) {
+ listener.onCapabilitiesRemoved(getCapabilities());
+ }
+ }
+ }
+
+
+ private static class MdsalMonitoringMapper implements NetconfOperationService {
+
+ private final NetconfMonitoringService serverMonitoringDependency;
+
+ public MdsalMonitoringMapper(final NetconfMonitoringService serverMonitoringDependency) {
+ this.serverMonitoringDependency = serverMonitoringDependency;
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return Collections.<NetconfOperation>singleton(new GetSchema(serverMonitoringDependency));
+ }
+
+ @Override
+ public void close() {
+ // NOOP
+ }
+ }
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-mdsal-monitoring yang module local name: netconf-mdsal-monitoring-mapper
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 18 10:22:17 CET 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.netconf.mdsal.monitoring;
+public class NetconfMdsalMonitoringMapperModuleFactory extends org.opendaylight.controller.config.yang.netconf.mdsal.monitoring.AbstractNetconfMdsalMonitoringMapperModuleFactory {
+
+}
--- /dev/null
+/*
+* 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
+*/
+package org.opendaylight.controller.netconf.monitoring;
+
+import com.google.common.collect.Sets;
+import java.util.Set;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.monitoring.Get;
+import org.opendaylight.controller.netconf.monitoring.GetSchema;
+
+public class NetconfMonitoringOperationService implements NetconfOperationService {
+
+ private final NetconfMonitoringService monitor;
+
+ public NetconfMonitoringOperationService(final NetconfMonitoringService monitor) {
+ this.monitor = monitor;
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return Sets.<NetconfOperation>newHashSet(new Get(monitor), new GetSchema(monitor));
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.monitoring;
+
+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.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+
+/**
+* Created by mmarsale on 18.2.2015.
+*/
+public class NetconfMonitoringOperationServiceFactory implements NetconfOperationServiceFactory, AutoCloseable {
+
+ private final NetconfMonitoringOperationService operationService;
+
+ private static final Set<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
+
+ @Override
+ public String getCapabilityUri() {
+ return MonitoringConstants.URI;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(MonitoringConstants.NAMESPACE);
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(MonitoringConstants.MODULE_NAME);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(MonitoringConstants.MODULE_REVISION);
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Collection<String> getLocation() {
+ return Collections.emptyList();
+ }
+ });
+
+ private static final AutoCloseable AUTO_CLOSEABLE = new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+ };
+
+ private final List<CapabilityListener> listeners = new ArrayList<>();
+
+ public NetconfMonitoringOperationServiceFactory(final NetconfMonitoringOperationService operationService) {
+ this.operationService = operationService;
+ }
+
+ @Override
+ public NetconfOperationService createService(final String netconfSessionIdForReporting) {
+ return operationService;
+ }
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return CAPABILITIES;
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ listener.onCapabilitiesAdded(getCapabilities());
+ listeners.add(listener);
+ return AUTO_CLOSEABLE;
+ }
+
+ @Override
+ public void close() {
+ for (final CapabilityListener listener : listeners) {
+ listener.onCapabilitiesRemoved(getCapabilities());
+ }
+ }
+}
--- /dev/null
+module netconf-mdsal-monitoring {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring";
+ prefix "nmmonitor";
+
+ import netconf-northbound-mapper { prefix nnm; revision-date 2015-01-14; }
+ import opendaylight-md-sal-binding {prefix md-sal-binding; revision-date 2013-10-28;}
+ import netconf-northbound { prefix nn; revision-date 2015-01-14; }
+ import config { prefix config; revision-date 2013-04-05; }
+
+ organization "Cisco Systems, Inc.";
+
+ description
+ "This module contains the base YANG definitions for
+ an MD-SAL monitoring mapper implementation";
+
+ revision "2015-02-18" {
+ description
+ "Initial revision.";
+ }
+
+ identity netconf-mdsal-monitoring-mapper {
+ base config:module-type;
+ config:provided-service nnm:netconf-northbound-mapper;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-mdsal-monitoring-mapper {
+ when "/config:modules/config:module/config:type = 'netconf-mdsal-monitoring-mapper'";
+
+ container server-monitoring {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity nn:netconf-server-monitoring;
+ }
+ }
+ }
+
+ container aggregator {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity nnm:netconf-mapper-registry;
+ }
+ }
+ }
+
+ container binding-aware-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-broker-osgi-registry;
+ }
+ }
+ }
+ }
+ }
+
+}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.mapping.api;
+package org.opendaylight.controller.netconf.api;
import com.google.common.base.Optional;
import java.util.Collection;
--- /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.netconf.api.monitoring;
+
+import java.util.Set;
+import org.opendaylight.controller.netconf.api.Capability;
+
+public interface CapabilityListener {
+
+ void onCapabilitiesAdded(Set<Capability> addedCaps);
+
+ void onCapabilitiesRemoved(Set<Capability> removedCaps);
+}
*/
package org.opendaylight.controller.netconf.api.monitoring;
+import com.google.common.base.Optional;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Capabilities;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
-public interface NetconfMonitoringService {
+public interface NetconfMonitoringService extends CapabilityListener, SessionListener {
Sessions getSessions();
Schemas getSchemas();
+
+ String getSchemaForCapability(String moduleName, Optional<String> revision);
+
+ Capabilities getCapabilities();
+
+ /**
+ * Allows push based state information transfer. After the listener is registered, current state is pushed to the listener.
+ */
+ AutoCloseable registerListener(MonitoringListener listener);
+
+ interface MonitoringListener {
+
+ // TODO more granular updates would make sense
+ void onStateChanged(NetconfState state);
+ }
}
/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ * 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.netconf.impl.osgi;
-import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
-
-public interface SessionMonitoringService {
+package org.opendaylight.controller.netconf.api.monitoring;
+/**
+ * Created by mmarsale on 13.2.2015.
+ */
+public interface SessionListener {
void onSessionUp(NetconfManagementSession session);
void onSessionDown(NetconfManagementSession session);
config:java-class "org.opendaylight.controller.netconf.api.NetconfServerDispatcher";
}
+ identity netconf-server-monitoring {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService";
+ }
+
}
\ No newline at end of file
<artifactId>netconf-monitoring</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>mdsal-netconf-monitoring</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-netty-util</artifactId>
--- /dev/null
+package org.opendaylight.controller.config.yang.config.netconf.northbound.impl;
+
+import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
+
+public class NetconfMapperAggregatorModule extends org.opendaylight.controller.config.yang.config.netconf.northbound.impl.AbstractNetconfMapperAggregatorModule {
+ public NetconfMapperAggregatorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfMapperAggregatorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.config.netconf.northbound.impl.NetconfMapperAggregatorModule oldModule, final java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {}
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new AggregatedNetconfOperationServiceFactory();
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-northbound-impl yang module local name: netconf-mapper-aggregator
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Tue Feb 17 17:24:19 CET 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.netconf.northbound.impl;
+public class NetconfMapperAggregatorModuleFactory extends org.opendaylight.controller.config.yang.config.netconf.northbound.impl.AbstractNetconfMapperAggregatorModuleFactory {
+
+}
package org.opendaylight.controller.config.yang.config.netconf.northbound.impl;
import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.impl.CommitNotifier;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
+import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
public class NetconfServerDispatcherModule extends org.opendaylight.controller.config.yang.config.netconf.northbound.impl.AbstractNetconfServerDispatcherModule {
@Override
public java.lang.AutoCloseable createInstance() {
- final NetconfOperationServiceFactoryListenerImpl aggregatedOpProvider = getAggregatedOpProvider();
- final SessionMonitoringService monitoringService = startMonitoringService(aggregatedOpProvider);
+ final AggregatedNetconfOperationServiceFactory aggregatedOpProvider = getAggregatedOpProvider();
+ final NetconfMonitoringService monitoringService = getServerMonitorDependency();
final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
getTimerDependency(), aggregatedOpProvider, new SessionIdProvider(), getConnectionTimeoutMillis(), CommitNotifier.NoopCommitNotifier.getInstance(), monitoringService);
final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
}
- private NetconfMonitoringServiceImpl startMonitoringService(final NetconfOperationServiceFactoryListenerImpl netconfOperationProvider) {
- return new NetconfMonitoringServiceImpl(netconfOperationProvider);
- }
-
- private NetconfOperationServiceFactoryListenerImpl getAggregatedOpProvider() {
- final NetconfOperationServiceFactoryListenerImpl netconfOperationProvider = new NetconfOperationServiceFactoryListenerImpl();
+ private AggregatedNetconfOperationServiceFactory getAggregatedOpProvider() {
+ final AggregatedNetconfOperationServiceFactory netconfOperationProvider = new AggregatedNetconfOperationServiceFactory();
for (final NetconfOperationServiceFactory netconfOperationServiceFactory : getMappersDependency()) {
netconfOperationProvider.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
}
--- /dev/null
+package org.opendaylight.controller.config.yang.config.netconf.northbound.impl;
+
+import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
+
+public class NetconfServerMonitoringModule extends org.opendaylight.controller.config.yang.config.netconf.northbound.impl.AbstractNetconfServerMonitoringModule {
+ public NetconfServerMonitoringModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfServerMonitoringModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.netconf.northbound.impl.NetconfServerMonitoringModule 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 NetconfMonitoringServiceImpl(getAggregatorDependency());
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-northbound-impl yang module local name: netconf-server-monitoring-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Tue Feb 17 17:24:19 CET 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.netconf.northbound.impl;
+public class NetconfServerMonitoringModuleFactory extends org.opendaylight.controller.config.yang.config.netconf.northbound.impl.AbstractNetconfServerMonitoringModuleFactory {
+
+}
+++ /dev/null
-/*
- * 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
- */
-
-package org.opendaylight.controller.netconf.impl;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class CapabilityProviderImpl implements CapabilityProvider {
- private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
- private final Set<String> capabilityURIs;
-
- private static final Logger LOG = LoggerFactory.getLogger(CapabilityProviderImpl.class);
-
- public CapabilityProviderImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
- this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
- Map<String, Capability> urisToCapabilitiesInternalMap = getCapabilitiesInternal(netconfOperationServiceSnapshot);
- List<String> capabilityURIs = new ArrayList<>(urisToCapabilitiesInternalMap.keySet());
- Collections.sort(capabilityURIs);
- this.capabilityURIs = Collections.unmodifiableSet(new TreeSet<>(capabilityURIs));
- }
-
- private static Map<String, Capability> getCapabilitiesInternal(
- NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
- Map<String, Capability> capabilityMap = Maps.newHashMap();
-
- for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
- final Set<Capability> caps = netconfOperationService.getCapabilities();
-
- for (Capability cap : caps) {
-
- if(capabilityMap.containsKey(cap.getCapabilityUri())) {
- LOG.debug("Duplicate capability {} from service {}", cap.getCapabilityUri(), netconfOperationService);
- }
-
- capabilityMap.put(cap.getCapabilityUri(), cap);
- }
- }
-
- return capabilityMap;
- }
-
- @Override
- public synchronized String getSchemaForCapability(String moduleName, Optional<String> revision) {
-
- Map<String, Map<String, String>> mappedModulesToRevisionToSchema = Maps.newHashMap();
-
- for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
- final Set<Capability> caps = netconfOperationService.getCapabilities();
-
- for (Capability cap : caps) {
- if (!cap.getModuleName().isPresent()
- || !cap.getRevision().isPresent()
- || !cap.getCapabilitySchema().isPresent()){
- continue;
- }
-
- final String currentModuleName = cap.getModuleName().get();
- Map<String, String> revisionMap = mappedModulesToRevisionToSchema.get(currentModuleName);
- if (revisionMap == null) {
- revisionMap = Maps.newHashMap();
- mappedModulesToRevisionToSchema.put(currentModuleName, revisionMap);
- }
-
- String currentRevision = cap.getRevision().get();
- revisionMap.put(currentRevision, cap.getCapabilitySchema().get());
- }
- }
-
- Map<String, String> revisionMapRequest = mappedModulesToRevisionToSchema.get(moduleName);
- Preconditions.checkState(revisionMapRequest != null, "Capability for module %s not present, " + ""
- + "available modules : %s", moduleName, capabilityURIs);
-
- if (revision.isPresent()) {
- String schema = revisionMapRequest.get(revision.get());
-
- Preconditions.checkState(schema != null,
- "Capability for module %s:%s not present, available revisions for module: %s", moduleName,
- revision.get(), revisionMapRequest.keySet());
-
- return schema;
- } else {
- Preconditions.checkState(revisionMapRequest.size() == 1,
- "Expected 1 capability for module %s, available revisions : %s", moduleName,
- revisionMapRequest.keySet());
- return revisionMapRequest.values().iterator().next();
- }
- }
-
- @Override
- public synchronized Set<String> getCapabilities() {
- return capabilityURIs;
- }
-
-}
@Override
protected void sessionUp() {
- super.sessionUp();
Preconditions.checkState(loginTime == null, "Session is already up");
this.loginTime = new Date();
+ super.sessionUp();
}
public void onIncommingRpcSuccess() {
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfSessionListener;
import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
public class NetconfServerSessionListener implements NetconfSessionListener<NetconfServerSession> {
private static final Logger LOG = LoggerFactory.getLogger(NetconfServerSessionListener.class);
- private final SessionMonitoringService monitoringService;
+ private final NetconfMonitoringService monitoringService;
private final NetconfOperationRouter operationRouter;
private final AutoCloseable onSessionDownCloseable;
- public NetconfServerSessionListener(final NetconfOperationRouter operationRouter, final SessionMonitoringService monitoringService,
+ public NetconfServerSessionListener(final NetconfOperationRouter operationRouter, NetconfMonitoringService monitoringService,
final AutoCloseable onSessionDownCloseable) {
this.operationRouter = operationRouter;
this.monitoringService = monitoringService;
@Override
public void onSessionUp(final NetconfServerSession netconfNetconfServerSession) {
monitoringService.onSessionUp(netconfNetconfServerSession);
+ // FIXME monitoring service should be also notified about all the other changes to netconf session (from ietf-netconf-monitoring point of view)
+ // This means also notifying after every message is processed
}
@Override
+++ /dev/null
-/*
- * 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
- */
-
-package org.opendaylight.controller.netconf.impl;
-
-import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
-import org.opendaylight.protocol.framework.SessionListenerFactory;
-
-public class NetconfServerSessionListenerFactory implements SessionListenerFactory<NetconfServerSessionListener> {
-
- private final CommitNotifier commitNotifier;
- private final SessionMonitoringService monitor;
- private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
- private final CapabilityProvider capabilityProvider;
-
- public NetconfServerSessionListenerFactory(final CommitNotifier commitNotifier,
- final SessionMonitoringService monitor,
- final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot,
- final CapabilityProvider capabilityProvider) {
-
- this.commitNotifier = commitNotifier;
- this.monitor = monitor;
- this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
- this.capabilityProvider = capabilityProvider;
- }
-
- @Override
- public NetconfServerSessionListener getSessionListener() {
- NetconfOperationRouter operationRouter = new NetconfOperationRouterImpl(netconfOperationServiceSnapshot, capabilityProvider, commitNotifier);
- return new NetconfServerSessionListener(operationRouter, monitor, netconfOperationServiceSnapshot);
- }
-}
package org.opendaylight.controller.netconf.impl;
-import static org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider.NetconfOperationProviderUtil.getNetconfSessionIdForReporting;
-
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Set;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
import org.opendaylight.protocol.framework.SessionListenerFactory;
import org.opendaylight.protocol.framework.SessionNegotiator;
private final Timer timer;
private final SessionIdProvider idProvider;
- private final NetconfOperationProvider netconfOperationProvider;
+ private final NetconfOperationServiceFactory aggregatedOpService;
private final long connectionTimeoutMillis;
private final CommitNotifier commitNotificationProducer;
- private final SessionMonitoringService monitoringService;
+ private final NetconfMonitoringService monitoringService;
private static final Logger LOG = LoggerFactory.getLogger(NetconfServerSessionNegotiatorFactory.class);
private final Set<String> baseCapabilities;
// TODO too many params, refactor
- public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider,
- SessionIdProvider idProvider, long connectionTimeoutMillis,
- CommitNotifier commitNot,
- SessionMonitoringService monitoringService) {
+ public NetconfServerSessionNegotiatorFactory(final Timer timer, final NetconfOperationServiceFactory netconfOperationProvider,
+ final SessionIdProvider idProvider, final long connectionTimeoutMillis,
+ final CommitNotifier commitNot,
+ final NetconfMonitoringService monitoringService) {
this(timer, netconfOperationProvider, idProvider, connectionTimeoutMillis, commitNot, monitoringService, DEFAULT_BASE_CAPABILITIES);
}
// TODO too many params, refactor
- public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider,
- SessionIdProvider idProvider, long connectionTimeoutMillis,
- CommitNotifier commitNot,
- SessionMonitoringService monitoringService, Set<String> baseCapabilities) {
+ public NetconfServerSessionNegotiatorFactory(final Timer timer, final NetconfOperationServiceFactory netconfOperationProvider,
+ final SessionIdProvider idProvider, final long connectionTimeoutMillis,
+ final CommitNotifier commitNot,
+ final NetconfMonitoringService monitoringService, final Set<String> baseCapabilities) {
this.timer = timer;
- this.netconfOperationProvider = netconfOperationProvider;
+ this.aggregatedOpService = netconfOperationProvider;
this.idProvider = idProvider;
this.connectionTimeoutMillis = connectionTimeoutMillis;
this.commitNotificationProducer = commitNot;
private ImmutableSet<String> validateBaseCapabilities(final Set<String> baseCapabilities) {
// Check base capabilities to be supported by the server
- Sets.SetView<String> unknownBaseCaps = Sets.difference(baseCapabilities, DEFAULT_BASE_CAPABILITIES);
+ final Sets.SetView<String> unknownBaseCaps = Sets.difference(baseCapabilities, DEFAULT_BASE_CAPABILITIES);
Preconditions.checkArgument(unknownBaseCaps.isEmpty(),
"Base capabilities that will be supported by netconf server have to be subset of %s, unknown base capabilities: %s",
DEFAULT_BASE_CAPABILITIES, unknownBaseCaps);
- ImmutableSet.Builder<String> b = ImmutableSet.builder();
+ final ImmutableSet.Builder<String> b = ImmutableSet.builder();
b.addAll(baseCapabilities);
// Base 1.0 capability is supported by default
b.add(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0);
* @return session negotiator
*/
@Override
- public SessionNegotiator<NetconfServerSession> getSessionNegotiator(SessionListenerFactory<NetconfServerSessionListener> defunctSessionListenerFactory,
- Channel channel, Promise<NetconfServerSession> promise) {
- long sessionId = idProvider.getNextSessionId();
- NetconfOperationServiceSnapshot netconfOperationServiceSnapshot = netconfOperationProvider.openSnapshot(
- getNetconfSessionIdForReporting(sessionId));
- CapabilityProvider capabilityProvider = new CapabilityProviderImpl(netconfOperationServiceSnapshot);
-
- NetconfServerSessionPreferences proposal = null;
+ public SessionNegotiator<NetconfServerSession> getSessionNegotiator(final SessionListenerFactory<NetconfServerSessionListener> defunctSessionListenerFactory,
+ final Channel channel, final Promise<NetconfServerSession> promise) {
+ final long sessionId = idProvider.getNextSessionId();
+
+ NetconfServerSessionPreferences proposal;
try {
- proposal = new NetconfServerSessionPreferences(
- createHelloMessage(sessionId, capabilityProvider), sessionId);
- } catch (NetconfDocumentedException e) {
- LOG.error("Unable to create hello mesage for session {} with capability provider {}", sessionId,capabilityProvider);
+ proposal = new NetconfServerSessionPreferences(createHelloMessage(sessionId, monitoringService), sessionId);
+ } catch (final NetconfDocumentedException e) {
+ LOG.error("Unable to create hello message for session {} with {}", sessionId, monitoringService);
throw new IllegalStateException(e);
}
- NetconfServerSessionListenerFactory sessionListenerFactory = new NetconfServerSessionListenerFactory(
- commitNotificationProducer, monitoringService,
- netconfOperationServiceSnapshot, capabilityProvider);
-
return new NetconfServerSessionNegotiator(proposal, promise, channel, timer,
- sessionListenerFactory.getSessionListener(), connectionTimeoutMillis);
+ getListener(Long.toString(sessionId)), connectionTimeoutMillis);
+ }
+
+ private NetconfServerSessionListener getListener(final String netconfSessionIdForReporting) {
+ final NetconfOperationService service =
+ this.aggregatedOpService.createService(netconfSessionIdForReporting);
+ final NetconfOperationRouter operationRouter =
+ new NetconfOperationRouterImpl(service, commitNotificationProducer, monitoringService, netconfSessionIdForReporting);
+ return new NetconfServerSessionListener(operationRouter, monitoringService, service);
+
}
- private NetconfHelloMessage createHelloMessage(long sessionId, CapabilityProvider capabilityProvider) throws NetconfDocumentedException {
- return NetconfHelloMessage.createServerHello(Sets.union(capabilityProvider.getCapabilities(), baseCapabilities), sessionId);
+ private NetconfHelloMessage createHelloMessage(final long sessionId, final NetconfMonitoringService capabilityProvider) throws NetconfDocumentedException {
+ return NetconfHelloMessage.createServerHello(Sets.union(DefaultCommit.transformCapabilities(capabilityProvider.getCapabilities()), baseCapabilities), sessionId);
}
}
+++ /dev/null
-/*
- * 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
- */
-
-package org.opendaylight.controller.netconf.impl.mapping;
-
-import com.google.common.base.Optional;
-import java.util.Set;
-
-public interface CapabilityProvider {
-
- String getSchemaForCapability(String moduleName, Optional<String> revision);
-
- Set<String> getCapabilities();
-
-}
package org.opendaylight.controller.netconf.impl.mapping.operations;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Sets;
import java.io.InputStream;
+import java.util.Set;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.impl.CommitNotifier;
-import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Capabilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
private static final String NOTIFY_ATTR = "notify";
private final CommitNotifier notificationProducer;
- private final CapabilityProvider cap;
+ private final NetconfMonitoringService cap;
private final NetconfOperationRouter operationRouter;
- public DefaultCommit(CommitNotifier notifier, CapabilityProvider cap,
+ public DefaultCommit(CommitNotifier notifier, NetconfMonitoringService cap,
String netconfSessionIdForReporting, NetconfOperationRouter netconfOperationRouter) {
super(netconfSessionIdForReporting);
this.notificationProducer = notifier;
removePersisterAttributes(requestMessage);
Element cfgSnapshot = getConfigSnapshot(operationRouter);
LOG.debug("Config snapshot retrieved successfully {}", cfgSnapshot);
- notificationProducer.sendCommitNotification("ok", cfgSnapshot, cap.getCapabilities());
+ notificationProducer.sendCommitNotification("ok", cfgSnapshot, transformCapabilities(cap.getCapabilities()));
}
return subsequentOperation.execute(requestMessage);
}
+ // FIXME move somewhere to util since this is required also by negotiatiorFactory
+ public static Set<String> transformCapabilities(final Capabilities capabilities) {
+ return Sets.newHashSet(Collections2.transform(capabilities.getCapability(), new Function<Uri, String>() {
+ @Override
+ public String apply(final Uri uri) {
+ return uri.getValue();
+ }
+ }));
+ }
+
@Override
protected Element handle(Document document, XmlElement message, NetconfOperationChainedExecution subsequentOperation) throws NetconfDocumentedException {
throw new UnsupportedOperationException("Never gets called");
--- /dev/null
+/*
+ * 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
+ */
+
+package org.opendaylight.controller.netconf.impl.osgi;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.util.CloseableUtil;
+
+/**
+ * NetconfOperationService aggregator. Makes a collection of operation services accessible as one.
+ */
+public class AggregatedNetconfOperationServiceFactory implements NetconfOperationServiceFactory, NetconfOperationServiceFactoryListener, AutoCloseable {
+
+ private final Set<NetconfOperationServiceFactory> factories = new HashSet<>();
+ private final Multimap<NetconfOperationServiceFactory, AutoCloseable> registrations = HashMultimap.create();
+ private final Set<CapabilityListener> listeners = Sets.newHashSet();
+
+ @Override
+ public synchronized void onAddNetconfOperationServiceFactory(NetconfOperationServiceFactory service) {
+ factories.add(service);
+
+ for (final CapabilityListener listener : listeners) {
+ AutoCloseable reg = service.registerCapabilityListener(listener);
+ registrations.put(service, reg);
+ }
+ }
+
+ @Override
+ public synchronized void onRemoveNetconfOperationServiceFactory(NetconfOperationServiceFactory service) {
+ factories.remove(service);
+
+ for (final AutoCloseable autoCloseable : registrations.get(service)) {
+ try {
+ autoCloseable.close();
+ } catch (Exception e) {
+ // FIXME Issue warning
+ }
+ }
+
+ registrations.removeAll(service);
+ }
+
+ @Override
+ public synchronized Set<Capability> getCapabilities() {
+ final HashSet<Capability> capabilities = Sets.newHashSet();
+ for (final NetconfOperationServiceFactory factory : factories) {
+ capabilities.addAll(factory.getCapabilities());
+ }
+ return capabilities;
+ }
+
+ @Override
+ public synchronized AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ final Map<NetconfOperationServiceFactory, AutoCloseable> regs = Maps.newHashMap();
+
+ for (final NetconfOperationServiceFactory factory : factories) {
+ final AutoCloseable reg = factory.registerCapabilityListener(listener);
+ regs.put(factory, reg);
+ }
+ listeners.add(listener);
+
+ return new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ synchronized (AggregatedNetconfOperationServiceFactory.this) {
+ listeners.remove(listener);
+ CloseableUtil.closeAll(regs.values());
+ for (final Map.Entry<NetconfOperationServiceFactory, AutoCloseable> reg : regs.entrySet()) {
+ registrations.remove(reg.getKey(), reg.getValue());
+ }
+ }
+ }
+ };
+ }
+
+ @Override
+ public synchronized NetconfOperationService createService(final String netconfSessionIdForReporting) {
+ return new AggregatedNetconfOperation(factories, netconfSessionIdForReporting);
+ }
+
+ @Override
+ public synchronized void close() throws Exception {
+ factories.clear();
+ for (AutoCloseable reg : registrations.values()) {
+ reg.close();
+ }
+ registrations.clear();
+ listeners.clear();
+ }
+
+ private static final class AggregatedNetconfOperation implements NetconfOperationService {
+
+ private final Set<NetconfOperationService> services;
+
+ public AggregatedNetconfOperation(final Set<NetconfOperationServiceFactory> factories, final String netconfSessionIdForReporting) {
+ final Builder<NetconfOperationService> b = ImmutableSet.builder();
+ for (final NetconfOperationServiceFactory factory : factories) {
+ b.add(factory.createService(netconfSessionIdForReporting));
+ }
+ this.services = b.build();
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ final HashSet<NetconfOperation> operations = Sets.newHashSet();
+ for (final NetconfOperationService service : services) {
+ operations.addAll(service.getNetconfOperations());
+ }
+ return operations;
+ }
+
+ @Override
+ public void close() {
+ try {
+ CloseableUtil.closeAll(services);
+ } catch (final Exception e) {
+ throw new IllegalStateException("Unable to properly close all aggregated services", e);
+ }
+ }
+ }
+}
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactoryListener;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
@Override
public void start(final BundleContext context) {
- NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ AggregatedNetconfOperationServiceFactory factoriesListener = new AggregatedNetconfOperationServiceFactory();
startOperationServiceFactoryTracker(context, factoriesListener);
SessionIdProvider idProvider = new SessionIdProvider();
commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
- SessionMonitoringService monitoringService = startMonitoringService(context, factoriesListener);
+ NetconfMonitoringService monitoringService = startMonitoringService(context, factoriesListener);
NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
timer, factoriesListener, idProvider, connectionTimeoutMillis, commitNot, monitoringService);
LocalAddress address = NetconfConfigUtil.getNetconfLocalAddress();
LOG.trace("Starting local netconf server at {}", address);
dispatch.createLocalServer(address);
-
- context.registerService(NetconfOperationProvider.class, factoriesListener, null);
-
}
- private void startOperationServiceFactoryTracker(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ private void startOperationServiceFactoryTracker(BundleContext context, NetconfOperationServiceFactoryListener factoriesListener) {
factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
factoriesTracker.open();
}
- private NetconfMonitoringServiceImpl startMonitoringService(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ private NetconfMonitoringServiceImpl startMonitoringService(BundleContext context, AggregatedNetconfOperationServiceFactory factoriesListener) {
NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener);
Dictionary<String, ?> dic = new Hashtable<>();
regMonitoring = context.registerService(NetconfMonitoringService.class, netconfMonitoringServiceImpl, dic);
package org.opendaylight.controller.netconf.impl.osgi;
import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
+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.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
+import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfStateBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Capabilities;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.CapabilitiesBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class NetconfMonitoringServiceImpl implements NetconfMonitoringService, SessionMonitoringService {
+public class NetconfMonitoringServiceImpl implements NetconfMonitoringService, AutoCloseable {
+
private static final Schema.Location NETCONF_LOCATION = new Schema.Location(Schema.Location.Enumeration.NETCONF);
private static final List<Schema.Location> NETCONF_LOCATIONS = ImmutableList.of(NETCONF_LOCATION);
private static final Logger LOG = LoggerFactory.getLogger(NetconfMonitoringServiceImpl.class);
return input.toManagementSession();
}
};
+ private static final Function<Capability, Uri> CAPABILITY_TO_URI = new Function<Capability, Uri>() {
+ @Override
+ public Uri apply(final Capability input) {
+ return new Uri(input.getCapabilityUri());
+ }
+ };
private final Set<NetconfManagementSession> sessions = new ConcurrentSet<>();
- private final NetconfOperationProvider netconfOperationProvider;
+ private final NetconfOperationServiceFactory netconfOperationProvider;
+ private final Map<Uri, Capability> capabilities = new ConcurrentHashMap<>();
+
+ private final Set<MonitoringListener> listeners = Sets.newHashSet();
- public NetconfMonitoringServiceImpl(final NetconfOperationProvider netconfOperationProvider) {
+ public NetconfMonitoringServiceImpl(final NetconfOperationServiceFactory netconfOperationProvider) {
this.netconfOperationProvider = netconfOperationProvider;
+ netconfOperationProvider.registerCapabilityListener(this);
}
@Override
- public void onSessionUp(final NetconfManagementSession session) {
+ public synchronized void onSessionUp(final NetconfManagementSession session) {
LOG.debug("Session {} up", session);
Preconditions.checkState(!sessions.contains(session), "Session %s was already added", session);
sessions.add(session);
+ notifyListeners();
}
@Override
- public void onSessionDown(final NetconfManagementSession session) {
+ public synchronized void onSessionDown(final NetconfManagementSession session) {
LOG.debug("Session {} down", session);
Preconditions.checkState(sessions.contains(session), "Session %s not present", session);
sessions.remove(session);
+ notifyListeners();
}
@Override
- public Sessions getSessions() {
+ public synchronized Sessions getSessions() {
return new SessionsBuilder().setSession(ImmutableList.copyOf(Collections2.transform(sessions, SESSION_FUNCTION))).build();
}
@Override
- public Schemas getSchemas() {
- // capabilities should be split from operations (it will allow to move getSchema operation to monitoring module)
- try (NetconfOperationServiceSnapshot snapshot = netconfOperationProvider.openSnapshot("netconf-monitoring")) {
- return transformSchemas(snapshot.getServices());
- } catch (RuntimeException e) {
+ public synchronized Schemas getSchemas() {
+ try {
+ return transformSchemas(netconfOperationProvider.getCapabilities());
+ } catch (final RuntimeException e) {
throw e;
- } catch (Exception e) {
+ } catch (final Exception e) {
throw new IllegalStateException("Exception while closing", e);
}
}
- private static Schemas transformSchemas(final Set<NetconfOperationService> services) {
- // FIXME: Capability implementations do not have hashcode/equals!
- final Set<Capability> caps = new HashSet<>();
- for (NetconfOperationService netconfOperationService : services) {
- // TODO check for duplicates ? move capability merging to snapshot
- // Split capabilities from operations first and delete this duplicate code
- caps.addAll(netconfOperationService.getCapabilities());
+ @Override
+ public synchronized String getSchemaForCapability(final String moduleName, final Optional<String> revision) {
+
+ // FIXME not effective at all
+
+ Map<String, Map<String, String>> mappedModulesToRevisionToSchema = Maps.newHashMap();
+
+ final Collection<Capability> caps = capabilities.values();
+
+ for (Capability cap : caps) {
+ if (!cap.getModuleName().isPresent()
+ || !cap.getRevision().isPresent()
+ || !cap.getCapabilitySchema().isPresent()){
+ continue;
+ }
+
+ final String currentModuleName = cap.getModuleName().get();
+ Map<String, String> revisionMap = mappedModulesToRevisionToSchema.get(currentModuleName);
+ if (revisionMap == null) {
+ revisionMap = Maps.newHashMap();
+ mappedModulesToRevisionToSchema.put(currentModuleName, revisionMap);
+ }
+
+ String currentRevision = cap.getRevision().get();
+ revisionMap.put(currentRevision, cap.getCapabilitySchema().get());
}
+ Map<String, String> revisionMapRequest = mappedModulesToRevisionToSchema.get(moduleName);
+ Preconditions.checkState(revisionMapRequest != null, "Capability for module %s not present, " + ""
+ + "available modules : %s", moduleName, Collections2.transform(caps, CAPABILITY_TO_URI));
+
+ if (revision.isPresent()) {
+ String schema = revisionMapRequest.get(revision.get());
+
+ Preconditions.checkState(schema != null,
+ "Capability for module %s:%s not present, available revisions for module: %s", moduleName,
+ revision.get(), revisionMapRequest.keySet());
+
+ return schema;
+ } else {
+ Preconditions.checkState(revisionMapRequest.size() == 1,
+ "Expected 1 capability for module %s, available revisions : %s", moduleName,
+ revisionMapRequest.keySet());
+ return revisionMapRequest.values().iterator().next();
+ }
+ }
+
+ @Override
+ public synchronized Capabilities getCapabilities() {
+ return new CapabilitiesBuilder().setCapability(Lists.newArrayList(capabilities.keySet())).build();
+ }
+
+ @Override
+ public synchronized AutoCloseable registerListener(final MonitoringListener listener) {
+ listeners.add(listener);
+ listener.onStateChanged(getCurrentNetconfState());
+ return new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ listeners.remove(listener);
+ }
+ };
+ }
+
+ private NetconfState getCurrentNetconfState() {
+ return new NetconfStateBuilder()
+ .setCapabilities(getCapabilities())
+ .setSchemas(getSchemas())
+ .setSessions(getSessions())
+ .build();
+ }
+
+ private static Schemas transformSchemas(final Set<Capability> caps) {
final List<Schema> schemas = new ArrayList<>(caps.size());
- for (Capability cap : caps) {
+ for (final Capability cap : caps) {
if (cap.getCapabilitySchema().isPresent()) {
- SchemaBuilder builder = new SchemaBuilder();
+ final SchemaBuilder builder = new SchemaBuilder();
Preconditions.checkState(cap.getModuleNamespace().isPresent());
builder.setNamespace(new Uri(cap.getModuleNamespace().get()));
Preconditions.checkState(cap.getRevision().isPresent());
- String version = cap.getRevision().get();
+ final String version = cap.getRevision().get();
builder.setVersion(version);
Preconditions.checkState(cap.getModuleName().isPresent());
- String identifier = cap.getModuleName().get();
+ final String identifier = cap.getModuleName().get();
builder.setIdentifier(identifier);
builder.setFormat(Yang.class);
final Builder<Schema.Location> b = ImmutableList.builder();
b.add(NETCONF_LOCATION);
- for (String location : locations) {
+ for (final String location : locations) {
b.add(new Schema.Location(new Uri(location)));
}
return b.build();
}
+
+ @Override
+ public synchronized void onCapabilitiesAdded(final Set<Capability> addedCaps) {
+ // FIXME howto check for duplicates
+ this.capabilities.putAll(Maps.uniqueIndex(addedCaps, CAPABILITY_TO_URI));
+ notifyListeners();
+ }
+
+ private void notifyListeners() {
+ for (final MonitoringListener listener : listeners) {
+ listener.onStateChanged(getCurrentNetconfState());
+ }
+ }
+
+ @Override
+ public synchronized void onCapabilitiesRemoved(final Set<Capability> addedCaps) {
+ for (final Capability addedCap : addedCaps) {
+ capabilities.remove(addedCap.getCapabilityUri());
+ }
+ notifyListeners();
+ }
+
+ @Override
+ public synchronized void close() throws Exception {
+ listeners.clear();
+ sessions.clear();
+ capabilities.clear();
+ }
}
import java.util.Set;
import java.util.TreeMap;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.impl.CommitNotifier;
import org.opendaylight.controller.netconf.impl.NetconfServerSession;
-import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit;
-import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultGetSchema;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultNetconfOperation;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStartExi;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStopExi;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
import org.opendaylight.controller.netconf.mapping.api.SessionAwareNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
public class NetconfOperationRouterImpl implements NetconfOperationRouter {
private static final Logger LOG = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
- private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
+ private final NetconfOperationService netconfOperationServiceSnapshot;
private final Collection<NetconfOperation> allNetconfOperations;
- public NetconfOperationRouterImpl(final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot, final CapabilityProvider capabilityProvider,
- final CommitNotifier commitNotifier) {
+ public NetconfOperationRouterImpl(final NetconfOperationService netconfOperationServiceSnapshot,
+ final CommitNotifier commitNotifier, final NetconfMonitoringService netconfMonitoringService, final String sessionId) {
this.netconfOperationServiceSnapshot = Preconditions.checkNotNull(netconfOperationServiceSnapshot);
- final String sessionId = netconfOperationServiceSnapshot.getNetconfSessionIdForReporting();
-
final Set<NetconfOperation> ops = new HashSet<>();
- ops.add(new DefaultGetSchema(capabilityProvider, sessionId));
ops.add(new DefaultCloseSession(sessionId, this));
ops.add(new DefaultStartExi(sessionId));
ops.add(new DefaultStopExi(sessionId));
- ops.add(new DefaultCommit(commitNotifier, capabilityProvider, sessionId, this));
+ ops.add(new DefaultCommit(commitNotifier, netconfMonitoringService, sessionId, this));
- for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
- for (NetconfOperation netconfOperation : netconfOperationService.getNetconfOperations()) {
- Preconditions.checkState(!ops.contains(netconfOperation),
- "Netconf operation %s already present", netconfOperation);
- ops.add(netconfOperation);
- }
- }
+ ops.addAll(netconfOperationServiceSnapshot.getNetconfOperations());
allNetconfOperations = ImmutableSet.copyOf(ops);
}
if (!handlingPriority.equals(HandlingPriority.CANNOT_HANDLE)) {
Preconditions.checkState(!sortedPriority.containsKey(handlingPriority),
- "Multiple %s available to handle message %s with priority %s",
- NetconfOperation.class.getName(), message, handlingPriority);
+ "Multiple %s available to handle message %s with priority %s, %s and %s",
+ NetconfOperation.class.getName(), message, handlingPriority, netconfOperation, sortedPriority.get(handlingPriority));
sortedPriority.put(handlingPriority, netconfOperation);
}
}
+++ /dev/null
-/*
- * 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
- */
-package org.opendaylight.controller.netconf.impl.osgi;
-
-import java.util.HashSet;
-import java.util.Set;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
-
-public class NetconfOperationServiceFactoryListenerImpl implements NetconfOperationServiceFactoryListener,
- NetconfOperationProvider {
- private final Set<NetconfOperationServiceFactory> allFactories = new HashSet<>();
-
- @Override
- public synchronized void onAddNetconfOperationServiceFactory(NetconfOperationServiceFactory service) {
- allFactories.add(service);
- }
-
- @Override
- public synchronized void onRemoveNetconfOperationServiceFactory(NetconfOperationServiceFactory service) {
- allFactories.remove(service);
- }
-
- @Override
- public synchronized NetconfOperationServiceSnapshotImpl openSnapshot(String sessionIdForReporting) {
- return new NetconfOperationServiceSnapshotImpl(allFactories, sessionIdForReporting);
- }
-
-}
import org.opendaylight.controller.netconf.api.util.NetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactoryListener;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
@Override
public void removedService(ServiceReference<NetconfOperationServiceFactory> reference,
NetconfOperationServiceFactory netconfOperationServiceFactory) {
- if (netconfOperationServiceFactory != null)
+ if (netconfOperationServiceFactory != null) {
factoriesListener.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ }
}
}
+++ /dev/null
-/*
- * 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
- */
-
-package org.opendaylight.controller.netconf.impl.osgi;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-import java.util.Set;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
-import org.opendaylight.controller.netconf.util.CloseableUtil;
-
-public class NetconfOperationServiceSnapshotImpl implements NetconfOperationServiceSnapshot {
-
- private final Set<NetconfOperationService> services;
- private final String netconfSessionIdForReporting;
-
- public NetconfOperationServiceSnapshotImpl(final Set<NetconfOperationServiceFactory> factories, final String sessionIdForReporting) {
- final Builder<NetconfOperationService> b = ImmutableSet.builder();
- netconfSessionIdForReporting = sessionIdForReporting;
- for (NetconfOperationServiceFactory factory : factories) {
- b.add(factory.createService(netconfSessionIdForReporting));
- }
- this.services = b.build();
- }
-
- @Override
- public String getNetconfSessionIdForReporting() {
- return netconfSessionIdForReporting;
- }
-
- @Override
- public Set<NetconfOperationService> getServices() {
- return services;
- }
-
- @Override
- public void close() throws Exception {
- CloseableUtil.closeAll(services);
- }
-
- @Override
- public String toString() {
- return "NetconfOperationServiceSnapshotImpl{" + netconfSessionIdForReporting + '}';
- }
-}
}
}
+ container server-monitor {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity nn:netconf-server-monitoring;
+ }
+ }
+ }
+
container timer {
uses config:service-ref {
refine type {
}
}
+
+ identity netconf-server-monitoring-impl {
+ base config:module-type;
+ config:provided-service nn:netconf-server-monitoring;
+ config:java-name-prefix NetconfServerMonitoring;
+ }
+
+ // TODO Monitoring could expose the monitoring data over JMX...
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-server-monitoring-impl {
+ when "/config:modules/config:module/config:type = 'netconf-server-monitoring-impl'";
+
+ container aggregator {
+ uses config:service-ref {
+ refine type {
+ config:required-identity nnm:netconf-northbound-mapper;
+ }
+ }
+ }
+
+ }
+ }
+
+ identity netconf-mapper-aggregator {
+ base config:module-type;
+ config:provided-service nnm:netconf-northbound-mapper;
+ config:provided-service nnm:netconf-mapper-registry;
+ config:java-name-prefix NetconfMapperAggregator;
+ description "Aggregated operation provider for netconf servers. Joins all the operations and capabilities of all the mappers it aggregates and exposes them as a single service. The dependency orientation is reversed in order to prevent cyclic dependencies when monitoring service is considered";
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-mapper-aggregator {
+ when "/config:modules/config:module/config:type = 'netconf-mapper-aggregator'";
+
+ }
+ }
+
}
\ No newline at end of file
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anySetOf;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import com.google.common.base.Preconditions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
import org.opendaylight.controller.netconf.client.TestingNetconfClient;
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.protocol.framework.NeverReconnectStrategy;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.CapabilitiesBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
HashedWheelTimer hashedWheelTimer;
private TestingNetconfOperation testingNetconfOperation;
- public static SessionMonitoringService createMockedMonitoringService() {
- SessionMonitoringService monitoring = mock(SessionMonitoringService.class);
+ public static NetconfMonitoringService createMockedMonitoringService() {
+ NetconfMonitoringService monitoring = mock(NetconfMonitoringService.class);
doNothing().when(monitoring).onSessionUp(any(NetconfServerSession.class));
doNothing().when(monitoring).onSessionDown(any(NetconfServerSession.class));
+ doReturn(new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+
+ }
+ }).when(monitoring).registerListener(any(NetconfMonitoringService.MonitoringListener.class));
+ doNothing().when(monitoring).onCapabilitiesAdded(anySetOf(Capability.class));
+ doNothing().when(monitoring).onCapabilitiesRemoved(anySetOf(Capability.class));
+ doReturn(new CapabilitiesBuilder().setCapability(Collections.<Uri>emptyList()).build()).when(monitoring).getCapabilities();
return monitoring;
}
nettyGroup = new NioEventLoopGroup(nettyThreads);
netconfClientDispatcher = new NetconfClientDispatcherImpl(nettyGroup, nettyGroup, hashedWheelTimer);
- NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ AggregatedNetconfOperationServiceFactory factoriesListener = new AggregatedNetconfOperationServiceFactory();
testingNetconfOperation = new TestingNetconfOperation();
factoriesListener.onAddNetconfOperationServiceFactory(new TestingOperationServiceFactory(testingNetconfOperation));
this.operations = operations;
}
+ @Override
+ public Set<Capability> getCapabilities() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ return new AutoCloseable(){
+ @Override
+ public void close() throws Exception {}
+ };
+ }
+
@Override
public NetconfOperationService createService(String netconfSessionIdForReporting) {
return new NetconfOperationService() {
- @Override
- public Set<Capability> getCapabilities() {
- return Collections.emptySet();
- }
@Override
public Set<NetconfOperation> getNetconfOperations() {
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
public class NetconfDispatcherImplTest {
commitNot = new DefaultCommitNotificationProducer(
ManagementFactory.getPlatformMBeanServer());
- NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ AggregatedNetconfOperationServiceFactory factoriesListener = new AggregatedNetconfOperationServiceFactory();
SessionIdProvider idProvider = new SessionIdProvider();
hashedWheelTimer = new HashedWheelTimer();
package org.opendaylight.controller.netconf.impl;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import io.netty.channel.Channel;
-import java.util.Set;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
-import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
-
public class NetconfMonitoringServiceImplTest {
- private NetconfMonitoringServiceImpl service;
-
- @Mock
- private NetconfOperationProvider operationProvider;
- @Mock
- private NetconfManagementSession managementSession;
- @Mock
- private NetconfOperationServiceSnapshot snapshot;
- @Mock
- private NetconfOperationService operationService;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- service = new NetconfMonitoringServiceImpl(operationProvider);
- }
-
- @Test
- public void testSessions() throws Exception {
- doReturn("sessToStr").when(managementSession).toString();
- service.onSessionUp(managementSession);
- }
-
- @Test(expected = RuntimeException.class)
- public void testGetSchemas() throws Exception {
- doThrow(RuntimeException.class).when(operationProvider).openSnapshot(anyString());
- service.getSchemas();
- }
-
- @Test(expected = IllegalStateException.class)
- public void testGetSchemas2() throws Exception {
- doThrow(Exception.class).when(operationProvider).openSnapshot(anyString());
- service.getSchemas();
- }
-
- @Test
- public void testGetSchemas3() throws Exception {
- doReturn("").when(managementSession).toString();
- Capability cap = mock(Capability.class);
- Set<Capability> caps = Sets.newHashSet(cap);
- Set<NetconfOperationService> services = Sets.newHashSet(operationService);
- doReturn(snapshot).when(operationProvider).openSnapshot(anyString());
- doReturn(services).when(snapshot).getServices();
- doReturn(caps).when(operationService).getCapabilities();
- Optional<String> opt = mock(Optional.class);
- doReturn(opt).when(cap).getCapabilitySchema();
- doReturn(true).when(opt).isPresent();
- doReturn(opt).when(cap).getModuleNamespace();
- doReturn("namespace").when(opt).get();
- Optional<String> optRev = Optional.of("rev");
- doReturn(optRev).when(cap).getRevision();
- doReturn(Optional.of("modName")).when(cap).getModuleName();
- doReturn(Lists.newArrayList("loc")).when(cap).getLocation();
- doNothing().when(snapshot).close();
-
- assertNotNull(service.getSchemas());
- verify(snapshot, times(1)).close();
-
- NetconfServerSessionListener sessionListener = mock(NetconfServerSessionListener.class);
- Channel channel = mock(Channel.class);
- doReturn("mockChannel").when(channel).toString();
- NetconfHelloMessageAdditionalHeader header = new NetconfHelloMessageAdditionalHeader("name", "addr", "2", "tcp", "id");
- NetconfServerSession sm = new NetconfServerSession(sessionListener, channel, 10, header);
- doNothing().when(sessionListener).onSessionUp(any(NetconfServerSession.class));
- sm.sessionUp();
- service.onSessionUp(sm);
- assertEquals(1, service.getSessions().getSession().size());
-
- assertEquals(Long.valueOf(10), service.getSessions().getSession().get(0).getSessionId());
-
- service.onSessionDown(sm);
- assertEquals(0, service.getSessions().getSession().size());
- }
+ // TODO redo test
}
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import com.google.common.collect.Sets;
+import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerSession;
-import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.CapabilitiesBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
private Document requestMessage;
private NetconfOperationRouter router;
private DefaultCommitNotificationProducer notifier;
- private CapabilityProvider cap;
+ private NetconfMonitoringService cap;
private DefaultCommit commit;
@Before
doReturn(false).when(operation).isExecutionTermination();
notifier = mock(DefaultCommitNotificationProducer.class);
doNothing().when(notifier).sendCommitNotification(anyString(), any(Element.class), anySetOf(String.class));
- cap = mock(CapabilityProvider.class);
- doReturn(Sets.newHashSet()).when(cap).getCapabilities();
+ cap = mock(NetconfMonitoringService.class);
+ doReturn(new CapabilitiesBuilder().setCapability(Collections.<Uri>emptyList()).build()).when(cap).getCapabilities();
Document rpcData = XmlFileLoader.xmlFileToDocument("netconfMessages/editConfig_expectedResult.xml");
doReturn(rpcData).when(router).onNetconfMessage(any(Document.class), any(NetconfServerSession.class));
commit = new DefaultCommit(notifier, cap, "", router);
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.Arrays;
doReturn(filter).when(bundle).createFilter(anyString());
doNothing().when(bundle).addServiceListener(any(ServiceListener.class), anyString());
- ServiceReference<?>[] refs = new ServiceReference[0];
+ ServiceReference<?>[] refs = {};
doReturn(refs).when(bundle).getServiceReferences(anyString(), anyString());
doReturn(Arrays.asList(refs)).when(bundle).getServiceReferences(any(Class.class), anyString());
doReturn("").when(bundle).getProperty(anyString());
- doReturn(registration).when(bundle).registerService(any(Class.class), any(NetconfOperationServiceFactoryListenerImpl.class), any(Dictionary.class));
+ doReturn(registration).when(bundle).registerService(any(Class.class), any(AggregatedNetconfOperationServiceFactory.class), any(Dictionary.class));
doNothing().when(registration).unregister();
doNothing().when(bundle).removeServiceListener(any(ServiceListener.class));
}
public void testStart() throws Exception {
NetconfImplActivator activator = new NetconfImplActivator();
activator.start(bundle);
- verify(bundle, times(2)).registerService(any(Class.class), any(NetconfOperationServiceFactoryListenerImpl.class), any(Dictionary.class));
+ verify(bundle).registerService(any(Class.class), any(AggregatedNetconfOperationServiceFactory.class), any(Dictionary.class));
activator.stop(bundle);
}
}
import org.mockito.MockitoAnnotations;
import org.opendaylight.controller.netconf.api.util.NetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactoryListener;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.ServiceReference;
import static org.mockito.Matchers.anySetOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import io.netty.channel.Channel;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory;
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener;
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
+import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshotImpl;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator;
+import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
import org.opendaylight.controller.netconf.notifications.BaseNetconfNotificationListener;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.protocol.framework.NeverReconnectStrategy;
setUpTestInitial();
- final NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ final AggregatedNetconfOperationServiceFactory factoriesListener = new AggregatedNetconfOperationServiceFactory();
+ final NetconfMonitoringService netconfMonitoringService = getNetconfMonitoringService(factoriesListener);
factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore()));
+ factoriesListener.onAddNetconfOperationServiceFactory(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(new NetconfMonitoringOperationService(netconfMonitoringService)));
- for (final NetconfOperationServiceFactory netconfOperationServiceFactory : getAdditionalServiceFactories()) {
+ for (final NetconfOperationServiceFactory netconfOperationServiceFactory : getAdditionalServiceFactories(factoriesListener)) {
factoriesListener.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
}
- serverTcpChannel = startNetconfTcpServer(factoriesListener);
+ serverTcpChannel = startNetconfTcpServer(factoriesListener, netconfMonitoringService);
clientDispatcher = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer());
}
return get;
}
- private Channel startNetconfTcpServer(final NetconfOperationServiceFactoryListenerImpl factoriesListener) throws Exception {
- final NetconfServerDispatcherImpl dispatch = createDispatcher(factoriesListener, getNetconfMonitoringService(), getNotificationProducer());
+ private Channel startNetconfTcpServer(final AggregatedNetconfOperationServiceFactory listener, final NetconfMonitoringService monitoring) throws Exception {
+ final NetconfServerDispatcherImpl dispatch = createDispatcher(listener, monitoring, getNotificationProducer());
final ChannelFuture s;
if(getTcpServerAddress() instanceof LocalAddress) {
return notificationProducer;
}
- protected Iterable<NetconfOperationServiceFactory> getAdditionalServiceFactories() {
+ protected Iterable<NetconfOperationServiceFactory> getAdditionalServiceFactories(final AggregatedNetconfOperationServiceFactory factoriesListener) throws Exception {
return Collections.emptySet();
}
- protected SessionMonitoringService getNetconfMonitoringService() throws Exception {
- final NetconfOperationProvider netconfOperationProvider = mock(NetconfOperationProvider.class);
- final NetconfOperationServiceSnapshotImpl snap = mock(NetconfOperationServiceSnapshotImpl.class);
- doReturn(Collections.<NetconfOperationService>emptySet()).when(snap).getServices();
- doReturn(snap).when(netconfOperationProvider).openSnapshot(anyString());
- return new NetconfMonitoringServiceImpl(netconfOperationProvider);
+ protected NetconfMonitoringService getNetconfMonitoringService(final AggregatedNetconfOperationServiceFactory factoriesListener) throws Exception {
+ return new NetconfMonitoringServiceImpl(factoriesListener);
}
protected abstract SocketAddress getTcpServerAddress();
}
protected NetconfServerDispatcherImpl createDispatcher(
- final NetconfOperationServiceFactoryListenerImpl factoriesListener, final SessionMonitoringService sessionMonitoringService,
+ final AggregatedNetconfOperationServiceFactory factoriesListener, final NetconfMonitoringService sessionMonitoringService,
final DefaultCommitNotificationProducer commitNotifier) {
final SessionIdProvider idProvider = new SessionIdProvider();
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.opendaylight.controller.netconf.util.test.XmlUnitUtil.assertContainsElementWithName;
import static org.opendaylight.controller.netconf.util.test.XmlUnitUtil.assertElementsCount;
import static org.opendaylight.controller.netconf.util.xml.XmlUtil.readXmlToDocument;
import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import java.util.Collections;
import java.util.List;
-import java.util.Set;
import javax.management.InstanceNotFoundException;
import javax.management.Notification;
import javax.management.NotificationListener;
import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
import org.opendaylight.controller.netconf.client.TestingNetconfClient;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshotImpl;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
-import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator;
-import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.w3c.dom.Document;
public static final int PORT = 12026;
private static final InetSocketAddress TCP_ADDRESS = new InetSocketAddress(LOOPBACK_ADDRESS, PORT);
- private NetconfMonitoringServiceImpl netconfMonitoringService;
-
- @Override
- protected void setUpTestInitial() {
- netconfMonitoringService = new NetconfMonitoringServiceImpl(getNetconfOperationProvider());
- }
-
- @Override
- protected SessionMonitoringService getNetconfMonitoringService() throws Exception {
- return netconfMonitoringService;
- }
@Override
protected SocketAddress getTcpServerAddress() {
return TCP_ADDRESS;
}
- @Override
- protected Iterable<NetconfOperationServiceFactory> getAdditionalServiceFactories() {
- return Collections.<NetconfOperationServiceFactory>singletonList(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
- new NetconfMonitoringOperationService(netconfMonitoringService)));
- }
-
@Override
protected DefaultCommitNotificationProducer getNotificationProducer() {
return new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
try (ConfigPersisterNotificationHandler configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(
platformMBeanServer, mockedAggregator)) {
-
try (TestingNetconfClient netconfClient = new TestingNetconfClient("client", getClientDispatcher(), getClientConfiguration(TCP_ADDRESS, 4000))) {
NetconfMessage response = netconfClient.sendMessage(loadGetConfigMessage());
assertContainsElementWithName(response.getDocument(), "modules");
}
notificationVerifier.assertNotificationCount(2);
- notificationVerifier.assertNotificationContent(0, 0, 0, 9);
- notificationVerifier.assertNotificationContent(1, 4, 3, 9);
+ notificationVerifier.assertNotificationContent(0, 0, 0, 8);
+ notificationVerifier.assertNotificationContent(1, 4, 3, 8);
mockedAggregator.assertSnapshotCount(2);
// Capabilities are stripped for persister
return XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/commit.xml");
}
-
- public NetconfOperationProvider getNetconfOperationProvider() {
- final NetconfOperationProvider factoriesListener = mock(NetconfOperationProvider.class);
- final NetconfOperationServiceSnapshotImpl snap = mock(NetconfOperationServiceSnapshotImpl.class);
- final NetconfOperationService service = mock(NetconfOperationService.class);
- final Set<Capability> caps = Sets.newHashSet();
- doReturn(caps).when(service).getCapabilities();
- final Set<NetconfOperationService> services = Sets.newHashSet(service);
- doReturn(services).when(snap).getServices();
- doReturn(snap).when(factoriesListener).openSnapshot(anyString());
-
- return factoriesListener;
- }
-
private static class VerifyingNotificationListener implements NotificationListener {
public List<Notification> notifications = Lists.newArrayList();
package org.opendaylight.controller.netconf.it;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import java.util.List;
import java.util.Set;
import org.junit.Test;
+import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.netconf.client.TestingNetconfClient;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshotImpl;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
+import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
-import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator;
-import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.slf4j.Logger;
import org.w3c.dom.Document;
public class NetconfITMonitoringTest extends AbstractNetconfConfigTest {
public static final InetSocketAddress TCP_ADDRESS = new InetSocketAddress(LOOPBACK_ADDRESS, PORT);
public static final TestingCapability TESTING_CAPABILITY = new TestingCapability();
- private NetconfMonitoringServiceImpl netconfMonitoringService;
-
- @Override
- protected void setUpTestInitial() {
- netconfMonitoringService = new NetconfMonitoringServiceImpl(getNetconfOperationProvider());
- }
-
- @Override
- protected SessionMonitoringService getNetconfMonitoringService() throws Exception {
- return netconfMonitoringService;
- }
-
- @Override
- protected Iterable<NetconfOperationServiceFactory> getAdditionalServiceFactories() {
- return Collections.<NetconfOperationServiceFactory>singletonList(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
- new NetconfMonitoringOperationService(netconfMonitoringService)));
- }
-
@Override
protected InetSocketAddress getTcpServerAddress() {
return TCP_ADDRESS;
}
- static SessionMonitoringService getNetconfMonitoringListenerService(final Logger LOG, final NetconfMonitoringServiceImpl monitor) {
- return new SessionMonitoringService() {
- @Override
- public void onSessionUp(final NetconfManagementSession session) {
- LOG.debug("Management session up {}", session);
- monitor.onSessionUp(session);
- }
-
- @Override
- public void onSessionDown(final NetconfManagementSession session) {
- LOG.debug("Management session down {}", session);
- monitor.onSessionDown(session);
- }
- };
- }
-
@Test
public void testGetResponseFromMonitoring() throws Exception {
try (TestingNetconfClient netconfClient = new TestingNetconfClient("client-monitoring", getClientDispatcher(), getClientConfiguration(TCP_ADDRESS, 10000))) {
assertEquals("Incorrect number of session-id tags in " + XmlUtil.toString(document), i, elementSize);
}
- public static NetconfOperationProvider getNetconfOperationProvider() {
- final NetconfOperationProvider factoriesListener = mock(NetconfOperationProvider.class);
- final NetconfOperationServiceSnapshotImpl snap = mock(NetconfOperationServiceSnapshotImpl.class);
+ public static AggregatedNetconfOperationServiceFactory getNetconfOperationProvider() throws Exception {
+ final AggregatedNetconfOperationServiceFactory factoriesListener = mock(AggregatedNetconfOperationServiceFactory.class);
+ final NetconfOperationService snap = mock(NetconfOperationService.class);
try {
doNothing().when(snap).close();
} catch (final Exception e) {
// not happening
throw new IllegalStateException(e);
}
- final NetconfOperationService service = mock(NetconfOperationService.class);
final Set<Capability> caps = Sets.newHashSet();
caps.add(TESTING_CAPABILITY);
- doReturn(caps).when(service).getCapabilities();
- final Set<NetconfOperationService> services = Sets.newHashSet(service);
- doReturn(services).when(snap).getServices();
- doReturn(snap).when(factoriesListener).openSnapshot(anyString());
+ doReturn(caps).when(factoriesListener).getCapabilities();
+ doReturn(snap).when(factoriesListener).createService(anyString());
+
+ AutoCloseable mock = mock(AutoCloseable.class);
+ doNothing().when(mock).close();
+ doReturn(mock).when(factoriesListener).registerCapabilityListener(any(CapabilityListener.class));
return factoriesListener;
}
+++ /dev/null
-/*
- * 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
- */
-
-package org.opendaylight.controller.netconf.mapping.api;
-
-public interface NetconfOperationProvider {
-
- NetconfOperationServiceSnapshot openSnapshot(String sessionIdForReporting);
-
- public static class NetconfOperationProviderUtil {
-
- public static String getNetconfSessionIdForReporting(long sessionId) {
- return "netconf session id " + sessionId;
- }
-
- }
-
-}
*/
public interface NetconfOperationService extends AutoCloseable {
- /**
- * Get capabilities announced by server hello message.
- */
- Set<Capability> getCapabilities();
-
/**
* Get set of netconf operations that are handled by this service.
*/
package org.opendaylight.controller.netconf.mapping.api;
+import java.util.Set;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
+
/**
* Factory that must be registered in OSGi service registry in order to be used
* by netconf-impl. Responsible for creating per-session instances of
*/
public interface NetconfOperationServiceFactory {
+ /**
+ * Get capabilities supported by current operation service.
+ */
+ Set<Capability> getCapabilities();
+
+ /**
+ * Supported capabilities may change over time, registering a listener allows for push based information retrieval about current notifications
+ */
+ AutoCloseable registerCapabilityListener(CapabilityListener listener);
+
NetconfOperationService createService(String netconfSessionIdForReporting);
}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.impl.osgi;
-
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+package org.opendaylight.controller.netconf.mapping.api;
public interface NetconfOperationServiceFactoryListener {
+++ /dev/null
-/*
- * 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
- */
-
-package org.opendaylight.controller.netconf.mapping.api;
-
-import java.util.Set;
-
-public interface NetconfOperationServiceSnapshot extends AutoCloseable {
- String getNetconfSessionIdForReporting();
-
- Set<NetconfOperationService> getServices();
-
-}
config:java-class "org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory";
}
+ identity netconf-mapper-registry {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactoryListener";
+ }
+
}
\ No newline at end of file
<type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
<name>inmemory-data-broker</name>
</dom-broker>
+ <mapper-aggregator xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">prefix:netconf-mapper-registry</type>
+ <name>mapper-aggregator-registry</name>
+ </mapper-aggregator>
</module>
<module>
<name>netconf-mdsal-server-dispatcher</name>
<mappers xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
<type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">dom:netconf-northbound-mapper</type>
- <name>netconf-mdsal-mapper</name>
+ <name>mapper-aggregator</name>
</mappers>
+ <server-monitor xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">prefix:netconf-server-monitoring</type>
+ <name>server-monitor</name>
+ </server-monitor>
<boss-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
<name>global-boss-group</name>
</timer>
</module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring">prefix:netconf-mdsal-monitoring-mapper</type>
+ <name>netconf-mdsal-monitoring-mapper</name>
+ <server-monitoring xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">prefix:netconf-server-monitoring</type>
+ <name>server-monitor</name>
+ </server-monitoring>
+ <binding-aware-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring">
+ <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-aware-broker>
+ <aggregator xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">prefix:netconf-mapper-registry</type>
+ <name>mapper-aggregator-registry</name>
+ </aggregator>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">prefix:netconf-mapper-aggregator</type>
+ <name>mapper-aggregator</name>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">prefix:netconf-server-monitoring-impl</type>
+ <name>server-monitor</name>
+ <aggregator xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">dom:netconf-northbound-mapper</type>
+ <name>mapper-aggregator</name>
+ </aggregator>
+ </module>
+
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">prefix:netconf-northbound-ssh</type>
<name>netconf-mdsal-ssh-server</name>
</modules>
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">prefix:netconf-server-monitoring</type>
+ <instance>
+ <name>server-monitor</name>
+ <provider>/modules/module[type='netconf-server-monitoring-impl'][name='server-monitor']</provider>
+ </instance>
+ </service>
<service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">prefix:netconf-northbound-mapper</type>
<instance>
<provider>/modules/module[type='netconf-mdsal-mapper'][name='netconf-mdsal-mapper']</provider>
</instance>
</service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">prefix:netconf-northbound-mapper</type>
+ <instance>
+ <name>mapper-aggregator</name>
+ <provider>/modules/module[type='netconf-mapper-aggregator'][name='mapper-aggregator']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">prefix:netconf-mapper-registry</type>
+ <instance>
+ <name>mapper-aggregator-registry</name>
+ <provider>/modules/module[type='netconf-mapper-aggregator'][name='mapper-aggregator']</provider>
+ </instance>
+ </service>
<service>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">prefix:netconf-server-dispatcher</type>
<instance>
</configuration>
<required-capabilities>
<capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper?module=netconf-mdsal-mapper&revision=2015-01-14</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring?module=netconf-mdsal-monitoring&revision=2015-02-18</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh?module=netconf-northbound-ssh&revision=2015-01-14</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl?module=netconf-northbound-impl&revision=2015-01-12</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&revision=2013-12-01</capability>
/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ * 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.netconf.impl.mapping.operations;
+package org.opendaylight.controller.netconf.monitoring;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import java.util.Map;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-public final class DefaultGetSchema extends AbstractLastNetconfOperation {
+public class GetSchema extends AbstractLastNetconfOperation {
public static final String GET_SCHEMA = "get-schema";
public static final String IDENTIFIER = "identifier";
public static final String VERSION = "version";
- private static final Logger LOG = LoggerFactory.getLogger(DefaultGetSchema.class);
- private final CapabilityProvider cap;
+ private static final Logger LOG = LoggerFactory.getLogger(GetSchema.class);
+ private final NetconfMonitoringService cap;
- public DefaultGetSchema(CapabilityProvider cap, String netconfSessionIdForReporting) {
- super(netconfSessionIdForReporting);
+ public GetSchema(final NetconfMonitoringService cap) {
+ super(MonitoringConstants.MODULE_NAME);
this.cap = cap;
}
}
@Override
- protected Element handleWithNoSubsequentOperations(Document document, XmlElement xml) throws NetconfDocumentedException {
- GetSchemaEntry entry;
+ protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement xml) throws NetconfDocumentedException {
+ final GetSchemaEntry entry;
entry = new GetSchemaEntry(xml);
- String schema;
+ final String schema;
try {
schema = cap.getSchemaForCapability(entry.identifier, entry.version);
- } catch (IllegalStateException e) {
- Map<String, String> errorInfo = Maps.newHashMap();
+ } catch (final IllegalStateException e) {
+ final Map<String, String> errorInfo = Maps.newHashMap();
errorInfo.put(entry.identifier, e.getMessage());
LOG.warn("Rpc error: {}", NetconfDocumentedException.ErrorTag.operation_failed, e);
throw new NetconfDocumentedException(e.getMessage(), NetconfDocumentedException.ErrorType.application,
NetconfDocumentedException.ErrorSeverity.error, errorInfo);
}
- Element getSchemaResult;
+ final Element getSchemaResult;
getSchemaResult = XmlUtil.createTextElement(document, XmlNetconfConstants.DATA_KEY, schema,
Optional.of(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING));
LOG.trace("{} operation successful", GET_SCHEMA);
private final String identifier;
private final Optional<String> version;
- GetSchemaEntry(XmlElement getSchemaElement) throws NetconfDocumentedException {
+ GetSchemaEntry(final XmlElement getSchemaElement) throws NetconfDocumentedException {
getSchemaElement.checkName(GET_SCHEMA);
getSchemaElement.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING);
XmlElement identifierElement = null;
try {
identifierElement = getSchemaElement.getOnlyChildElementWithSameNamespace(IDENTIFIER);
- } catch (MissingNameSpaceException e) {
+ } catch (final MissingNameSpaceException e) {
LOG.trace("Can't get identifier element as only child element with same namespace due to ",e);
throw NetconfDocumentedException.wrap(e);
}
identifier = identifierElement.getTextContent();
- Optional<XmlElement> versionElement = getSchemaElement
+ final Optional<XmlElement> versionElement = getSchemaElement
.getOnlyChildElementWithSameNamespaceOptionally(VERSION);
if (versionElement.isPresent()) {
version = Optional.of(versionElement.get().getTextContent());
*/
package org.opendaylight.controller.netconf.monitoring.osgi;
+import java.util.Collections;
+import java.util.Set;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.osgi.framework.BundleActivator;
if(monitor!=null) {
try {
monitor.close();
- } catch (Exception e) {
+ } catch (final Exception e) {
LOG.warn("Ignoring exception while closing {}", monitor, e);
}
}
}
- public static class NetconfMonitoringOperationServiceFactory implements NetconfOperationServiceFactory {
+ public static class NetconfMonitoringOperationServiceFactory implements NetconfOperationServiceFactory, AutoCloseable {
+
private final NetconfMonitoringOperationService operationService;
- public NetconfMonitoringOperationServiceFactory(NetconfMonitoringOperationService operationService) {
+ private static final AutoCloseable AUTO_CLOSEABLE = new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+ };
+
+ public NetconfMonitoringOperationServiceFactory(final NetconfMonitoringOperationService operationService) {
this.operationService = operationService;
}
@Override
- public NetconfOperationService createService(String netconfSessionIdForReporting) {
+ public NetconfOperationService createService(final String netconfSessionIdForReporting) {
return operationService;
}
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ return AUTO_CLOSEABLE;
+ }
+
+ @Override
+ public void close() {}
}
}
*/
package org.opendaylight.controller.netconf.monitoring.osgi;
-import com.google.common.base.Optional;
import com.google.common.collect.Sets;
-import java.util.Collection;
-import java.util.Collections;
import java.util.Set;
import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.monitoring.Get;
-import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
+import org.opendaylight.controller.netconf.monitoring.GetSchema;
public class NetconfMonitoringOperationService implements NetconfOperationService {
- private static final Set<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
-
- @Override
- public String getCapabilityUri() {
- return MonitoringConstants.URI;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.of(MonitoringConstants.NAMESPACE);
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.of(MonitoringConstants.MODULE_NAME);
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.of(MonitoringConstants.MODULE_REVISION);
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.absent();
- }
-
- @Override
- public Collection<String> getLocation() {
- return Collections.emptyList();
- }
- });
-
private final NetconfMonitoringService monitor;
public NetconfMonitoringOperationService(final NetconfMonitoringService monitor) {
this.monitor = monitor;
}
- @Override
- public Set<Capability> getCapabilities() {
- return CAPABILITIES;
- }
-
@Override
public Set<NetconfOperation> getNetconfOperations() {
- return Sets.<NetconfOperation>newHashSet(new Get(monitor));
+ return Sets.<NetconfOperation>newHashSet(new Get(monitor), new GetSchema(monitor));
}
@Override
private static final Logger LOG = LoggerFactory.getLogger(NetconfMonitoringServiceTracker.class);
private ServiceRegistration<NetconfOperationServiceFactory> reg;
+ private NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory factory;
NetconfMonitoringServiceTracker(final BundleContext context) {
super(context, NetconfMonitoringService.class, null);
final NetconfMonitoringOperationService operationService = new NetconfMonitoringOperationService(
netconfMonitoringService);
- final NetconfOperationServiceFactory factory = new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+ factory = new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
operationService);
Dictionary<String, String> properties = new Hashtable<>();
LOG.warn("Ignoring exception while unregistering {}", reg, e);
}
}
+ if(factory!=null) {
+ factory.close();
+ }
}
}
/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * 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.netconf.impl.mapping.operations;
+package org.opendaylight.controller.netconf.monitoring;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
-public class DefaultGetSchemaTest {
+public class GetSchemaTest {
- private CapabilityProvider cap;
+
+ private NetconfMonitoringService cap;
private Document doc;
private String getSchema;
@Before
public void setUp() throws Exception {
- cap = mock(CapabilityProvider.class);
+ cap = mock(NetconfMonitoringService.class);
doc = XmlUtil.newDocument();
getSchema = "<get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
" <identifier>threadpool-api</identifier>\n" +
@Test(expected = NetconfDocumentedException.class)
public void testDefaultGetSchema() throws Exception {
- DefaultGetSchema schema = new DefaultGetSchema(cap, "");
+ GetSchema schema = new GetSchema(cap);
doThrow(IllegalStateException.class).when(cap).getSchemaForCapability(anyString(), any(Optional.class));
schema.handleWithNoSubsequentOperations(doc, XmlElement.fromDomElement(XmlUtil.readXmlToElement(getSchema)));
}
@Test
public void handleWithNoSubsequentOperations() throws Exception {
- DefaultGetSchema schema = new DefaultGetSchema(cap, "");
+ GetSchema schema = new GetSchema(cap);
doReturn("").when(cap).getSchemaForCapability(anyString(), any(Optional.class));
assertNotNull(schema.handleWithNoSubsequentOperations(doc, XmlElement.fromDomElement(XmlUtil.readXmlToElement(getSchema))));
}
-}
+
+}
\ No newline at end of file
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
-import com.google.common.base.Optional;
-import java.util.Collections;
import org.junit.Test;
import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
-import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
public class NetconfMonitoringOperationServiceTest {
@Test
public void testGetters() throws Exception {
NetconfMonitoringService monitor = mock(NetconfMonitoringService.class);
NetconfMonitoringOperationService service = new NetconfMonitoringOperationService(monitor);
+ NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory serviceFactory = new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(service);
- assertEquals(1, service.getNetconfOperations().size());
+ assertEquals(2, service.getNetconfOperations().size());
- assertEquals(Optional.<String>absent(), service.getCapabilities().iterator().next().getCapabilitySchema());
- assertEquals(Collections.<String>emptyList(), service.getCapabilities().iterator().next().getLocation());
- assertEquals(Optional.of(MonitoringConstants.MODULE_REVISION), service.getCapabilities().iterator().next().getRevision());
- assertEquals(Optional.of(MonitoringConstants.MODULE_NAME), service.getCapabilities().iterator().next().getModuleName());
- assertEquals(Optional.of(MonitoringConstants.NAMESPACE), service.getCapabilities().iterator().next().getModuleNamespace());
- assertEquals(MonitoringConstants.URI, service.getCapabilities().iterator().next().getCapabilityUri());
}
}
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import com.google.common.base.Optional;
import com.google.common.collect.Lists;
+import java.util.Set;
import org.hamcrest.CoreMatchers;
import org.junit.Test;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Transport;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Capabilities;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
final NetconfMonitoringService service = new NetconfMonitoringService() {
+ @Override
+ public void onSessionUp(final NetconfManagementSession session) {
+
+ }
+
+ @Override
+ public void onSessionDown(final NetconfManagementSession session) {
+
+ }
+
+ @Override
+ public void onCapabilitiesAdded(final Set<Capability> addedCaps) {
+
+ }
+
+ @Override
+ public void onCapabilitiesRemoved(final Set<Capability> addedCaps) {
+
+ }
+
@Override
public Sessions getSessions() {
return new SessionsBuilder().setSession(Lists.newArrayList(getMockSession(NetconfTcp.class), getMockSession(NetconfSsh.class))).build();
public Schemas getSchemas() {
return new SchemasBuilder().setSchema(Lists.newArrayList(getMockSchema("id", "v1", Yang.class), getMockSchema("id2", "", Yang.class))).build();
}
+
+ @Override
+ public String getSchemaForCapability(final String moduleName, final Optional<String> revision) {
+ return null;
+ }
+
+ @Override
+ public Capabilities getCapabilities() {
+ return null;
+ }
+
+ @Override
+ public AutoCloseable registerListener(final MonitoringListener listener) {
+ return new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+ };
+ }
};
final NetconfState model = new NetconfState(service);
final String xml = XmlUtil.toString(new JaxBSerializer().toXml(model)).replaceAll("\\s", "");
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Set;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.netconf.api.util.NetconfConstants;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
final NetconfOperationServiceFactory netconfOperationServiceFactory = new NetconfOperationServiceFactory() {
+ private final Set<Capability> capabilities = Collections.<Capability>singleton(new NotificationsCapability());
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return capabilities;
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ listener.onCapabilitiesAdded(capabilities);
+ return new AutoCloseable() {
+ @Override
+ public void close() {
+ listener.onCapabilitiesRemoved(capabilities);
+ }
+ };
+ }
+
@Override
public NetconfOperationService createService(final String netconfSessionIdForReporting) {
return new NetconfOperationService() {
private final CreateSubscription createSubscription = new CreateSubscription(netconfSessionIdForReporting, netconfNotificationManager);
- @Override
- public Set<Capability> getCapabilities() {
- return Collections.<Capability>singleton(new NotificationsCapability());
- }
@Override
public Set<NetconfOperation> getNetconfOperations() {
}
};
- Dictionary<String, String> properties = new Hashtable<>();
+ final Dictionary<String, String> properties = new Hashtable<>();
properties.put(NetconfConstants.SERVICE_NAME, NetconfConstants.NETCONF_MONITORING);
operationaServiceRegistration = context.registerService(NetconfOperationServiceFactory.class, netconfOperationServiceFactory, properties);
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
-import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
+import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
-import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
-import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator;
import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
import org.opendaylight.controller.netconf.ssh.SshProxyServer;
import org.opendaylight.controller.netconf.ssh.SshProxyServerConfiguration;
final SessionIdProvider idProvider = new SessionIdProvider();
+
+ final AggregatedNetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = new AggregatedNetconfOperationServiceFactory();
final SimulatedOperationProvider simulatedOperationProvider = new SimulatedOperationProvider(idProvider, capabilities, notificationsFile);
- final NetconfMonitoringOperationService monitoringService = new NetconfMonitoringOperationService(new NetconfMonitoringServiceImpl(simulatedOperationProvider));
- simulatedOperationProvider.addService(monitoringService);
+
+ final NetconfMonitoringService monitoringService1 = new NetconfMonitoringServiceImpl(aggregatedNetconfOperationServiceFactory);
+ final NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory monitoringService =
+ new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(new NetconfMonitoringOperationService(monitoringService1));
+ aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(simulatedOperationProvider);
+ aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(monitoringService);
final DefaultCommitNotificationProducer commitNotifier = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
: Sets.newHashSet(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0, XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1);
final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
- hashedWheelTimer, simulatedOperationProvider, idProvider, generateConfigsTimeout, commitNotifier, new LoggingMonitoringService(), serverCapabilities);
+ hashedWheelTimer, aggregatedNetconfOperationServiceFactory, idProvider, generateConfigsTimeout, commitNotifier, monitoringService1, serverCapabilities);
final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
serverNegotiatorFactory);
// close Everything
}
- private static class SimulatedOperationProvider implements NetconfOperationProvider {
- private final SessionIdProvider idProvider;
- private final Set<NetconfOperationService> netconfOperationServices;
+ private static class SimulatedOperationProvider implements NetconfOperationServiceFactory {
+ private final Set<Capability> caps;
+ private final SimulatedOperationService simulatedOperationService;
public SimulatedOperationProvider(final SessionIdProvider idProvider, final Set<Capability> caps, final Optional<File> notificationsFile) {
- this.idProvider = idProvider;
- final SimulatedOperationService simulatedOperationService = new SimulatedOperationService(caps, idProvider.getCurrentSessionId(), notificationsFile);
- this.netconfOperationServices = Sets.<NetconfOperationService>newHashSet(simulatedOperationService);
+ this.caps = caps;
+ simulatedOperationService = new SimulatedOperationService(idProvider.getCurrentSessionId(), notificationsFile);
}
@Override
- public NetconfOperationServiceSnapshot openSnapshot(final String sessionIdForReporting) {
- return new SimulatedServiceSnapshot(idProvider, netconfOperationServices);
+ public Set<Capability> getCapabilities() {
+ return caps;
}
- public void addService(final NetconfOperationService monitoringService) {
- netconfOperationServices.add(monitoringService);
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ return new AutoCloseable() {
+ @Override
+ public void close() throws Exception {}
+ };
}
- private static class SimulatedServiceSnapshot implements NetconfOperationServiceSnapshot {
- private final SessionIdProvider idProvider;
- private final Set<NetconfOperationService> netconfOperationServices;
-
- public SimulatedServiceSnapshot(final SessionIdProvider idProvider, final Set<NetconfOperationService> netconfOperationServices) {
- this.idProvider = idProvider;
- this.netconfOperationServices = netconfOperationServices;
- }
-
- @Override
- public String getNetconfSessionIdForReporting() {
- return String.valueOf(idProvider.getCurrentSessionId());
- }
-
- @Override
- public Set<NetconfOperationService> getServices() {
- return netconfOperationServices;
- }
-
- @Override
- public void close() throws Exception {}
+ @Override
+ public NetconfOperationService createService(final String netconfSessionIdForReporting) {
+ return simulatedOperationService;
}
static class SimulatedOperationService implements NetconfOperationService {
- private final Set<Capability> capabilities;
private final long currentSessionId;
private final Optional<File> notificationsFile;
- public SimulatedOperationService(final Set<Capability> capabilities, final long currentSessionId, final Optional<File> notificationsFile) {
- this.capabilities = capabilities;
+ public SimulatedOperationService(final long currentSessionId, final Optional<File> notificationsFile) {
this.currentSessionId = currentSessionId;
this.notificationsFile = notificationsFile;
}
- @Override
- public Set<Capability> getCapabilities() {
- return capabilities;
- }
-
@Override
public Set<NetconfOperation> getNetconfOperations() {
final DataList storage = new DataList();
}
}
- private class LoggingMonitoringService implements SessionMonitoringService {
- @Override
- public void onSessionUp(final NetconfManagementSession session) {
- LOG.debug("Session {} established", session);
- }
-
- @Override
- public void onSessionDown(final NetconfManagementSession session) {
- LOG.debug("Session {} down", session);
- }
- }
-
}
<module>netconf-impl</module>
<module>config-netconf-connector</module>
<module>mdsal-netconf-connector</module>
+ <module>mdsal-netconf-monitoring</module>
<module>netconf-util</module>
<module>netconf-netty-util</module>
<module>config-persister-impl</module>